Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / renderer / media / media_stream_dispatcher.cc
blob361a4ceae92ddde18b8dc8a8a585454194f70eb9
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"
13 #include "url/gurl.h"
15 namespace content {
17 namespace {
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);
25 return true;
28 return false;
31 } // namespace
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,
40 int request_id,
41 int ipc_request)
42 : handler(handler),
43 request_id(request_id),
44 ipc_request(ipc_request) {
46 bool IsThisRequest(
47 int request_id1,
48 const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler1) {
49 return (request_id1 == request_id && handler1.get() == handler.get());
51 base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
52 int request_id;
53 int ipc_request;
56 struct MediaStreamDispatcher::Stream {
57 Stream() {}
58 ~Stream() {}
59 base::WeakPtr<MediaStreamDispatcherEventHandler> handler;
60 StreamDeviceInfoArray audio_array;
61 StreamDeviceInfoArray video_array;
64 MediaStreamDispatcher::MediaStreamDispatcher(RenderFrame* render_frame)
65 : RenderFrameObserver(render_frame),
66 next_ipc_id_(0) {
69 MediaStreamDispatcher::~MediaStreamDispatcher() {}
71 void MediaStreamDispatcher::GenerateStream(
72 int request_id,
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(
86 int request_id,
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;
96 requests_.erase(it);
97 Send(new MediaStreamHostMsg_CancelGenerateStream(routing_id(),
98 ipc_request));
99 break;
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)) {
118 device_found = true;
119 if (audio_array.empty() && video_array.empty()) {
120 label_stream_map_.erase(stream_it++);
121 continue;
124 ++stream_it;
126 DCHECK(device_found);
128 Send(new MediaStreamHostMsg_StopStreamDevice(routing_id(),
129 device_info.device.id));
132 void MediaStreamDispatcher::EnumerateDevices(
133 int request_id,
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();
145 ++it) {
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(),
151 next_ipc_id_++,
152 type,
153 security_origin));
156 void MediaStreamDispatcher::StopEnumerateDevices(
157 int request_id,
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();
163 ++it) {
164 if (it->IsThisRequest(request_id, event_handler)) {
165 Send(new MediaStreamHostMsg_CancelEnumerateDevices(routing_id(),
166 it->ipc_request));
167 requests_.erase(it);
168 break;
173 void MediaStreamDispatcher::OpenDevice(
174 int request_id,
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(),
184 next_ipc_id_++,
185 device_id,
186 type,
187 security_origin));
190 void MediaStreamDispatcher::CancelOpenDevice(
191 int request_id,
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())
204 return;
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()) {
216 delete message;
217 return false;
220 return RenderThread::Get()->Send(message);
223 bool MediaStreamDispatcher::OnMessageReceived(const IPC::Message& message) {
224 bool handled = true;
225 IPC_BEGIN_MESSAGE_MAP(MediaStreamDispatcher, message)
226 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerated,
227 OnStreamGenerated)
228 IPC_MESSAGE_HANDLER(MediaStreamMsg_StreamGenerationFailed,
229 OnStreamGenerationFailed)
230 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceStopped,
231 OnDeviceStopped)
232 IPC_MESSAGE_HANDLER(MediaStreamMsg_DevicesEnumerated,
233 OnDevicesEnumerated)
234 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpened,
235 OnDeviceOpened)
236 IPC_MESSAGE_HANDLER(MediaStreamMsg_DeviceOpenFailed,
237 OnDeviceOpenFailed)
238 IPC_MESSAGE_UNHANDLED(handled = false)
239 IPC_END_MESSAGE_MAP()
240 return handled;
243 void MediaStreamDispatcher::OnStreamGenerated(
244 int request_id,
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) {
254 Stream new_stream;
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 << ")";
265 requests_.erase(it);
266 break;
271 void MediaStreamDispatcher::OnStreamGenerationFailed(
272 int request_id,
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";
284 requests_.erase(it);
285 break;
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.
302 return;
304 Stream* stream = &it->second;
305 if (IsAudioInputMediaType(device_info.device.type))
306 RemoveStreamDeviceFromArray(device_info, &stream->audio_array);
307 else
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(
318 int request_id,
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();
324 ++it) {
325 if (it->ipc_request == request_id && it->handler.get()) {
326 it->handler->OnDevicesEnumerated(it->request_id, device_array);
327 break;
332 void MediaStreamDispatcher::OnDeviceOpened(
333 int request_id,
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) {
341 Stream new_stream;
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);
347 } else {
348 NOTREACHED();
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 << ")";
356 requests_.erase(it);
357 break;
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";
373 requests_.erase(it);
374 break;
379 int MediaStreamDispatcher::audio_session_id(const std::string& label,
380 int index) {
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,
396 int index) {
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)
414 return true;
416 ++stream_it;
418 return false;
421 } // namespace content