Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / media / media_stream_manager.cc
index aa7a043..0124f21 100644 (file)
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
+#include "base/power_monitor/power_monitor.h"
 #include "base/rand_util.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
 #include "base/threading/thread.h"
+#include "content/browser/browser_main_loop.h"
+#include "content/browser/media/capture/web_contents_capture_util.h"
 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
-#include "content/browser/renderer_host/media/device_request_message_filter.h"
+#include "content/browser/renderer_host/media/media_capture_devices_impl.h"
 #include "content/browser/renderer_host/media/media_stream_requester.h"
 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
 #include "content/browser/renderer_host/media/video_capture_manager.h"
-#include "content/browser/renderer_host/media/web_contents_capture_util.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/media_device_id.h"
 #include "content/public/browser/media_observer.h"
 #include "content/public/browser/media_request_state.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_client.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/media_stream_request.h"
 #include "media/audio/audio_manager_base.h"
 #include "media/audio/audio_parameters.h"
 #include "media/base/channel_layout.h"
+#include "media/base/media_switches.h"
+#include "media/video/capture/video_capture_device_factory.h"
 #include "url/gurl.h"
 
 #if defined(OS_WIN)
 #include "base/win/scoped_com_initializer.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "chromeos/audio/cras_audio_handler.h"
+#endif
+
 namespace content {
 
+// Forward declaration of DeviceMonitorMac and its only useable method.
+class DeviceMonitorMac {
+ public:
+  void StartMonitoring(
+    const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner);
+};
+
+namespace {
 // Creates a random label used to identify requests.
-static std::string RandomLabel() {
+std::string RandomLabel() {
   // An earlier PeerConnection spec,
   // http://dev.w3.org/2011/webrtc/editor/webrtc.html, specified the
   // MediaStream::label alphabet as containing 36 characters from
@@ -56,29 +77,205 @@ static std::string RandomLabel() {
   return label;
 }
 
-// Helper to verify if a media stream type is part of options or not.
-static bool Requested(const MediaStreamRequest& request,
-                      MediaStreamType stream_type) {
-  return (request.audio_type == stream_type ||
-          request.video_type == stream_type);
+void ParseStreamType(const StreamOptions& options,
+                     MediaStreamType* audio_type,
+                     MediaStreamType* video_type) {
+  *audio_type = MEDIA_NO_SERVICE;
+  *video_type = MEDIA_NO_SERVICE;
+  if (options.audio_requested) {
+     std::string audio_stream_source;
+     bool mandatory = false;
+     if (options.GetFirstAudioConstraintByName(kMediaStreamSource,
+                                               &audio_stream_source,
+                                               &mandatory)) {
+       DCHECK(mandatory);
+       // This is tab or screen capture.
+       if (audio_stream_source == kMediaStreamSourceTab) {
+         *audio_type = content::MEDIA_TAB_AUDIO_CAPTURE;
+       } else if (audio_stream_source == kMediaStreamSourceSystem) {
+         *audio_type = content::MEDIA_LOOPBACK_AUDIO_CAPTURE;
+       }
+     } else {
+       // This is normal audio device capture.
+       *audio_type = MEDIA_DEVICE_AUDIO_CAPTURE;
+     }
+  }
+  if (options.video_requested) {
+     std::string video_stream_source;
+     bool mandatory = false;
+     if (options.GetFirstVideoConstraintByName(kMediaStreamSource,
+                                               &video_stream_source,
+                                               &mandatory)) {
+       DCHECK(mandatory);
+       // This is tab or screen capture.
+       if (video_stream_source == kMediaStreamSourceTab) {
+         *video_type = content::MEDIA_TAB_VIDEO_CAPTURE;
+       } else if (video_stream_source == kMediaStreamSourceScreen) {
+         *video_type = content::MEDIA_DESKTOP_VIDEO_CAPTURE;
+       } else if (video_stream_source == kMediaStreamSourceDesktop) {
+         *video_type = content::MEDIA_DESKTOP_VIDEO_CAPTURE;
+       }
+     } else {
+       // This is normal video device capture.
+       *video_type = MEDIA_DEVICE_VIDEO_CAPTURE;
+     }
+  }
+}
+
+// Turns off available audio effects (removes the flag) if the options
+// explicitly turn them off.
+void FilterAudioEffects(const StreamOptions& options, int* effects) {
+  DCHECK(effects);
+  // TODO(ajm): Should we also handle ECHO_CANCELLER here?
+  std::string value;
+  if (options.GetFirstAudioConstraintByName(
+          kMediaStreamAudioDucking, &value, NULL) && value == "false") {
+    *effects &= ~media::AudioParameters::DUCKING;
+  }
+}
+
+// Private helper method for SendMessageToNativeLog() that obtains the global
+// MediaStreamManager instance on the UI thread before sending |message| to the
+// webrtcLoggingPrivate API.
+void DoAddLogMessage(const std::string& message) {
+  // Must be on the UI thread to access BrowserMainLoop.
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // May be null in tests.
+  // TODO(vrk): Handle this more elegantly by having native log messages become
+  // no-ops until MediaStreamManager is aware that a renderer process has
+  // started logging. crbug.com/333894
+  if (content::BrowserMainLoop::GetInstance()) {
+    BrowserThread::PostTask(
+        BrowserThread::IO,
+        FROM_HERE,
+        base::Bind(&MediaStreamManager::AddLogMessageOnIOThread,
+                   base::Unretained(content::BrowserMainLoop::GetInstance()
+                                        ->media_stream_manager()),
+                   message));
+  }
+}
+
+// Private helper method to generate a string for the log message that lists the
+// human readable names of |devices|.
+std::string GetLogMessageString(MediaStreamType stream_type,
+                                const StreamDeviceInfoArray& devices) {
+  std::string output_string =
+      base::StringPrintf("Getting devices for stream type %d:\n", stream_type);
+  if (devices.empty()) {
+    output_string += "No devices found.";
+  } else {
+    for (StreamDeviceInfoArray::const_iterator it = devices.begin();
+         it != devices.end(); ++it) {
+      output_string += "  " + it->device.name + "\n";
+    }
+  }
+  return output_string;
+}
+
+// Needed for MediaStreamManager::GenerateStream below.
+std::string ReturnEmptySalt() {
+  return std::string();
 }
 
-// TODO(xians): Merge DeviceRequest with MediaStreamRequest.
+// Clears the MediaStreamDevice.name from all devices in |devices|.
+static void ClearDeviceLabels(content::StreamDeviceInfoArray* devices) {
+  for (content::StreamDeviceInfoArray::iterator device_itr = devices->begin();
+       device_itr != devices->end();
+       ++device_itr) {
+    device_itr->device.name.clear();
+  }
+}
+
+}  // namespace
+
+
+// MediaStreamManager::DeviceRequest represents a request to either enumerate
+// available devices or open one or more devices.
+// TODO(perkj): MediaStreamManager still needs refactoring. I propose we create
+// several subclasses of DeviceRequest and move some of the responsibility of
+// the MediaStreamManager to the subclasses to get rid of the way too many if
+// statements in MediaStreamManager.
 class MediaStreamManager::DeviceRequest {
  public:
   DeviceRequest(MediaStreamRequester* requester,
-                const MediaStreamRequest& request,
                 int requesting_process_id,
-                int requesting_view_id)
+                int requesting_frame_id,
+                int page_request_id,
+                const GURL& security_origin,
+                bool user_gesture,
+                MediaStreamRequestType request_type,
+                const StreamOptions& options,
+                const ResourceContext::SaltCallback& salt_callback)
       : requester(requester),
-        request(request),
         requesting_process_id(requesting_process_id),
-        requesting_view_id(requesting_view_id),
-        state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED) {
+        requesting_frame_id(requesting_frame_id),
+        page_request_id(page_request_id),
+        security_origin(security_origin),
+        user_gesture(user_gesture),
+        request_type(request_type),
+        options(options),
+        salt_callback(salt_callback),
+        state_(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_NOT_REQUESTED),
+        audio_type_(MEDIA_NO_SERVICE),
+        video_type_(MEDIA_NO_SERVICE) {
   }
 
   ~DeviceRequest() {}
 
+  void SetAudioType(MediaStreamType audio_type) {
+    DCHECK(IsAudioInputMediaType(audio_type) ||
+           audio_type == MEDIA_DEVICE_AUDIO_OUTPUT ||
+           audio_type == MEDIA_NO_SERVICE);
+    audio_type_ = audio_type;
+  }
+
+  MediaStreamType audio_type() const { return audio_type_; }
+
+  void SetVideoType(MediaStreamType video_type) {
+    DCHECK(IsVideoMediaType(video_type) || video_type == MEDIA_NO_SERVICE);
+    video_type_ = video_type;
+  }
+
+  MediaStreamType video_type() const { return video_type_; }
+
+  // Creates a MediaStreamRequest object that is used by this request when UI
+  // is asked for permission and device selection.
+  void CreateUIRequest(const std::string& requested_audio_device_id,
+                       const std::string& requested_video_device_id) {
+    DCHECK(!ui_request_);
+    ui_request_.reset(new MediaStreamRequest(requesting_process_id,
+                                             requesting_frame_id,
+                                             page_request_id,
+                                             security_origin,
+                                             user_gesture,
+                                             request_type,
+                                             requested_audio_device_id,
+                                             requested_video_device_id,
+                                             audio_type_,
+                                             video_type_));
+  }
+
+  // Creates a tab capture specific MediaStreamRequest object that is used by
+  // this request when UI is asked for permission and device selection.
+  void CreateTabCaptureUIRequest(int target_render_process_id,
+                                 int target_render_frame_id,
+                                 const std::string& tab_capture_id) {
+    DCHECK(!ui_request_);
+    ui_request_.reset(new MediaStreamRequest(target_render_process_id,
+                                             target_render_frame_id,
+                                             page_request_id,
+                                             security_origin,
+                                             user_gesture,
+                                             request_type,
+                                             "",
+                                             "",
+                                             audio_type_,
+                                             video_type_));
+    ui_request_->tab_capture_device_id = tab_capture_id;
+  }
+
+  const MediaStreamRequest* UIRequest() const { return ui_request_.get(); }
+
   // Update the request state and notify observers.
   void SetState(MediaStreamType stream_type, MediaRequestState new_state) {
     if (stream_type == NUM_MEDIA_TYPES) {
@@ -90,28 +287,20 @@ class MediaStreamManager::DeviceRequest {
       state_[stream_type] = new_state;
     }
 
-    if (request.video_type != MEDIA_TAB_VIDEO_CAPTURE &&
-        request.audio_type != MEDIA_TAB_AUDIO_CAPTURE &&
-        new_state != MEDIA_REQUEST_STATE_CLOSING) {
-      return;
-    }
-
     MediaObserver* media_observer =
         GetContentClient()->browser()->GetMediaObserver();
-    if (media_observer == NULL)
+    if (!media_observer)
       return;
 
-    // If we appended a device_id scheme, we want to remove it when notifying
-    // observers which may be in different modules since this scheme is only
-    // used internally within the content module.
-    std::string device_id =
-        WebContentsCaptureUtil::StripWebContentsDeviceScheme(
-            request.tab_capture_device_id);
+    // If |ui_request_| doesn't exist, it means that the request has not yet
+    // been setup fully and there are no valid observers.
+    if (!ui_request_)
+      return;
 
     media_observer->OnMediaRequestStateChanged(
-        request.render_process_id, request.render_view_id,
-        request.page_request_id,
-        MediaStreamDevice(stream_type, device_id, device_id), new_state);
+        ui_request_->render_process_id, ui_request_->render_frame_id,
+        ui_request_->page_request_id, ui_request_->security_origin,
+        stream_type, new_state);
   }
 
   MediaRequestState state(MediaStreamType stream_type) const {
@@ -119,7 +308,7 @@ class MediaStreamManager::DeviceRequest {
   }
 
   MediaStreamRequester* const requester;  // Can be NULL.
-  MediaStreamRequest request;
+
 
   // The render process id that requested this stream to be generated and that
   // will receive a handle to the MediaStream. This may be different from
@@ -127,11 +316,24 @@ class MediaStreamManager::DeviceRequest {
   // specifies the target renderer from which audio and video is captured.
   const int requesting_process_id;
 
-  // The render view id that requested this stream to be generated and that
+  // The render frame id that requested this stream to be generated and that
   // will receive a handle to the MediaStream. This may be different from
-  // MediaStreamRequest::render_view_id which in the tab capture case
+  // MediaStreamRequest::render_frame_id which in the tab capture case
   // specifies the target renderer from which audio and video is captured.
-  const int requesting_view_id;
+  const int requesting_frame_id;
+
+  // An ID the render frame provided to identify this request.
+  const int page_request_id;
+
+  const GURL security_origin;
+
+  const bool user_gesture;
+
+  const MediaStreamRequestType request_type;
+
+  const StreamOptions options;
+
+  ResourceContext::SaltCallback salt_callback;
 
   StreamDeviceInfoArray devices;
 
@@ -144,6 +346,9 @@ class MediaStreamManager::DeviceRequest {
 
  private:
   std::vector<MediaRequestState> state_;
+  scoped_ptr<MediaStreamRequest> ui_request_;
+  MediaStreamType audio_type_;
+  MediaStreamType video_type_;
 };
 
 MediaStreamManager::EnumerationCache::EnumerationCache()
@@ -155,13 +360,25 @@ MediaStreamManager::EnumerationCache::~EnumerationCache() {
 
 MediaStreamManager::MediaStreamManager()
     : audio_manager_(NULL),
+#if defined(OS_WIN)
+      video_capture_thread_("VideoCaptureThread"),
+#endif
       monitoring_started_(false),
+#if defined(OS_CHROMEOS)
+      has_checked_keyboard_mic_(false),
+#endif
       io_loop_(NULL),
       use_fake_ui_(false) {}
 
 MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager)
     : audio_manager_(audio_manager),
+#if defined(OS_WIN)
+      video_capture_thread_("VideoCaptureThread"),
+#endif
       monitoring_started_(false),
+#if defined(OS_CHROMEOS)
+      has_checked_keyboard_mic_(false),
+#endif
       io_loop_(NULL),
       use_fake_ui_(false) {
   DCHECK(audio_manager_);
@@ -178,169 +395,164 @@ MediaStreamManager::MediaStreamManager(media::AudioManager* audio_manager)
         base::Bind(&MediaStreamManager::InitializeDeviceManagersOnIOThread,
                    base::Unretained(this)));
   }
+
+  base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
+  // BrowserMainLoop always creates the PowerMonitor instance before creating
+  // MediaStreamManager, but power_monitor may be NULL in unit tests.
+  if (power_monitor)
+    power_monitor->AddObserver(this);
 }
 
 MediaStreamManager::~MediaStreamManager() {
+  DVLOG(1) << "~MediaStreamManager";
   DCHECK(requests_.empty());
-  DCHECK(!device_thread_.get());
+  DCHECK(!device_task_runner_.get());
+
+  base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
+  // The PowerMonitor instance owned by BrowserMainLoops always outlives the
+  // MediaStreamManager, but it may be NULL in unit tests.
+  if (power_monitor)
+    power_monitor->RemoveObserver(this);
 }
 
 VideoCaptureManager* MediaStreamManager::video_capture_manager() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(video_capture_manager_.get());
   return video_capture_manager_.get();
 }
 
 AudioInputDeviceManager* MediaStreamManager::audio_input_device_manager() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(audio_input_device_manager_.get());
   return audio_input_device_manager_.get();
 }
 
 std::string MediaStreamManager::MakeMediaAccessRequest(
     int render_process_id,
-    int render_view_id,
+    int render_frame_id,
     int page_request_id,
     const StreamOptions& options,
     const GURL& security_origin,
     const MediaRequestResponseCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  // Create a new request based on options.
-  MediaStreamRequest stream_request(
-      render_process_id, render_view_id, page_request_id, std::string(),
-      security_origin, MEDIA_DEVICE_ACCESS, std::string(), std::string(),
-      options.audio_type, options.video_type);
-  DeviceRequest* request = new DeviceRequest(NULL, stream_request,
-                                             render_process_id, render_view_id);
-  const std::string& label = AddRequest(request);
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  request->callback = callback;
+  // TODO(perkj): The argument list with NULL parameters to DeviceRequest
+  // suggests that this is the wrong design. Can this be refactored?
+  DeviceRequest* request = new DeviceRequest(NULL,
+                                             render_process_id,
+                                             render_frame_id,
+                                             page_request_id,
+                                             security_origin,
+                                             false,  // user gesture
+                                             MEDIA_DEVICE_ACCESS,
+                                             options,
+                                             base::Bind(&ReturnEmptySalt));
 
-  HandleRequest(label);
+  const std::string& label = AddRequest(request);
 
+  request->callback = callback;
+  // Post a task and handle the request asynchronously. The reason is that the
+  // requester won't have a label for the request until this function returns
+  // and thus can not handle a response. Using base::Unretained is safe since
+  // MediaStreamManager is deleted on the UI thread, after the IO thread has
+  // been stopped.
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&MediaStreamManager::SetupRequest,
+                 base::Unretained(this), label));
   return label;
 }
 
-std::string MediaStreamManager::GenerateStream(
-    MediaStreamRequester* requester,
-    int render_process_id,
-    int render_view_id,
-    int page_request_id,
-    const StreamOptions& options,
-    const GURL& security_origin) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  if (CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kUseFakeDeviceForMediaStream)) {
-    UseFakeDevice();
-  }
+void MediaStreamManager::GenerateStream(MediaStreamRequester* requester,
+                                        int render_process_id,
+                                        int render_frame_id,
+                                        const ResourceContext::SaltCallback& sc,
+                                        int page_request_id,
+                                        const StreamOptions& options,
+                                        const GURL& security_origin,
+                                        bool user_gesture) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DVLOG(1) << "GenerateStream()";
   if (CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kUseFakeUIForMediaStream)) {
+          switches::kUseFakeUIForMediaStream)) {
     UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
   }
 
-  int target_render_process_id = render_process_id;
-  int target_render_view_id = render_view_id;
-  std::string tab_capture_device_id;
+  DeviceRequest* request = new DeviceRequest(requester,
+                                             render_process_id,
+                                             render_frame_id,
+                                             page_request_id,
+                                             security_origin,
+                                             user_gesture,
+                                             MEDIA_GENERATE_STREAM,
+                                             options,
+                                             sc);
 
-  // Customize options for a WebContents based capture.
-  if (options.audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
-      options.video_type == MEDIA_TAB_VIDEO_CAPTURE) {
-    // TODO(justinlin): Can't plumb audio mirroring using stream type right
-    // now, so plumbing by device_id. Will revisit once it's refactored.
-    // http://crbug.com/163100
-    tab_capture_device_id =
-        WebContentsCaptureUtil::AppendWebContentsDeviceScheme(
-            !options.video_device_id.empty() ?
-            options.video_device_id : options.audio_device_id);
-
-    bool has_valid_device_id = WebContentsCaptureUtil::ExtractTabCaptureTarget(
-        tab_capture_device_id, &target_render_process_id,
-        &target_render_view_id);
-    if (!has_valid_device_id ||
-        (options.audio_type != MEDIA_TAB_AUDIO_CAPTURE &&
-         options.audio_type != MEDIA_NO_SERVICE) ||
-        (options.video_type != MEDIA_TAB_VIDEO_CAPTURE &&
-         options.video_type != MEDIA_NO_SERVICE)) {
-      LOG(ERROR) << "Invalid request.";
-      return std::string();
-    }
-  }
+  const std::string& label = AddRequest(request);
+
+  // Post a task and handle the request asynchronously. The reason is that the
+  // requester won't have a label for the request until this function returns
+  // and thus can not handle a response. Using base::Unretained is safe since
+  // MediaStreamManager is deleted on the UI thread, after the IO thread has
+  // been stopped.
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&MediaStreamManager::SetupRequest,
+                 base::Unretained(this), label));
+}
 
-  std::string translated_audio_device_id;
-  std::string translated_video_device_id;
-  if (options.audio_type == MEDIA_DEVICE_AUDIO_CAPTURE) {
-    bool found_match = TranslateGUIDToRawId(
-        MEDIA_DEVICE_AUDIO_CAPTURE, security_origin, options.audio_device_id,
-        &translated_audio_device_id);
-    DCHECK(found_match || translated_audio_device_id.empty());
-  }
-
-  if (options.video_type == MEDIA_DEVICE_VIDEO_CAPTURE) {
-    bool found_match = TranslateGUIDToRawId(
-        MEDIA_DEVICE_VIDEO_CAPTURE, security_origin, options.video_device_id,
-        &translated_video_device_id);
-    DCHECK(found_match || translated_video_device_id.empty());
-  }
-
-  if (options.video_type == MEDIA_DESKTOP_VIDEO_CAPTURE ||
-      options.audio_type == MEDIA_LOOPBACK_AUDIO_CAPTURE) {
-    // For screen capture we only support two valid combinations:
-    // (1) screen video capture only, or
-    // (2) screen video capture with loopback audio capture.
-    if (options.video_type != MEDIA_DESKTOP_VIDEO_CAPTURE ||
-        (options.audio_type != MEDIA_NO_SERVICE &&
-         options.audio_type != MEDIA_LOOPBACK_AUDIO_CAPTURE)) {
-      // TODO(sergeyu): Surface error message to the calling JS code.
-      LOG(ERROR) << "Invalid screen capture request.";
-      return std::string();
+void MediaStreamManager::CancelRequest(int render_process_id,
+                                       int render_frame_id,
+                                       int page_request_id) {
+  for (DeviceRequests::const_iterator request_it = requests_.begin();
+       request_it != requests_.end(); ++request_it) {
+    const DeviceRequest* request = request_it->second;
+    if (request->requesting_process_id == render_process_id &&
+        request->requesting_frame_id == render_frame_id &&
+        request->page_request_id == page_request_id) {
+      CancelRequest(request_it->first);
+      return;
     }
-    translated_video_device_id = options.video_device_id;
   }
-
-  // Create a new request based on options.
-  MediaStreamRequest stream_request(
-      target_render_process_id, target_render_view_id, page_request_id,
-      tab_capture_device_id, security_origin, MEDIA_GENERATE_STREAM,
-      translated_audio_device_id, translated_video_device_id,
-      options.audio_type, options.video_type);
-  DeviceRequest* request = new DeviceRequest(requester, stream_request,
-                                             render_process_id,
-                                             render_view_id);
-  const std::string& label = AddRequest(request);
-  HandleRequest(label);
-  return label;
+  NOTREACHED();
 }
 
 void MediaStreamManager::CancelRequest(const std::string& label) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
-  DeviceRequests::iterator request_it = requests_.find(label);
-  if (request_it == requests_.end()) {
-    NOTREACHED();
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DVLOG(1) << "CancelRequest({label = " << label <<  "})";
+  DeviceRequest* request = FindRequest(label);
+  if (!request) {
+    // The request does not exist.
+    LOG(ERROR) << "The request with label = " << label  << " does not exist.";
     return;
   }
-  scoped_ptr<DeviceRequest> request(request_it->second);
-  RemoveRequest(request_it);
 
-  if (request->request.request_type == MEDIA_ENUMERATE_DEVICES) {
+  if (request->request_type == MEDIA_ENUMERATE_DEVICES) {
+    // It isn't an ideal use of "CancelRequest" to make it a requirement
+    // for enumeration requests to be deleted via "CancelRequest" _after_
+    // the request has been successfully fulfilled.
+    // See note in FinalizeEnumerateDevices for a recommendation on how
+    // we should refactor this.
+    DeleteRequest(label);
     return;
   }
 
   // This is a request for opening one or more devices.
   for (StreamDeviceInfoArray::iterator device_it = request->devices.begin();
-      device_it != request->devices.end(); ++device_it) {
+       device_it != request->devices.end(); ++device_it) {
+    MediaRequestState state = request->state(device_it->device.type);
     // If we have not yet requested the device to be opened - just ignore it.
-    if (request->state(device_it->device.type) != MEDIA_REQUEST_STATE_OPENING
-        &&
-        request->state(device_it->device.type) != MEDIA_REQUEST_STATE_DONE) {
+    if (state != MEDIA_REQUEST_STATE_OPENING &&
+        state != MEDIA_REQUEST_STATE_DONE) {
       continue;
     }
     // Stop the opening/opened devices of the requests.
-    StopDevice(*device_it);
+    CloseDevice(device_it->device.type, device_it->session_id);
   }
 
   // Cancel the request if still pending at UI side.
   request->SetState(NUM_MEDIA_TYPES, MEDIA_REQUEST_STATE_CLOSING);
+  DeleteRequest(label);
 }
 
 void MediaStreamManager::CancelAllRequests(int render_process_id) {
@@ -358,68 +570,89 @@ void MediaStreamManager::CancelAllRequests(int render_process_id) {
 }
 
 void MediaStreamManager::StopStreamDevice(int render_process_id,
-                                          int render_view_id,
+                                          int render_frame_id,
                                           const std::string& device_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  DVLOG(1) << "StopStreamDevice({render_view_id = " << render_view_id <<  "} "
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DVLOG(1) << "StopStreamDevice({render_frame_id = " << render_frame_id <<  "} "
            << ", {device_id = " << device_id << "})";
-
-  // Find all requests for this |render_process_id| and |render_view_id| of type
-  // MEDIA_GENERATE_STREAM that has requested to use |device_id|.
-  DeviceRequests::iterator request_it = requests_.begin();
-  while (request_it  != requests_.end()) {
+  // Find the first request for this |render_process_id| and |render_frame_id|
+  // of type MEDIA_GENERATE_STREAM that has requested to use |device_id| and
+  // stop it.
+  for (DeviceRequests::iterator request_it = requests_.begin();
+       request_it != requests_.end(); ++request_it) {
     DeviceRequest* request = request_it->second;
-    const MediaStreamRequest& ms_request = request->request;
     if (request->requesting_process_id != render_process_id ||
-        request->requesting_view_id != render_view_id ||
-        ms_request.request_type != MEDIA_GENERATE_STREAM) {
-      ++request_it;
+        request->requesting_frame_id != render_frame_id ||
+        request->request_type != MEDIA_GENERATE_STREAM) {
       continue;
     }
 
+    StreamDeviceInfoArray& devices = request->devices;
+    for (StreamDeviceInfoArray::iterator device_it = devices.begin();
+         device_it != devices.end(); ++device_it) {
+      if (device_it->device.id == device_id) {
+        StopDevice(device_it->device.type, device_it->session_id);
+        return;
+      }
+    }
+  }
+}
+
+void MediaStreamManager::StopDevice(MediaStreamType type, int session_id) {
+  DVLOG(1) << "StopDevice"
+           << "{type = " << type << "}"
+           << "{session_id = " << session_id << "}";
+  DeviceRequests::iterator request_it = requests_.begin();
+  while (request_it != requests_.end()) {
+    DeviceRequest* request = request_it->second;
     StreamDeviceInfoArray* devices = &request->devices;
+    if (devices->empty()) {
+      // There is no device in use yet by this request.
+      ++request_it;
+      continue;
+    }
     StreamDeviceInfoArray::iterator device_it = devices->begin();
     while (device_it != devices->end()) {
-      MediaStreamType device_type = device_it->device.type;
-      if (device_it->device.id == device_id) {
-        if (request->state(device_type) == MEDIA_REQUEST_STATE_DONE) {
-          StopDevice(*device_it);
-        }
-        device_it = devices->erase(device_it);
-      } else {
+      if (device_it->device.type != type ||
+          device_it->session_id != session_id) {
         ++device_it;
+        continue;
       }
+
+      if (request->state(type) == MEDIA_REQUEST_STATE_DONE)
+        CloseDevice(type, session_id);
+      device_it = devices->erase(device_it);
     }
 
-    // If this request doesn't have any active devices, remove the request.
+    // If this request doesn't have any active devices after a device
+    // has been stopped above, remove the request. Note that the request is
+    // only deleted if a device as been removed from |devices|.
     if (devices->empty()) {
-      DeviceRequests::iterator del_itor(request_it);
+      std::string label = request_it->first;
       ++request_it;
-      scoped_ptr<DeviceRequest> request(del_itor->second);
-      RemoveRequest(del_itor);
+      DeleteRequest(label);
     } else {
       ++request_it;
     }
   }
 }
 
-void MediaStreamManager::StopDevice(const StreamDeviceInfo& device_info) {
-  DVLOG(1) << "StopDevice("
-           << "{device_info.session_id = " << device_info.session_id <<  "} "
-           << "{device_id = " << device_info.device.id << "})";
-  GetDeviceManager(device_info.device.type)->Close(device_info.session_id);
+void MediaStreamManager::CloseDevice(MediaStreamType type, int session_id) {
+  DVLOG(1) << "CloseDevice("
+           << "{type = " << type <<  "} "
+           << "{session_id = " << session_id << "})";
+  GetDeviceManager(type)->Close(session_id);
 
   for (DeviceRequests::iterator request_it = requests_.begin();
        request_it != requests_.end() ; ++request_it) {
     StreamDeviceInfoArray* devices = &request_it->second->devices;
     for (StreamDeviceInfoArray::iterator device_it = devices->begin();
          device_it != devices->end(); ++device_it) {
-      if (device_it->session_id == device_info.session_id &&
-          device_it->device.type == device_info.device.type) {
+      if (device_it->session_id == session_id &&
+          device_it->device.type == type) {
         // Notify observers that this device is being closed.
         // Note that only one device per type can be opened.
-        request_it->second->SetState(device_it->device.type,
-                                     MEDIA_REQUEST_STATE_CLOSING);
+        request_it->second->SetState(type, MEDIA_REQUEST_STATE_CLOSING);
       }
     }
   }
@@ -428,111 +661,220 @@ void MediaStreamManager::StopDevice(const StreamDeviceInfo& device_info) {
 std::string MediaStreamManager::EnumerateDevices(
     MediaStreamRequester* requester,
     int render_process_id,
-    int render_view_id,
+    int render_frame_id,
+    const ResourceContext::SaltCallback& sc,
     int page_request_id,
     MediaStreamType type,
     const GURL& security_origin) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(requester);
   DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
-         type == MEDIA_DEVICE_VIDEO_CAPTURE);
+         type == MEDIA_DEVICE_VIDEO_CAPTURE ||
+         type == MEDIA_DEVICE_AUDIO_OUTPUT);
 
-  // When the requester is NULL, the request is made by the UI to ensure MSM
-  // starts monitoring devices.
-  if (!requester) {
-    if (!monitoring_started_)
-      StartMonitoring();
+  DeviceRequest* request = new DeviceRequest(requester,
+                                             render_process_id,
+                                             render_frame_id,
+                                             page_request_id,
+                                             security_origin,
+                                             false,  // user gesture
+                                             MEDIA_ENUMERATE_DEVICES,
+                                             StreamOptions(),
+                                             sc);
+  if (IsAudioInputMediaType(type) || type == MEDIA_DEVICE_AUDIO_OUTPUT)
+    request->SetAudioType(type);
+  else if (IsVideoMediaType(type))
+    request->SetVideoType(type);
 
-    return std::string();
+  const std::string& label = AddRequest(request);
+  // Post a task and handle the request asynchronously. The reason is that the
+  // requester won't have a label for the request until this function returns
+  // and thus can not handle a response. Using base::Unretained is safe since
+  // MediaStreamManager is deleted on the UI thread, after the IO thread has
+  // been stopped.
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&MediaStreamManager::DoEnumerateDevices,
+                 base::Unretained(this), label));
+  return label;
+}
+
+void MediaStreamManager::DoEnumerateDevices(const std::string& label) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DeviceRequest* request = FindRequest(label);
+  if (!request)
+    return;  // This can happen if the request has been canceled.
+
+  if (request->audio_type() == MEDIA_DEVICE_AUDIO_OUTPUT) {
+    DCHECK_EQ(MEDIA_NO_SERVICE, request->video_type());
+    DCHECK_GE(active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_OUTPUT], 0);
+    request->SetState(MEDIA_DEVICE_AUDIO_OUTPUT, MEDIA_REQUEST_STATE_REQUESTED);
+    if (active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_OUTPUT] == 0) {
+      ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_OUTPUT];
+      device_task_runner_->PostTask(
+          FROM_HERE,
+          base::Bind(&MediaStreamManager::EnumerateAudioOutputDevices,
+                     base::Unretained(this),
+                     label));
+    }
+    return;
   }
 
-  // Create a new request.
-  StreamOptions options;
-  EnumerationCache* cache = NULL;
-  if (type == MEDIA_DEVICE_AUDIO_CAPTURE) {
-    options.audio_type = type;
+  MediaStreamType type;
+  EnumerationCache* cache;
+  if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE) {
+    DCHECK_EQ(MEDIA_NO_SERVICE, request->video_type());
+    type = MEDIA_DEVICE_AUDIO_CAPTURE;
     cache = &audio_enumeration_cache_;
-  } else if (type == MEDIA_DEVICE_VIDEO_CAPTURE) {
-    options.video_type = type;
-    cache = &video_enumeration_cache_;
   } else {
-    NOTREACHED();
-    return std::string();
+    DCHECK_EQ(MEDIA_DEVICE_VIDEO_CAPTURE, request->video_type());
+    DCHECK_EQ(MEDIA_NO_SERVICE, request->audio_type());
+    type = MEDIA_DEVICE_VIDEO_CAPTURE;
+    cache = &video_enumeration_cache_;
   }
 
-  MediaStreamRequest stream_request(
-      render_process_id, render_view_id, page_request_id, std::string(),
-      security_origin, MEDIA_ENUMERATE_DEVICES, std::string(), std::string(),
-      options.audio_type, options.video_type);
-  DeviceRequest* request = new DeviceRequest(requester, stream_request,
-                                             render_process_id,
-                                             render_view_id);
-  const std::string& label = AddRequest(request);
-
-  if (cache->valid) {
+  if (!EnumerationRequired(cache, type)) {
     // Cached device list of this type exists. Just send it out.
     request->SetState(type, MEDIA_REQUEST_STATE_REQUESTED);
-
-    // Need to post a task since the requester won't have label till
-    // this function returns.
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE,
-        base::Bind(&MediaStreamManager::SendCachedDeviceList,
-                   base::Unretained(this), cache, label));
+    request->devices = cache->devices;
+    FinalizeEnumerateDevices(label, request);
   } else {
     StartEnumeration(request);
   }
+  DVLOG(1) << "Enumerate Devices ({label = " << label <<  "})";
+}
 
-  return label;
+void MediaStreamManager::EnumerateAudioOutputDevices(const std::string& label) {
+  DCHECK(device_task_runner_->BelongsToCurrentThread());
+
+  scoped_ptr<media::AudioDeviceNames> device_names(
+      new media::AudioDeviceNames());
+  audio_manager_->GetAudioOutputDeviceNames(device_names.get());
+  StreamDeviceInfoArray devices;
+  for (media::AudioDeviceNames::iterator it = device_names->begin();
+       it != device_names->end(); ++it) {
+    StreamDeviceInfo device(MEDIA_DEVICE_AUDIO_OUTPUT,
+                            it->device_name,
+                            it->unique_id);
+    devices.push_back(device);
+  }
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&MediaStreamManager::AudioOutputDevicesEnumerated,
+                 base::Unretained(this),
+                 devices));
 }
 
-std::string MediaStreamManager::OpenDevice(
-    MediaStreamRequester* requester,
-    int render_process_id,
-    int render_view_id,
-    int page_request_id,
-    const std::string& device_id,
-    MediaStreamType type,
-    const GURL& security_origin) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+void MediaStreamManager::AudioOutputDevicesEnumerated(
+    const StreamDeviceInfoArray& devices) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DVLOG(1) << "AudioOutputDevicesEnumerated()";
+
+  std::string log_message = "New device enumeration result:\n" +
+                            GetLogMessageString(MEDIA_DEVICE_AUDIO_OUTPUT,
+                                                devices);
+  SendMessageToNativeLog(log_message);
+
+  // Publish the result for all requests waiting for device list(s).
+  for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
+       ++it) {
+    if (it->second->state(MEDIA_DEVICE_AUDIO_OUTPUT) ==
+            MEDIA_REQUEST_STATE_REQUESTED &&
+        it->second->audio_type() == MEDIA_DEVICE_AUDIO_OUTPUT) {
+      DCHECK_EQ(MEDIA_ENUMERATE_DEVICES, it->second->request_type);
+      it->second->SetState(MEDIA_DEVICE_AUDIO_OUTPUT,
+                           MEDIA_REQUEST_STATE_PENDING_APPROVAL);
+      it->second->devices = devices;
+      FinalizeEnumerateDevices(it->first, it->second);
+    }
+  }
+
+  --active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_OUTPUT];
+  DCHECK_GE(active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_OUTPUT], 0);
+}
+
+void MediaStreamManager::OpenDevice(MediaStreamRequester* requester,
+                                    int render_process_id,
+                                    int render_frame_id,
+                                    const ResourceContext::SaltCallback& sc,
+                                    int page_request_id,
+                                    const std::string& device_id,
+                                    MediaStreamType type,
+                                    const GURL& security_origin) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
          type == MEDIA_DEVICE_VIDEO_CAPTURE);
-
-  // Create a new request.
+  DVLOG(1) << "OpenDevice ({page_request_id = " << page_request_id <<  "})";
   StreamOptions options;
-  if (IsAudioMediaType(type)) {
-    options.audio_type = type;
-    options.audio_device_id = device_id;
+  if (IsAudioInputMediaType(type)) {
+    options.audio_requested = true;
+    options.mandatory_audio.push_back(
+        StreamOptions::Constraint(kMediaStreamSourceInfoId, device_id));
   } else if (IsVideoMediaType(type)) {
-    options.video_type = type;
-    options.video_device_id = device_id;
+    options.video_requested = true;
+    options.mandatory_video.push_back(
+        StreamOptions::Constraint(kMediaStreamSourceInfoId, device_id));
   } else {
     NOTREACHED();
-    return std::string();
   }
-
-  MediaStreamRequest stream_request(
-      render_process_id, render_view_id, page_request_id, std::string(),
-      security_origin, MEDIA_OPEN_DEVICE, options.audio_device_id,
-      options.video_device_id, options.audio_type, options.video_type);
-  DeviceRequest* request = new DeviceRequest(requester, stream_request,
+  DeviceRequest* request = new DeviceRequest(requester,
                                              render_process_id,
-                                             render_view_id);
-  const std::string& label = AddRequest(request);
-  StartEnumeration(request);
+                                             render_frame_id,
+                                             page_request_id,
+                                             security_origin,
+                                             false,  // user gesture
+                                             MEDIA_OPEN_DEVICE,
+                                             options,
+                                             sc);
 
-  return label;
+  const std::string& label = AddRequest(request);
+  // Post a task and handle the request asynchronously. The reason is that the
+  // requester won't have a label for the request until this function returns
+  // and thus can not handle a response. Using base::Unretained is safe since
+  // MediaStreamManager is deleted on the UI thread, after the IO thread has
+  // been stopped.
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&MediaStreamManager::SetupRequest,
+                 base::Unretained(this), label));
 }
 
-void MediaStreamManager::SendCachedDeviceList(
-    EnumerationCache* cache,
-    const std::string& label) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  if (cache->valid) {
-    DeviceRequests::iterator it = requests_.find(label);
-    if (it != requests_.end()) {
-      it->second->requester->DevicesEnumerated(label, cache->devices);
+bool MediaStreamManager::TranslateSourceIdToDeviceId(
+    MediaStreamType stream_type,
+    const ResourceContext::SaltCallback& sc,
+    const GURL& security_origin,
+    const std::string& source_id,
+    std::string* device_id) const {
+  DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
+         stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
+  // The source_id can be empty if the constraint is set but empty.
+  if (source_id.empty())
+    return false;
+
+  const EnumerationCache* cache =
+      stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
+      &audio_enumeration_cache_ : &video_enumeration_cache_;
+
+  // If device monitoring hasn't started, the |device_guid| is not valid.
+  if (!cache->valid)
+    return false;
+
+  for (StreamDeviceInfoArray::const_iterator it = cache->devices.begin();
+       it != cache->devices.end();
+       ++it) {
+    if (content::DoesMediaDeviceIDMatchHMAC(sc, security_origin, source_id,
+                                            it->device.id)) {
+      *device_id = it->device.id;
+      return true;
     }
   }
+  return false;
+}
+
+void MediaStreamManager::EnsureDeviceMonitorStarted() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  StartMonitoring();
 }
 
 void MediaStreamManager::StopRemovedDevices(
@@ -544,8 +886,8 @@ void MediaStreamManager::StopRemovedDevices(
   for (StreamDeviceInfoArray::const_iterator old_dev_it = old_devices.begin();
        old_dev_it != old_devices.end(); ++old_dev_it) {
     bool device_found = false;
-    for (StreamDeviceInfoArray::const_iterator new_dev_it = new_devices.begin();
-         new_dev_it != new_devices.end(); ++new_dev_it) {
+    StreamDeviceInfoArray::const_iterator new_dev_it = new_devices.begin();
+    for (; new_dev_it != new_devices.end(); ++new_dev_it) {
       if (old_dev_it->device.id == new_dev_it->device.id) {
         device_found = true;
         break;
@@ -555,37 +897,83 @@ void MediaStreamManager::StopRemovedDevices(
     if (!device_found) {
       // A device has been removed. We need to check if it is used by a
       // MediaStream and in that case cleanup and notify the render process.
-      do {
-        std::string label =
-            FindFirstMediaStreamRequestWithDevice(old_dev_it->device);
-        if (label.empty())
-          break;
-        // TODO(perkj): We would like to stop all tracks that use the removed
-        // device, not the MediaStream. But at the moment, there is no way of
-        // doing that from the browser process. crbug/315585
-        StopMediaStreamFromBrowser(label);
-      } while(true);
+      StopRemovedDevice(old_dev_it->device);
+    }
+  }
+}
+
+void MediaStreamManager::StopRemovedDevice(const MediaStreamDevice& device) {
+  std::vector<int> session_ids;
+  for (DeviceRequests::const_iterator it = requests_.begin();
+       it != requests_.end() ; ++it) {
+    const DeviceRequest* request = it->second;
+    for (StreamDeviceInfoArray::const_iterator device_it =
+             request->devices.begin();
+         device_it != request->devices.end(); ++device_it) {
+      std::string source_id = content::GetHMACForMediaDeviceID(
+          request->salt_callback,
+          request->security_origin,
+          device.id);
+      if (device_it->device.id == source_id &&
+          device_it->device.type == device.type) {
+        session_ids.push_back(device_it->session_id);
+        if (it->second->requester) {
+          it->second->requester->DeviceStopped(
+              it->second->requesting_frame_id,
+              it->first,
+              *device_it);
+        }
+      }
     }
   }
+  for (std::vector<int>::const_iterator it = session_ids.begin();
+       it != session_ids.end(); ++it) {
+    StopDevice(device.type, *it);
+  }
+
+  std::ostringstream oss;
+  oss << "Media input device removed: type = " <<
+    (device.type == MEDIA_DEVICE_AUDIO_CAPTURE ? "audio" : "video") <<
+    ", id = " << device.id << ", name = " << device.name;
+  AddLogMessageOnIOThread(oss.str());
 }
 
 void MediaStreamManager::StartMonitoring() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (monitoring_started_)
+    return;
+
   if (!base::SystemMonitor::Get())
     return;
 
-  if (!monitoring_started_) {
-    monitoring_started_ = true;
-    base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
+  monitoring_started_ = true;
+  base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
+
+  // Enumerate both the audio and video devices to cache the device lists
+  // and send them to media observer.
+  ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_CAPTURE];
+  audio_input_device_manager_->EnumerateDevices(MEDIA_DEVICE_AUDIO_CAPTURE);
+  ++active_enumeration_ref_count_[MEDIA_DEVICE_VIDEO_CAPTURE];
+  video_capture_manager_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
+
+#if defined(OS_MACOSX)
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&MediaStreamManager::StartMonitoringOnUIThread,
+                 base::Unretained(this)));
+#endif
+}
 
-    // Enumerate both the audio and video devices to cache the device lists
-    // and send them to media observer.
-    ++active_enumeration_ref_count_[MEDIA_DEVICE_AUDIO_CAPTURE];
-    audio_input_device_manager_->EnumerateDevices(MEDIA_DEVICE_AUDIO_CAPTURE);
-    ++active_enumeration_ref_count_[MEDIA_DEVICE_VIDEO_CAPTURE];
-    video_capture_manager_->EnumerateDevices(MEDIA_DEVICE_VIDEO_CAPTURE);
+#if defined(OS_MACOSX)
+void MediaStreamManager::StartMonitoringOnUIThread() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  BrowserMainLoop* browser_main_loop = content::BrowserMainLoop::GetInstance();
+  if (browser_main_loop) {
+    browser_main_loop->device_monitor_mac()
+        ->StartMonitoring(audio_manager_->GetWorkerTaskRunner());
   }
 }
+#endif
 
 void MediaStreamManager::StopMonitoring() {
   DCHECK_EQ(base::MessageLoop::current(), io_loop_);
@@ -597,33 +985,70 @@ void MediaStreamManager::StopMonitoring() {
   }
 }
 
-bool MediaStreamManager::TranslateGUIDToRawId(MediaStreamType stream_type,
-                                              const GURL& security_origin,
-                                              const std::string& device_guid,
-                                              std::string* raw_device_id) {
-  DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
-         stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
-  if (device_guid.empty())
+bool MediaStreamManager::GetRequestedDeviceCaptureId(
+    const DeviceRequest* request,
+    MediaStreamType type,
+    std::string* device_id) const {
+  DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE ||
+         type == MEDIA_DEVICE_VIDEO_CAPTURE);
+  const StreamOptions::Constraints* mandatory =
+      (type == MEDIA_DEVICE_AUDIO_CAPTURE) ?
+          &request->options.mandatory_audio : &request->options.mandatory_video;
+  const StreamOptions::Constraints* optional =
+      (type == MEDIA_DEVICE_AUDIO_CAPTURE) ?
+          &request->options.optional_audio : &request->options.optional_video;
+
+  std::vector<std::string> source_ids;
+  StreamOptions::GetConstraintsByName(*mandatory,
+                                      kMediaStreamSourceInfoId, &source_ids);
+  if (source_ids.size() > 1) {
+    LOG(ERROR) << "Only one mandatory " << kMediaStreamSourceInfoId
+        << " is supported.";
     return false;
-
-  EnumerationCache* cache =
-      stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ?
-      &audio_enumeration_cache_ : &video_enumeration_cache_;
-
-  // If device monitoring hasn't started, the |device_guid| is not valid.
-  if (!cache->valid)
+  }
+  // If a specific device has been requested we need to find the real device
+  // id.
+  if (source_ids.size() == 1 &&
+      !TranslateSourceIdToDeviceId(type,
+                                   request->salt_callback,
+                                   request->security_origin,
+                                   source_ids[0], device_id)) {
+    LOG(WARNING) << "Invalid mandatory " << kMediaStreamSourceInfoId
+                 << " = " << source_ids[0] << ".";
     return false;
-
-  for (StreamDeviceInfoArray::const_iterator it = cache->devices.begin();
-       it != cache->devices.end();
-       ++it) {
-    if (content::DoesMediaDeviceIDMatchHMAC(
-            security_origin, device_guid, it->device.id)) {
-      *raw_device_id = it->device.id;
-      return true;
+  }
+  // Check for optional audio sourceIDs.
+  if (device_id->empty()) {
+    StreamOptions::GetConstraintsByName(*optional,
+                                        kMediaStreamSourceInfoId,
+                                        &source_ids);
+    // Find the first sourceID that translates to device. Note that only one
+    // device per type can call to GenerateStream is ever opened.
+    for (std::vector<std::string>::const_iterator it = source_ids.begin();
+         it != source_ids.end(); ++it) {
+      if (TranslateSourceIdToDeviceId(type,
+                                      request->salt_callback,
+                                      request->security_origin,
+                                      *it,
+                                      device_id)) {
+        break;
+      }
     }
   }
-  return false;
+  return true;
+}
+
+void MediaStreamManager::TranslateDeviceIdToSourceId(
+    DeviceRequest* request,
+    MediaStreamDevice* device) {
+  if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
+      request->audio_type() == MEDIA_DEVICE_AUDIO_OUTPUT ||
+      request->video_type() == MEDIA_DEVICE_VIDEO_CAPTURE) {
+    device->id = content::GetHMACForMediaDeviceID(
+        request->salt_callback,
+        request->security_origin,
+        device->id);
+  }
 }
 
 void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) {
@@ -631,49 +1056,103 @@ void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) {
   cache->valid = false;
 }
 
+bool MediaStreamManager::EnumerationRequired(EnumerationCache* cache,
+                                             MediaStreamType stream_type) {
+  DCHECK_EQ(base::MessageLoop::current(), io_loop_);
+  if (stream_type == MEDIA_NO_SERVICE)
+    return false;
+
+  DCHECK(stream_type == MEDIA_DEVICE_AUDIO_CAPTURE ||
+         stream_type == MEDIA_DEVICE_VIDEO_CAPTURE);
+
+#if defined(OS_ANDROID)
+  // There's no SystemMonitor on Android that notifies us when devices are
+  // added or removed, so we need to populate the cache on every request.
+  // Fortunately, there is an already up-to-date cache in the browser side
+  // audio manager that we can rely on, so the performance impact of
+  // invalidating the cache like this, is minimal.
+  if (stream_type == MEDIA_DEVICE_AUDIO_CAPTURE) {
+    // Make sure the cache is marked as invalid so that FinalizeEnumerateDevices
+    // will be called at the end of the enumeration.
+    ClearEnumerationCache(cache);
+  }
+#endif
+  // If the cache isn't valid, we need to start a full enumeration.
+  return !cache->valid;
+}
+
 void MediaStreamManager::StartEnumeration(DeviceRequest* request) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   // Start monitoring the devices when doing the first enumeration.
-  if (!monitoring_started_ && base::SystemMonitor::Get()) {
-    StartMonitoring();
-  }
+  StartMonitoring();
 
   // Start enumeration for devices of all requested device types.
-  for (int i = MEDIA_NO_SERVICE + 1; i < NUM_MEDIA_TYPES; ++i) {
-    const MediaStreamType stream_type = static_cast<MediaStreamType>(i);
-    if (Requested(request->request, stream_type)) {
-      request->SetState(stream_type, MEDIA_REQUEST_STATE_REQUESTED);
-      DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
-      if (active_enumeration_ref_count_[stream_type] == 0) {
-        ++active_enumeration_ref_count_[stream_type];
-        GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
-      }
+  const MediaStreamType streams[] = { request->audio_type(),
+                                      request->video_type() };
+  for (size_t i = 0; i < arraysize(streams); ++i) {
+    if (streams[i] == MEDIA_NO_SERVICE)
+      continue;
+    request->SetState(streams[i], MEDIA_REQUEST_STATE_REQUESTED);
+    DCHECK_GE(active_enumeration_ref_count_[streams[i]], 0);
+    if (active_enumeration_ref_count_[streams[i]] == 0) {
+      ++active_enumeration_ref_count_[streams[i]];
+      GetDeviceManager(streams[i])->EnumerateDevices(streams[i]);
     }
   }
 }
 
 std::string MediaStreamManager::AddRequest(DeviceRequest* request) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   // Create a label for this request and verify it is unique.
   std::string unique_label;
   do {
     unique_label = RandomLabel();
-  } while (requests_.find(unique_label) != requests_.end());
+  } while (FindRequest(unique_label) != NULL);
 
-  requests_.insert(std::make_pair(unique_label, request));
+  requests_.push_back(std::make_pair(unique_label, request));
 
   return unique_label;
 }
 
-void MediaStreamManager::RemoveRequest(DeviceRequests::iterator it) {
-  requests_.erase(it);
+MediaStreamManager::DeviceRequest*
+MediaStreamManager::FindRequest(const std::string& label) const {
+  for (DeviceRequests::const_iterator request_it = requests_.begin();
+       request_it != requests_.end(); ++request_it) {
+    if (request_it->first == label)
+      return request_it->second;
+  }
+  return NULL;
+}
+
+void MediaStreamManager::DeleteRequest(const std::string& label) {
+  DVLOG(1) << "DeleteRequest({label= " << label << "})";
+  for (DeviceRequests::iterator request_it = requests_.begin();
+       request_it != requests_.end(); ++request_it) {
+    if (request_it->first == label) {
+      scoped_ptr<DeviceRequest> request(request_it->second);
+      requests_.erase(request_it);
+      return;
+    }
+  }
+  NOTREACHED();
 }
 
-void MediaStreamManager::PostRequestToUI(const std::string& label) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  DeviceRequest* request = requests_[label];
+void MediaStreamManager::PostRequestToUI(const std::string& label,
+                                         DeviceRequest* request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK(request->UIRequest());
+  DVLOG(1) << "PostRequestToUI({label= " << label << "})";
+
+  const MediaStreamType audio_type = request->audio_type();
+  const MediaStreamType video_type = request->video_type();
+
+  // Post the request to UI and set the state.
+  if (IsAudioInputMediaType(audio_type))
+    request->SetState(audio_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
+  if (IsVideoMediaType(video_type))
+    request->SetState(video_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
 
   if (use_fake_ui_) {
     if (!fake_ui_)
@@ -703,65 +1182,237 @@ void MediaStreamManager::PostRequestToUI(const std::string& label) {
   }
 
   request->ui_proxy->RequestAccess(
-      request->request,
+      *request->UIRequest(),
       base::Bind(&MediaStreamManager::HandleAccessRequestResponse,
                  base::Unretained(this), label));
 }
 
-void MediaStreamManager::HandleRequest(const std::string& label) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  DeviceRequest* request = requests_[label];
+void MediaStreamManager::SetupRequest(const std::string& label) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DeviceRequest* request = FindRequest(label);
+  if (!request) {
+    DVLOG(1) << "SetupRequest label " << label << " doesn't exist!!";
+    return;  // This can happen if the request has been canceled.
+  }
 
-  const MediaStreamType audio_type = request->request.audio_type;
-  const MediaStreamType video_type = request->request.video_type;
+  if (!request->security_origin.is_valid()) {
+    LOG(ERROR) << "Invalid security origin. "
+               << request->security_origin;
+    FinalizeRequestFailed(label,
+                          request,
+                          MEDIA_DEVICE_INVALID_SECURITY_ORIGIN);
+    return;
+  }
+
+  MediaStreamType audio_type = MEDIA_NO_SERVICE;
+  MediaStreamType video_type = MEDIA_NO_SERVICE;
+  ParseStreamType(request->options, &audio_type, &video_type);
+  request->SetAudioType(audio_type);
+  request->SetVideoType(video_type);
 
   bool is_web_contents_capture =
       audio_type == MEDIA_TAB_AUDIO_CAPTURE ||
       video_type == MEDIA_TAB_VIDEO_CAPTURE;
+  if (is_web_contents_capture && !SetupTabCaptureRequest(request)) {
+    FinalizeRequestFailed(label,
+                          request,
+                          MEDIA_DEVICE_TAB_CAPTURE_FAILURE);
+    return;
+  }
 
   bool is_screen_capture =
       video_type == MEDIA_DESKTOP_VIDEO_CAPTURE;
-
-  if (!is_web_contents_capture &&
-      !is_screen_capture &&
-      ((IsAudioMediaType(audio_type) && !audio_enumeration_cache_.valid) ||
-       (IsVideoMediaType(video_type) && !video_enumeration_cache_.valid))) {
-    // Enumerate the devices if there is no valid device lists to be used.
-    StartEnumeration(request);
+  if (is_screen_capture && !SetupScreenCaptureRequest(request)) {
+    FinalizeRequestFailed(label,
+                          request,
+                          MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE);
     return;
   }
 
-  // No need to do new device enumerations, post the request to UI
-  // immediately.
-  if (IsAudioMediaType(audio_type))
-    request->SetState(audio_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
-  if (IsVideoMediaType(video_type))
-    request->SetState(video_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
+#if defined(OS_CHROMEOS)
+  EnsureKeyboardMicChecked();
+#endif
+
+  if (!is_web_contents_capture && !is_screen_capture) {
+    if (EnumerationRequired(&audio_enumeration_cache_, audio_type) ||
+        EnumerationRequired(&video_enumeration_cache_, video_type)) {
+      // Enumerate the devices if there is no valid device lists to be used.
+      StartEnumeration(request);
+      return;
+    } else {
+        // Cache is valid, so log the cached devices for MediaStream requests.
+      if (request->request_type == MEDIA_GENERATE_STREAM) {
+        std::string log_message("Using cached devices for request.\n");
+        if (audio_type != MEDIA_NO_SERVICE) {
+          log_message +=
+              GetLogMessageString(audio_type, audio_enumeration_cache_.devices);
+        }
+        if (video_type != MEDIA_NO_SERVICE) {
+          log_message +=
+              GetLogMessageString(video_type, video_enumeration_cache_.devices);
+        }
+        SendMessageToNativeLog(log_message);
+      }
+    }
+
+    if (!SetupDeviceCaptureRequest(request)) {
+      FinalizeRequestFailed(label, request, MEDIA_DEVICE_NO_HARDWARE);
+      return;
+    }
+  }
+  PostRequestToUI(label, request);
+}
+
+bool MediaStreamManager::SetupDeviceCaptureRequest(DeviceRequest* request) {
+  DCHECK((request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
+          request->audio_type() == MEDIA_NO_SERVICE) &&
+         (request->video_type() == MEDIA_DEVICE_VIDEO_CAPTURE ||
+          request->video_type() == MEDIA_NO_SERVICE));
+  std::string audio_device_id;
+  if (request->options.audio_requested &&
+      !GetRequestedDeviceCaptureId(request, request->audio_type(),
+                                     &audio_device_id)) {
+    return false;
+  }
+
+  std::string video_device_id;
+  if (request->options.video_requested &&
+      !GetRequestedDeviceCaptureId(request, request->video_type(),
+                                   &video_device_id)) {
+    return false;
+  }
+  request->CreateUIRequest(audio_device_id, video_device_id);
+  DVLOG(3) << "Audio requested " << request->options.audio_requested
+           << " device id = " << audio_device_id
+           << "Video requested " << request->options.video_requested
+           << " device id = " << video_device_id;
+  return true;
+}
+
+bool MediaStreamManager::SetupTabCaptureRequest(DeviceRequest* request) {
+  DCHECK(request->audio_type() == MEDIA_TAB_AUDIO_CAPTURE ||
+         request->video_type() == MEDIA_TAB_VIDEO_CAPTURE);
+
+  std::string capture_device_id;
+  bool mandatory_audio = false;
+  bool mandatory_video = false;
+  if (!request->options.GetFirstAudioConstraintByName(kMediaStreamSourceId,
+                                                      &capture_device_id,
+                                                      &mandatory_audio) &&
+      !request->options.GetFirstVideoConstraintByName(kMediaStreamSourceId,
+                                                      &capture_device_id,
+                                                      &mandatory_video)) {
+    return false;
+  }
+  DCHECK(mandatory_audio || mandatory_video);
+
+  // Customize options for a WebContents based capture.
+  int target_render_process_id = 0;
+  int target_render_frame_id = 0;
+
+  bool has_valid_device_id = WebContentsCaptureUtil::ExtractTabCaptureTarget(
+      capture_device_id, &target_render_process_id, &target_render_frame_id);
+  if (!has_valid_device_id ||
+      (request->audio_type() != MEDIA_TAB_AUDIO_CAPTURE &&
+       request->audio_type() != MEDIA_NO_SERVICE) ||
+      (request->video_type() != MEDIA_TAB_VIDEO_CAPTURE &&
+       request->video_type() != MEDIA_NO_SERVICE)) {
+    return false;
+  }
+
+  request->CreateTabCaptureUIRequest(target_render_process_id,
+                                     target_render_frame_id,
+                                     capture_device_id);
+
+  DVLOG(3) << "SetupTabCaptureRequest "
+           << ", {capture_device_id = " << capture_device_id <<  "}"
+           << ", {target_render_process_id = " << target_render_process_id
+           << "}"
+           << ", {target_render_frame_id = " << target_render_frame_id << "}";
+  return true;
+}
+
+bool MediaStreamManager::SetupScreenCaptureRequest(DeviceRequest* request) {
+  DCHECK(request->audio_type() == MEDIA_LOOPBACK_AUDIO_CAPTURE ||
+         request->video_type() == MEDIA_DESKTOP_VIDEO_CAPTURE);
+
+  // For screen capture we only support two valid combinations:
+  // (1) screen video capture only, or
+  // (2) screen video capture with loopback audio capture.
+  if (request->video_type() != MEDIA_DESKTOP_VIDEO_CAPTURE ||
+      (request->audio_type() != MEDIA_NO_SERVICE &&
+       request->audio_type() != MEDIA_LOOPBACK_AUDIO_CAPTURE)) {
+    LOG(ERROR) << "Invalid screen capture request.";
+    return false;
+  }
+
+  std::string video_device_id;
+  if (request->video_type() == MEDIA_DESKTOP_VIDEO_CAPTURE) {
+    std::string video_stream_source;
+    bool mandatory = false;
+    if (!request->options.GetFirstVideoConstraintByName(
+        kMediaStreamSource,
+        &video_stream_source,
+        &mandatory)) {
+      LOG(ERROR) << kMediaStreamSource << " not found.";
+      return false;
+    }
+    DCHECK(mandatory);
+
+    if (video_stream_source == kMediaStreamSourceDesktop) {
+      if (!request->options.GetFirstVideoConstraintByName(
+          kMediaStreamSourceId,
+          &video_device_id,
+          &mandatory)) {
+        LOG(ERROR) << kMediaStreamSourceId << " not found.";
+        return false;
+      }
+      DCHECK(mandatory);
+    }
+  }
+
+  request->CreateUIRequest("", video_device_id);
+  return true;
+}
 
-  PostRequestToUI(label);
+StreamDeviceInfoArray MediaStreamManager::GetDevicesOpenedByRequest(
+    const std::string& label) const {
+  DeviceRequest* request = FindRequest(label);
+  if (!request)
+    return StreamDeviceInfoArray();
+  return request->devices;
 }
 
 bool MediaStreamManager::FindExistingRequestedDeviceInfo(
-    int render_process_id,
-    int render_view_id,
-    MediaStreamRequestType type,
-    const std::string& device_id,
-    StreamDeviceInfo* device_info,
-    MediaRequestState* request_state) const {
-  DCHECK(device_info);
-  DCHECK(request_state);
+    const DeviceRequest& new_request,
+    const MediaStreamDevice& new_device_info,
+    StreamDeviceInfo* existing_device_info,
+    MediaRequestState* existing_request_state) const {
+  DCHECK(existing_device_info);
+  DCHECK(existing_request_state);
+
+  std::string source_id = content::GetHMACForMediaDeviceID(
+      new_request.salt_callback,
+      new_request.security_origin,
+      new_device_info.id);
+
   for (DeviceRequests::const_iterator it = requests_.begin();
        it != requests_.end() ; ++it) {
     const DeviceRequest* request = it->second;
-    if (request->requesting_process_id ==render_process_id &&
-        request->requesting_view_id == render_view_id &&
-        request->request.request_type == type) {
+    if (request->requesting_process_id == new_request.requesting_process_id &&
+        request->requesting_frame_id == new_request.requesting_frame_id &&
+        request->request_type == new_request.request_type) {
       for (StreamDeviceInfoArray::const_iterator device_it =
                request->devices.begin();
            device_it != request->devices.end(); ++device_it) {
-        if (device_it->device.id == device_id) {
-            *device_info = *device_it;
-            *request_state = request->state(device_it->device.type);
+        if (device_it->device.id == source_id &&
+            device_it->device.type == new_device_info.type) {
+          *existing_device_info = *device_it;
+          // Make sure that the audio |effects| reflect what the request
+          // is set to and not what the capabilities are.
+          FilterAudioEffects(request->options,
+              &existing_device_info->device.input.effects);
+          *existing_request_state = request->state(device_it->device.type);
           return true;
         }
       }
@@ -770,53 +1421,201 @@ bool MediaStreamManager::FindExistingRequestedDeviceInfo(
   return false;
 }
 
-std::string MediaStreamManager::FindFirstMediaStreamRequestWithDevice(
-    const MediaStreamDevice& device) const {
-  for (DeviceRequests::const_iterator it = requests_.begin();
-       it != requests_.end() ; ++it) {
-    const DeviceRequest* request = it->second;
-    if (request->request.request_type != MEDIA_GENERATE_STREAM)
-      continue;
-    for (StreamDeviceInfoArray::const_iterator device_it =
-             request->devices.begin();
-         device_it != request->devices.end(); ++device_it) {
-      if (device_it->device.id == device.id &&
-          device_it->device.type == device.type) {
-        return it->first;
-      }
+void MediaStreamManager::FinalizeGenerateStream(const std::string& label,
+                                                DeviceRequest* request) {
+  DVLOG(1) << "FinalizeGenerateStream label " << label;
+  const StreamDeviceInfoArray& requested_devices = request->devices;
+
+  // Partition the array of devices into audio vs video.
+  StreamDeviceInfoArray audio_devices, video_devices;
+  for (StreamDeviceInfoArray::const_iterator device_it =
+           requested_devices.begin();
+       device_it != requested_devices.end(); ++device_it) {
+    if (IsAudioInputMediaType(device_it->device.type)) {
+      audio_devices.push_back(*device_it);
+    } else if (IsVideoMediaType(device_it->device.type)) {
+      video_devices.push_back(*device_it);
+    } else {
+      NOTREACHED();
     }
   }
-  return std::string();
+
+  request->requester->StreamGenerated(
+      request->requesting_frame_id,
+      request->page_request_id,
+      label, audio_devices, video_devices);
+}
+
+void MediaStreamManager::FinalizeRequestFailed(
+    const std::string& label,
+    DeviceRequest* request,
+    content::MediaStreamRequestResult result) {
+  if (request->requester)
+    request->requester->StreamGenerationFailed(
+        request->requesting_frame_id,
+        request->page_request_id,
+        result);
+
+  if (request->request_type == MEDIA_DEVICE_ACCESS &&
+      !request->callback.is_null()) {
+    request->callback.Run(MediaStreamDevices(), request->ui_proxy.Pass());
+  }
+
+  DeleteRequest(label);
+}
+
+void MediaStreamManager::FinalizeOpenDevice(const std::string& label,
+                                            DeviceRequest* request) {
+  const StreamDeviceInfoArray& requested_devices = request->devices;
+  request->requester->DeviceOpened(request->requesting_frame_id,
+                                   request->page_request_id,
+                                   label, requested_devices.front());
+}
+
+void MediaStreamManager::FinalizeEnumerateDevices(const std::string& label,
+                                                  DeviceRequest* request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DCHECK_EQ(request->request_type, MEDIA_ENUMERATE_DEVICES);
+  DCHECK(((request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
+           request->audio_type() == MEDIA_DEVICE_AUDIO_OUTPUT) &&
+          request->video_type() == MEDIA_NO_SERVICE) ||
+         (request->audio_type() == MEDIA_NO_SERVICE &&
+          request->video_type() == MEDIA_DEVICE_VIDEO_CAPTURE));
+
+  if (request->security_origin.is_valid()) {
+    for (StreamDeviceInfoArray::iterator it = request->devices.begin();
+         it != request->devices.end(); ++it) {
+      TranslateDeviceIdToSourceId(request, &it->device);
+    }
+  } else {
+    request->devices.clear();
+  }
+
+  if (use_fake_ui_) {
+    if (!fake_ui_)
+      fake_ui_.reset(new FakeMediaStreamUIProxy());
+    request->ui_proxy = fake_ui_.Pass();
+  } else {
+    request->ui_proxy = MediaStreamUIProxy::Create();
+  }
+
+  // Output label permissions are based on input permission.
+  MediaStreamType type =
+      request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE ||
+      request->audio_type() == MEDIA_DEVICE_AUDIO_OUTPUT
+      ? MEDIA_DEVICE_AUDIO_CAPTURE
+      : MEDIA_DEVICE_VIDEO_CAPTURE;
+
+  request->ui_proxy->CheckAccess(
+      request->security_origin,
+      type,
+      request->requesting_process_id,
+      request->requesting_frame_id,
+      base::Bind(&MediaStreamManager::HandleCheckMediaAccessResponse,
+                 base::Unretained(this),
+                 label));
+}
+
+void MediaStreamManager::HandleCheckMediaAccessResponse(
+    const std::string& label,
+    bool have_access) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  DeviceRequest* request = FindRequest(label);
+  if (!request) {
+    // This can happen if the request was cancelled.
+    DVLOG(1) << "The request with label " << label << " does not exist.";
+    return;
+  }
+
+  if (!have_access)
+    ClearDeviceLabels(&request->devices);
+
+  request->requester->DevicesEnumerated(
+      request->requesting_frame_id,
+      request->page_request_id,
+      label,
+      request->devices);
+
+  // TODO(tommi):
+  // Ideally enumeration requests should be deleted once they have been served
+  // (as any request).  However, this implementation mixes requests and
+  // notifications together so enumeration requests are kept open by some
+  // implementations (only Pepper?) and enumerations are done again when
+  // device notifications are fired.
+  // Implementations that just want to request the device list and be done
+  // (e.g. DeviceRequestMessageFilter), they must (confusingly) call
+  // CancelRequest() after the request has been fulfilled.  This is not
+  // obvious, not consistent in this class (see e.g. FinalizeMediaAccessRequest)
+  // and can lead to subtle bugs (requests not deleted at all deleted too
+  // early).
+  //
+  // Basically, it is not clear that using requests as an additional layer on
+  // top of device notifications is necessary or good.
+  //
+  // To add to this, MediaStreamManager currently relies on the external
+  // implementations of MediaStreamRequester to delete enumeration requests via
+  // CancelRequest and e.g. DeviceRequestMessageFilter does this.  However the
+  // Pepper implementation does not seem to to this at all (and from what I can
+  // see, it is the only implementation that uses an enumeration request as a
+  // notification mechanism).
+  //
+  // We should decouple notifications from enumeration requests and once that
+  // has been done, remove the requirement to call CancelRequest() to delete
+  // enumeration requests and uncomment the following line:
+  //
+  // DeleteRequest(label);
+}
+
+void MediaStreamManager::FinalizeMediaAccessRequest(
+    const std::string& label,
+    DeviceRequest* request,
+    const MediaStreamDevices& devices) {
+  if (!request->callback.is_null())
+    request->callback.Run(devices, request->ui_proxy.Pass());
+
+  // Delete the request since it is done.
+  DeleteRequest(label);
 }
 
 void MediaStreamManager::InitializeDeviceManagersOnIOThread() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  if (device_thread_)
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (device_task_runner_.get())
     return;
 
-  device_thread_.reset(new base::Thread("MediaStreamDeviceThread"));
-#if defined(OS_WIN)
-  device_thread_->init_com_with_mta(true);
-#endif
-  CHECK(device_thread_->Start());
+  device_task_runner_ = audio_manager_->GetWorkerTaskRunner();
 
   audio_input_device_manager_ = new AudioInputDeviceManager(audio_manager_);
-  audio_input_device_manager_->Register(
-      this, device_thread_->message_loop_proxy().get());
-
-  video_capture_manager_ = new VideoCaptureManager();
-  video_capture_manager_->Register(this,
-                                   device_thread_->message_loop_proxy().get());
+  audio_input_device_manager_->Register(this, device_task_runner_);
 
   // We want to be notified of IO message loop destruction to delete the thread
   // and the device managers.
   io_loop_ = base::MessageLoop::current();
   io_loop_->AddDestructionObserver(this);
+
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kUseFakeDeviceForMediaStream)) {
+    audio_input_device_manager()->UseFakeDevice();
+  }
+
+  video_capture_manager_ =
+      new VideoCaptureManager(media::VideoCaptureDeviceFactory::CreateFactory(
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)));
+#if defined(OS_WIN)
+  // Use an STA Video Capture Thread to try to avoid crashes on enumeration of
+  // buggy third party Direct Show modules, http://crbug.com/428958.
+  video_capture_thread_.init_com_with_mta(false);
+  CHECK(video_capture_thread_.Start());
+  video_capture_manager_->Register(this,
+                                   video_capture_thread_.message_loop_proxy());
+#else
+  video_capture_manager_->Register(this, device_task_runner_);
+#endif
 }
 
 void MediaStreamManager::Opened(MediaStreamType stream_type,
                                 int capture_session_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DVLOG(1) << "Opened({stream_type = " << stream_type <<  "} "
            << "{capture_session_id = " << capture_session_id << "})";
   // Find the request(s) containing this device and mark it as used.
@@ -830,12 +1629,13 @@ void MediaStreamManager::Opened(MediaStreamType stream_type,
     for (StreamDeviceInfoArray::iterator device_it = devices->begin();
          device_it != devices->end(); ++device_it) {
       if (device_it->device.type == stream_type &&
-          device_it->session_id == capture_session_id &&
-          request->state(device_it->device.type) != MEDIA_REQUEST_STATE_DONE) {
+          device_it->session_id == capture_session_id) {
+        CHECK(request->state(device_it->device.type) ==
+            MEDIA_REQUEST_STATE_OPENING);
         // We've found a matching request.
         request->SetState(device_it->device.type, MEDIA_REQUEST_STATE_DONE);
 
-        if (IsAudioMediaType(device_it->device.type)) {
+        if (IsAudioInputMediaType(device_it->device.type)) {
           // Store the native audio parameters in the device struct.
           // TODO(xians): Handle the tab capture sample rate/channel layout
           // in AudioInputDeviceManager::Open().
@@ -843,8 +1643,15 @@ void MediaStreamManager::Opened(MediaStreamType stream_type,
             const StreamDeviceInfo* info =
                 audio_input_device_manager_->GetOpenedDeviceInfoById(
                     device_it->session_id);
-            DCHECK_EQ(info->device.id, device_it->device.id);
             device_it->device.input = info->device.input;
+
+            // Since the audio input device manager will set the input
+            // parameters to the default settings (including supported effects),
+            // we need to adjust those settings here according to what the
+            // request asks for.
+            FilterAudioEffects(request->options,
+                &device_it->device.input.effects);
+
             device_it->device.matched_output = info->device.matched_output;
           }
         }
@@ -862,27 +1669,12 @@ void MediaStreamManager::HandleRequestDone(const std::string& label,
   DVLOG(1) << "HandleRequestDone("
            << ", {label = " << label <<  "})";
 
-  const StreamDeviceInfoArray& requested_devices = request->devices;
-  switch (request->request.request_type) {
+  switch (request->request_type) {
     case MEDIA_OPEN_DEVICE:
-      request->requester->DeviceOpened(label, requested_devices.front());
+      FinalizeOpenDevice(label, request);
       break;
     case MEDIA_GENERATE_STREAM: {
-      // Partition the array of devices into audio vs video.
-      StreamDeviceInfoArray audio_devices, video_devices;
-      for (StreamDeviceInfoArray::const_iterator device_it =
-               requested_devices.begin();
-           device_it != requested_devices.end(); ++device_it) {
-        if (IsAudioMediaType(device_it->device.type)) {
-          audio_devices.push_back(*device_it);
-        } else if (IsVideoMediaType(device_it->device.type)) {
-          video_devices.push_back(*device_it);
-        } else {
-          NOTREACHED();
-        }
-      }
-
-      request->requester->StreamGenerated(label, audio_devices, video_devices);
+      FinalizeGenerateStream(label, request);
       break;
     }
     default:
@@ -893,20 +1685,29 @@ void MediaStreamManager::HandleRequestDone(const std::string& label,
   if (request->ui_proxy.get()) {
     request->ui_proxy->OnStarted(
         base::Bind(&MediaStreamManager::StopMediaStreamFromBrowser,
-                   base::Unretained(this), label));
+                   base::Unretained(this),
+                   label),
+        base::Bind(&MediaStreamManager::OnMediaStreamUIWindowId,
+                   base::Unretained(this),
+                   request->video_type(),
+                   request->devices));
   }
 }
 
 void MediaStreamManager::Closed(MediaStreamType stream_type,
                                 int capture_session_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
 void MediaStreamManager::DevicesEnumerated(
     MediaStreamType stream_type, const StreamDeviceInfoArray& devices) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DVLOG(1) << "DevicesEnumerated("
-           << ", {stream_type = " << stream_type <<  "})";
+           << "{stream_type = " << stream_type << "})" << std::endl;
+
+  std::string log_message = "New device enumeration result:\n" +
+                            GetLogMessageString(stream_type, devices);
+  SendMessageToNativeLog(log_message);
 
   // Only cache the device list when the device list has been changed.
   bool need_update_clients = false;
@@ -918,9 +1719,14 @@ void MediaStreamManager::DevicesEnumerated(
       !std::equal(devices.begin(), devices.end(), cache->devices.begin(),
                   StreamDeviceInfo::IsEqual)) {
     StopRemovedDevices(cache->devices, devices);
-    cache->valid = true;
     cache->devices = devices;
     need_update_clients = true;
+
+    // The device might not be able to be enumerated when it is not warmed up,
+    // for example, when the machine just wakes up from sleep. We set the cache
+    // to be invalid so that the next media request will trigger the
+    // enumeration again. See issue/317673.
+    cache->valid = !devices.empty();
   }
 
   if (need_update_clients && monitoring_started_)
@@ -934,33 +1740,41 @@ void MediaStreamManager::DevicesEnumerated(
   for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
        ++it) {
     if (it->second->state(stream_type) == MEDIA_REQUEST_STATE_REQUESTED &&
-        Requested(it->second->request, stream_type)) {
-      if (it->second->request.request_type != MEDIA_ENUMERATE_DEVICES)
+        (it->second->audio_type() == stream_type ||
+         it->second->video_type() == stream_type)) {
+      if (it->second->request_type != MEDIA_ENUMERATE_DEVICES)
         it->second->SetState(stream_type, MEDIA_REQUEST_STATE_PENDING_APPROVAL);
       label_list.push_back(it->first);
     }
   }
+
   for (std::list<std::string>::iterator it = label_list.begin();
        it != label_list.end(); ++it) {
-    DeviceRequest* request = requests_[*it];
-    switch (request->request.request_type) {
+    DeviceRequest* request = FindRequest(*it);
+    switch (request->request_type) {
       case MEDIA_ENUMERATE_DEVICES:
-        if (need_update_clients && request->requester)
-          request->requester->DevicesEnumerated(*it, devices);
+        if (need_update_clients && request->requester) {
+          request->devices = devices;
+          FinalizeEnumerateDevices(*it, request);
+        }
         break;
       default:
-        if (request->state(request->request.audio_type) ==
+        if (request->state(request->audio_type()) ==
                 MEDIA_REQUEST_STATE_REQUESTED ||
-            request->state(request->request.video_type) ==
+            request->state(request->video_type()) ==
                 MEDIA_REQUEST_STATE_REQUESTED) {
           // We are doing enumeration for other type of media, wait until it is
           // all done before posting the request to UI because UI needs
           // the device lists to handle the request.
           break;
         }
-
-        // Post the request to UI for permission approval.
-        PostRequestToUI(*it);
+        if (!SetupDeviceCaptureRequest(request)) {
+          FinalizeRequestFailed(*it,
+                                request,
+                                MEDIA_DEVICE_NO_HARDWARE);
+        } else {
+          PostRequestToUI(*it, request);
+        }
         break;
     }
   }
@@ -969,109 +1783,102 @@ void MediaStreamManager::DevicesEnumerated(
   DCHECK_GE(active_enumeration_ref_count_[stream_type], 0);
 }
 
-void MediaStreamManager::Error(MediaStreamType stream_type,
-                               int capture_session_id,
-                               MediaStreamProviderError error) {
-  // Find the device for the error call.
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  DVLOG(1) << "Error("
-           << "{stream_type = " << stream_type << "} ,"
-           << "{capture_session_id = " << capture_session_id <<  "})";
+void MediaStreamManager::Aborted(MediaStreamType stream_type,
+                                 int capture_session_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  DVLOG(1) << "Aborted({stream_type = " << stream_type <<  "} "
+           << "{capture_session_id = " << capture_session_id << "})";
+  StopDevice(stream_type, capture_session_id);
+}
 
+// static
+void MediaStreamManager::SendMessageToNativeLog(const std::string& message) {
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(DoAddLogMessage, message));
+}
 
-  for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end();
-       ++it) {
-    StreamDeviceInfoArray& devices = it->second->devices;
+void MediaStreamManager::OnSuspend() {
+  SendMessageToNativeLog("Power state suspended.");
+}
 
-    // TODO(miu): BUG.  It's possible for the audio (or video) device array in
-    // the "requester" to become out-of-sync with the order of devices we have
-    // here.  See http://crbug.com/147650
-    int audio_device_idx = -1;
-    int video_device_idx = -1;
-    for (StreamDeviceInfoArray::iterator device_it = devices.begin();
-         device_it != devices.end(); ++device_it) {
-      if (IsAudioMediaType(device_it->device.type)) {
-        ++audio_device_idx;
-      } else if (IsVideoMediaType(device_it->device.type)) {
-        ++video_device_idx;
-      } else {
-        NOTREACHED();
-        continue;
-      }
-      if (device_it->device.type != stream_type ||
-          device_it->session_id != capture_session_id) {
-        continue;
-      }
-      // We've found the failing device. Find the error case:
-      // An error should only be reported to the MediaStreamManager if
-      // the request has not been fulfilled yet.
-      DCHECK(it->second->state(stream_type) != MEDIA_REQUEST_STATE_DONE);
-      if (it->second->state(stream_type) != MEDIA_REQUEST_STATE_DONE) {
-        // Request is not done, devices are not opened in this case.
-        if (devices.size() <= 1) {
-          scoped_ptr<DeviceRequest> request(it->second);
-          // 1. Device not opened and no other devices for this request ->
-          //    signal stream error and remove the request.
-          if (request->requester)
-            request->requester->StreamGenerationFailed(it->first);
-
-          RemoveRequest(it);
-        } else {
-          // 2. Not opened but other devices exists for this request -> remove
-          //    device from list, but don't signal an error.
-          devices.erase(device_it);  // NOTE: This invalidates device_it!
-          it->second->SetState(stream_type, MEDIA_REQUEST_STATE_ERROR);
-          DVLOG(1) << "Error("
-                   << ", {capture_session_id = " << capture_session_id <<  "})";
-        }
-      }
-      if (RequestDone(*it->second))
-        HandleRequestDone(it->first, it->second);
-      break;
-    }
+void MediaStreamManager::OnResume() {
+  SendMessageToNativeLog("Power state resumed.");
+}
+
+void MediaStreamManager::AddLogMessageOnIOThread(const std::string& message) {
+  // Get render process ids on the IO thread.
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // Grab all unique process ids that request a MediaStream or have a
+  // MediaStream running.
+  std::set<int> requesting_process_ids;
+  for (DeviceRequests::const_iterator it = requests_.begin();
+       it != requests_.end(); ++it) {
+    DeviceRequest* request = it->second;
+    if (request->request_type == MEDIA_GENERATE_STREAM)
+      requesting_process_ids.insert(request->requesting_process_id);
   }
+
+  // MediaStreamManager is a singleton in BrowserMainLoop, which owns the UI
+  // thread. MediaStreamManager has the same lifetime as the UI thread, so it is
+  // safe to use base::Unretained.
+  BrowserThread::PostTask(
+      BrowserThread::UI,
+      FROM_HERE,
+      base::Bind(&MediaStreamManager::AddLogMessageOnUIThread,
+                 base::Unretained(this),
+                 requesting_process_ids,
+                 message));
+}
+
+void MediaStreamManager::AddLogMessageOnUIThread(
+    const std::set<int>& requesting_process_ids,
+    const std::string& message) {
+#if defined(ENABLE_WEBRTC)
+  // Must be on the UI thread to access RenderProcessHost from process ID.
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  for (std::set<int>::const_iterator it = requesting_process_ids.begin();
+       it != requesting_process_ids.end(); ++it) {
+    // Log the message to all renderers that are requesting a MediaStream or
+    // have a MediaStream running.
+    content::RenderProcessHostImpl* render_process_host_impl =
+        static_cast<content::RenderProcessHostImpl*>(
+            content::RenderProcessHost::FromID(*it));
+    if (render_process_host_impl)
+      render_process_host_impl->WebRtcLogMessage(message);
+  }
+#endif
 }
 
 void MediaStreamManager::HandleAccessRequestResponse(
     const std::string& label,
-    const MediaStreamDevices& devices) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+    const MediaStreamDevices& devices,
+    content::MediaStreamRequestResult result) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DVLOG(1) << "HandleAccessRequestResponse("
            << ", {label = " << label <<  "})";
 
-  DeviceRequests::iterator request_it = requests_.find(label);
-  if (request_it == requests_.end()) {
+  DeviceRequest* request = FindRequest(label);
+  if (!request) {
+    // The request has been canceled before the UI returned.
     return;
   }
 
-  // Handle the case when the request was denied.
-  if (devices.empty()) {
-    // Notify the users about the request result.
-    scoped_ptr<DeviceRequest> request(request_it->second);
-    if (request->requester)
-      request->requester->StreamGenerationFailed(label);
-
-    if (request->request.request_type == MEDIA_DEVICE_ACCESS &&
-        !request->callback.is_null()) {
-      request->callback.Run(MediaStreamDevices(), request->ui_proxy.Pass());
-    }
-
-    RemoveRequest(request_it);
+  if (request->request_type == MEDIA_DEVICE_ACCESS) {
+    FinalizeMediaAccessRequest(label, request, devices);
     return;
   }
 
-  if (request_it->second->request.request_type == MEDIA_DEVICE_ACCESS) {
-    scoped_ptr<DeviceRequest> request(request_it->second);
-    if (!request->callback.is_null())
-      request->callback.Run(devices, request->ui_proxy.Pass());
-
-    // Delete the request since it is done.
-    RemoveRequest(request_it);
+  // Handle the case when the request was denied.
+  if (result != MEDIA_DEVICE_OK) {
+    FinalizeRequestFailed(label, request, result);
     return;
   }
+  DCHECK(!devices.empty());
 
   // Process all newly-accepted devices for this request.
-  DeviceRequest* request = request_it->second;
   bool found_audio = false;
   bool found_video = false;
   for (MediaStreamDevices::const_iterator device_it = devices.begin();
@@ -1079,11 +1886,9 @@ void MediaStreamManager::HandleAccessRequestResponse(
     StreamDeviceInfo device_info;
     device_info.device = *device_it;
 
-    // TODO(justinlin): Nicer way to do this?
-    // Re-append the device's id since we lost it when posting request to UI.
     if (device_info.device.type == content::MEDIA_TAB_VIDEO_CAPTURE ||
         device_info.device.type == content::MEDIA_TAB_AUDIO_CAPTURE) {
-      device_info.device.id = request->request.tab_capture_device_id;
+      device_info.device.id = request->UIRequest()->tab_capture_device_id;
 
       // Initialize the sample_rate and channel_layout here since for audio
       // mirroring, we don't go through EnumerateDevices where these are usually
@@ -1102,93 +1907,91 @@ void MediaStreamManager::HandleAccessRequestResponse(
       }
     }
 
-    if (device_info.device.type == request->request.audio_type) {
+    if (device_info.device.type == request->audio_type()) {
       found_audio = true;
-    } else if (device_info.device.type == request->request.video_type) {
+    } else if (device_info.device.type == request->video_type()) {
       found_video = true;
     }
 
     // If this is request for a new MediaStream, a device is only opened once
-    // per render view. This is so that the permission to use a device can be
+    // per render frame. This is so that the permission to use a device can be
     // revoked by a single call to StopStreamDevice regardless of how many
     // MediaStreams it is being used in.
-
-    if (request->request.request_type == MEDIA_GENERATE_STREAM) {
+    if (request->request_type == MEDIA_GENERATE_STREAM) {
       MediaRequestState state;
-      if (FindExistingRequestedDeviceInfo(request->requesting_process_id,
-                                          request->requesting_view_id,
-                                          request->request.request_type,
-                                          device_it->id,
+      if (FindExistingRequestedDeviceInfo(*request,
+                                          device_info.device,
                                           &device_info,
                                           &state)) {
         request->devices.push_back(device_info);
         request->SetState(device_info.device.type, state);
         DVLOG(1) << "HandleAccessRequestResponse - device already opened "
-            << ", {label = " << label <<  "}"
-            << ", device_id = " << device_it->id << "}";
+                 << ", {label = " << label <<  "}"
+                 << ", device_id = " << device_it->id << "}";
         continue;
       }
     }
     device_info.session_id =
         GetDeviceManager(device_info.device.type)->Open(device_info);
+    TranslateDeviceIdToSourceId(request, &device_info.device);
     request->devices.push_back(device_info);
+
     request->SetState(device_info.device.type, MEDIA_REQUEST_STATE_OPENING);
     DVLOG(1) << "HandleAccessRequestResponse - opening device "
              << ", {label = " << label <<  "}"
-             << ", {device_id = " << device_it->id << "}"
+             << ", {device_id = " << device_info.device.id << "}"
              << ", {session_id = " << device_info.session_id << "}";
   }
 
   // Check whether we've received all stream types requested.
-  if (!found_audio && IsAudioMediaType(request->request.audio_type)) {
-    request->SetState(request->request.audio_type, MEDIA_REQUEST_STATE_ERROR);
+  if (!found_audio && IsAudioInputMediaType(request->audio_type())) {
+    request->SetState(request->audio_type(), MEDIA_REQUEST_STATE_ERROR);
     DVLOG(1) << "Set no audio found label " << label;
   }
 
-  if (!found_video && IsVideoMediaType(request->request.video_type))
-    request->SetState(request->request.video_type, MEDIA_REQUEST_STATE_ERROR);
+  if (!found_video && IsVideoMediaType(request->video_type()))
+    request->SetState(request->video_type(), MEDIA_REQUEST_STATE_ERROR);
 
   if (RequestDone(*request))
     HandleRequestDone(label, request);
 }
 
 void MediaStreamManager::StopMediaStreamFromBrowser(const std::string& label) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  DeviceRequests::iterator it = requests_.find(label);
-  if (it == requests_.end())
+  DeviceRequest* request = FindRequest(label);
+  if (!request)
     return;
 
-  // Notify renderers that the stream has been stopped.
-  if (it->second->requester)
-    it->second->requester->StopGeneratedStream(
-        it->second->request.render_view_id,
-        label);
+  // Notify renderers that the devices in the stream will be stopped.
+  if (request->requester) {
+    for (StreamDeviceInfoArray::iterator device_it = request->devices.begin();
+         device_it != request->devices.end(); ++device_it) {
+      request->requester->DeviceStopped(request->requesting_frame_id,
+                                        label,
+                                        *device_it);
+    }
+  }
 
   CancelRequest(label);
 }
 
-void MediaStreamManager::UseFakeDevice() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  video_capture_manager()->UseFakeDevice();
-  audio_input_device_manager()->UseFakeDevice();
-}
-
 void MediaStreamManager::UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy> fake_ui) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   use_fake_ui_ = true;
   fake_ui_ = fake_ui.Pass();
 }
 
 void MediaStreamManager::WillDestroyCurrentMessageLoop() {
+  DVLOG(3) << "MediaStreamManager::WillDestroyCurrentMessageLoop()";
   DCHECK_EQ(base::MessageLoop::current(), io_loop_);
   DCHECK(requests_.empty());
-  if (device_thread_) {
+  if (device_task_runner_.get()) {
     StopMonitoring();
 
     video_capture_manager_->Unregister();
     audio_input_device_manager_->Unregister();
-    device_thread_.reset();
+    device_task_runner_ = NULL;
   }
 
   audio_input_device_manager_ = NULL;
@@ -1198,11 +2001,9 @@ void MediaStreamManager::WillDestroyCurrentMessageLoop() {
 void MediaStreamManager::NotifyDevicesChanged(
     MediaStreamType stream_type,
     const StreamDeviceInfoArray& devices) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
   MediaObserver* media_observer =
       GetContentClient()->browser()->GetMediaObserver();
-  if (media_observer == NULL)
-    return;
 
   // Map the devices to MediaStreamDevices.
   MediaStreamDevices new_devices;
@@ -1211,36 +2012,38 @@ void MediaStreamManager::NotifyDevicesChanged(
     new_devices.push_back(it->device);
   }
 
-  if (IsAudioMediaType(stream_type)) {
-    media_observer->OnAudioCaptureDevicesChanged(new_devices);
+  if (IsAudioInputMediaType(stream_type)) {
+    MediaCaptureDevicesImpl::GetInstance()->OnAudioCaptureDevicesChanged(
+        new_devices);
+    if (media_observer)
+      media_observer->OnAudioCaptureDevicesChanged();
   } else if (IsVideoMediaType(stream_type)) {
-    media_observer->OnVideoCaptureDevicesChanged(new_devices);
+    MediaCaptureDevicesImpl::GetInstance()->OnVideoCaptureDevicesChanged(
+        new_devices);
+    if (media_observer)
+      media_observer->OnVideoCaptureDevicesChanged();
   } else {
     NOTREACHED();
   }
 }
 
 bool MediaStreamManager::RequestDone(const DeviceRequest& request) const {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-  const bool requested_audio = IsAudioMediaType(request.request.audio_type);
-  const bool requested_video = IsVideoMediaType(request.request.video_type);
+  const bool requested_audio = IsAudioInputMediaType(request.audio_type());
+  const bool requested_video = IsVideoMediaType(request.video_type());
 
   const bool audio_done =
       !requested_audio ||
-      request.state(request.request.audio_type) ==
-      MEDIA_REQUEST_STATE_DONE ||
-      request.state(request.request.audio_type) ==
-      MEDIA_REQUEST_STATE_ERROR;
+      request.state(request.audio_type()) == MEDIA_REQUEST_STATE_DONE ||
+      request.state(request.audio_type()) == MEDIA_REQUEST_STATE_ERROR;
   if (!audio_done)
     return false;
 
   const bool video_done =
       !requested_video ||
-      request.state(request.request.video_type) ==
-      MEDIA_REQUEST_STATE_DONE ||
-      request.state(request.request.video_type) ==
-      MEDIA_REQUEST_STATE_ERROR;
+      request.state(request.video_type()) == MEDIA_REQUEST_STATE_DONE ||
+      request.state(request.video_type()) == MEDIA_REQUEST_STATE_ERROR;
   if (!video_done)
     return false;
 
@@ -1251,7 +2054,7 @@ MediaStreamProvider* MediaStreamManager::GetDeviceManager(
     MediaStreamType stream_type) {
   if (IsVideoMediaType(stream_type)) {
     return video_capture_manager();
-  } else if (IsAudioMediaType(stream_type)) {
+  } else if (IsAudioInputMediaType(stream_type)) {
     return audio_input_device_manager();
   }
   NOTREACHED();
@@ -1260,7 +2063,7 @@ MediaStreamProvider* MediaStreamManager::GetDeviceManager(
 
 void MediaStreamManager::OnDevicesChanged(
     base::SystemMonitor::DeviceType device_type) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   // NOTE: This method is only called in response to physical audio/video device
   // changes (from the operating system).
@@ -1281,4 +2084,57 @@ void MediaStreamManager::OnDevicesChanged(
   GetDeviceManager(stream_type)->EnumerateDevices(stream_type);
 }
 
+void MediaStreamManager::OnMediaStreamUIWindowId(MediaStreamType video_type,
+                                                 StreamDeviceInfoArray devices,
+                                                 gfx::NativeViewId window_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!window_id)
+    return;
+
+  // Pass along for desktop capturing. Ignored for other stream types.
+  if (video_type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
+    for (StreamDeviceInfoArray::iterator it = devices.begin();
+         it != devices.end();
+         ++it) {
+      if (it->device.type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
+        video_capture_manager_->SetDesktopCaptureWindowId(it->session_id,
+                                                          window_id);
+        break;
+      }
+    }
+  }
+}
+
+#if defined(OS_CHROMEOS)
+void MediaStreamManager::EnsureKeyboardMicChecked() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!has_checked_keyboard_mic_) {
+    has_checked_keyboard_mic_ = true;
+    BrowserThread::PostTask(
+        BrowserThread::UI, FROM_HERE,
+        base::Bind(&MediaStreamManager::CheckKeyboardMicOnUIThread,
+                   base::Unretained(this)));
+  }
+}
+
+void MediaStreamManager::CheckKeyboardMicOnUIThread() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  // We will post this on the device thread before the media media access
+  // request is posted on the UI thread, so setting the keyboard mic info will
+  // be done before any stream is created.
+  if (chromeos::CrasAudioHandler::Get()->HasKeyboardMic()) {
+    device_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&MediaStreamManager::SetKeyboardMicOnDeviceThread,
+                   base::Unretained(this)));
+  }
+}
+
+void MediaStreamManager::SetKeyboardMicOnDeviceThread() {
+  DCHECK(device_task_runner_->BelongsToCurrentThread());
+  audio_manager_->SetHasKeyboardMic();
+}
+#endif
+
 }  // namespace content