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