• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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