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++);
128 // TODO(perkj): Revisit this. It used to be true (but isn't anymore) that
129 // there was one MediaStreamDispatcher per RenderView, but one
130 // MediaStreamImpl per RenderFrame. Now both MediaStreamDispatcher and
131 // MediaStreamImpl are 1:1 per RenderFrame. http://crbug/368030.
135 Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(),
136 device_info.device.id));
139 void MediaStreamDispatcher::EnumerateDevices(
141 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
142 MediaStreamType type,
143 const GURL& security_origin) {
144 DCHECK(thread_checker_.CalledOnValidThread());
145 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
146 type == MEDIA_DEVICE_VIDEO_CAPTURE ||
147 type == MEDIA_DEVICE_AUDIO_OUTPUT);
148 DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices("
149 << request_id << ")";
151 for (RequestList::iterator it = requests_.begin(); it != requests_.end();
153 DCHECK(!it->IsThisRequest(request_id, event_handler));
156 requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
157 Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(),
163 void MediaStreamDispatcher::StopEnumerateDevices(
165 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
166 DCHECK(thread_checker_.CalledOnValidThread());
167 DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices("
168 << request_id << ")";
169 for (RequestList::iterator it = requests_.begin(); it != requests_.end();
171 if (it->IsThisRequest(request_id, event_handler)) {
172 Send(new MediaStreamHostMsg_CancelEnumerateDevices(routing_id(),
180 void MediaStreamDispatcher::OpenDevice(
182 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler,
183 const std::string& device_id,
184 MediaStreamType type,
185 const GURL& security_origin) {
186 DCHECK(thread_checker_.CalledOnValidThread());
187 DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id << ")";
189 requests_.push_back(Request(event_handler, request_id, next_ipc_id_));
190 Send(new MediaStreamHostMsg_OpenDevice(routing_id(),
197 void MediaStreamDispatcher::CancelOpenDevice(
199 const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) {
200 CancelGenerateStream(request_id, event_handler);
203 void MediaStreamDispatcher::CloseDevice(const std::string& label) {
204 DCHECK(thread_checker_.CalledOnValidThread());
205 DCHECK(!label.empty());
206 DVLOG(1) << "MediaStreamDispatcher::CloseDevice"
207 << ", {label = " << label << "}";
209 LabelStreamMap::iterator it = label_stream_map_.find(label);
210 if (it == label_stream_map_.end())
212 label_stream_map_.erase(it);
214 Send(new MediaStreamHostMsg_CloseDevice(routing_id(), label));
217 void MediaStreamDispatcher::OnDestruct() {
218 // Do not self-destruct. MediaStreamImpl owns |this|.
221 bool MediaStreamDispatcher::Send(IPC::Message* message) {
222 if (!RenderThread::Get()) {
227 return RenderThread::Get()->Send(message);
230 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) {
232 IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message)
233 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated,
235 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed,
236 OnStreamGenerationFailed)
237 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped,
239 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated,
241 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened,
243 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed,
245 IPC_MESSAGE_UNHANDLED(handled = false)
246 IPC_END_MESSAGE_MAP()
250 void MediaStreamDispatcher::OnStreamGenerated(
252 const std::string& label,
253 const StreamDeviceInfoArray& audio_array,
254 const StreamDeviceInfoArray& video_array) {
255 DCHECK(thread_checker_.CalledOnValidThread());
257 for (RequestList::iterator it = requests_.begin();
258 it != requests_.end(); ++it) {
259 Request& request = *it;
260 if (request.ipc_request == request_id) {
262 new_stream.handler = request.handler;
263 new_stream.audio_array = audio_array;
264 new_stream.video_array = video_array;
265 label_stream_map_[label] = new_stream;
266 if (request.handler.get()) {
267 request.handler->OnStreamGenerated(
268 request.request_id, label, audio_array, video_array);
269 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerated("
270 << request.request_id << ", " << label << ")";
278 void MediaStreamDispatcher::OnStreamGenerationFailed(
280 content::MediaStreamRequestResult result) {
281 DCHECK(thread_checker_.CalledOnValidThread());
282 for (RequestList::iterator it = requests_.begin();
283 it != requests_.end(); ++it) {
284 Request& request = *it;
285 if (request.ipc_request == request_id) {
286 if (request.handler.get()) {
287 request.handler->OnStreamGenerationFailed(request.request_id, result);
288 DVLOG(1) << "MediaStreamDispatcher::OnStreamGenerationFailed("
289 << request.request_id << ")\n";
297 void MediaStreamDispatcher::OnDeviceStopped(
298 const std::string& label,
299 const StreamDeviceInfo& device_info) {
300 DCHECK(thread_checker_.CalledOnValidThread());
301 DVLOG(1) << "MediaStreamDispatcher::OnDeviceStopped("
302 << "{label = " << label << "})"
303 << ", {device_id = " << device_info.device.id << "})";
305 LabelStreamMap::iterator it = label_stream_map_.find(label);
306 if (it == label_stream_map_.end()) {
307 // This can happen if a user happen stop a the device from JS at the same
308 // time as the underlying media device is unplugged from the system.
311 Stream* stream = &it->second;
312 if (IsAudioInputMediaType(device_info.device.type))
313 RemoveStreamDeviceFromArray(device_info, &stream->audio_array);
315 RemoveStreamDeviceFromArray(device_info, &stream->video_array);
317 if (stream->handler.get())
318 stream->handler->OnDeviceStopped(label, device_info);
320 if (stream->audio_array.empty() && stream->video_array.empty())
321 label_stream_map_.erase(it);
324 void MediaStreamDispatcher::OnDevicesEnumerated(
326 const StreamDeviceInfoArray& device_array) {
327 DCHECK(thread_checker_.CalledOnValidThread());
328 DCHECK_GE(request_id, 0);
330 for (RequestList::iterator it = requests_.begin(); it != requests_.end();
332 if (it->ipc_request == request_id && it->handler.get()) {
333 it->handler->OnDevicesEnumerated(it->request_id, device_array);
339 void MediaStreamDispatcher::OnDeviceOpened(
341 const std::string& label,
342 const StreamDeviceInfo& device_info) {
343 DCHECK(thread_checker_.CalledOnValidThread());
344 for (RequestList::iterator it = requests_.begin();
345 it != requests_.end(); ++it) {
346 Request& request = *it;
347 if (request.ipc_request == request_id) {
349 new_stream.handler = request.handler;
350 if (IsAudioInputMediaType(device_info.device.type)) {
351 new_stream.audio_array.push_back(device_info);
352 } else if (IsVideoMediaType(device_info.device.type)) {
353 new_stream.video_array.push_back(device_info);
357 label_stream_map_[label] = new_stream;
358 if (request.handler.get()) {
359 request.handler->OnDeviceOpened(request.request_id, label, device_info);
360 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpened("
361 << request.request_id << ", " << label << ")";
369 void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) {
370 DCHECK(thread_checker_.CalledOnValidThread());
371 for (RequestList::iterator it = requests_.begin();
372 it != requests_.end(); ++it) {
373 Request& request = *it;
374 if (request.ipc_request == request_id) {
375 if (request.handler.get()) {
376 request.handler->OnDeviceOpenFailed(request.request_id);
377 DVLOG(1) << "MediaStreamDispatcher::OnDeviceOpenFailed("
378 << request.request_id << ")\n";
386 int MediaStreamDispatcher::audio_session_id(const std::string& label,
388 DCHECK(thread_checker_.CalledOnValidThread());
389 LabelStreamMap::iterator it = label_stream_map_.find(label);
390 if (it == label_stream_map_.end() ||
391 it->second.audio_array.size() <= static_cast<size_t>(index)) {
392 return StreamDeviceInfo::kNoId;
394 return it->second.audio_array[index].session_id;
397 bool MediaStreamDispatcher::IsStream(const std::string& label) {
398 DCHECK(thread_checker_.CalledOnValidThread());
399 return label_stream_map_.find(label) != label_stream_map_.end();
402 int MediaStreamDispatcher::video_session_id(const std::string& label,
404 DCHECK(thread_checker_.CalledOnValidThread());
405 LabelStreamMap::iterator it = label_stream_map_.find(label);
406 if (it == label_stream_map_.end() ||
407 it->second.video_array.size() <= static_cast<size_t>(index)) {
408 return StreamDeviceInfo::kNoId;
410 return it->second.video_array[index].session_id;
413 bool MediaStreamDispatcher::IsAudioDuckingActive() const {
414 DCHECK(thread_checker_.CalledOnValidThread());
415 LabelStreamMap::const_iterator stream_it = label_stream_map_.begin();
416 while (stream_it != label_stream_map_.end()) {
417 const StreamDeviceInfoArray& audio_array = stream_it->second.audio_array;
418 for (StreamDeviceInfoArray::const_iterator device_it = audio_array.begin();
419 device_it != audio_array.end(); ++device_it) {
420 if (device_it->device.input.effects & media::AudioParameters::DUCKING)
428 } // namespace content