普通文本  |  865行  |  30.39 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/media/media_stream_impl.h"

#include <utility>

#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "content/renderer/media/media_stream_audio_renderer.h"
#include "content/renderer/media/media_stream_dependency_factory.h"
#include "content/renderer/media/media_stream_dispatcher.h"
#include "content/renderer/media/media_stream_extra_data.h"
#include "content/renderer/media/media_stream_source_extra_data.h"
#include "content/renderer/media/rtc_video_renderer.h"
#include "content/renderer/media/webrtc_audio_capturer.h"
#include "content/renderer/media/webrtc_audio_renderer.h"
#include "content/renderer/media/webrtc_local_audio_renderer.h"
#include "content/renderer/media/webrtc_logging.h"
#include "content/renderer/media/webrtc_uma_histograms.h"
#include "content/renderer/render_thread_impl.h"
#include "media/base/audio_hardware_config.h"
#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebMediaStreamRegistry.h"

namespace content {
namespace {

void CopyStreamConstraints(const blink::WebMediaConstraints& constraints,
                           StreamOptions::Constraints* mandatory,
                           StreamOptions::Constraints* optional) {
  blink::WebVector<blink::WebMediaConstraint> mandatory_constraints;
  constraints.getMandatoryConstraints(mandatory_constraints);
  for (size_t i = 0; i < mandatory_constraints.size(); i++) {
    mandatory->push_back(StreamOptions::Constraint(
        mandatory_constraints[i].m_name.utf8(),
        mandatory_constraints[i].m_value.utf8()));
  }

  blink::WebVector<blink::WebMediaConstraint> optional_constraints;
  constraints.getOptionalConstraints(optional_constraints);
  for (size_t i = 0; i < optional_constraints.size(); i++) {
    optional->push_back(StreamOptions::Constraint(
        optional_constraints[i].m_name.utf8(),
        optional_constraints[i].m_value.utf8()));
  }
}

static int g_next_request_id  = 0;

webrtc::MediaStreamInterface* GetNativeMediaStream(
    const blink::WebMediaStream& web_stream) {
  content::MediaStreamExtraData* extra_data =
      static_cast<content::MediaStreamExtraData*>(web_stream.extraData());
  if (!extra_data)
    return NULL;
  return extra_data->stream().get();
}

void GetDefaultOutputDeviceParams(
    int* output_sample_rate, int* output_buffer_size) {
  // Fetch the default audio output hardware config.
  media::AudioHardwareConfig* hardware_config =
      RenderThreadImpl::current()->GetAudioHardwareConfig();
  *output_sample_rate = hardware_config->GetOutputSampleRate();
  *output_buffer_size = hardware_config->GetOutputBufferSize();
}

void RemoveSource(const blink::WebMediaStreamSource& source,
                  std::vector<blink::WebMediaStreamSource>* sources) {
  for (std::vector<blink::WebMediaStreamSource>::iterator it =
           sources->begin();
       it != sources->end(); ++it) {
    if (source.id() == it->id()) {
      sources->erase(it);
      return;
    }
  }
}

}  // namespace

MediaStreamImpl::MediaStreamImpl(
    RenderView* render_view,
    MediaStreamDispatcher* media_stream_dispatcher,
    MediaStreamDependencyFactory* dependency_factory)
    : RenderViewObserver(render_view),
      dependency_factory_(dependency_factory),
      media_stream_dispatcher_(media_stream_dispatcher) {
}

MediaStreamImpl::~MediaStreamImpl() {
}

void MediaStreamImpl::requestUserMedia(
    const blink::WebUserMediaRequest& user_media_request) {
  // Save histogram data so we can see how much GetUserMedia is used.
  // The histogram counts the number of calls to the JS API
  // webGetUserMedia.
  UpdateWebRTCMethodCount(WEBKIT_GET_USER_MEDIA);
  DCHECK(CalledOnValidThread());
  int request_id = g_next_request_id++;
  StreamOptions options;
  blink::WebFrame* frame = NULL;
  GURL security_origin;
  bool enable_automatic_output_device_selection = false;

  // |user_media_request| can't be mocked. So in order to test at all we check
  // if it isNull.
  if (user_media_request.isNull()) {
    // We are in a test.
    options.audio_requested = true;
    options.video_requested = true;
  } else {
    if (user_media_request.audio()) {
      options.audio_requested = true;
      CopyStreamConstraints(user_media_request.audioConstraints(),
                            &options.mandatory_audio,
                            &options.optional_audio);

      // Check if this input device should be used to select a matching output
      // device for audio rendering.
      std::string enable;
      if (options.GetFirstAudioConstraintByName(
              kMediaStreamRenderToAssociatedSink, &enable, NULL) &&
          LowerCaseEqualsASCII(enable, "true")) {
        enable_automatic_output_device_selection = true;
      }
    }
    if (user_media_request.video()) {
      options.video_requested = true;
      CopyStreamConstraints(user_media_request.videoConstraints(),
                            &options.mandatory_video,
                            &options.optional_video);
    }

    security_origin = GURL(user_media_request.securityOrigin().toString());
    // Get the WebFrame that requested a MediaStream.
    // The frame is needed to tell the MediaStreamDispatcher when a stream goes
    // out of scope.
    frame = user_media_request.ownerDocument().frame();
    DCHECK(frame);
  }

  DVLOG(1) << "MediaStreamImpl::requestUserMedia(" << request_id << ", [ "
           << "audio=" << (options.audio_requested)
           << " select associated sink: "
           << enable_automatic_output_device_selection
           << ", video=" << (options.video_requested) << " ], "
           << security_origin.spec() << ")";

  std::string audio_device_id;
  bool mandatory_audio;
  options.GetFirstAudioConstraintByName(kMediaStreamSourceInfoId,
                                        &audio_device_id, &mandatory_audio);
  std::string video_device_id;
  bool mandatory_video;
  options.GetFirstVideoConstraintByName(kMediaStreamSourceInfoId,
                                        &video_device_id, &mandatory_video);

  WebRtcLogMessage(base::StringPrintf(
      "MSI::requestUserMedia. request_id=%d"
      ", audio source id=%s mandatory= %s "
      ", video source id=%s mandatory= %s",
      request_id,
      audio_device_id.c_str(),
      mandatory_audio ? "true":"false",
      video_device_id.c_str(),
      mandatory_video ? "true":"false"));

  user_media_requests_.push_back(
      new UserMediaRequestInfo(request_id, frame, user_media_request,
          enable_automatic_output_device_selection));

  media_stream_dispatcher_->GenerateStream(
      request_id,
      AsWeakPtr(),
      options,
      security_origin);
}

void MediaStreamImpl::cancelUserMediaRequest(
    const blink::WebUserMediaRequest& user_media_request) {
  DCHECK(CalledOnValidThread());
  UserMediaRequestInfo* request = FindUserMediaRequestInfo(user_media_request);
  if (request) {
    // We can't abort the stream generation process.
    // Instead, erase the request. Once the stream is generated we will stop the
    // stream if the request does not exist.
    DeleteUserMediaRequestInfo(request);
  }
}

blink::WebMediaStream MediaStreamImpl::GetMediaStream(
    const GURL& url) {
  return blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url);
}

bool MediaStreamImpl::IsMediaStream(const GURL& url) {
  blink::WebMediaStream web_stream(
      blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url));

  if (web_stream.isNull() || !web_stream.extraData())
    return false;  // This is not a valid stream.

  webrtc::MediaStreamInterface* stream = GetNativeMediaStream(web_stream);
  return (stream &&
      (!stream->GetVideoTracks().empty() || !stream->GetAudioTracks().empty()));
}

scoped_refptr<VideoFrameProvider>
MediaStreamImpl::GetVideoFrameProvider(
    const GURL& url,
    const base::Closure& error_cb,
    const VideoFrameProvider::RepaintCB& repaint_cb) {
  DCHECK(CalledOnValidThread());
  blink::WebMediaStream web_stream(GetMediaStream(url));

  if (web_stream.isNull() || !web_stream.extraData())
    return NULL;  // This is not a valid stream.

  DVLOG(1) << "MediaStreamImpl::GetVideoFrameProvider stream:"
           << UTF16ToUTF8(web_stream.id());

  blink::WebVector<blink::WebMediaStreamTrack> video_tracks;
  web_stream.videoTracks(video_tracks);
  if (video_tracks.isEmpty())
    return NULL;

  return new RTCVideoRenderer(video_tracks[0], error_cb, repaint_cb);
}

scoped_refptr<MediaStreamAudioRenderer>
MediaStreamImpl::GetAudioRenderer(const GURL& url) {
  DCHECK(CalledOnValidThread());
  blink::WebMediaStream web_stream(GetMediaStream(url));

  if (web_stream.isNull() || !web_stream.extraData())
    return NULL;  // This is not a valid stream.

  DVLOG(1) << "MediaStreamImpl::GetAudioRenderer stream:"
           << UTF16ToUTF8(web_stream.id());

  MediaStreamExtraData* extra_data =
      static_cast<MediaStreamExtraData*>(web_stream.extraData());

  if (extra_data->is_local()) {
    // Create the local audio renderer if the stream contains audio tracks.
    blink::WebVector<blink::WebMediaStreamTrack> audio_tracks;
    web_stream.audioTracks(audio_tracks);
    if (audio_tracks.isEmpty())
      return NULL;

    // TODO(xians): Add support for the case that the media stream contains
    // multiple audio tracks.
    return CreateLocalAudioRenderer(audio_tracks[0]);
  }

  webrtc::MediaStreamInterface* stream = extra_data->stream().get();
  if (!stream || stream->GetAudioTracks().empty())
    return NULL;

  // This is a remote media stream.
  WebRtcAudioDeviceImpl* audio_device =
      dependency_factory_->GetWebRtcAudioDevice();

  // Share the existing renderer if any, otherwise create a new one.
  scoped_refptr<WebRtcAudioRenderer> renderer(audio_device->renderer());
  if (!renderer.get()) {
    renderer = CreateRemoteAudioRenderer(extra_data->stream().get());

    if (renderer.get() && !audio_device->SetAudioRenderer(renderer.get()))
      renderer = NULL;
  }

  return renderer.get() ? renderer->CreateSharedAudioRendererProxy() : NULL;
}

// Callback from MediaStreamDispatcher.
// The requested stream have been generated by the MediaStreamDispatcher.
void MediaStreamImpl::OnStreamGenerated(
    int request_id,
    const std::string& label,
    const StreamDeviceInfoArray& audio_array,
    const StreamDeviceInfoArray& video_array) {
  DCHECK(CalledOnValidThread());
  DVLOG(1) << "MediaStreamImpl::OnStreamGenerated stream:" << label;

  UserMediaRequestInfo* request_info = FindUserMediaRequestInfo(request_id);
  if (!request_info) {
    // This can happen if the request is canceled or the frame reloads while
    // MediaStreamDispatcher is processing the request.
    // Only stop the device if the device is not used in another MediaStream.
    for (StreamDeviceInfoArray::const_iterator device_it = audio_array.begin();
         device_it != audio_array.end(); ++device_it) {
      if (!FindLocalSource(*device_it))
        media_stream_dispatcher_->StopStreamDevice(*device_it);
    }

    for (StreamDeviceInfoArray::const_iterator device_it = video_array.begin();
         device_it != video_array.end(); ++device_it) {
      if (!FindLocalSource(*device_it))
        media_stream_dispatcher_->StopStreamDevice(*device_it);
    }

    DVLOG(1) << "Request ID not found";
    return;
  }
  request_info->generated = true;

  blink::WebVector<blink::WebMediaStreamSource> audio_source_vector(
        audio_array.size());

  // Log the device names for this request.
  for (StreamDeviceInfoArray::const_iterator it = audio_array.begin();
       it != audio_array.end(); ++it) {
    WebRtcLogMessage(base::StringPrintf(
        "Generated media stream for request id %d contains audio device name"
        " \"%s\"",
        request_id,
        it->device.name.c_str()));
  }

  StreamDeviceInfoArray overridden_audio_array = audio_array;
  if (!request_info->enable_automatic_output_device_selection) {
    // If the GetUserMedia request did not explicitly set the constraint
    // kMediaStreamRenderToAssociatedSink, the output device parameters must
    // be removed.
    for (StreamDeviceInfoArray::iterator it = overridden_audio_array.begin();
         it != overridden_audio_array.end(); ++it) {
      it->device.matched_output_device_id = "";
      it->device.matched_output = MediaStreamDevice::AudioDeviceParameters();
    }
  }
  CreateWebKitSourceVector(label, overridden_audio_array,
                           blink::WebMediaStreamSource::TypeAudio,
                           request_info->frame,
                           audio_source_vector);

  blink::WebVector<blink::WebMediaStreamSource> video_source_vector(
      video_array.size());
  CreateWebKitSourceVector(label, video_array,
                           blink::WebMediaStreamSource::TypeVideo,
                           request_info->frame,
                           video_source_vector);
  blink::WebUserMediaRequest* request = &(request_info->request);
  blink::WebString webkit_id = UTF8ToUTF16(label);
  blink::WebMediaStream* web_stream = &(request_info->web_stream);

  blink::WebVector<blink::WebMediaStreamTrack> audio_track_vector(
      audio_array.size());
  for (size_t i = 0; i < audio_track_vector.size(); ++i) {
    audio_track_vector[i].initialize(audio_source_vector[i]);
    request_info->sources.push_back(audio_source_vector[i]);
  }

  blink::WebVector<blink::WebMediaStreamTrack> video_track_vector(
      video_array.size());
  for (size_t i = 0; i < video_track_vector.size(); ++i) {
    video_track_vector[i].initialize(video_source_vector[i]);
    request_info->sources.push_back(video_source_vector[i]);
  }

  web_stream->initialize(webkit_id, audio_track_vector,
                         video_track_vector);

  // WebUserMediaRequest don't have an implementation in unit tests.
  // Therefore we need to check for isNull here.
  blink::WebMediaConstraints audio_constraints = request->isNull() ?
      blink::WebMediaConstraints() : request->audioConstraints();
  blink::WebMediaConstraints video_constraints = request->isNull() ?
      blink::WebMediaConstraints() : request->videoConstraints();

  dependency_factory_->CreateNativeMediaSources(
      RenderViewObserver::routing_id(),
      audio_constraints, video_constraints, web_stream,
      base::Bind(&MediaStreamImpl::OnCreateNativeSourcesComplete, AsWeakPtr()));
}

// Callback from MediaStreamDispatcher.
// The requested stream failed to be generated.
void MediaStreamImpl::OnStreamGenerationFailed(int request_id) {
  DCHECK(CalledOnValidThread());
  DVLOG(1) << "MediaStreamImpl::OnStreamGenerationFailed("
           << request_id << ")";
  UserMediaRequestInfo* request_info = FindUserMediaRequestInfo(request_id);
  if (!request_info) {
    // This can happen if the request is canceled or the frame reloads while
    // MediaStreamDispatcher is processing the request.
    DVLOG(1) << "Request ID not found";
    return;
  }
  CompleteGetUserMediaRequest(request_info->web_stream,
                              &request_info->request,
                              false);
  DeleteUserMediaRequestInfo(request_info);
}

// Callback from MediaStreamDispatcher.
// The browser process has stopped a device used by a MediaStream.
void MediaStreamImpl::OnDeviceStopped(
    const std::string& label,
    const StreamDeviceInfo& device_info) {
  DCHECK(CalledOnValidThread());
  DVLOG(1) << "MediaStreamImpl::OnDeviceStopped("
           << "{device_id = " << device_info.device.id << "})";

  const blink::WebMediaStreamSource* source_ptr = FindLocalSource(device_info);
  if (!source_ptr) {
    // This happens if the same device is used in several guM requests or
    // if a user happen stop a track from JS at the same time
    // as the underlying media device is unplugged from the system.
    return;
  }
  // By creating |source| it is guaranteed that the blink::WebMediaStreamSource
  // object is valid during the cleanup.
  blink::WebMediaStreamSource source(*source_ptr);
  StopLocalSource(source, false);

  for (LocalStreamSources::iterator device_it = local_sources_.begin();
       device_it != local_sources_.end(); ++device_it) {
    if (device_it->source.id() == source.id()) {
      local_sources_.erase(device_it);
      break;
    }
  }

  // Remove the reference to this source from all |user_media_requests_|.
  // TODO(perkj): The below is not necessary once we don't need to support
  // MediaStream::Stop().
  UserMediaRequests::iterator it = user_media_requests_.begin();
  while (it != user_media_requests_.end()) {
    RemoveSource(source, &(*it)->sources);
    if ((*it)->sources.empty()) {
      it = user_media_requests_.erase(it);
    } else {
      ++it;
    }
  }
}

void MediaStreamImpl::CreateWebKitSourceVector(
    const std::string& label,
    const StreamDeviceInfoArray& devices,
    blink::WebMediaStreamSource::Type type,
    blink::WebFrame* frame,
    blink::WebVector<blink::WebMediaStreamSource>& webkit_sources) {
  CHECK_EQ(devices.size(), webkit_sources.size());
  for (size_t i = 0; i < devices.size(); ++i) {
    const blink::WebMediaStreamSource* existing_source =
        FindLocalSource(devices[i]);
    if (existing_source) {
      webkit_sources[i] = *existing_source;
      DVLOG(1) << "Source already exist. Reusing source with id "
               << webkit_sources[i]. id().utf8();
      continue;
    }
    webkit_sources[i].initialize(
        UTF8ToUTF16(devices[i].device.id),
        type,
        UTF8ToUTF16(devices[i].device.name));
    MediaStreamSourceExtraData* source_extra_data(
        new content::MediaStreamSourceExtraData(
            devices[i],
            base::Bind(&MediaStreamImpl::OnLocalSourceStop, AsWeakPtr())));
    // |source_extra_data| is owned by webkit_sources[i].
    webkit_sources[i].setExtraData(source_extra_data);
    local_sources_.push_back(LocalStreamSource(frame, webkit_sources[i]));
  }
}

// Callback from MediaStreamDependencyFactory when the sources in |web_stream|
// have been generated.
void MediaStreamImpl::OnCreateNativeSourcesComplete(
    blink::WebMediaStream* web_stream,
    bool request_succeeded) {
  UserMediaRequestInfo* request_info = FindUserMediaRequestInfo(web_stream);
  if (!request_info) {
    // This can happen if the request is canceled or the frame reloads while
    // MediaStreamDependencyFactory is creating the sources.
    DVLOG(1) << "Request ID not found";
    return;
  }

  // Create a native representation of the stream.
  if (request_succeeded) {
    dependency_factory_->CreateNativeLocalMediaStream(
        web_stream,
        base::Bind(&MediaStreamImpl::OnLocalMediaStreamStop, AsWeakPtr()));
  }
  DVLOG(1) << "MediaStreamImpl::OnCreateNativeSourcesComplete("
           << "{request_id = " << request_info->request_id << "} "
           << "{request_succeeded = " << request_succeeded << "})";
  CompleteGetUserMediaRequest(request_info->web_stream, &request_info->request,
                              request_succeeded);
  if (!request_succeeded) {
    // TODO(perkj): Once we don't support MediaStream::Stop the |request_info|
    // can be deleted even if the request succeeds.
    DeleteUserMediaRequestInfo(request_info);
    StopUnreferencedSources(true);
  }
}

void MediaStreamImpl::OnDevicesEnumerated(
    int request_id,
    const StreamDeviceInfoArray& device_array) {
  DVLOG(1) << "MediaStreamImpl::OnDevicesEnumerated("
           << request_id << ")";
  NOTIMPLEMENTED();
}

void MediaStreamImpl::OnDeviceOpened(
    int request_id,
    const std::string& label,
    const StreamDeviceInfo& video_device) {
  DVLOG(1) << "MediaStreamImpl::OnDeviceOpened("
           << request_id << ", " << label << ")";
  NOTIMPLEMENTED();
}

void MediaStreamImpl::OnDeviceOpenFailed(int request_id) {
  DVLOG(1) << "MediaStreamImpl::VideoDeviceOpenFailed("
           << request_id << ")";
  NOTIMPLEMENTED();
}

void MediaStreamImpl::CompleteGetUserMediaRequest(
    const blink::WebMediaStream& stream,
    blink::WebUserMediaRequest* request_info,
    bool request_succeeded) {
  if (request_succeeded) {
    request_info->requestSucceeded(stream);
  } else {
    request_info->requestFailed();
  }
}

const blink::WebMediaStreamSource* MediaStreamImpl::FindLocalSource(
    const StreamDeviceInfo& device) const {
  for (LocalStreamSources::const_iterator it = local_sources_.begin();
       it != local_sources_.end(); ++it) {
    MediaStreamSourceExtraData* extra_data =
        static_cast<MediaStreamSourceExtraData*>(
            it->source.extraData());
    const StreamDeviceInfo& active_device = extra_data->device_info();
    if (active_device.device.id == device.device.id &&
        active_device.device.type == device.device.type &&
        active_device.session_id == device.session_id) {
      return &it->source;
    }
  }
  return NULL;
}

bool MediaStreamImpl::FindSourceInRequests(
    const blink::WebMediaStreamSource& source) const {
  for (UserMediaRequests::const_iterator req_it = user_media_requests_.begin();
       req_it != user_media_requests_.end(); ++req_it) {
    const std::vector<blink::WebMediaStreamSource>& sources =
        (*req_it)->sources;
    for (std::vector<blink::WebMediaStreamSource>::const_iterator source_it =
             sources.begin();
         source_it != sources.end(); ++source_it) {
      if (source_it->id() == source.id()) {
        return true;
      }
    }
  }
  return false;
}

MediaStreamImpl::UserMediaRequestInfo*
MediaStreamImpl::FindUserMediaRequestInfo(int request_id) {
  UserMediaRequests::iterator it = user_media_requests_.begin();
  for (; it != user_media_requests_.end(); ++it) {
    if ((*it)->request_id == request_id)
      return (*it);
  }
  return NULL;
}

MediaStreamImpl::UserMediaRequestInfo*
MediaStreamImpl::FindUserMediaRequestInfo(
    const blink::WebUserMediaRequest& request) {
  UserMediaRequests::iterator it = user_media_requests_.begin();
  for (; it != user_media_requests_.end(); ++it) {
    if ((*it)->request == request)
      return (*it);
  }
  return NULL;
}

MediaStreamImpl::UserMediaRequestInfo*
MediaStreamImpl::FindUserMediaRequestInfo(const std::string& label) {
  UserMediaRequests::iterator it = user_media_requests_.begin();
  for (; it != user_media_requests_.end(); ++it) {
    if ((*it)->generated && (*it)->web_stream.id() == UTF8ToUTF16(label))
      return (*it);
  }
  return NULL;
}

MediaStreamImpl::UserMediaRequestInfo*
MediaStreamImpl::FindUserMediaRequestInfo(
    blink::WebMediaStream* web_stream) {
  UserMediaRequests::iterator it = user_media_requests_.begin();
  for (; it != user_media_requests_.end(); ++it) {
    if (&((*it)->web_stream) == web_stream)
      return  (*it);
  }
  return NULL;
}

void MediaStreamImpl::DeleteUserMediaRequestInfo(
    UserMediaRequestInfo* request) {
  UserMediaRequests::iterator it = user_media_requests_.begin();
  for (; it != user_media_requests_.end(); ++it) {
    if ((*it) == request) {
      user_media_requests_.erase(it);
      return;
    }
  }
  NOTREACHED();
}

void MediaStreamImpl::FrameDetached(blink::WebFrame* frame) {
  // Do same thing as FrameWillClose.
  FrameWillClose(frame);
}

void MediaStreamImpl::FrameWillClose(blink::WebFrame* frame) {
  // Loop through all UserMediaRequests and find the requests that belong to the
  // frame that is being closed.
  UserMediaRequests::iterator request_it = user_media_requests_.begin();
  while (request_it != user_media_requests_.end()) {
    if ((*request_it)->frame == frame) {
      DVLOG(1) << "MediaStreamImpl::FrameWillClose: "
               << "Cancel user media request " << (*request_it)->request_id;
      // If the request is not generated, it means that a request
      // has been sent to the MediaStreamDispatcher to generate a stream
      // but MediaStreamDispatcher has not yet responded and we need to cancel
      // the request.
      if (!(*request_it)->generated) {
        media_stream_dispatcher_->CancelGenerateStream(
            (*request_it)->request_id, AsWeakPtr());
      }
      request_it = user_media_requests_.erase(request_it);
    } else {
      ++request_it;
    }
  }

  // Loop through all current local sources and stop the sources that were
  // created by the frame that will be closed.
  LocalStreamSources::iterator sources_it = local_sources_.begin();
  while (sources_it != local_sources_.end()) {
    if (sources_it->frame == frame) {
      StopLocalSource(sources_it->source, true);
      sources_it = local_sources_.erase(sources_it);
    } else {
      ++sources_it;
    }
  }
}

void MediaStreamImpl::OnLocalMediaStreamStop(
    const std::string& label) {
  DVLOG(1) << "MediaStreamImpl::OnLocalMediaStreamStop(" << label << ")";

  UserMediaRequestInfo* user_media_request = FindUserMediaRequestInfo(label);
  if (user_media_request) {
    DeleteUserMediaRequestInfo(user_media_request);
  }
  StopUnreferencedSources(true);
}

void MediaStreamImpl::OnLocalSourceStop(
    const blink::WebMediaStreamSource& source) {
  DCHECK(CalledOnValidThread());

  StopLocalSource(source, true);

  bool device_found = false;
  for (LocalStreamSources::iterator device_it = local_sources_.begin();
       device_it != local_sources_.end(); ++device_it) {
    if (device_it->source.id()  == source.id()) {
      device_found = true;
      local_sources_.erase(device_it);
      break;
    }
  }
  CHECK(device_found);

  // Remove the reference to this source from all |user_media_requests_|.
  // TODO(perkj): The below is not necessary once we don't need to support
  // MediaStream::Stop().
  UserMediaRequests::iterator it = user_media_requests_.begin();
  while (it != user_media_requests_.end()) {
    RemoveSource(source, &(*it)->sources);
    if ((*it)->sources.empty()) {
      it = user_media_requests_.erase(it);
    } else {
      ++it;
    }
  }
}

void MediaStreamImpl::StopLocalSource(
    const blink::WebMediaStreamSource& source,
    bool notify_dispatcher) {
  MediaStreamSourceExtraData* extra_data =
        static_cast<MediaStreamSourceExtraData*> (source.extraData());
  CHECK(extra_data);
  DVLOG(1) << "MediaStreamImpl::StopLocalSource("
           << "{device_id = " << extra_data->device_info().device.id << "})";

  if (source.type() == blink::WebMediaStreamSource::TypeAudio) {
    if (extra_data->GetAudioCapturer()) {
      extra_data->GetAudioCapturer()->Stop();
    }
  }

  if (notify_dispatcher)
    media_stream_dispatcher_->StopStreamDevice(extra_data->device_info());

  blink::WebMediaStreamSource writable_source(source);
  writable_source.setReadyState(
      blink::WebMediaStreamSource::ReadyStateEnded);
  writable_source.setExtraData(NULL);
}

void MediaStreamImpl::StopUnreferencedSources(bool notify_dispatcher) {
  LocalStreamSources::iterator source_it = local_sources_.begin();
  while (source_it != local_sources_.end()) {
    if (!FindSourceInRequests(source_it->source)) {
      StopLocalSource(source_it->source, notify_dispatcher);
      source_it = local_sources_.erase(source_it);
    } else {
      ++source_it;
    }
  }
}

scoped_refptr<WebRtcAudioRenderer> MediaStreamImpl::CreateRemoteAudioRenderer(
    webrtc::MediaStreamInterface* stream) {
  if (stream->GetAudioTracks().empty())
    return NULL;

  DVLOG(1) << "MediaStreamImpl::CreateRemoteAudioRenderer label:"
           << stream->label();

  // TODO(tommi): Change the default value of session_id to be
  // StreamDeviceInfo::kNoId.  Also update AudioOutputDevice etc.
  int session_id = 0, sample_rate = 0, buffer_size = 0;
  if (!GetAuthorizedDeviceInfoForAudioRenderer(&session_id,
                                               &sample_rate,
                                               &buffer_size)) {
    GetDefaultOutputDeviceParams(&sample_rate, &buffer_size);
  }

  return new WebRtcAudioRenderer(RenderViewObserver::routing_id(),
      session_id, sample_rate, buffer_size);
}

scoped_refptr<WebRtcLocalAudioRenderer>
MediaStreamImpl::CreateLocalAudioRenderer(
    const blink::WebMediaStreamTrack& audio_track) {
  DVLOG(1) << "MediaStreamImpl::CreateLocalAudioRenderer";

  int session_id = 0, sample_rate = 0, buffer_size = 0;
  if (!GetAuthorizedDeviceInfoForAudioRenderer(&session_id,
                                               &sample_rate,
                                               &buffer_size)) {
    GetDefaultOutputDeviceParams(&sample_rate, &buffer_size);
  }

  // Create a new WebRtcLocalAudioRenderer instance and connect it to the
  // existing WebRtcAudioCapturer so that the renderer can use it as source.
  return new WebRtcLocalAudioRenderer(
      audio_track,
      RenderViewObserver::routing_id(),
      session_id,
      buffer_size);
}

bool MediaStreamImpl::GetAuthorizedDeviceInfoForAudioRenderer(
    int* session_id,
    int* output_sample_rate,
    int* output_frames_per_buffer) {
  DCHECK(CalledOnValidThread());

  WebRtcAudioDeviceImpl* audio_device =
      dependency_factory_->GetWebRtcAudioDevice();
  if (!audio_device)
    return false;

  if (!audio_device->GetDefaultCapturer())
    return false;

  return audio_device->GetDefaultCapturer()->GetPairedOutputParameters(
      session_id,
      output_sample_rate,
      output_frames_per_buffer);
}

MediaStreamSourceExtraData::MediaStreamSourceExtraData(
    const StreamDeviceInfo& device_info,
    const SourceStopCallback& stop_callback)
    : device_info_(device_info),
      stop_callback_(stop_callback) {
}

MediaStreamSourceExtraData::MediaStreamSourceExtraData() {
}

MediaStreamSourceExtraData::~MediaStreamSourceExtraData() {}

void MediaStreamSourceExtraData::OnLocalSourceStop() {
  if (!stop_callback_.is_null())
    stop_callback_.Run(owner());
}

MediaStreamExtraData::MediaStreamExtraData(
    webrtc::MediaStreamInterface* stream, bool is_local)
    : stream_(stream),
      is_local_(is_local) {
}

MediaStreamExtraData::~MediaStreamExtraData() {
}

void MediaStreamExtraData::SetLocalStreamStopCallback(
    const StreamStopCallback& stop_callback) {
  stream_stop_callback_ = stop_callback;
}

void MediaStreamExtraData::OnLocalStreamStop() {
  if (!stream_stop_callback_.is_null())
    stream_stop_callback_.Run(stream_->label());
}

MediaStreamImpl::UserMediaRequestInfo::UserMediaRequestInfo(
    int request_id,
    blink::WebFrame* frame,
    const blink::WebUserMediaRequest& request,
    bool enable_automatic_output_device_selection)
    : request_id(request_id),
      generated(false),
      enable_automatic_output_device_selection(
          enable_automatic_output_device_selection),
      frame(frame),
      request(request) {
}

MediaStreamImpl::UserMediaRequestInfo::~UserMediaRequestInfo() {
}

}  // namespace content