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 "content/common/media/media_stream_messages.h"
9 #include "content/renderer/media/media_stream_dispatcher_eventhandler.h"
10 #include "content/renderer/render_thread_impl.h"
11 #include "media/audio/audio_parameters.h"
12 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
19 bool RemoveStreamDeviceFromArray(const StreamDeviceInfo device_info
,
20 StreamDeviceInfoArray
* array
) {
21 for (StreamDeviceInfoArray::iterator device_it
= array
->begin();
22 device_it
!= array
->end(); ++device_it
) {
23 if (StreamDeviceInfo::IsEqual(*device_it
, device_info
)) {
24 array
->erase(device_it
);
33 // A request is identified by pair (request_id, handler), or ipc_request.
34 // There could be multiple clients making requests and each has its own
35 // request_id sequence.
36 // The ipc_request is garanteed to be unique when it's created in
37 // MediaStreamDispatcher.
38 struct MediaStreamDispatcher::Request
{
39 Request(const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& handler
,
43 request_id(request_id
),
44 ipc_request(ipc_request
) {
48 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& handler1
) {
49 return (request_id1
== request_id
&& handler1
.get() == handler
.get());
51 base::WeakPtr
<MediaStreamDispatcherEventHandler
> handler
;
56 struct MediaStreamDispatcher::Stream
{
59 base::WeakPtr
<MediaStreamDispatcherEventHandler
> handler
;
60 StreamDeviceInfoArray audio_array
;
61 StreamDeviceInfoArray video_array
;
64 MediaStreamDispatcher::MediaStreamDispatcher(RenderFrame
* render_frame
)
65 : RenderFrameObserver(render_frame
),
69 MediaStreamDispatcher::~MediaStreamDispatcher() {}
71 void MediaStreamDispatcher::GenerateStream(
73 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
,
74 const StreamOptions
& components
,
75 const GURL
& security_origin
) {
76 DCHECK(thread_checker_
.CalledOnValidThread());
77 DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id
<< ")";
79 requests_
.push_back(Request(event_handler
, request_id
, next_ipc_id_
));
80 Send(new MediaStreamHostMsg_GenerateStream(
81 routing_id(), next_ipc_id_
++, components
, security_origin
,
82 blink::WebUserGestureIndicator::isProcessingUserGesture()));
85 void MediaStreamDispatcher::CancelGenerateStream(
87 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
) {
88 DCHECK(thread_checker_
.CalledOnValidThread());
89 DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream"
90 << ", {request_id = " << request_id
<< "}";
92 RequestList::iterator it
= requests_
.begin();
93 for (; it
!= requests_
.end(); ++it
) {
94 if (it
->IsThisRequest(request_id
, event_handler
)) {
95 int ipc_request
= it
->ipc_request
;
97 Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(),
104 void MediaStreamDispatcher::StopStreamDevice(
105 const StreamDeviceInfo
& device_info
) {
106 DCHECK(thread_checker_
.CalledOnValidThread());
107 DVLOG(1) << "MediaStreamDispatcher::StopStreamDevice"
108 << ", {device_id = " << device_info
.device
.id
<< "}";
109 // Remove |device_info| from all streams in |label_stream_map_|.
110 bool device_found
= false;
111 LabelStreamMap::iterator stream_it
= label_stream_map_
.begin();
112 while (stream_it
!= label_stream_map_
.end()) {
113 StreamDeviceInfoArray
& audio_array
= stream_it
->second
.audio_array
;
114 StreamDeviceInfoArray
& video_array
= stream_it
->second
.video_array
;
116 if (RemoveStreamDeviceFromArray(device_info
, &audio_array
) ||
117 RemoveStreamDeviceFromArray(device_info
, &video_array
)) {
119 if (audio_array
.empty() && video_array
.empty()) {
120 label_stream_map_
.erase(stream_it
++);
126 DCHECK(device_found
);
128 Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(),
129 device_info
.device
.id
));
132 void MediaStreamDispatcher::EnumerateDevices(
134 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
,
135 MediaStreamType type
,
136 const GURL
& security_origin
) {
137 DCHECK(thread_checker_
.CalledOnValidThread());
138 DCHECK(type
== MEDIA_DEVICE_AUDIO_CAPTURE
||
139 type
== MEDIA_DEVICE_VIDEO_CAPTURE
||
140 type
== MEDIA_DEVICE_AUDIO_OUTPUT
);
141 DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices("
142 << request_id
<< ")";
144 for (RequestList::iterator it
= requests_
.begin(); it
!= requests_
.end();
146 DCHECK(!it
->IsThisRequest(request_id
, event_handler
));
149 requests_
.push_back(Request(event_handler
, request_id
, next_ipc_id_
));
150 Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(),
156 void MediaStreamDispatcher::StopEnumerateDevices(
158 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
) {
159 DCHECK(thread_checker_
.CalledOnValidThread());
160 DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices("
161 << request_id
<< ")";
162 for (RequestList::iterator it
= requests_
.begin(); it
!= requests_
.end();
164 if (it
->IsThisRequest(request_id
, event_handler
)) {
165 Send(new MediaStreamHostMsg_CancelEnumerateDevices(routing_id(),
173 void MediaStreamDispatcher::OpenDevice(
175 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
,
176 const std::string
& device_id
,
177 MediaStreamType type
,
178 const GURL
& security_origin
) {
179 DCHECK(thread_checker_
.CalledOnValidThread());
180 DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id
<< ")";
182 requests_
.push_back(Request(event_handler
, request_id
, next_ipc_id_
));
183 Send(new MediaStreamHostMsg_OpenDevice(routing_id(),
190 void MediaStreamDispatcher::CancelOpenDevice(
192 const base::WeakPtr
<MediaStreamDispatcherEventHandler
>& event_handler
) {
193 CancelGenerateStream(request_id
, event_handler
);
196 void MediaStreamDispatcher::CloseDevice(const std::string
& label
) {
197 DCHECK(thread_checker_
.CalledOnValidThread());
198 DCHECK(!label
.empty());
199 DVLOG(1) << "MediaStreamDispatcher::CloseDevice"
200 << ", {label = " << label
<< "}";
202 LabelStreamMap::iterator it
= label_stream_map_
.find(label
);
203 if (it
== label_stream_map_
.end())
205 label_stream_map_
.erase(it
);
207 Send(new MediaStreamHostMsg_CloseDevice(routing_id(), label
));
210 void MediaStreamDispatcher::OnDestruct() {
211 // Do not self-destruct. UserMediaClientImpl owns |this|.
214 bool MediaStreamDispatcher::Send(IPC::Message
* message
) {
215 if (!RenderThread::Get()) {
220 return RenderThread::Get()->Send(message
);
223 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message
& message
) {
225 IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher
, message
)
226 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated
,
228 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed
,
229 OnStreamGenerationFailed
)
230 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped
,
232 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated
,
234 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened
,
236 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed
,
238 IPC_MESSAGE_UNHANDLED(handled
= false)
239 IPC_END_MESSAGE_MAP()
243 void MediaStreamDispatcher::OnStreamGenerated(
245 const std::string
& label
,
246 const StreamDeviceInfoArray
& audio_array
,
247 const StreamDeviceInfoArray
& video_array
) {
248 DCHECK(thread_checker_
.CalledOnValidThread());
250 for (RequestList::iterator it
= requests_
.begin();
251 it
!= requests_
.end(); ++it
) {
252 Request
& request
= *it
;
253 if (request
.ipc_request
== request_id
) {
255 new_stream
.handler
= request
.handler
;
256 new_stream
.audio_array
= audio_array
;
257 new_stream
.video_array
= video_array
;
258 label_stream_map_
[label
] = new_stream
;
259 if (request
.handler
.get()) {
260 request
.handler
->OnStreamGenerated(
261 request
.request_id
, label
, audio_array
, video_array
);
262 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated("
263 << request
.request_id
<< ", " << label
<< ")";
271 void MediaStreamDispatcher::OnStreamGenerationFailed(
273 content::MediaStreamRequestResult result
) {
274 DCHECK(thread_checker_
.CalledOnValidThread());
275 for (RequestList::iterator it
= requests_
.begin();
276 it
!= requests_
.end(); ++it
) {
277 Request
& request
= *it
;
278 if (request
.ipc_request
== request_id
) {
279 if (request
.handler
.get()) {
280 request
.handler
->OnStreamGenerationFailed(request
.request_id
, result
);
281 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed("
282 << request
.request_id
<< ")\n";
290 void MediaStreamDispatcher::OnDeviceStopped(
291 const std::string
& label
,
292 const StreamDeviceInfo
& device_info
) {
293 DCHECK(thread_checker_
.CalledOnValidThread());
294 DVLOG(1) << "MediaStreamDispatcher::OnDeviceStopped("
295 << "{label = " << label
<< "})"
296 << ", {device_id = " << device_info
.device
.id
<< "})";
298 LabelStreamMap::iterator it
= label_stream_map_
.find(label
);
299 if (it
== label_stream_map_
.end()) {
300 // This can happen if a user happen stop a the device from JS at the same
301 // time as the underlying media device is unplugged from the system.
304 Stream
* stream
= &it
->second
;
305 if (IsAudioInputMediaType(device_info
.device
.type
))
306 RemoveStreamDeviceFromArray(device_info
, &stream
->audio_array
);
308 RemoveStreamDeviceFromArray(device_info
, &stream
->video_array
);
310 if (stream
->handler
.get())
311 stream
->handler
->OnDeviceStopped(label
, device_info
);
313 if (stream
->audio_array
.empty() && stream
->video_array
.empty())
314 label_stream_map_
.erase(it
);
317 void MediaStreamDispatcher::OnDevicesEnumerated(
319 const StreamDeviceInfoArray
& device_array
) {
320 DCHECK(thread_checker_
.CalledOnValidThread());
321 DCHECK_GE(request_id
, 0);
323 for (RequestList::iterator it
= requests_
.begin(); it
!= requests_
.end();
325 if (it
->ipc_request
== request_id
&& it
->handler
.get()) {
326 it
->handler
->OnDevicesEnumerated(it
->request_id
, device_array
);
332 void MediaStreamDispatcher::OnDeviceOpened(
334 const std::string
& label
,
335 const StreamDeviceInfo
& device_info
) {
336 DCHECK(thread_checker_
.CalledOnValidThread());
337 for (RequestList::iterator it
= requests_
.begin();
338 it
!= requests_
.end(); ++it
) {
339 Request
& request
= *it
;
340 if (request
.ipc_request
== request_id
) {
342 new_stream
.handler
= request
.handler
;
343 if (IsAudioInputMediaType(device_info
.device
.type
)) {
344 new_stream
.audio_array
.push_back(device_info
);
345 } else if (IsVideoMediaType(device_info
.device
.type
)) {
346 new_stream
.video_array
.push_back(device_info
);
350 label_stream_map_
[label
] = new_stream
;
351 if (request
.handler
.get()) {
352 request
.handler
->OnDeviceOpened(request
.request_id
, label
, device_info
);
353 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened("
354 << request
.request_id
<< ", " << label
<< ")";
362 void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id
) {
363 DCHECK(thread_checker_
.CalledOnValidThread());
364 for (RequestList::iterator it
= requests_
.begin();
365 it
!= requests_
.end(); ++it
) {
366 Request
& request
= *it
;
367 if (request
.ipc_request
== request_id
) {
368 if (request
.handler
.get()) {
369 request
.handler
->OnDeviceOpenFailed(request
.request_id
);
370 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed("
371 << request
.request_id
<< ")\n";
379 int MediaStreamDispatcher::audio_session_id(const std::string
& label
,
381 DCHECK(thread_checker_
.CalledOnValidThread());
382 LabelStreamMap::iterator it
= label_stream_map_
.find(label
);
383 if (it
== label_stream_map_
.end() ||
384 it
->second
.audio_array
.size() <= static_cast<size_t>(index
)) {
385 return StreamDeviceInfo::kNoId
;
387 return it
->second
.audio_array
[index
].session_id
;
390 bool MediaStreamDispatcher::IsStream(const std::string
& label
) {
391 DCHECK(thread_checker_
.CalledOnValidThread());
392 return label_stream_map_
.find(label
) != label_stream_map_
.end();
395 int MediaStreamDispatcher::video_session_id(const std::string
& label
,
397 DCHECK(thread_checker_
.CalledOnValidThread());
398 LabelStreamMap::iterator it
= label_stream_map_
.find(label
);
399 if (it
== label_stream_map_
.end() ||
400 it
->second
.video_array
.size() <= static_cast<size_t>(index
)) {
401 return StreamDeviceInfo::kNoId
;
403 return it
->second
.video_array
[index
].session_id
;
406 bool MediaStreamDispatcher::IsAudioDuckingActive() const {
407 DCHECK(thread_checker_
.CalledOnValidThread());
408 LabelStreamMap::const_iterator stream_it
= label_stream_map_
.begin();
409 while (stream_it
!= label_stream_map_
.end()) {
410 const StreamDeviceInfoArray
& audio_array
= stream_it
->second
.audio_array
;
411 for (StreamDeviceInfoArray::const_iterator device_it
= audio_array
.begin();
412 device_it
!= audio_array
.end(); ++device_it
) {
413 if (device_it
->device
.input
.effects
& media::AudioParameters::DUCKING
)
421 } // namespace content