#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
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) {
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 {
}
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
// 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;
private:
std::vector<MediaRequestState> state_;
+ scoped_ptr<MediaStreamRequest> ui_request_;
+ MediaStreamType audio_type_;
+ MediaStreamType video_type_;
};
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_);
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) {
}
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);
}
}
}
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(
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;
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_);
}
}
-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) {
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_)
}
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;
}
}
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.
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().
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;
}
}
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:
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;
!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_)
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;
}
}
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();
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
}
}
- 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;
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;
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;
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();
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).
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