diff options
author | grunell@chromium.org <grunell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-04 14:38:31 +0000 |
---|---|---|
committer | grunell@chromium.org <grunell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-04 14:38:31 +0000 |
commit | bfd666154471a2d18da5362b852d0931c99e21e0 (patch) | |
tree | 120298e67b3a039762ea9d4f829bc24dfe87b22d | |
parent | ee37d41fdeba02e578149ae1269db9e89f7c9a51 (diff) | |
download | chromium_src-bfd666154471a2d18da5362b852d0931c99e21e0.zip chromium_src-bfd666154471a2d18da5362b852d0931c99e21e0.tar.gz chromium_src-bfd666154471a2d18da5362b852d0931c99e21e0.tar.bz2 |
Support for audio output devices for getMediaDevices.
* This CL depends on https://codereview.chromium.org/287383002/
* There's no caching or monitoring in this CL.
* Browser test will be done in follow-up CL. (That's pending blink interface landing.)
BUG=338511
Review URL: https://codereview.chromium.org/312773002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@274823 0039d316-1c4b-4281-b951-d872f2087c98
14 files changed, 262 insertions, 70 deletions
diff --git a/chrome/browser/media/media_stream_capture_indicator.cc b/chrome/browser/media/media_stream_capture_indicator.cc index 244a645..2522a65 100644 --- a/chrome/browser/media/media_stream_capture_indicator.cc +++ b/chrome/browser/media/media_stream_capture_indicator.cc @@ -230,7 +230,7 @@ void MediaStreamCaptureIndicator::WebContentsDeviceUsage::AddDevices( if (it->type == content::MEDIA_TAB_AUDIO_CAPTURE || it->type == content::MEDIA_TAB_VIDEO_CAPTURE) { ++mirroring_ref_count_; - } else if (content::IsAudioMediaType(it->type)) { + } else if (content::IsAudioInputMediaType(it->type)) { ++audio_ref_count_; } else if (content::IsVideoMediaType(it->type)) { ++video_ref_count_; @@ -252,7 +252,7 @@ void MediaStreamCaptureIndicator::WebContentsDeviceUsage::RemoveDevices( if (it->type == content::MEDIA_TAB_AUDIO_CAPTURE || it->type == content::MEDIA_TAB_VIDEO_CAPTURE) { --mirroring_ref_count_; - } else if (content::IsAudioMediaType(it->type)) { + } else if (content::IsAudioInputMediaType(it->type)) { --audio_ref_count_; } else if (content::IsVideoMediaType(it->type)) { --video_ref_count_; diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc index 78372bb5..36f6794 100644 --- a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc @@ -181,10 +181,12 @@ void MediaStreamDispatcherHost::OnEnumerateDevices( return; DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE || - type == MEDIA_DEVICE_VIDEO_CAPTURE); - bool have_permission = type == MEDIA_DEVICE_AUDIO_CAPTURE ? - resource_context_->AllowMicAccess(security_origin) : - resource_context_->AllowCameraAccess(security_origin); + type == MEDIA_DEVICE_VIDEO_CAPTURE || + type == MEDIA_DEVICE_AUDIO_OUTPUT); + bool have_permission = + type == MEDIA_DEVICE_AUDIO_CAPTURE || type == MEDIA_DEVICE_AUDIO_OUTPUT ? + resource_context_->AllowMicAccess(security_origin) : + resource_context_->AllowCameraAccess(security_origin); media_stream_manager_->EnumerateDevices( this, render_process_id_, render_view_id, salt_callback_, diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc index a09ffea..9e060d4 100644 --- a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc @@ -176,7 +176,7 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost, const content::StreamDeviceInfo& device) { if (IsVideoMediaType(device.device.type)) EXPECT_TRUE(StreamDeviceInfo::IsEqual(device, video_devices_[0])); - if (IsAudioMediaType(device.device.type)) + if (IsAudioInputMediaType(device.device.type)) EXPECT_TRUE(StreamDeviceInfo::IsEqual(device, audio_devices_[0])); OnDeviceStopped(current_ipc_->routing_id()); diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc index 2e2e3af..9a2779b 100644 --- a/content/browser/renderer_host/media/media_stream_manager.cc +++ b/content/browser/renderer_host/media/media_stream_manager.cc @@ -209,7 +209,9 @@ class MediaStreamManager::DeviceRequest { ~DeviceRequest() {} void SetAudioType(MediaStreamType audio_type) { - DCHECK(IsAudioMediaType(audio_type) || audio_type == MEDIA_NO_SERVICE); + DCHECK(IsAudioInputMediaType(audio_type) || + audio_type == MEDIA_DEVICE_AUDIO_OUTPUT || + audio_type == MEDIA_NO_SERVICE); audio_type_ = audio_type; } @@ -655,7 +657,8 @@ std::string MediaStreamManager::EnumerateDevices( 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); DeviceRequest* request = new DeviceRequest(requester, render_process_id, @@ -667,7 +670,7 @@ std::string MediaStreamManager::EnumerateDevices( MEDIA_ENUMERATE_DEVICES, StreamOptions(), sc); - if (IsAudioMediaType(type)) + if (IsAudioInputMediaType(type) || type == MEDIA_DEVICE_AUDIO_OUTPUT) request->SetAudioType(type); else if (IsVideoMediaType(type)) request->SetVideoType(type); @@ -691,6 +694,21 @@ void MediaStreamManager::DoEnumerateDevices(const std::string& 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; + } + MediaStreamType type; EnumerationCache* cache; if (request->audio_type() == MEDIA_DEVICE_AUDIO_CAPTURE) { @@ -715,6 +733,57 @@ void MediaStreamManager::DoEnumerateDevices(const std::string& label) { DVLOG(1) << "Enumerate Devices ({label = " << 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)); +} + +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_view_id, @@ -728,7 +797,7 @@ void MediaStreamManager::OpenDevice(MediaStreamRequester* requester, type == MEDIA_DEVICE_VIDEO_CAPTURE); DVLOG(1) << "OpenDevice ({page_request_id = " << page_request_id << "})"; StreamOptions options; - if (IsAudioMediaType(type)) { + if (IsAudioInputMediaType(type)) { options.audio_requested = true; options.mandatory_audio.push_back( StreamOptions::Constraint(kMediaStreamSourceInfoId, device_id)); @@ -964,6 +1033,7 @@ 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, @@ -1060,7 +1130,7 @@ void MediaStreamManager::PostRequestToUI(const std::string& label, const MediaStreamType video_type = request->video_type(); // Post the request to UI and set the state. - if (IsAudioMediaType(audio_type)) + 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); @@ -1341,7 +1411,7 @@ void MediaStreamManager::FinalizeGenerateStream(const std::string& label, for (StreamDeviceInfoArray::const_iterator device_it = requested_devices.begin(); device_it != requested_devices.end(); ++device_it) { - if (IsAudioMediaType(device_it->device.type)) { + 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); @@ -1494,7 +1564,7 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, // 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(). @@ -1797,7 +1867,7 @@ void MediaStreamManager::HandleAccessRequestResponse( } // Check whether we've received all stream types requested. - if (!found_audio && IsAudioMediaType(request->audio_type())) { + if (!found_audio && IsAudioInputMediaType(request->audio_type())) { request->SetState(request->audio_type(), MEDIA_REQUEST_STATE_ERROR); DVLOG(1) << "Set no audio found label " << label; } @@ -1865,7 +1935,7 @@ void MediaStreamManager::NotifyDevicesChanged( new_devices.push_back(it->device); } - if (IsAudioMediaType(stream_type)) { + if (IsAudioInputMediaType(stream_type)) { MediaCaptureDevicesImpl::GetInstance()->OnAudioCaptureDevicesChanged( new_devices); if (media_observer) @@ -1883,7 +1953,7 @@ void MediaStreamManager::NotifyDevicesChanged( bool MediaStreamManager::RequestDone(const DeviceRequest& request) const { DCHECK_CURRENTLY_ON(BrowserThread::IO); - const bool requested_audio = IsAudioMediaType(request.audio_type()); + const bool requested_audio = IsAudioInputMediaType(request.audio_type()); const bool requested_video = IsVideoMediaType(request.video_type()); const bool audio_done = @@ -1907,7 +1977,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(); diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h index e437a82..2d2ec87 100644 --- a/content/browser/renderer_host/media/media_stream_manager.h +++ b/content/browser/renderer_host/media/media_stream_manager.h @@ -246,6 +246,11 @@ class CONTENT_EXPORT MediaStreamManager void DoEnumerateDevices(const std::string& label); + // Enumerates audio output devices. No caching. + void EnumerateAudioOutputDevices(const std::string& label); + + void AudioOutputDevicesEnumerated(const StreamDeviceInfoArray& devices); + // Helpers. // Checks if all devices that was requested in the request identififed by // |label| has been opened and set the request state accordingly. @@ -353,7 +358,8 @@ class CONTENT_EXPORT MediaStreamManager StreamDeviceInfoArray devices, gfx::NativeViewId window_id); - // Task runner shared by VideoCaptureManager and AudioInputDeviceManager. + // Task runner shared by VideoCaptureManager and AudioInputDeviceManager and + // used for enumerating audio output devices. // Note: Enumeration tasks may take seconds to complete so must never be run // on any of the BrowserThreads (UI, IO, etc). See http://crbug.com/256945. scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_; diff --git a/content/browser/renderer_host/media/media_stream_ui_proxy.cc b/content/browser/renderer_host/media/media_stream_ui_proxy.cc index fdf7cca..1c92424 100644 --- a/content/browser/renderer_host/media/media_stream_ui_proxy.cc +++ b/content/browser/renderer_host/media/media_stream_ui_proxy.cc @@ -229,8 +229,8 @@ void FakeMediaStreamUIProxy::RequestAccess( for (MediaStreamDevices::const_iterator it = devices_.begin(); it != devices_.end(); ++it) { if (!accepted_audio && - IsAudioMediaType(request.audio_type) && - IsAudioMediaType(it->type) && + IsAudioInputMediaType(request.audio_type) && + IsAudioInputMediaType(it->type) && (request.requested_audio_device_id.empty() || request.requested_audio_device_id == it->id)) { devices_to_use.push_back(*it); diff --git a/content/public/common/media_stream_request.cc b/content/public/common/media_stream_request.cc index fa12fc8..cb3b8ed8 100644 --- a/content/public/common/media_stream_request.cc +++ b/content/public/common/media_stream_request.cc @@ -8,7 +8,7 @@ namespace content { -bool IsAudioMediaType(MediaStreamType type) { +bool IsAudioInputMediaType(MediaStreamType type) { return (type == MEDIA_DEVICE_AUDIO_CAPTURE || type == content::MEDIA_TAB_AUDIO_CAPTURE || type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE); diff --git a/content/public/common/media_stream_request.h b/content/public/common/media_stream_request.h index 37172c0..5a91fd1 100644 --- a/content/public/common/media_stream_request.h +++ b/content/public/common/media_stream_request.h @@ -38,6 +38,11 @@ enum MediaStreamType { // TODO(sergeyu): Replace with MEDIA_DESKTOP_AUDIO_CAPTURE. MEDIA_LOOPBACK_AUDIO_CAPTURE, + // This is used for enumerating audio output devices. + // TODO(grunell): Output isn't really a part of media streams. Device + // enumeration should be decoupled from media streams and related code. + MEDIA_DEVICE_AUDIO_OUTPUT, + NUM_MEDIA_TYPES }; @@ -77,7 +82,7 @@ enum MediaStreamRequestResult { // Convenience predicates to determine whether the given type represents some // audio or some video device. -CONTENT_EXPORT bool IsAudioMediaType(MediaStreamType type); +CONTENT_EXPORT bool IsAudioInputMediaType(MediaStreamType type); CONTENT_EXPORT bool IsVideoMediaType(MediaStreamType type); // TODO(xians): Change the structs to classes. @@ -152,7 +157,7 @@ struct CONTENT_EXPORT MediaStreamDevice { }; // These below two member variables are valid only when the type of device is - // audio (i.e. IsAudioMediaType returns true). + // audio (i.e. IsAudioInputMediaType returns true). // Contains the device properties of the capture device. AudioDeviceParameters input; diff --git a/content/renderer/media/media_stream_dispatcher.cc b/content/renderer/media/media_stream_dispatcher.cc index 6446c3c..0a2da78 100644 --- a/content/renderer/media/media_stream_dispatcher.cc +++ b/content/renderer/media/media_stream_dispatcher.cc @@ -137,7 +137,8 @@ void MediaStreamDispatcher::EnumerateDevices( const GURL& security_origin) { DCHECK(main_loop_->BelongsToCurrentThread()); DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE || - type == MEDIA_DEVICE_VIDEO_CAPTURE); + type == MEDIA_DEVICE_VIDEO_CAPTURE || + type == MEDIA_DEVICE_AUDIO_OUTPUT); DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices(" << request_id << ")"; @@ -298,7 +299,7 @@ void MediaStreamDispatcher::OnDeviceStopped( return; } Stream* stream = &it->second; - if (IsAudioMediaType(device_info.device.type)) + if (IsAudioInputMediaType(device_info.device.type)) RemoveStreamDeviceFromArray(device_info, &stream->audio_array); else RemoveStreamDeviceFromArray(device_info, &stream->video_array); @@ -336,7 +337,7 @@ void MediaStreamDispatcher::OnDeviceOpened( if (request.ipc_request == request_id) { Stream new_stream; new_stream.handler = request.handler; - if (IsAudioMediaType(device_info.device.type)) { + if (IsAudioInputMediaType(device_info.device.type)) { new_stream.audio_array.push_back(device_info); } else if (IsVideoMediaType(device_info.device.type)) { new_stream.video_array.push_back(device_info); diff --git a/content/renderer/media/media_stream_dispatcher_unittest.cc b/content/renderer/media/media_stream_dispatcher_unittest.cc index 6ab0cf7..3df5d85 100644 --- a/content/renderer/media/media_stream_dispatcher_unittest.cc +++ b/content/renderer/media/media_stream_dispatcher_unittest.cc @@ -65,7 +65,7 @@ class MockMediaStreamDispatcherEventHandler if (IsVideoMediaType(device_info.device.type)) { EXPECT_TRUE(StreamDeviceInfo::IsEqual(video_device_, device_info)); } - if (IsAudioMediaType(device_info.device.type)) { + if (IsAudioInputMediaType(device_info.device.type)) { EXPECT_TRUE(StreamDeviceInfo::IsEqual(audio_device_, device_info)); } } diff --git a/content/renderer/media/media_stream_impl.cc b/content/renderer/media/media_stream_impl.cc index d576a68..8bb9fd5 100644 --- a/content/renderer/media/media_stream_impl.cc +++ b/content/renderer/media/media_stream_impl.cc @@ -6,7 +6,9 @@ #include <utility> +#include "base/hash.h" #include "base/logging.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" @@ -57,20 +59,26 @@ static int g_next_request_id = 0; struct MediaStreamImpl::MediaDevicesRequestInfo { MediaDevicesRequestInfo(const blink::WebMediaDevicesRequest& request, int audio_input_request_id, - int video_input_request_id) + int video_input_request_id, + int audio_output_request_id) : request(request), audio_input_request_id(audio_input_request_id), video_input_request_id(video_input_request_id), + audio_output_request_id(audio_output_request_id), has_audio_input_returned(false), - has_video_input_returned(false) {} + has_video_input_returned(false), + has_audio_output_returned(false) {} blink::WebMediaDevicesRequest request; int audio_input_request_id; int video_input_request_id; + int audio_output_request_id; bool has_audio_input_returned; bool has_video_input_returned; + bool has_audio_output_returned; StreamDeviceInfoArray audio_input_devices; StreamDeviceInfoArray video_input_devices; + StreamDeviceInfoArray audio_output_devices; }; MediaStreamImpl::MediaStreamImpl( @@ -197,6 +205,7 @@ void MediaStreamImpl::requestMediaDevices( int audio_input_request_id = g_next_request_id++; int video_input_request_id = g_next_request_id++; + int audio_output_request_id = g_next_request_id++; // |media_devices_request| can't be mocked, so in tests it will be empty (the // underlying pointer is null). In order to use this function in a test we @@ -206,11 +215,14 @@ void MediaStreamImpl::requestMediaDevices( security_origin = GURL(media_devices_request.securityOrigin().toString()); DVLOG(1) << "MediaStreamImpl::requestMediaDevices(" << audio_input_request_id - << ", " << video_input_request_id << ", " - << security_origin.spec() << ")"; + << ", " << video_input_request_id << ", " << audio_output_request_id + << ", " << security_origin.spec() << ")"; media_devices_requests_.push_back(new MediaDevicesRequestInfo( - media_devices_request, audio_input_request_id, video_input_request_id)); + media_devices_request, + audio_input_request_id, + video_input_request_id, + audio_output_request_id)); media_stream_dispatcher_->EnumerateDevices( audio_input_request_id, @@ -223,6 +235,12 @@ void MediaStreamImpl::requestMediaDevices( AsWeakPtr(), MEDIA_DEVICE_VIDEO_CAPTURE, security_origin); + + media_stream_dispatcher_->EnumerateDevices( + audio_output_request_id, + AsWeakPtr(), + MEDIA_DEVICE_AUDIO_OUTPUT, + security_origin); } void MediaStreamImpl::cancelMediaDevicesRequest( @@ -240,6 +258,9 @@ void MediaStreamImpl::cancelMediaDevicesRequest( media_stream_dispatcher_->StopEnumerateDevices( request->video_input_request_id, AsWeakPtr()); + media_stream_dispatcher_->StopEnumerateDevices( + request->audio_output_request_id, + AsWeakPtr()); DeleteMediaDevicesRequestInfo(request); } @@ -500,42 +521,65 @@ void MediaStreamImpl::OnDevicesEnumerated( request->has_audio_input_returned = true; DCHECK(request->audio_input_devices.empty()); request->audio_input_devices = device_array; - } else { - DCHECK(request_id == request->video_input_request_id); + } else if (request_id == request->video_input_request_id) { request->has_video_input_returned = true; DCHECK(request->video_input_devices.empty()); request->video_input_devices = device_array; + } else { + DCHECK_EQ(request->audio_output_request_id, request_id); + request->has_audio_output_returned = true; + DCHECK(request->audio_output_devices.empty()); + request->audio_output_devices = device_array; } if (!request->has_audio_input_returned || - !request->has_video_input_returned) { + !request->has_video_input_returned || + !request->has_audio_output_returned) { // Wait for the rest of the devices to complete. return; } - // Both audio and video devices are ready for copying. - // TODO(grunell): Add support for output devices and group id. + // All devices are ready for copying. We use a hashed audio output device id + // as the group id for input and output audio devices. If an input device + // doesn't have an associated output device, we use the input device's own id. + // We don't support group id for video devices, that's left empty. blink::WebVector<blink::WebMediaDeviceInfo> devices(request->audio_input_devices.size() + - request->video_input_devices.size()); + request->video_input_devices.size() + + request->audio_output_devices.size()); for (size_t i = 0; i < request->audio_input_devices.size(); ++i) { const MediaStreamDevice& device = request->audio_input_devices[i].device; DCHECK_EQ(device.type, MEDIA_DEVICE_AUDIO_CAPTURE); - devices[i].initialize(blink::WebString::fromUTF8(device.id), - blink::WebMediaDeviceInfo::MediaDeviceKindAudioInput, - blink::WebString::fromUTF8(device.name), - blink::WebString()); + std::string group_id = base::UintToString(base::Hash( + !device.matched_output_device_id.empty() ? + device.matched_output_device_id : + device.id)); + devices[i].initialize( + blink::WebString::fromUTF8(device.id), + blink::WebMediaDeviceInfo::MediaDeviceKindAudioInput, + blink::WebString::fromUTF8(device.name), + blink::WebString::fromUTF8(group_id)); } - size_t audio_size = request->audio_input_devices.size(); + size_t offset = request->audio_input_devices.size(); for (size_t i = 0; i < request->video_input_devices.size(); ++i) { const MediaStreamDevice& device = request->video_input_devices[i].device; DCHECK_EQ(device.type, MEDIA_DEVICE_VIDEO_CAPTURE); - devices[audio_size + i].initialize( + devices[offset + i].initialize( blink::WebString::fromUTF8(device.id), blink::WebMediaDeviceInfo::MediaDeviceKindVideoInput, blink::WebString::fromUTF8(device.name), blink::WebString()); } + offset += request->video_input_devices.size(); + for (size_t i = 0; i < request->audio_output_devices.size(); ++i) { + const MediaStreamDevice& device = request->audio_output_devices[i].device; + DCHECK_EQ(device.type, MEDIA_DEVICE_AUDIO_OUTPUT); + devices[offset + i].initialize( + blink::WebString::fromUTF8(device.id), + blink::WebMediaDeviceInfo::MediaDeviceKindAudioOutput, + blink::WebString::fromUTF8(device.name), + blink::WebString::fromUTF8(base::UintToString(base::Hash(device.id)))); + } EnumerateDevicesSucceded(&request->request, devices); @@ -546,6 +590,9 @@ void MediaStreamImpl::OnDevicesEnumerated( media_stream_dispatcher_->StopEnumerateDevices( request->video_input_request_id, AsWeakPtr()); + media_stream_dispatcher_->StopEnumerateDevices( + request->audio_output_request_id, + AsWeakPtr()); DeleteMediaDevicesRequestInfo(request); } @@ -673,7 +720,8 @@ MediaStreamImpl::FindMediaDevicesRequestInfo( MediaDevicesRequests::iterator it = media_devices_requests_.begin(); for (; it != media_devices_requests_.end(); ++it) { if ((*it)->audio_input_request_id == request_id || - (*it)->video_input_request_id == request_id) { + (*it)->video_input_request_id == request_id || + (*it)->audio_output_request_id == request_id) { return (*it); } } diff --git a/content/renderer/media/media_stream_impl_unittest.cc b/content/renderer/media/media_stream_impl_unittest.cc index 7db70db..cddf119 100644 --- a/content/renderer/media/media_stream_impl_unittest.cc +++ b/content/renderer/media/media_stream_impl_unittest.cc @@ -163,15 +163,17 @@ class MediaStreamImplTest : public ::testing::Test { void FakeMediaStreamDispatcherRequestUserMediaComplete() { // Audio request ID is used as the shared request ID. - ms_impl_->OnStreamGenerated(ms_dispatcher_->audio_request_id(), + ms_impl_->OnStreamGenerated(ms_dispatcher_->audio_input_request_id(), ms_dispatcher_->stream_label(), - ms_dispatcher_->audio_array(), + ms_dispatcher_->audio_input_array(), ms_dispatcher_->video_array()); } void FakeMediaStreamDispatcherRequestMediaDevicesComplete() { - ms_impl_->OnDevicesEnumerated(ms_dispatcher_->audio_request_id(), - ms_dispatcher_->audio_array()); + ms_impl_->OnDevicesEnumerated(ms_dispatcher_->audio_input_request_id(), + ms_dispatcher_->audio_input_array()); + ms_impl_->OnDevicesEnumerated(ms_dispatcher_->audio_output_request_id(), + ms_dispatcher_->audio_output_array()); ms_impl_->OnDevicesEnumerated(ms_dispatcher_->video_request_id(), ms_dispatcher_->video_array()); } @@ -435,15 +437,39 @@ TEST_F(MediaStreamImplTest, EnumerateMediaDevices) { EXPECT_EQ(MediaStreamImplUnderTest::REQUEST_SUCCEEDED, ms_impl_->request_state()); + // Audio input device with matched output ID. EXPECT_FALSE(ms_impl_->last_devices()[0].deviceId().isEmpty()); EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindAudioInput, ms_impl_->last_devices()[0].kind()); EXPECT_FALSE(ms_impl_->last_devices()[0].label().isEmpty()); + EXPECT_FALSE(ms_impl_->last_devices()[0].groupId().isEmpty()); + // Audio input device without matched output ID. EXPECT_FALSE(ms_impl_->last_devices()[1].deviceId().isEmpty()); - EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindVideoInput, + EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindAudioInput, ms_impl_->last_devices()[1].kind()); EXPECT_FALSE(ms_impl_->last_devices()[1].label().isEmpty()); + EXPECT_FALSE(ms_impl_->last_devices()[1].groupId().isEmpty()); + + // Video input device. + EXPECT_FALSE(ms_impl_->last_devices()[2].deviceId().isEmpty()); + EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindVideoInput, + ms_impl_->last_devices()[2].kind()); + EXPECT_FALSE(ms_impl_->last_devices()[2].label().isEmpty()); + EXPECT_TRUE(ms_impl_->last_devices()[2].groupId().isEmpty()); + + // Audio output device. + EXPECT_FALSE(ms_impl_->last_devices()[3].deviceId().isEmpty()); + EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindAudioOutput, + ms_impl_->last_devices()[3].kind()); + EXPECT_FALSE(ms_impl_->last_devices()[3].label().isEmpty()); + EXPECT_FALSE(ms_impl_->last_devices()[3].groupId().isEmpty()); + + // Verfify group IDs. + EXPECT_TRUE(ms_impl_->last_devices()[0].groupId().equals( + ms_impl_->last_devices()[3].groupId())); + EXPECT_FALSE(ms_impl_->last_devices()[1].groupId().equals( + ms_impl_->last_devices()[3].groupId())); } } // namespace content diff --git a/content/renderer/media/mock_media_stream_dispatcher.cc b/content/renderer/media/mock_media_stream_dispatcher.cc index 64f2386..7854a8d 100644 --- a/content/renderer/media/mock_media_stream_dispatcher.cc +++ b/content/renderer/media/mock_media_stream_dispatcher.cc @@ -8,11 +8,16 @@ #include "content/public/common/media_stream_request.h" #include "testing/gtest/include/gtest/gtest.h" +// Used for ID for output devices and for matching output device ID for input +// devices. +const char kAudioOutputDeviceIdPrefix[] = "audio_output_device_id"; + namespace content { MockMediaStreamDispatcher::MockMediaStreamDispatcher() : MediaStreamDispatcher(NULL), - audio_request_id_(-1), + audio_input_request_id_(-1), + audio_output_request_id_(-1), video_request_id_(-1), request_stream_counter_(0), stop_audio_device_counter_(0), @@ -27,15 +32,16 @@ void MockMediaStreamDispatcher::GenerateStream( const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, const StreamOptions& components, const GURL& url) { - // Audio and video share the same request so we use |audio_request_id_| only. - audio_request_id_ = request_id; + // Audio and video share the same request so we use |audio_input_request_id_| + // only. + audio_input_request_id_ = request_id; stream_label_ = "local_stream" + base::IntToString(request_id); - audio_array_.clear(); + audio_input_array_.clear(); video_array_.clear(); if (components.audio_requested) { - AddAudioDeviceToArray(); + AddAudioInputDeviceToArray(false); } if (components.video_requested) { AddVideoDeviceToArray(); @@ -46,7 +52,7 @@ void MockMediaStreamDispatcher::GenerateStream( void MockMediaStreamDispatcher::CancelGenerateStream( int request_id, const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) { - EXPECT_EQ(request_id, audio_request_id_); + EXPECT_EQ(request_id, audio_input_request_id_); } void MockMediaStreamDispatcher::EnumerateDevices( @@ -55,9 +61,14 @@ void MockMediaStreamDispatcher::EnumerateDevices( MediaStreamType type, const GURL& security_origin) { if (type == MEDIA_DEVICE_AUDIO_CAPTURE) { - audio_request_id_ = request_id; - audio_array_.clear(); - AddAudioDeviceToArray(); + audio_input_request_id_ = request_id; + audio_input_array_.clear(); + AddAudioInputDeviceToArray(true); + AddAudioInputDeviceToArray(false); + } else if (type == MEDIA_DEVICE_AUDIO_OUTPUT) { + audio_output_request_id_ = request_id; + audio_output_array_.clear(); + AddAudioOutputDeviceToArray(); } else if (type == MEDIA_DEVICE_VIDEO_CAPTURE) { video_request_id_ = request_id; video_array_.clear(); @@ -67,7 +78,7 @@ void MockMediaStreamDispatcher::EnumerateDevices( void MockMediaStreamDispatcher::StopStreamDevice( const StreamDeviceInfo& device_info) { - if (IsAudioMediaType(device_info.device.type)) { + if (IsAudioInputMediaType(device_info.device.type)) { ++stop_audio_device_counter_; return; } @@ -92,13 +103,27 @@ int MockMediaStreamDispatcher::audio_session_id(const std::string& label, return -1; } -void MockMediaStreamDispatcher::AddAudioDeviceToArray() { +void MockMediaStreamDispatcher::AddAudioInputDeviceToArray( + bool matched_output) { StreamDeviceInfo audio; - audio.device.id = "audio_device_id" + base::IntToString(session_id_); + audio.device.id = "audio_input_device_id" + base::IntToString(session_id_); audio.device.name = "microphone"; audio.device.type = MEDIA_DEVICE_AUDIO_CAPTURE; + if (matched_output) { + audio.device.matched_output_device_id = + kAudioOutputDeviceIdPrefix + base::IntToString(session_id_); + } + audio.session_id = session_id_; + audio_input_array_.push_back(audio); +} + +void MockMediaStreamDispatcher::AddAudioOutputDeviceToArray() { + StreamDeviceInfo audio; + audio.device.id = kAudioOutputDeviceIdPrefix + base::IntToString(session_id_); + audio.device.name = "speaker"; + audio.device.type = MEDIA_DEVICE_AUDIO_OUTPUT; audio.session_id = session_id_; - audio_array_.push_back(audio); + audio_output_array_.push_back(audio); } void MockMediaStreamDispatcher::AddVideoDeviceToArray() { diff --git a/content/renderer/media/mock_media_stream_dispatcher.h b/content/renderer/media/mock_media_stream_dispatcher.h index c624263..1548853 100644 --- a/content/renderer/media/mock_media_stream_dispatcher.h +++ b/content/renderer/media/mock_media_stream_dispatcher.h @@ -37,7 +37,8 @@ class MockMediaStreamDispatcher : public MediaStreamDispatcher { virtual int video_session_id(const std::string& label, int index) OVERRIDE; virtual int audio_session_id(const std::string& label, int index) OVERRIDE; - int audio_request_id() const { return audio_request_id_; } + int audio_input_request_id() const { return audio_input_request_id_; } + int audio_output_request_id() const { return audio_output_request_id_; } int video_request_id() const { return video_request_id_; } int request_stream_counter() const { return request_stream_counter_; } void IncrementSessionId() { ++session_id_; } @@ -46,14 +47,21 @@ class MockMediaStreamDispatcher : public MediaStreamDispatcher { int stop_video_device_counter() const { return stop_video_device_counter_; } const std::string& stream_label() const { return stream_label_;} - StreamDeviceInfoArray audio_array() const { return audio_array_; } - StreamDeviceInfoArray video_array() const { return video_array_; } + const StreamDeviceInfoArray& audio_input_array() const { + return audio_input_array_; + } + const StreamDeviceInfoArray& audio_output_array() const { + return audio_output_array_; + } + const StreamDeviceInfoArray& video_array() const { return video_array_; } private: - void AddAudioDeviceToArray(); + void AddAudioInputDeviceToArray(bool matched_output); + void AddAudioOutputDeviceToArray(); void AddVideoDeviceToArray(); - int audio_request_id_; + int audio_input_request_id_; + int audio_output_request_id_; // Only used for EnumerateDevices. int video_request_id_; // Only used for EnumerateDevices. base::WeakPtr<MediaStreamDispatcherEventHandler> event_handler_; int request_stream_counter_; @@ -62,7 +70,8 @@ class MockMediaStreamDispatcher : public MediaStreamDispatcher { std::string stream_label_; int session_id_; - StreamDeviceInfoArray audio_array_; + StreamDeviceInfoArray audio_input_array_; + StreamDeviceInfoArray audio_output_array_; StreamDeviceInfoArray video_array_; DISALLOW_COPY_AND_ASSIGN(MockMediaStreamDispatcher); |