summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgrunell@chromium.org <grunell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-04 14:38:31 +0000
committergrunell@chromium.org <grunell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-04 14:38:31 +0000
commitbfd666154471a2d18da5362b852d0931c99e21e0 (patch)
tree120298e67b3a039762ea9d4f829bc24dfe87b22d
parentee37d41fdeba02e578149ae1269db9e89f7c9a51 (diff)
downloadchromium_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
-rw-r--r--chrome/browser/media/media_stream_capture_indicator.cc4
-rw-r--r--content/browser/renderer_host/media/media_stream_dispatcher_host.cc10
-rw-r--r--content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc2
-rw-r--r--content/browser/renderer_host/media/media_stream_manager.cc92
-rw-r--r--content/browser/renderer_host/media/media_stream_manager.h8
-rw-r--r--content/browser/renderer_host/media/media_stream_ui_proxy.cc4
-rw-r--r--content/public/common/media_stream_request.cc2
-rw-r--r--content/public/common/media_stream_request.h9
-rw-r--r--content/renderer/media/media_stream_dispatcher.cc7
-rw-r--r--content/renderer/media/media_stream_dispatcher_unittest.cc2
-rw-r--r--content/renderer/media/media_stream_impl.cc84
-rw-r--r--content/renderer/media/media_stream_impl_unittest.cc36
-rw-r--r--content/renderer/media/mock_media_stream_dispatcher.cc51
-rw-r--r--content/renderer/media/mock_media_stream_dispatcher.h21
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);