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