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
34namespace content {
35namespace {
36
37void 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
57static int g_next_request_id  = 0;
58
59}  // namespace
60
61struct UserMediaClientImpl::MediaDevicesRequestInfo {
62  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
74  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
86  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
105UserMediaClientImpl::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
117UserMediaClientImpl::~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
124void 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
214void 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
227void 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
273void 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
283void 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.
324void 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
381void 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
398void 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
445void 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.
488void 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.
508void 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
536void 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
579MediaStreamVideoSource* 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
588void 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
606void 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
646void 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
663void 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
701void 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
710void UserMediaClientImpl::OnDeviceOpenFailed(int request_id) {
711  DVLOG(1) << "UserMediaClientImpl::VideoDeviceOpenFailed("
712           << request_id << ")";
713  NOTIMPLEMENTED();
714}
715
716void 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
724void 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
763void 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
781void UserMediaClientImpl::EnumerateDevicesSucceded(
782    blink::WebMediaDevicesRequest* request,
783    blink::WebVector<blink::WebMediaDeviceInfo>& devices) {
784  request->requestSucceeded(devices);
785}
786
787void UserMediaClientImpl::EnumerateSourcesSucceded(
788    blink::WebMediaStreamTrackSourcesRequest* request,
789    blink::WebVector<blink::WebSourceInfo>& sources) {
790  request->requestSucceeded(sources);
791}
792
793const 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
809UserMediaClientImpl::UserMediaRequestInfo*
810UserMediaClientImpl::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
819UserMediaClientImpl::UserMediaRequestInfo*
820UserMediaClientImpl::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
830void 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
842void 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
866UserMediaClientImpl::MediaDevicesRequestInfo*
867UserMediaClientImpl::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
880UserMediaClientImpl::MediaDevicesRequestInfo*
881UserMediaClientImpl::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
891void 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
911void 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
923void 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
944void 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
959UserMediaClientImpl::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
972UserMediaClientImpl::UserMediaRequestInfo::~UserMediaRequestInfo() {
973  DVLOG(1) << "~UserMediaRequestInfo";
974}
975
976void 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
992blink::WebMediaStreamTrack
993UserMediaClientImpl::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
1009void UserMediaClientImpl::UserMediaRequestInfo::CallbackOnTracksStarted(
1010    const ResourcesReady& callback) {
1011  DCHECK(ready_callback_.is_null());
1012  ready_callback_ = callback;
1013  CheckAllTracksStarted();
1014}
1015
1016void 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
1037void 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
1043bool 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
1054void 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
1066bool UserMediaClientImpl::UserMediaRequestInfo::HasPendingSources() const {
1067  return !sources_waiting_for_callback_.empty();
1068}
1069
1070}  // namespace content
1071