1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/media/media_stream_dispatcher.h"
7 #include "base/logging.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "content/common/media/media_stream_messages.h"
10 #include "content/renderer/media/media_stream_dispatcher_eventhandler.h"
11 #include "content/renderer/render_thread_impl.h"
12 #include "content/renderer/render_view_impl.h"
13 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
20 bool RemoveStreamDeviceFromArray(const StreamDeviceInfo device_info
,
21 StreamDeviceInfoArray
* array
) {
22 for (StreamDeviceInfoArray::iterator device_it
= array
->begin();
23 device_it
!= array
->end(); ++device_it
) {
24 if (StreamDeviceInfo::IsEqual(*device_it
, device_info
)) {
25 array
->erase(device_it
);
34 // A request is identified by pair (request_id, handler), or ipc_request.
35 // There could be multiple clients making requests and each has its own
36 // request_id sequence.
37 // The ipc_request is garanteed to be unique when it's created in
38 // MediaStreamDispatcher.
39 struct MediaStreamDispatcher::Request
{
40 Request(const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& handler
,
44 request_id(request_id
),
45 ipc_request(ipc_request
) {
49 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& handler1
) {
50 return (request_id1
== request_id
&& handler1
.get() == handler
.get());
52 base::WeakPtr
<MediaStreamDispatcherEventHandler
> handler
;
57 struct MediaStreamDispatcher::Stream
{
60 base::WeakPtr
<MediaStreamDispatcherEventHandler
> handler
;
61 StreamDeviceInfoArray audio_array
;
62 StreamDeviceInfoArray video_array
;
65 MediaStreamDispatcher::MediaStreamDispatcher(RenderViewImpl
* render_view
)
66 : RenderViewObserver(render_view
),
67 main_loop_(base::MessageLoopProxy::current()),
71 MediaStreamDispatcher::~MediaStreamDispatcher() {}
73 void MediaStreamDispatcher::GenerateStream(
75 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
,
76 const StreamOptions
& components
,
77 const GURL
& security_origin
) {
78 DCHECK(main_loop_
->BelongsToCurrentThread());
79 DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id
<< ")";
81 requests_
.push_back(Request(event_handler
, request_id
, next_ipc_id_
));
82 Send(new MediaStreamHostMsg_GenerateStream(
83 routing_id(), next_ipc_id_
++, components
, security_origin
,
84 blink::WebUserGestureIndicator::isProcessingUserGesture()));
87 void MediaStreamDispatcher::CancelGenerateStream(
89 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
) {
90 DCHECK(main_loop_
->BelongsToCurrentThread());
91 DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream"
92 << ", {request_id = " << request_id
<< "}";
94 RequestList::iterator it
= requests_
.begin();
95 for (; it
!= requests_
.end(); ++it
) {
96 if (it
->IsThisRequest(request_id
, event_handler
)) {
97 int ipc_request
= it
->ipc_request
;
99 Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(),
106 void MediaStreamDispatcher::StopStreamDevice(
107 const StreamDeviceInfo
& device_info
) {
108 DVLOG(1) << "MediaStreamDispatcher::StopStreamDevice"
109 << ", {device_id = " << device_info
.device
.id
<< "}";
110 // Remove |device_info| from all streams in |label_stream_map_|.
111 bool device_found
= false;
112 LabelStreamMap::iterator stream_it
= label_stream_map_
.begin();
113 while (stream_it
!= label_stream_map_
.end()) {
114 StreamDeviceInfoArray
& audio_array
= stream_it
->second
.audio_array
;
115 StreamDeviceInfoArray
& video_array
= stream_it
->second
.video_array
;
117 if (RemoveStreamDeviceFromArray(device_info
, &audio_array
) ||
118 RemoveStreamDeviceFromArray(device_info
, &video_array
)) {
120 if (audio_array
.empty() && video_array
.empty()) {
121 label_stream_map_
.erase(stream_it
++);
127 DCHECK(device_found
);
129 Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(),
130 device_info
.device
.id
));
133 void MediaStreamDispatcher::EnumerateDevices(
135 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
,
136 MediaStreamType type
,
137 const GURL
& security_origin
) {
138 DCHECK(main_loop_
->BelongsToCurrentThread());
139 DCHECK(type
== MEDIA_DEVICE_AUDIO_CAPTURE
||
140 type
== MEDIA_DEVICE_VIDEO_CAPTURE
||
141 type
== MEDIA_DEVICE_AUDIO_OUTPUT
);
142 DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices("
143 << request_id
<< ")";
145 for (RequestList::iterator it
= requests_
.begin(); it
!= requests_
.end();
147 DCHECK(!it
->IsThisRequest(request_id
, event_handler
));
150 requests_
.push_back(Request(event_handler
, request_id
, next_ipc_id_
));
151 Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(),
157 void MediaStreamDispatcher::StopEnumerateDevices(
159 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
) {
160 DCHECK(main_loop_
->BelongsToCurrentThread());
161 DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices("
162 << request_id
<< ")";
163 for (RequestList::iterator it
= requests_
.begin(); it
!= requests_
.end();
165 if (it
->IsThisRequest(request_id
, event_handler
)) {
166 Send(new MediaStreamHostMsg_CancelEnumerateDevices(routing_id(),
174 void MediaStreamDispatcher::OpenDevice(
176 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
,
177 const std::string
& device_id
,
178 MediaStreamType type
,
179 const GURL
& security_origin
) {
180 DCHECK(main_loop_
->BelongsToCurrentThread());
181 DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id
<< ")";
183 requests_
.push_back(Request(event_handler
, request_id
, next_ipc_id_
));
184 Send(new MediaStreamHostMsg_OpenDevice(routing_id(),
191 void MediaStreamDispatcher::CancelOpenDevice(
193 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
) {
194 CancelGenerateStream(request_id
, event_handler
);
197 void MediaStreamDispatcher::CloseDevice(const std::string
& label
) {
198 DCHECK(main_loop_
->BelongsToCurrentThread());
199 DCHECK(!label
.empty());
200 DVLOG(1) << "MediaStreamDispatcher::CloseDevice"
201 << ", {label = " << label
<< "}";
203 LabelStreamMap::iterator it
= label_stream_map_
.find(label
);
204 if (it
== label_stream_map_
.end())
206 label_stream_map_
.erase(it
);
208 Send(new MediaStreamHostMsg_CloseDevice(routing_id(), label
));
211 bool MediaStreamDispatcher::Send(IPC::Message
* message
) {
212 if (!RenderThread::Get()) {
217 return RenderThread::Get()->Send(message
);
220 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message
& message
) {
222 IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher
, message
)
223 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated
,
225 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed
,
226 OnStreamGenerationFailed
)
227 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped
,
229 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated
,
231 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened
,
233 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed
,
235 IPC_MESSAGE_UNHANDLED(handled
= false)
236 IPC_END_MESSAGE_MAP()
240 void MediaStreamDispatcher::OnStreamGenerated(
242 const std::string
& label
,
243 const StreamDeviceInfoArray
& audio_array
,
244 const StreamDeviceInfoArray
& video_array
) {
245 DCHECK(main_loop_
->BelongsToCurrentThread());
247 for (RequestList::iterator it
= requests_
.begin();
248 it
!= requests_
.end(); ++it
) {
249 Request
& request
= *it
;
250 if (request
.ipc_request
== request_id
) {
252 new_stream
.handler
= request
.handler
;
253 new_stream
.audio_array
= audio_array
;
254 new_stream
.video_array
= video_array
;
255 label_stream_map_
[label
] = new_stream
;
256 if (request
.handler
.get()) {
257 request
.handler
->OnStreamGenerated(
258 request
.request_id
, label
, audio_array
, video_array
);
259 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated("
260 << request
.request_id
<< ", " << label
<< ")";
268 void MediaStreamDispatcher::OnStreamGenerationFailed(
270 content::MediaStreamRequestResult result
) {
271 DCHECK(main_loop_
->BelongsToCurrentThread());
272 for (RequestList::iterator it
= requests_
.begin();
273 it
!= requests_
.end(); ++it
) {
274 Request
& request
= *it
;
275 if (request
.ipc_request
== request_id
) {
276 if (request
.handler
.get()) {
277 request
.handler
->OnStreamGenerationFailed(request
.request_id
, result
);
278 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed("
279 << request
.request_id
<< ")\n";
287 void MediaStreamDispatcher::OnDeviceStopped(
288 const std::string
& label
,
289 const StreamDeviceInfo
& device_info
) {
290 DCHECK(main_loop_
->BelongsToCurrentThread());
291 DVLOG(1) << "MediaStreamDispatcher::OnDeviceStopped("
292 << "{label = " << label
<< "})"
293 << ", {device_id = " << device_info
.device
.id
<< "})";
295 LabelStreamMap::iterator it
= label_stream_map_
.find(label
);
296 if (it
== label_stream_map_
.end()) {
297 // This can happen if a user happen stop a the device from JS at the same
298 // time as the underlying media device is unplugged from the system.
301 Stream
* stream
= &it
->second
;
302 if (IsAudioInputMediaType(device_info
.device
.type
))
303 RemoveStreamDeviceFromArray(device_info
, &stream
->audio_array
);
305 RemoveStreamDeviceFromArray(device_info
, &stream
->video_array
);
307 if (stream
->handler
.get())
308 stream
->handler
->OnDeviceStopped(label
, device_info
);
310 if (stream
->audio_array
.empty() && stream
->video_array
.empty())
311 label_stream_map_
.erase(it
);
314 void MediaStreamDispatcher::OnDevicesEnumerated(
316 const StreamDeviceInfoArray
& device_array
) {
317 DCHECK(main_loop_
->BelongsToCurrentThread());
318 DCHECK_GE(request_id
, 0);
320 for (RequestList::iterator it
= requests_
.begin(); it
!= requests_
.end();
322 if (it
->ipc_request
== request_id
&& it
->handler
.get()) {
323 it
->handler
->OnDevicesEnumerated(it
->request_id
, device_array
);
329 void MediaStreamDispatcher::OnDeviceOpened(
331 const std::string
& label
,
332 const StreamDeviceInfo
& device_info
) {
333 DCHECK(main_loop_
->BelongsToCurrentThread());
334 for (RequestList::iterator it
= requests_
.begin();
335 it
!= requests_
.end(); ++it
) {
336 Request
& request
= *it
;
337 if (request
.ipc_request
== request_id
) {
339 new_stream
.handler
= request
.handler
;
340 if (IsAudioInputMediaType(device_info
.device
.type
)) {
341 new_stream
.audio_array
.push_back(device_info
);
342 } else if (IsVideoMediaType(device_info
.device
.type
)) {
343 new_stream
.video_array
.push_back(device_info
);
347 label_stream_map_
[label
] = new_stream
;
348 if (request
.handler
.get()) {
349 request
.handler
->OnDeviceOpened(request
.request_id
, label
, device_info
);
350 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened("
351 << request
.request_id
<< ", " << label
<< ")";
359 void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id
) {
360 DCHECK(main_loop_
->BelongsToCurrentThread());
361 for (RequestList::iterator it
= requests_
.begin();
362 it
!= requests_
.end(); ++it
) {
363 Request
& request
= *it
;
364 if (request
.ipc_request
== request_id
) {
365 if (request
.handler
.get()) {
366 request
.handler
->OnDeviceOpenFailed(request
.request_id
);
367 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed("
368 << request
.request_id
<< ")\n";
376 int MediaStreamDispatcher::audio_session_id(const std::string
& label
,
378 LabelStreamMap::iterator it
= label_stream_map_
.find(label
);
379 if (it
== label_stream_map_
.end() ||
380 it
->second
.audio_array
.size() <= static_cast<size_t>(index
)) {
381 return StreamDeviceInfo::kNoId
;
383 return it
->second
.audio_array
[index
].session_id
;
386 bool MediaStreamDispatcher::IsStream(const std::string
& label
) {
387 return label_stream_map_
.find(label
) != label_stream_map_
.end();
390 int MediaStreamDispatcher::video_session_id(const std::string
& label
,
392 LabelStreamMap::iterator it
= label_stream_map_
.find(label
);
393 if (it
== label_stream_map_
.end() ||
394 it
->second
.video_array
.size() <= static_cast<size_t>(index
)) {
395 return StreamDeviceInfo::kNoId
;
397 return it
->second
.video_array
[index
].session_id
;
400 } // namespace content