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.
4
5 #include "content/renderer/media/user_media_client_impl.h"
6
7 #include <utility>
8
9 #include "base/hash.h"
10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "content/public/renderer/render_frame.h"
16 #include "content/renderer/media/media_stream.h"
17 #include "content/renderer/media/media_stream_audio_source.h"
18 #include "content/renderer/media/media_stream_dispatcher.h"
19 #include "content/renderer/media/media_stream_video_capturer_source.h"
20 #include "content/renderer/media/media_stream_video_track.h"
21 #include "content/renderer/media/peer_connection_tracker.h"
22 #include "content/renderer/media/webrtc/webrtc_video_capturer_adapter.h"
23 #include "content/renderer/media/webrtc_audio_capturer.h"
24 #include "content/renderer/media/webrtc_logging.h"
25 #include "content/renderer/media/webrtc_uma_histograms.h"
26 #include "content/renderer/render_thread_impl.h"
27 #include "third_party/WebKit/public/platform/WebMediaConstraints.h"
28 #include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h"
29 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
30 #include "third_party/WebKit/public/platform/WebMediaStreamTrackSourcesRequest.h"
31 #include "third_party/WebKit/public/web/WebDocument.h"
32 #include "third_party/WebKit/public/web/WebLocalFrame.h"
33
34 namespace content {
35 namespace {
36
CopyStreamConstraints(const blink::WebMediaConstraints & constraints,StreamOptions::Constraints * mandatory,StreamOptions::Constraints * optional)37 void CopyStreamConstraints(const blink::WebMediaConstraints& constraints,
38 StreamOptions::Constraints* mandatory,
39 StreamOptions::Constraints* optional) {
40 blink::WebVector<blink::WebMediaConstraint> mandatory_constraints;
41 constraints.getMandatoryConstraints(mandatory_constraints);
42 for (size_t i = 0; i < mandatory_constraints.size(); i++) {
43 mandatory->push_back(StreamOptions::Constraint(
44 mandatory_constraints[i].m_name.utf8(),
45 mandatory_constraints[i].m_value.utf8()));
46 }
47
48 blink::WebVector<blink::WebMediaConstraint> optional_constraints;
49 constraints.getOptionalConstraints(optional_constraints);
50 for (size_t i = 0; i < optional_constraints.size(); i++) {
51 optional->push_back(StreamOptions::Constraint(
52 optional_constraints[i].m_name.utf8(),
53 optional_constraints[i].m_value.utf8()));
54 }
55 }
56
57 static int g_next_request_id = 0;
58
59 } // namespace
60
61 struct UserMediaClientImpl::MediaDevicesRequestInfo {
MediaDevicesRequestInfocontent::UserMediaClientImpl::MediaDevicesRequestInfo62 MediaDevicesRequestInfo(const blink::WebMediaDevicesRequest& request,
63 int audio_input_request_id,
64 int video_input_request_id,
65 int audio_output_request_id)
66 : media_devices_request(request),
67 audio_input_request_id(audio_input_request_id),
68 video_input_request_id(video_input_request_id),
69 audio_output_request_id(audio_output_request_id),
70 has_audio_input_returned(false),
71 has_video_input_returned(false),
72 has_audio_output_returned(false) {}
73
MediaDevicesRequestInfocontent::UserMediaClientImpl::MediaDevicesRequestInfo74 MediaDevicesRequestInfo(
75 const blink::WebMediaStreamTrackSourcesRequest& request,
76 int audio_input_request_id,
77 int video_input_request_id)
78 : sources_request(request),
79 audio_input_request_id(audio_input_request_id),
80 video_input_request_id(video_input_request_id),
81 audio_output_request_id(-1),
82 has_audio_input_returned(false),
83 has_video_input_returned(false),
84 has_audio_output_returned(false) {}
85
IsSourcesRequestcontent::UserMediaClientImpl::MediaDevicesRequestInfo86 bool IsSourcesRequest() {
87 // We can't check isNull() on |media_devices_request| and |sources_request|,
88 // because in unit tests they will always be null.
89 return audio_output_request_id == -1;
90 }
91
92 blink::WebMediaDevicesRequest media_devices_request;
93 blink::WebMediaStreamTrackSourcesRequest sources_request;
94 int audio_input_request_id;
95 int video_input_request_id;
96 int audio_output_request_id;
97 bool has_audio_input_returned;
98 bool has_video_input_returned;
99 bool has_audio_output_returned;
100 StreamDeviceInfoArray audio_input_devices;
101 StreamDeviceInfoArray video_input_devices;
102 StreamDeviceInfoArray audio_output_devices;
103 };
104
UserMediaClientImpl(RenderFrame * render_frame,PeerConnectionDependencyFactory * dependency_factory,scoped_ptr<MediaStreamDispatcher> media_stream_dispatcher)105 UserMediaClientImpl::UserMediaClientImpl(
106 RenderFrame* render_frame,
107 PeerConnectionDependencyFactory* dependency_factory,
108 scoped_ptr<MediaStreamDispatcher> media_stream_dispatcher)
109 : RenderFrameObserver(render_frame),
110 dependency_factory_(dependency_factory),
111 media_stream_dispatcher_(media_stream_dispatcher.Pass()),
112 weak_factory_(this) {
113 DCHECK(dependency_factory_);
114 DCHECK(media_stream_dispatcher_.get());
115 }
116
~UserMediaClientImpl()117 UserMediaClientImpl::~UserMediaClientImpl() {
118 // Force-close all outstanding user media requests and local sources here,
119 // before the outstanding WeakPtrs are invalidated, to ensure a clean
120 // shutdown.
121 FrameWillClose();
122 }
123
requestUserMedia(const blink::WebUserMediaRequest & user_media_request)124 void UserMediaClientImpl::requestUserMedia(
125 const blink::WebUserMediaRequest& user_media_request) {
126 // Save histogram data so we can see how much GetUserMedia is used.
127 // The histogram counts the number of calls to the JS API
128 // webGetUserMedia.
129 UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA);
130 DCHECK(CalledOnValidThread());
131
132 if (RenderThreadImpl::current()) {
133 RenderThreadImpl::current()->peer_connection_tracker()->TrackGetUserMedia(
134 user_media_request);
135 }
136
137 int request_id = g_next_request_id++;
138 StreamOptions options;
139 GURL security_origin;
140 bool enable_automatic_output_device_selection = false;
141
142 // |user_media_request| can't be mocked. So in order to test at all we check
143 // if it isNull.
144 if (user_media_request.isNull()) {
145 // We are in a test.
146 options.audio_requested = true;
147 options.video_requested = true;
148 } else {
149 if (user_media_request.audio()) {
150 options.audio_requested = true;
151 CopyStreamConstraints(user_media_request.audioConstraints(),
152 &options.mandatory_audio,
153 &options.optional_audio);
154
155 // Check if this input device should be used to select a matching output
156 // device for audio rendering.
157 std::string enable;
158 if (options.GetFirstAudioConstraintByName(
159 kMediaStreamRenderToAssociatedSink, &enable, NULL) &&
160 LowerCaseEqualsASCII(enable, "true")) {
161 enable_automatic_output_device_selection = true;
162 }
163 }
164 if (user_media_request.video()) {
165 options.video_requested = true;
166 CopyStreamConstraints(user_media_request.videoConstraints(),
167 &options.mandatory_video,
168 &options.optional_video);
169 }
170
171 security_origin = GURL(user_media_request.securityOrigin().toString());
172 DCHECK(render_frame()->GetWebFrame() ==
173 static_cast<blink::WebFrame*>(
174 user_media_request.ownerDocument().frame()));
175 }
176
177 DVLOG(1) << "UserMediaClientImpl::requestUserMedia(" << request_id << ", [ "
178 << "audio=" << (options.audio_requested)
179 << " select associated sink: "
180 << enable_automatic_output_device_selection
181 << ", video=" << (options.video_requested) << " ], "
182 << security_origin.spec() << ")";
183
184 std::string audio_device_id;
185 bool mandatory_audio;
186 options.GetFirstAudioConstraintByName(kMediaStreamSourceInfoId,
187 &audio_device_id, &mandatory_audio);
188 std::string video_device_id;
189 bool mandatory_video;
190 options.GetFirstVideoConstraintByName(kMediaStreamSourceInfoId,
191 &video_device_id, &mandatory_video);
192
193 WebRtcLogMessage(base::StringPrintf(
194 "MSI::requestUserMedia. request_id=%d"
195 ", audio source id=%s mandatory= %s "
196 ", video source id=%s mandatory= %s",
197 request_id,
198 audio_device_id.c_str(),
199 mandatory_audio ? "true":"false",
200 video_device_id.c_str(),
201 mandatory_video ? "true":"false"));
202
203 user_media_requests_.push_back(
204 new UserMediaRequestInfo(request_id, user_media_request,
205 enable_automatic_output_device_selection));
206
207 media_stream_dispatcher_->GenerateStream(
208 request_id,
209 weak_factory_.GetWeakPtr(),
210 options,
211 security_origin);
212 }
213
cancelUserMediaRequest(const blink::WebUserMediaRequest & user_media_request)214 void UserMediaClientImpl::cancelUserMediaRequest(
215 const blink::WebUserMediaRequest& user_media_request) {
216 DCHECK(CalledOnValidThread());
217 UserMediaRequestInfo* request = FindUserMediaRequestInfo(user_media_request);
218 if (request) {
219 // We can't abort the stream generation process.
220 // Instead, erase the request. Once the stream is generated we will stop the
221 // stream if the request does not exist.
222 LogUserMediaRequestWithNoResult(MEDIA_STREAM_REQUEST_EXPLICITLY_CANCELLED);
223 DeleteUserMediaRequestInfo(request);
224 }
225 }
226
requestMediaDevices(const blink::WebMediaDevicesRequest & media_devices_request)227 void UserMediaClientImpl::requestMediaDevices(
228 const blink::WebMediaDevicesRequest& media_devices_request) {
229 UpdateWebRTCMethodCount(WEBKIT_GET_MEDIA_DEVICES);
230 DCHECK(CalledOnValidThread());
231
232 int audio_input_request_id = g_next_request_id++;
233 int video_input_request_id = g_next_request_id++;
234 int audio_output_request_id = g_next_request_id++;
235
236 // |media_devices_request| can't be mocked, so in tests it will be empty (the
237 // underlying pointer is null). In order to use this function in a test we
238 // need to check if it isNull.
239 GURL security_origin;
240 if (!media_devices_request.isNull())
241 security_origin = GURL(media_devices_request.securityOrigin().toString());
242
243 DVLOG(1) << "UserMediaClientImpl::requestMediaDevices("
244 << audio_input_request_id
245 << ", " << video_input_request_id << ", " << audio_output_request_id
246 << ", " << security_origin.spec() << ")";
247
248 media_devices_requests_.push_back(new MediaDevicesRequestInfo(
249 media_devices_request,
250 audio_input_request_id,
251 video_input_request_id,
252 audio_output_request_id));
253
254 media_stream_dispatcher_->EnumerateDevices(
255 audio_input_request_id,
256 weak_factory_.GetWeakPtr(),
257 MEDIA_DEVICE_AUDIO_CAPTURE,
258 security_origin);
259
260 media_stream_dispatcher_->EnumerateDevices(
261 video_input_request_id,
262 weak_factory_.GetWeakPtr(),
263 MEDIA_DEVICE_VIDEO_CAPTURE,
264 security_origin);
265
266 media_stream_dispatcher_->EnumerateDevices(
267 audio_output_request_id,
268 weak_factory_.GetWeakPtr(),
269 MEDIA_DEVICE_AUDIO_OUTPUT,
270 security_origin);
271 }
272
cancelMediaDevicesRequest(const blink::WebMediaDevicesRequest & media_devices_request)273 void UserMediaClientImpl::cancelMediaDevicesRequest(
274 const blink::WebMediaDevicesRequest& media_devices_request) {
275 DCHECK(CalledOnValidThread());
276 MediaDevicesRequestInfo* request =
277 FindMediaDevicesRequestInfo(media_devices_request);
278 if (!request)
279 return;
280 CancelAndDeleteMediaDevicesRequest(request);
281 }
282
requestSources(const blink::WebMediaStreamTrackSourcesRequest & sources_request)283 void UserMediaClientImpl::requestSources(
284 const blink::WebMediaStreamTrackSourcesRequest& sources_request) {
285 // We don't call UpdateWebRTCMethodCount() here to track the API count in UMA
286 // stats. This is instead counted in MediaStreamTrack::getSources in blink.
287 DCHECK(CalledOnValidThread());
288
289 int audio_input_request_id = g_next_request_id++;
290 int video_input_request_id = g_next_request_id++;
291
292 // |sources_request| can't be mocked, so in tests it will be empty (the
293 // underlying pointer is null). In order to use this function in a test we
294 // need to check if it isNull.
295 GURL security_origin;
296 if (!sources_request.isNull())
297 security_origin = GURL(sources_request.origin().utf8());
298
299 DVLOG(1) << "UserMediaClientImpl::requestSources("
300 << audio_input_request_id
301 << ", " << video_input_request_id
302 << ", " << security_origin.spec() << ")";
303
304 media_devices_requests_.push_back(new MediaDevicesRequestInfo(
305 sources_request,
306 audio_input_request_id,
307 video_input_request_id));
308
309 media_stream_dispatcher_->EnumerateDevices(
310 audio_input_request_id,
311 weak_factory_.GetWeakPtr(),
312 MEDIA_DEVICE_AUDIO_CAPTURE,
313 security_origin);
314
315 media_stream_dispatcher_->EnumerateDevices(
316 video_input_request_id,
317 weak_factory_.GetWeakPtr(),
318 MEDIA_DEVICE_VIDEO_CAPTURE,
319 security_origin);
320 }
321
322 // Callback from MediaStreamDispatcher.
323 // The requested stream have been generated by the MediaStreamDispatcher.
OnStreamGenerated(int request_id,const std::string & label,const StreamDeviceInfoArray & audio_array,const StreamDeviceInfoArray & video_array)324 void UserMediaClientImpl::OnStreamGenerated(
325 int request_id,
326 const std::string& label,
327 const StreamDeviceInfoArray& audio_array,
328 const StreamDeviceInfoArray& video_array) {
329 DCHECK(CalledOnValidThread());
330 DVLOG(1) << "UserMediaClientImpl::OnStreamGenerated stream:" << label;
331
332 UserMediaRequestInfo* request_info = FindUserMediaRequestInfo(request_id);
333 if (!request_info) {
334 // This can happen if the request is canceled or the frame reloads while
335 // MediaStreamDispatcher is processing the request.
336 DVLOG(1) << "Request ID not found";
337 OnStreamGeneratedForCancelledRequest(audio_array, video_array);
338 return;
339 }
340 request_info->generated = true;
341
342 // WebUserMediaRequest don't have an implementation in unit tests.
343 // Therefore we need to check for isNull here and initialize the
344 // constraints.
345 blink::WebUserMediaRequest* request = &(request_info->request);
346 blink::WebMediaConstraints audio_constraints;
347 blink::WebMediaConstraints video_constraints;
348 if (request->isNull()) {
349 audio_constraints.initialize();
350 video_constraints.initialize();
351 } else {
352 audio_constraints = request->audioConstraints();
353 video_constraints = request->videoConstraints();
354 }
355
356 blink::WebVector<blink::WebMediaStreamTrack> audio_track_vector(
357 audio_array.size());
358 CreateAudioTracks(audio_array, audio_constraints, &audio_track_vector,
359 request_info);
360
361 blink::WebVector<blink::WebMediaStreamTrack> video_track_vector(
362 video_array.size());
363 CreateVideoTracks(video_array, video_constraints, &video_track_vector,
364 request_info);
365
366 blink::WebString webkit_id = base::UTF8ToUTF16(label);
367 blink::WebMediaStream* web_stream = &(request_info->web_stream);
368
369 web_stream->initialize(webkit_id, audio_track_vector,
370 video_track_vector);
371 web_stream->setExtraData(
372 new MediaStream(
373 *web_stream));
374
375 // Wait for the tracks to be started successfully or to fail.
376 request_info->CallbackOnTracksStarted(
377 base::Bind(&UserMediaClientImpl::OnCreateNativeTracksCompleted,
378 weak_factory_.GetWeakPtr()));
379 }
380
OnStreamGeneratedForCancelledRequest(const StreamDeviceInfoArray & audio_array,const StreamDeviceInfoArray & video_array)381 void UserMediaClientImpl::OnStreamGeneratedForCancelledRequest(
382 const StreamDeviceInfoArray& audio_array,
383 const StreamDeviceInfoArray& video_array) {
384 // Only stop the device if the device is not used in another MediaStream.
385 for (StreamDeviceInfoArray::const_iterator device_it = audio_array.begin();
386 device_it != audio_array.end(); ++device_it) {
387 if (!FindLocalSource(*device_it))
388 media_stream_dispatcher_->StopStreamDevice(*device_it);
389 }
390
391 for (StreamDeviceInfoArray::const_iterator device_it = video_array.begin();
392 device_it != video_array.end(); ++device_it) {
393 if (!FindLocalSource(*device_it))
394 media_stream_dispatcher_->StopStreamDevice(*device_it);
395 }
396 }
397
FinalizeEnumerateDevices(MediaDevicesRequestInfo * request)398 void UserMediaClientImpl::FinalizeEnumerateDevices(
399 MediaDevicesRequestInfo* request) {
400 // All devices are ready for copying. We use a hashed audio output device id
401 // as the group id for input and output audio devices. If an input device
402 // doesn't have an associated output device, we use the input device's own id.
403 // We don't support group id for video devices, that's left empty.
404 blink::WebVector<blink::WebMediaDeviceInfo>
405 devices(request->audio_input_devices.size() +
406 request->video_input_devices.size() +
407 request->audio_output_devices.size());
408 for (size_t i = 0; i < request->audio_input_devices.size(); ++i) {
409 const MediaStreamDevice& device = request->audio_input_devices[i].device;
410 DCHECK_EQ(device.type, MEDIA_DEVICE_AUDIO_CAPTURE);
411 std::string group_id = base::UintToString(base::Hash(
412 !device.matched_output_device_id.empty() ?
413 device.matched_output_device_id :
414 device.id));
415 devices[i].initialize(
416 blink::WebString::fromUTF8(device.id),
417 blink::WebMediaDeviceInfo::MediaDeviceKindAudioInput,
418 blink::WebString::fromUTF8(device.name),
419 blink::WebString::fromUTF8(group_id));
420 }
421 size_t offset = request->audio_input_devices.size();
422 for (size_t i = 0; i < request->video_input_devices.size(); ++i) {
423 const MediaStreamDevice& device = request->video_input_devices[i].device;
424 DCHECK_EQ(device.type, MEDIA_DEVICE_VIDEO_CAPTURE);
425 devices[offset + i].initialize(
426 blink::WebString::fromUTF8(device.id),
427 blink::WebMediaDeviceInfo::MediaDeviceKindVideoInput,
428 blink::WebString::fromUTF8(device.name),
429 blink::WebString());
430 }
431 offset += request->video_input_devices.size();
432 for (size_t i = 0; i < request->audio_output_devices.size(); ++i) {
433 const MediaStreamDevice& device = request->audio_output_devices[i].device;
434 DCHECK_EQ(device.type, MEDIA_DEVICE_AUDIO_OUTPUT);
435 devices[offset + i].initialize(
436 blink::WebString::fromUTF8(device.id),
437 blink::WebMediaDeviceInfo::MediaDeviceKindAudioOutput,
438 blink::WebString::fromUTF8(device.name),
439 blink::WebString::fromUTF8(base::UintToString(base::Hash(device.id))));
440 }
441
442 EnumerateDevicesSucceded(&request->media_devices_request, devices);
443 }
444
FinalizeEnumerateSources(MediaDevicesRequestInfo * request)445 void UserMediaClientImpl::FinalizeEnumerateSources(
446 MediaDevicesRequestInfo* request) {
447 blink::WebVector<blink::WebSourceInfo>
448 sources(request->audio_input_devices.size() +
449 request->video_input_devices.size());
450 for (size_t i = 0; i < request->audio_input_devices.size(); ++i) {
451 const MediaStreamDevice& device = request->audio_input_devices[i].device;
452 DCHECK_EQ(device.type, MEDIA_DEVICE_AUDIO_CAPTURE);
453 std::string group_id = base::UintToString(base::Hash(
454 !device.matched_output_device_id.empty() ?
455 device.matched_output_device_id :
456 device.id));
457 sources[i].initialize(blink::WebString::fromUTF8(device.id),
458 blink::WebSourceInfo::SourceKindAudio,
459 blink::WebString::fromUTF8(device.name),
460 blink::WebSourceInfo::VideoFacingModeNone);
461 }
462 size_t offset = request->audio_input_devices.size();
463 for (size_t i = 0; i < request->video_input_devices.size(); ++i) {
464 const MediaStreamDevice& device = request->video_input_devices[i].device;
465 DCHECK_EQ(device.type, MEDIA_DEVICE_VIDEO_CAPTURE);
466 blink::WebSourceInfo::VideoFacingMode video_facing;
467 switch (device.video_facing) {
468 case MEDIA_VIDEO_FACING_USER:
469 video_facing = blink::WebSourceInfo::VideoFacingModeUser;
470 break;
471 case MEDIA_VIDEO_FACING_ENVIRONMENT:
472 video_facing = blink::WebSourceInfo::VideoFacingModeEnvironment;
473 break;
474 default:
475 video_facing = blink::WebSourceInfo::VideoFacingModeNone;
476 }
477 sources[offset + i].initialize(blink::WebString::fromUTF8(device.id),
478 blink::WebSourceInfo::SourceKindVideo,
479 blink::WebString::fromUTF8(device.name),
480 video_facing);
481 }
482
483 EnumerateSourcesSucceded(&request->sources_request, sources);
484 }
485
486 // Callback from MediaStreamDispatcher.
487 // The requested stream failed to be generated.
OnStreamGenerationFailed(int request_id,MediaStreamRequestResult result)488 void UserMediaClientImpl::OnStreamGenerationFailed(
489 int request_id,
490 MediaStreamRequestResult result) {
491 DCHECK(CalledOnValidThread());
492 DVLOG(1) << "UserMediaClientImpl::OnStreamGenerationFailed("
493 << request_id << ")";
494 UserMediaRequestInfo* request_info = FindUserMediaRequestInfo(request_id);
495 if (!request_info) {
496 // This can happen if the request is canceled or the frame reloads while
497 // MediaStreamDispatcher is processing the request.
498 DVLOG(1) << "Request ID not found";
499 return;
500 }
501
502 GetUserMediaRequestFailed(&request_info->request, result);
503 DeleteUserMediaRequestInfo(request_info);
504 }
505
506 // Callback from MediaStreamDispatcher.
507 // The browser process has stopped a device used by a MediaStream.
OnDeviceStopped(const std::string & label,const StreamDeviceInfo & device_info)508 void UserMediaClientImpl::OnDeviceStopped(
509 const std::string& label,
510 const StreamDeviceInfo& device_info) {
511 DCHECK(CalledOnValidThread());
512 DVLOG(1) << "UserMediaClientImpl::OnDeviceStopped("
513 << "{device_id = " << device_info.device.id << "})";
514
515 const blink::WebMediaStreamSource* source_ptr = FindLocalSource(device_info);
516 if (!source_ptr) {
517 // This happens if the same device is used in several guM requests or
518 // if a user happen stop a track from JS at the same time
519 // as the underlying media device is unplugged from the system.
520 return;
521 }
522 // By creating |source| it is guaranteed that the blink::WebMediaStreamSource
523 // object is valid during the cleanup.
524 blink::WebMediaStreamSource source(*source_ptr);
525 StopLocalSource(source, false);
526
527 for (LocalStreamSources::iterator device_it = local_sources_.begin();
528 device_it != local_sources_.end(); ++device_it) {
529 if (device_it->id() == source.id()) {
530 local_sources_.erase(device_it);
531 break;
532 }
533 }
534 }
535
InitializeSourceObject(const StreamDeviceInfo & device,blink::WebMediaStreamSource::Type type,const blink::WebMediaConstraints & constraints,blink::WebMediaStreamSource * webkit_source)536 void UserMediaClientImpl::InitializeSourceObject(
537 const StreamDeviceInfo& device,
538 blink::WebMediaStreamSource::Type type,
539 const blink::WebMediaConstraints& constraints,
540 blink::WebMediaStreamSource* webkit_source) {
541 const blink::WebMediaStreamSource* existing_source =
542 FindLocalSource(device);
543 if (existing_source) {
544 *webkit_source = *existing_source;
545 DVLOG(1) << "Source already exist. Reusing source with id "
546 << webkit_source->id().utf8();
547 return;
548 }
549
550 webkit_source->initialize(
551 base::UTF8ToUTF16(device.device.id),
552 type,
553 base::UTF8ToUTF16(device.device.name));
554
555 DVLOG(1) << "Initialize source object :"
556 << "id = " << webkit_source->id().utf8()
557 << ", name = " << webkit_source->name().utf8();
558
559 if (type == blink::WebMediaStreamSource::TypeVideo) {
560 webkit_source->setExtraData(
561 CreateVideoSource(
562 device,
563 base::Bind(&UserMediaClientImpl::OnLocalSourceStopped,
564 weak_factory_.GetWeakPtr())));
565 } else {
566 DCHECK_EQ(blink::WebMediaStreamSource::TypeAudio, type);
567 MediaStreamAudioSource* audio_source(
568 new MediaStreamAudioSource(
569 RenderFrameObserver::routing_id(),
570 device,
571 base::Bind(&UserMediaClientImpl::OnLocalSourceStopped,
572 weak_factory_.GetWeakPtr()),
573 dependency_factory_));
574 webkit_source->setExtraData(audio_source);
575 }
576 local_sources_.push_back(*webkit_source);
577 }
578
CreateVideoSource(const StreamDeviceInfo & device,const MediaStreamSource::SourceStoppedCallback & stop_callback)579 MediaStreamVideoSource* UserMediaClientImpl::CreateVideoSource(
580 const StreamDeviceInfo& device,
581 const MediaStreamSource::SourceStoppedCallback& stop_callback) {
582 return new content::MediaStreamVideoCapturerSource(
583 device,
584 stop_callback,
585 new VideoCapturerDelegate(device));
586 }
587
CreateVideoTracks(const StreamDeviceInfoArray & devices,const blink::WebMediaConstraints & constraints,blink::WebVector<blink::WebMediaStreamTrack> * webkit_tracks,UserMediaRequestInfo * request)588 void UserMediaClientImpl::CreateVideoTracks(
589 const StreamDeviceInfoArray& devices,
590 const blink::WebMediaConstraints& constraints,
591 blink::WebVector<blink::WebMediaStreamTrack>* webkit_tracks,
592 UserMediaRequestInfo* request) {
593 DCHECK_EQ(devices.size(), webkit_tracks->size());
594
595 for (size_t i = 0; i < devices.size(); ++i) {
596 blink::WebMediaStreamSource webkit_source;
597 InitializeSourceObject(devices[i],
598 blink::WebMediaStreamSource::TypeVideo,
599 constraints,
600 &webkit_source);
601 (*webkit_tracks)[i] =
602 request->CreateAndStartVideoTrack(webkit_source, constraints);
603 }
604 }
605
CreateAudioTracks(const StreamDeviceInfoArray & devices,const blink::WebMediaConstraints & constraints,blink::WebVector<blink::WebMediaStreamTrack> * webkit_tracks,UserMediaRequestInfo * request)606 void UserMediaClientImpl::CreateAudioTracks(
607 const StreamDeviceInfoArray& devices,
608 const blink::WebMediaConstraints& constraints,
609 blink::WebVector<blink::WebMediaStreamTrack>* webkit_tracks,
610 UserMediaRequestInfo* request) {
611 DCHECK_EQ(devices.size(), webkit_tracks->size());
612
613 // Log the device names for this request.
614 for (StreamDeviceInfoArray::const_iterator it = devices.begin();
615 it != devices.end(); ++it) {
616 WebRtcLogMessage(base::StringPrintf(
617 "Generated media stream for request id %d contains audio device name"
618 " \"%s\"",
619 request->request_id,
620 it->device.name.c_str()));
621 }
622
623 StreamDeviceInfoArray overridden_audio_array = devices;
624 if (!request->enable_automatic_output_device_selection) {
625 // If the GetUserMedia request did not explicitly set the constraint
626 // kMediaStreamRenderToAssociatedSink, the output device parameters must
627 // be removed.
628 for (StreamDeviceInfoArray::iterator it = overridden_audio_array.begin();
629 it != overridden_audio_array.end(); ++it) {
630 it->device.matched_output_device_id = "";
631 it->device.matched_output = MediaStreamDevice::AudioDeviceParameters();
632 }
633 }
634
635 for (size_t i = 0; i < overridden_audio_array.size(); ++i) {
636 blink::WebMediaStreamSource webkit_source;
637 InitializeSourceObject(overridden_audio_array[i],
638 blink::WebMediaStreamSource::TypeAudio,
639 constraints,
640 &webkit_source);
641 (*webkit_tracks)[i].initialize(webkit_source);
642 request->StartAudioTrack((*webkit_tracks)[i], constraints);
643 }
644 }
645
OnCreateNativeTracksCompleted(UserMediaRequestInfo * request,MediaStreamRequestResult result,const blink::WebString & result_name)646 void UserMediaClientImpl::OnCreateNativeTracksCompleted(
647 UserMediaRequestInfo* request,
648 MediaStreamRequestResult result,
649 const blink::WebString& result_name) {
650 DVLOG(1) << "UserMediaClientImpl::OnCreateNativeTracksComplete("
651 << "{request_id = " << request->request_id << "} "
652 << "{result = " << result << "})";
653 if (result == content::MEDIA_DEVICE_OK)
654 GetUserMediaRequestSucceeded(request->web_stream, &request->request);
655 else
656 GetUserMediaRequestTrackStartedFailed(&request->request,
657 result,
658 result_name);
659
660 DeleteUserMediaRequestInfo(request);
661 }
662
OnDevicesEnumerated(int request_id,const StreamDeviceInfoArray & device_array)663 void UserMediaClientImpl::OnDevicesEnumerated(
664 int request_id,
665 const StreamDeviceInfoArray& device_array) {
666 DVLOG(1) << "UserMediaClientImpl::OnDevicesEnumerated(" << request_id << ")";
667
668 MediaDevicesRequestInfo* request = FindMediaDevicesRequestInfo(request_id);
669 DCHECK(request);
670
671 if (request_id == request->audio_input_request_id) {
672 request->has_audio_input_returned = true;
673 DCHECK(request->audio_input_devices.empty());
674 request->audio_input_devices = device_array;
675 } else if (request_id == request->video_input_request_id) {
676 request->has_video_input_returned = true;
677 DCHECK(request->video_input_devices.empty());
678 request->video_input_devices = device_array;
679 } else {
680 DCHECK_EQ(request->audio_output_request_id, request_id);
681 request->has_audio_output_returned = true;
682 DCHECK(request->audio_output_devices.empty());
683 request->audio_output_devices = device_array;
684 }
685
686 if (!request->has_audio_input_returned ||
687 !request->has_video_input_returned ||
688 (!request->IsSourcesRequest() && !request->has_audio_output_returned)) {
689 // Wait for the rest of the devices to complete.
690 return;
691 }
692
693 if (request->IsSourcesRequest())
694 FinalizeEnumerateSources(request);
695 else
696 FinalizeEnumerateDevices(request);
697
698 CancelAndDeleteMediaDevicesRequest(request);
699 }
700
OnDeviceOpened(int request_id,const std::string & label,const StreamDeviceInfo & video_device)701 void UserMediaClientImpl::OnDeviceOpened(
702 int request_id,
703 const std::string& label,
704 const StreamDeviceInfo& video_device) {
705 DVLOG(1) << "UserMediaClientImpl::OnDeviceOpened("
706 << request_id << ", " << label << ")";
707 NOTIMPLEMENTED();
708 }
709
OnDeviceOpenFailed(int request_id)710 void UserMediaClientImpl::OnDeviceOpenFailed(int request_id) {
711 DVLOG(1) << "UserMediaClientImpl::VideoDeviceOpenFailed("
712 << request_id << ")";
713 NOTIMPLEMENTED();
714 }
715
GetUserMediaRequestSucceeded(const blink::WebMediaStream & stream,blink::WebUserMediaRequest * request_info)716 void UserMediaClientImpl::GetUserMediaRequestSucceeded(
717 const blink::WebMediaStream& stream,
718 blink::WebUserMediaRequest* request_info) {
719 DVLOG(1) << "UserMediaClientImpl::GetUserMediaRequestSucceeded";
720 LogUserMediaRequestResult(MEDIA_DEVICE_OK);
721 request_info->requestSucceeded(stream);
722 }
723
GetUserMediaRequestFailed(blink::WebUserMediaRequest * request_info,MediaStreamRequestResult result)724 void UserMediaClientImpl::GetUserMediaRequestFailed(
725 blink::WebUserMediaRequest* request_info,
726 MediaStreamRequestResult result) {
727 LogUserMediaRequestResult(result);
728 switch (result) {
729 case MEDIA_DEVICE_OK:
730 NOTREACHED();
731 break;
732 case MEDIA_DEVICE_PERMISSION_DENIED:
733 request_info->requestDenied();
734 break;
735 case MEDIA_DEVICE_PERMISSION_DISMISSED:
736 request_info->requestFailedUASpecific("PermissionDismissedError");
737 break;
738 case MEDIA_DEVICE_INVALID_STATE:
739 request_info->requestFailedUASpecific("InvalidStateError");
740 break;
741 case MEDIA_DEVICE_NO_HARDWARE:
742 request_info->requestFailedUASpecific("DevicesNotFoundError");
743 break;
744 case MEDIA_DEVICE_INVALID_SECURITY_ORIGIN:
745 request_info->requestFailedUASpecific("InvalidSecurityOriginError");
746 break;
747 case MEDIA_DEVICE_TAB_CAPTURE_FAILURE:
748 request_info->requestFailedUASpecific("TabCaptureError");
749 break;
750 case MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE:
751 request_info->requestFailedUASpecific("ScreenCaptureError");
752 break;
753 case MEDIA_DEVICE_CAPTURE_FAILURE:
754 request_info->requestFailedUASpecific("DeviceCaptureError");
755 break;
756 default:
757 NOTREACHED();
758 request_info->requestFailed();
759 break;
760 }
761 }
762
GetUserMediaRequestTrackStartedFailed(blink::WebUserMediaRequest * request_info,MediaStreamRequestResult result,const blink::WebString & result_name)763 void UserMediaClientImpl::GetUserMediaRequestTrackStartedFailed(
764 blink::WebUserMediaRequest* request_info,
765 MediaStreamRequestResult result,
766 const blink::WebString& result_name) {
767 switch (result) {
768 case MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED:
769 request_info->requestFailedConstraint(result_name);
770 break;
771 case MEDIA_DEVICE_TRACK_START_FAILURE:
772 request_info->requestFailedUASpecific("TrackStartError");
773 break;
774 default:
775 NOTREACHED();
776 request_info->requestFailed();
777 break;
778 }
779 }
780
EnumerateDevicesSucceded(blink::WebMediaDevicesRequest * request,blink::WebVector<blink::WebMediaDeviceInfo> & devices)781 void UserMediaClientImpl::EnumerateDevicesSucceded(
782 blink::WebMediaDevicesRequest* request,
783 blink::WebVector<blink::WebMediaDeviceInfo>& devices) {
784 request->requestSucceeded(devices);
785 }
786
EnumerateSourcesSucceded(blink::WebMediaStreamTrackSourcesRequest * request,blink::WebVector<blink::WebSourceInfo> & sources)787 void UserMediaClientImpl::EnumerateSourcesSucceded(
788 blink::WebMediaStreamTrackSourcesRequest* request,
789 blink::WebVector<blink::WebSourceInfo>& sources) {
790 request->requestSucceeded(sources);
791 }
792
FindLocalSource(const StreamDeviceInfo & device) const793 const blink::WebMediaStreamSource* UserMediaClientImpl::FindLocalSource(
794 const StreamDeviceInfo& device) const {
795 for (LocalStreamSources::const_iterator it = local_sources_.begin();
796 it != local_sources_.end(); ++it) {
797 MediaStreamSource* const source =
798 static_cast<MediaStreamSource*>(it->extraData());
799 const StreamDeviceInfo& active_device = source->device_info();
800 if (active_device.device.id == device.device.id &&
801 active_device.device.type == device.device.type &&
802 active_device.session_id == device.session_id) {
803 return &(*it);
804 }
805 }
806 return NULL;
807 }
808
809 UserMediaClientImpl::UserMediaRequestInfo*
FindUserMediaRequestInfo(int request_id)810 UserMediaClientImpl::FindUserMediaRequestInfo(int request_id) {
811 UserMediaRequests::iterator it = user_media_requests_.begin();
812 for (; it != user_media_requests_.end(); ++it) {
813 if ((*it)->request_id == request_id)
814 return (*it);
815 }
816 return NULL;
817 }
818
819 UserMediaClientImpl::UserMediaRequestInfo*
FindUserMediaRequestInfo(const blink::WebUserMediaRequest & request)820 UserMediaClientImpl::FindUserMediaRequestInfo(
821 const blink::WebUserMediaRequest& request) {
822 UserMediaRequests::iterator it = user_media_requests_.begin();
823 for (; it != user_media_requests_.end(); ++it) {
824 if ((*it)->request == request)
825 return (*it);
826 }
827 return NULL;
828 }
829
DeleteUserMediaRequestInfo(UserMediaRequestInfo * request)830 void UserMediaClientImpl::DeleteUserMediaRequestInfo(
831 UserMediaRequestInfo* request) {
832 UserMediaRequests::iterator it = user_media_requests_.begin();
833 for (; it != user_media_requests_.end(); ++it) {
834 if ((*it) == request) {
835 user_media_requests_.erase(it);
836 return;
837 }
838 }
839 NOTREACHED();
840 }
841
DeleteAllUserMediaRequests()842 void UserMediaClientImpl::DeleteAllUserMediaRequests() {
843 UserMediaRequests::iterator request_it = user_media_requests_.begin();
844 while (request_it != user_media_requests_.end()) {
845 DVLOG(1) << "UserMediaClientImpl@" << this
846 << "::DeleteAllUserMediaRequests: "
847 << "Cancel user media request " << (*request_it)->request_id;
848 // If the request is not generated, it means that a request
849 // has been sent to the MediaStreamDispatcher to generate a stream
850 // but MediaStreamDispatcher has not yet responded and we need to cancel
851 // the request.
852 if (!(*request_it)->generated) {
853 DCHECK(!(*request_it)->HasPendingSources());
854 media_stream_dispatcher_->CancelGenerateStream(
855 (*request_it)->request_id, weak_factory_.GetWeakPtr());
856 LogUserMediaRequestWithNoResult(MEDIA_STREAM_REQUEST_NOT_GENERATED);
857 } else {
858 DCHECK((*request_it)->HasPendingSources());
859 LogUserMediaRequestWithNoResult(
860 MEDIA_STREAM_REQUEST_PENDING_MEDIA_TRACKS);
861 }
862 request_it = user_media_requests_.erase(request_it);
863 }
864 }
865
866 UserMediaClientImpl::MediaDevicesRequestInfo*
FindMediaDevicesRequestInfo(int request_id)867 UserMediaClientImpl::FindMediaDevicesRequestInfo(
868 int request_id) {
869 MediaDevicesRequests::iterator it = media_devices_requests_.begin();
870 for (; it != media_devices_requests_.end(); ++it) {
871 if ((*it)->audio_input_request_id == request_id ||
872 (*it)->video_input_request_id == request_id ||
873 (*it)->audio_output_request_id == request_id) {
874 return (*it);
875 }
876 }
877 return NULL;
878 }
879
880 UserMediaClientImpl::MediaDevicesRequestInfo*
FindMediaDevicesRequestInfo(const blink::WebMediaDevicesRequest & request)881 UserMediaClientImpl::FindMediaDevicesRequestInfo(
882 const blink::WebMediaDevicesRequest& request) {
883 MediaDevicesRequests::iterator it = media_devices_requests_.begin();
884 for (; it != media_devices_requests_.end(); ++it) {
885 if ((*it)->media_devices_request == request)
886 return (*it);
887 }
888 return NULL;
889 }
890
CancelAndDeleteMediaDevicesRequest(MediaDevicesRequestInfo * request)891 void UserMediaClientImpl::CancelAndDeleteMediaDevicesRequest(
892 MediaDevicesRequestInfo* request) {
893 MediaDevicesRequests::iterator it = media_devices_requests_.begin();
894 for (; it != media_devices_requests_.end(); ++it) {
895 if ((*it) == request) {
896 // Cancel device enumeration.
897 media_stream_dispatcher_->StopEnumerateDevices(
898 request->audio_input_request_id, weak_factory_.GetWeakPtr());
899 media_stream_dispatcher_->StopEnumerateDevices(
900 request->video_input_request_id, weak_factory_.GetWeakPtr());
901 media_stream_dispatcher_->StopEnumerateDevices(
902 request->audio_output_request_id, weak_factory_.GetWeakPtr());
903
904 media_devices_requests_.erase(it);
905 return;
906 }
907 }
908 NOTREACHED();
909 }
910
FrameWillClose()911 void UserMediaClientImpl::FrameWillClose() {
912 // Cancel all outstanding UserMediaRequests.
913 DeleteAllUserMediaRequests();
914
915 // Loop through all current local sources and stop the sources.
916 LocalStreamSources::iterator sources_it = local_sources_.begin();
917 while (sources_it != local_sources_.end()) {
918 StopLocalSource(*sources_it, true);
919 sources_it = local_sources_.erase(sources_it);
920 }
921 }
922
OnLocalSourceStopped(const blink::WebMediaStreamSource & source)923 void UserMediaClientImpl::OnLocalSourceStopped(
924 const blink::WebMediaStreamSource& source) {
925 DCHECK(CalledOnValidThread());
926 DVLOG(1) << "UserMediaClientImpl::OnLocalSourceStopped";
927
928 bool device_found = false;
929 for (LocalStreamSources::iterator device_it = local_sources_.begin();
930 device_it != local_sources_.end(); ++device_it) {
931 if (device_it->id() == source.id()) {
932 device_found = true;
933 local_sources_.erase(device_it);
934 break;
935 }
936 }
937 CHECK(device_found);
938
939 MediaStreamSource* source_impl =
940 static_cast<MediaStreamSource*>(source.extraData());
941 media_stream_dispatcher_->StopStreamDevice(source_impl->device_info());
942 }
943
StopLocalSource(const blink::WebMediaStreamSource & source,bool notify_dispatcher)944 void UserMediaClientImpl::StopLocalSource(
945 const blink::WebMediaStreamSource& source,
946 bool notify_dispatcher) {
947 MediaStreamSource* source_impl =
948 static_cast<MediaStreamSource*>(source.extraData());
949 DVLOG(1) << "UserMediaClientImpl::StopLocalSource("
950 << "{device_id = " << source_impl->device_info().device.id << "})";
951
952 if (notify_dispatcher)
953 media_stream_dispatcher_->StopStreamDevice(source_impl->device_info());
954
955 source_impl->ResetSourceStoppedCallback();
956 source_impl->StopSource();
957 }
958
UserMediaRequestInfo(int request_id,const blink::WebUserMediaRequest & request,bool enable_automatic_output_device_selection)959 UserMediaClientImpl::UserMediaRequestInfo::UserMediaRequestInfo(
960 int request_id,
961 const blink::WebUserMediaRequest& request,
962 bool enable_automatic_output_device_selection)
963 : request_id(request_id),
964 generated(false),
965 enable_automatic_output_device_selection(
966 enable_automatic_output_device_selection),
967 request(request),
968 request_result_(MEDIA_DEVICE_OK),
969 request_result_name_("") {
970 }
971
~UserMediaRequestInfo()972 UserMediaClientImpl::UserMediaRequestInfo::~UserMediaRequestInfo() {
973 DVLOG(1) << "~UserMediaRequestInfo";
974 }
975
StartAudioTrack(const blink::WebMediaStreamTrack & track,const blink::WebMediaConstraints & constraints)976 void UserMediaClientImpl::UserMediaRequestInfo::StartAudioTrack(
977 const blink::WebMediaStreamTrack& track,
978 const blink::WebMediaConstraints& constraints) {
979 DCHECK(track.source().type() == blink::WebMediaStreamSource::TypeAudio);
980 MediaStreamAudioSource* native_source =
981 static_cast <MediaStreamAudioSource*>(track.source().extraData());
982 DCHECK(native_source);
983
984 sources_.push_back(track.source());
985 sources_waiting_for_callback_.push_back(native_source);
986 native_source->AddTrack(
987 track, constraints, base::Bind(
988 &UserMediaClientImpl::UserMediaRequestInfo::OnTrackStarted,
989 AsWeakPtr()));
990 }
991
992 blink::WebMediaStreamTrack
CreateAndStartVideoTrack(const blink::WebMediaStreamSource & source,const blink::WebMediaConstraints & constraints)993 UserMediaClientImpl::UserMediaRequestInfo::CreateAndStartVideoTrack(
994 const blink::WebMediaStreamSource& source,
995 const blink::WebMediaConstraints& constraints) {
996 DCHECK(source.type() == blink::WebMediaStreamSource::TypeVideo);
997 MediaStreamVideoSource* native_source =
998 MediaStreamVideoSource::GetVideoSource(source);
999 DCHECK(native_source);
1000 sources_.push_back(source);
1001 sources_waiting_for_callback_.push_back(native_source);
1002 return MediaStreamVideoTrack::CreateVideoTrack(
1003 native_source, constraints, base::Bind(
1004 &UserMediaClientImpl::UserMediaRequestInfo::OnTrackStarted,
1005 AsWeakPtr()),
1006 true);
1007 }
1008
CallbackOnTracksStarted(const ResourcesReady & callback)1009 void UserMediaClientImpl::UserMediaRequestInfo::CallbackOnTracksStarted(
1010 const ResourcesReady& callback) {
1011 DCHECK(ready_callback_.is_null());
1012 ready_callback_ = callback;
1013 CheckAllTracksStarted();
1014 }
1015
OnTrackStarted(MediaStreamSource * source,MediaStreamRequestResult result,const blink::WebString & result_name)1016 void UserMediaClientImpl::UserMediaRequestInfo::OnTrackStarted(
1017 MediaStreamSource* source,
1018 MediaStreamRequestResult result,
1019 const blink::WebString& result_name) {
1020 DVLOG(1) << "OnTrackStarted result " << result;
1021 std::vector<MediaStreamSource*>::iterator it =
1022 std::find(sources_waiting_for_callback_.begin(),
1023 sources_waiting_for_callback_.end(),
1024 source);
1025 DCHECK(it != sources_waiting_for_callback_.end());
1026 sources_waiting_for_callback_.erase(it);
1027 // All tracks must be started successfully. Otherwise the request is a
1028 // failure.
1029 if (result != MEDIA_DEVICE_OK) {
1030 request_result_ = result;
1031 request_result_name_ = result_name;
1032 }
1033
1034 CheckAllTracksStarted();
1035 }
1036
CheckAllTracksStarted()1037 void UserMediaClientImpl::UserMediaRequestInfo::CheckAllTracksStarted() {
1038 if (!ready_callback_.is_null() && sources_waiting_for_callback_.empty()) {
1039 ready_callback_.Run(this, request_result_, request_result_name_);
1040 }
1041 }
1042
IsSourceUsed(const blink::WebMediaStreamSource & source) const1043 bool UserMediaClientImpl::UserMediaRequestInfo::IsSourceUsed(
1044 const blink::WebMediaStreamSource& source) const {
1045 for (std::vector<blink::WebMediaStreamSource>::const_iterator source_it =
1046 sources_.begin();
1047 source_it != sources_.end(); ++source_it) {
1048 if (source_it->id() == source.id())
1049 return true;
1050 }
1051 return false;
1052 }
1053
RemoveSource(const blink::WebMediaStreamSource & source)1054 void UserMediaClientImpl::UserMediaRequestInfo::RemoveSource(
1055 const blink::WebMediaStreamSource& source) {
1056 for (std::vector<blink::WebMediaStreamSource>::iterator it =
1057 sources_.begin();
1058 it != sources_.end(); ++it) {
1059 if (source.id() == it->id()) {
1060 sources_.erase(it);
1061 return;
1062 }
1063 }
1064 }
1065
HasPendingSources() const1066 bool UserMediaClientImpl::UserMediaRequestInfo::HasPendingSources() const {
1067 return !sources_waiting_for_callback_.empty();
1068 }
1069
1070 } // namespace content
1071