0

[gUM] Use ID vector instead of single ID for TrackControls

This is part 1 of a change to gUM device selection until after
permission is granted, which will allow the user preference
to influence which stream is returned to the site in the
presence of multiple eligible devices.

This CL changes TrackControls and MediaStreamRequest to hold
vectors of device ids instead of a single camera/mic id. This
will be a NOOP for now as the vectors will still only contain
a max of 1 device.

Bug: b:310972736
Change-Id: Iff3e4b8e0f952bc01217b5451b254c514ebe0f70
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5132210
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Reviewed-by: Theresa Sullivan <twellington@chromium.org>
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Reviewed-by: Peter Williamson <petewil@chromium.org>
Reviewed-by: Nate Fischer <ntfschr@chromium.org>
Reviewed-by: Chris Bookholt <bookholt@chromium.org>
Reviewed-by: Zijie He <zijiehe@google.com>
Commit-Queue: Bryant Chandler <bryantchandler@chromium.org>
Reviewed-by: Thomas Guilbert <tguilbert@chromium.org>
Reviewed-by: Sean Topping <seantopping@chromium.org>
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1254777}
This commit is contained in:
Bryant Chandler
2024-01-31 22:15:14 +00:00
committed by Chromium LUCI CQ
parent 86fecabf43
commit 5698e7b2fb
36 changed files with 289 additions and 215 deletions

@ -26,18 +26,19 @@ namespace {
// available device is returned.
const MediaStreamDevice* GetDeviceByIdOrFirstAvailable(
const MediaStreamDevices& devices,
const std::string& device_id) {
const std::vector<std::string>& device_ids) {
if (devices.empty())
return NULL;
return nullptr;
if (!device_id.empty()) {
for (size_t i = 0; i < devices.size(); ++i) {
if (devices[i].id == device_id)
return &devices[i];
if (!device_ids.empty()) {
for (const auto& device : devices) {
if (device.id == device_ids.front()) {
return &device;
}
}
}
return &devices[0];
return &devices.front();
}
} // namespace
@ -81,7 +82,7 @@ void MediaAccessPermissionRequest::NotifyRequestResult(bool allowed) {
? MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices()
: audio_test_devices_;
const MediaStreamDevice* device = GetDeviceByIdOrFirstAvailable(
audio_devices, request_.requested_audio_device_id);
audio_devices, request_.requested_audio_device_ids);
if (device)
devices.audio_device = *device;
if (base::FeatureList::IsEnabled(features::kWebViewEnumerateDevicesCache) &&
@ -99,7 +100,7 @@ void MediaAccessPermissionRequest::NotifyRequestResult(bool allowed) {
? MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices()
: video_test_devices_;
const MediaStreamDevice* device = GetDeviceByIdOrFirstAvailable(
video_devices, request_.requested_video_device_id);
video_devices, request_.requested_video_device_ids);
if (device)
devices.video_device = *device;
if (base::FeatureList::IsEnabled(features::kWebViewEnumerateDevicesCache) &&

@ -66,7 +66,7 @@ class MediaAccessPermissionRequestTest : public testing::Test {
GURL origin("https://www.google.com");
content::MediaStreamRequest request(
0, 0, 0, url::Origin::Create(origin), false,
blink::MEDIA_GENERATE_STREAM, audio_id, video_id,
blink::MEDIA_GENERATE_STREAM, {audio_id}, {video_id},
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
false /* disable_local_echo */,

@ -120,7 +120,7 @@ content::MediaStreamRequest CreateMediaStreamRequest(
web_contents->GetPrimaryMainFrame()->GetRoutingID(),
/*page_request_id=*/0, url::Origin::Create(GURL(kExampleUrl)),
/*user_gesture=*/false, blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_id=*/std::string(), requested_video_device_id,
/*requested_audio_device_id=*/{}, {requested_video_device_id},
blink::mojom::MediaStreamType::NO_SERVICE, video_type,
/*disable_local_echo=*/false,
/*request_pan_tilt_zoom_permission=*/false);

@ -53,19 +53,21 @@ void MediaAccessHandler::CheckDevicesAndRunCallback(
// Get the exact audio or video device if an id is specified.
// We only set any error result here and before running the callback change
// it to OK if we have any device.
if (audio_allowed && !request.requested_audio_device_id.empty()) {
if (audio_allowed && !request.requested_audio_device_ids.empty() &&
!request.requested_audio_device_ids.front().empty()) {
const blink::MediaStreamDevice* audio_device =
MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedAudioDevice(
request.requested_audio_device_id);
request.requested_audio_device_ids.front());
if (audio_device) {
stream_devices.audio_device = *audio_device;
get_default_audio_device = false;
}
}
if (video_allowed && !request.requested_video_device_id.empty()) {
if (video_allowed && !request.requested_video_device_ids.empty() &&
!request.requested_video_device_ids.front().empty()) {
const blink::MediaStreamDevice* video_device =
MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedVideoDevice(
request.requested_video_device_id);
request.requested_video_device_ids.front());
if (video_device) {
stream_devices.video_device = *video_device;
get_default_video_device = false;

@ -371,7 +371,8 @@ void DesktopCaptureAccessHandler::HandleRequest(
// If the device id wasn't specified then this is a screen capture request
// (i.e. chooseDesktopMedia() API wasn't used to generate device id).
if (request.requested_video_device_id.empty()) {
if (request.requested_video_device_ids.empty() ||
request.requested_video_device_ids.front().empty()) {
if (allowed_capture_level < AllowedScreenCaptureLevel::kDesktop) {
std::move(pending_request->callback)
.Run(blink::mojom::StreamDevicesSet(),
@ -407,9 +408,11 @@ void DesktopCaptureAccessHandler::HandleRequest(
web_contents_for_stream ? web_contents_for_stream->GetPrimaryMainFrame()
: nullptr;
if (main_frame) {
// This function would have already returned if this vector was empty.
CHECK(!request.requested_video_device_ids.empty());
media_id =
content::DesktopStreamsRegistry::GetInstance()->RequestMediaForStreamId(
request.requested_video_device_id,
request.requested_video_device_ids.front(),
main_frame->GetProcess()->GetID(), main_frame->GetRoutingID(),
url::Origin::Create(request.security_origin),
content::kRegistryStreamTypeDesktop);
@ -480,7 +483,8 @@ void DesktopCaptureAccessHandler::ProcessChangeSourceRequest(
DCHECK_EQ(pending_request->request.video_type,
blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE);
if (pending_request->request.requested_video_device_id.empty()) {
if (pending_request->request.requested_video_device_ids.empty() ||
pending_request->request.requested_video_device_ids.front().empty()) {
// Passing nullptr selects the default picker (DesktopMediaPickerViews).
pending_request->picker = picker_factory_->CreatePicker(nullptr);
if (!pending_request->picker) {
@ -535,10 +539,10 @@ void DesktopCaptureAccessHandler::ProcessQueuedAccessRequest(
const PendingAccessRequest& pending_request = *queue.front();
if (!pending_request.picker) {
DCHECK(!pending_request.request.requested_video_device_id.empty());
DCHECK(!pending_request.request.requested_video_device_ids.empty());
content::WebContentsMediaCaptureId web_contents_id;
if (content::WebContentsMediaCaptureId::Parse(
pending_request.request.requested_video_device_id,
pending_request.request.requested_video_device_ids.front(),
&web_contents_id)) {
content::DesktopMediaID media_id(
content::DesktopMediaID::TYPE_WEB_CONTENTS,

@ -58,7 +58,7 @@ class DesktopCaptureAccessHandlerTest : public ChromeRenderViewHostTestHarness {
}
void ProcessGenerateStreamRequest(
const std::string& requested_video_device_id,
const std::vector<std::string>& requested_video_device_ids,
const GURL& origin,
const extensions::Extension* extension,
blink::mojom::MediaStreamRequestResult* request_result,
@ -74,7 +74,7 @@ class DesktopCaptureAccessHandlerTest : public ChromeRenderViewHostTestHarness {
web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
/*page_request_id=*/0, url::Origin::Create(origin),
/*user_gesture=*/false, blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_id=*/std::string(), requested_video_device_id,
/*requested_audio_device_ids=*/{}, requested_video_device_ids,
blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
/*disable_local_echo=*/false,
@ -126,7 +126,8 @@ class DesktopCaptureAccessHandlerTest : public ChromeRenderViewHostTestHarness {
: blink::mojom::MediaStreamType::NO_SERVICE;
content::MediaStreamRequest request(
0, 0, 0, url::Origin::Create(GURL(kOrigin)), false, request_type,
std::string(), std::string(), audio_type,
/*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{}, audio_type,
blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
/*disable_local_echo=*/false,
/*request_pan_tilt_zoom_permission=*/false);
@ -235,8 +236,8 @@ TEST_F(DesktopCaptureAccessHandlerTest,
content::MediaStreamRequest request(
render_process_id, render_frame_id, page_request_id,
url::Origin::Create(GURL(kOrigin)), false, blink::MEDIA_DEVICE_UPDATE,
std::string(), std::string(), blink::mojom::MediaStreamType::NO_SERVICE,
stream_type,
/*requested_audio_device_ids=*/{}, /*requested_video_device_ids=*/{},
blink::mojom::MediaStreamType::NO_SERVICE, stream_type,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
content::MediaResponseCallback callback;
access_handler_->HandleRequest(web_contents(), request, std::move(callback),
@ -267,7 +268,8 @@ TEST_F(DesktopCaptureAccessHandlerTest, ChangeSourceWebContentsDestroyed) {
picker_factory_->SetTestFlags(test_flags, std::size(test_flags));
content::MediaStreamRequest request(
0, 0, 0, url::Origin::Create(GURL(kOrigin)), false,
blink::MEDIA_DEVICE_UPDATE, std::string(), std::string(),
blink::MEDIA_DEVICE_UPDATE, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{},
blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
@ -308,7 +310,8 @@ TEST_F(DesktopCaptureAccessHandlerTest, ChangeSourceMultipleRequests) {
for (base::RunLoop& loop : wait_loop) {
content::MediaStreamRequest request(
0, 0, 0, url::Origin::Create(GURL(kOrigin)), false,
blink::MEDIA_DEVICE_UPDATE, std::string(), std::string(),
blink::MEDIA_DEVICE_UPDATE, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{},
blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
/*disable_local_echo=*/false,
@ -364,7 +367,7 @@ TEST_F(DesktopCaptureAccessHandlerTest, GenerateStreamSuccess) {
content::DesktopMediaID::kFakeId),
content::DesktopStreamRegistryType::kRegistryStreamTypeDesktop);
ProcessGenerateStreamRequest(id, origin, /*extension=*/nullptr, &result,
ProcessGenerateStreamRequest({id}, origin, /*extension=*/nullptr, &result,
&devices);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
@ -390,9 +393,9 @@ TEST_F(DesktopCaptureAccessHandlerTest, ScreenCaptureAccessSuccess) {
blink::mojom::MediaStreamRequestResult result;
blink::mojom::StreamDevices devices;
ProcessGenerateStreamRequest(/*requested_video_device_id=*/std::string(),
GURL(kOrigin), extensionBuilder.Build().get(),
&result, &devices);
ProcessGenerateStreamRequest(/*requested_video_device_ids=*/{}, GURL(kOrigin),
extensionBuilder.Build().get(), &result,
&devices);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_TRUE(devices.video_device.has_value());
@ -425,9 +428,9 @@ TEST_F(DesktopCaptureAccessHandlerTest, ScreenCaptureAccessDlpRestricted) {
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
blink::mojom::StreamDevices devices;
ProcessGenerateStreamRequest(/*requested_video_device_id=*/std::string(),
GURL(kOrigin), extensionBuilder.Build().get(),
&result, &devices);
ProcessGenerateStreamRequest(/*requested_video_device_ids=*/{}, GURL(kOrigin),
extensionBuilder.Build().get(), &result,
&devices);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, result);
EXPECT_FALSE(devices.video_device.has_value());
@ -459,9 +462,9 @@ TEST_F(DesktopCaptureAccessHandlerTest, ScreenCaptureAccessDlpNotRestricted) {
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
blink::mojom::StreamDevices devices;
ProcessGenerateStreamRequest(/*requested_video_device_id=*/std::string(),
GURL(kOrigin), extensionBuilder.Build().get(),
&result, &devices);
ProcessGenerateStreamRequest(/*requested_video_device_id=*/{}, GURL(kOrigin),
extensionBuilder.Build().get(), &result,
&devices);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);
EXPECT_TRUE(devices.video_device.has_value());
@ -496,9 +499,9 @@ TEST_F(DesktopCaptureAccessHandlerTest,
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
blink::mojom::StreamDevices devices;
ProcessGenerateStreamRequest(/*requested_video_device_id=*/std::string(),
GURL(kOrigin), extensionBuilder.Build().get(),
&result, &devices, /*expect_result=*/false);
ProcessGenerateStreamRequest(/*requested_video_device_id=*/{}, GURL(kOrigin),
extensionBuilder.Build().get(), &result,
&devices, /*expect_result=*/false);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED, result);
EXPECT_FALSE(devices.video_device.has_value());
@ -529,7 +532,7 @@ TEST_F(DesktopCaptureAccessHandlerTest, GenerateStreamDlpRestricted) {
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
blink::mojom::StreamDevices devices;
ProcessGenerateStreamRequest(id, GURL(kOrigin), /*extension=*/nullptr,
ProcessGenerateStreamRequest({id}, GURL(kOrigin), /*extension=*/nullptr,
&result, &devices);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED, result);
@ -561,7 +564,7 @@ TEST_F(DesktopCaptureAccessHandlerTest, GenerateStreamDlpNotRestricted) {
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED;
blink::mojom::StreamDevices devices;
ProcessGenerateStreamRequest(id, GURL(kOrigin), /*extension=*/nullptr,
ProcessGenerateStreamRequest({id}, GURL(kOrigin), /*extension=*/nullptr,
&result, &devices);
EXPECT_EQ(blink::mojom::MediaStreamRequestResult::OK, result);

@ -155,7 +155,8 @@ void DisplayMediaAccessHandler::HandleRequest(
#endif // BUILDFLAG(IS_MAC)
if (request.request_type == blink::MEDIA_DEVICE_UPDATE) {
DCHECK(!request.requested_video_device_id.empty());
CHECK(!request.requested_video_device_ids.empty());
CHECK(!request.requested_video_device_ids.front().empty());
ProcessChangeSourceRequest(web_contents, request, std::move(callback));
return;
}
@ -368,11 +369,11 @@ void DisplayMediaAccessHandler::ProcessQueuedChangeSourceRequest(
const content::MediaStreamRequest& request,
content::WebContents* web_contents) {
DCHECK(web_contents);
DCHECK(!request.requested_video_device_id.empty());
DCHECK(!request.requested_video_device_ids.empty());
content::WebContentsMediaCaptureId web_contents_id;
if (!content::WebContentsMediaCaptureId::Parse(
request.requested_video_device_id, &web_contents_id)) {
request.requested_video_device_ids.front(), &web_contents_id)) {
RejectRequest(web_contents,
blink::mojom::MediaStreamRequestResult::INVALID_STATE);
return;

@ -75,7 +75,8 @@ class DisplayMediaAccessHandlerTest : public ChromeRenderViewHostTestHarness {
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
web_contents()->GetPrimaryMainFrame()->GetRoutingID(), 0,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{},
request_audio ? blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE
: blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
@ -87,8 +88,8 @@ class DisplayMediaAccessHandlerTest : public ChromeRenderViewHostTestHarness {
content::MediaStreamRequest request =
MakeRequest(request_audio /* request_audio */);
request.request_type = blink::MEDIA_DEVICE_UPDATE;
request.requested_video_device_id =
GetWebContentsMediaCaptureId().ToString();
request.requested_video_device_ids = {
GetWebContentsMediaCaptureId().ToString()};
return request;
}
@ -406,8 +407,8 @@ TEST_F(DisplayMediaAccessHandlerTest, UpdateMediaRequestStateWithClosing) {
content::MediaStreamRequest request(
render_process_id, render_frame_id, page_request_id,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
audio_stream_type, video_stream_type,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{}, audio_stream_type, video_stream_type,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
content::MediaResponseCallback callback;
access_handler_->HandleRequest(web_contents(), request, std::move(callback),
@ -445,8 +446,8 @@ TEST_F(DisplayMediaAccessHandlerTest, CorrectHostAsksForPermissions) {
content::MediaStreamRequest request(
render_process_id, render_frame_id, page_request_id,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
audio_stream_type, video_stream_type,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{}, audio_stream_type, video_stream_type,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
content::MediaResponseCallback callback;
content::WebContents* test_web_contents = web_contents();
@ -481,8 +482,8 @@ TEST_F(DisplayMediaAccessHandlerTest, CorrectHostAsksForPermissionsNormalURLs) {
content::MediaStreamRequest request(
render_process_id, render_frame_id, page_request_id,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
audio_stream_type, video_stream_type,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{}, audio_stream_type, video_stream_type,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
content::MediaResponseCallback callback;
content::WebContents* test_web_contents = web_contents();
@ -526,8 +527,8 @@ TEST_F(DisplayMediaAccessHandlerTest, IsolatedWebAppNameAsksForPermissions) {
content::MediaStreamRequest request(
render_process_id, render_frame_id, page_request_id,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
audio_stream_type, video_stream_type,
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{}, audio_stream_type, video_stream_type,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
content::MediaResponseCallback callback;
content::WebContents* test_web_contents = web_contents();
@ -554,7 +555,8 @@ TEST_F(DisplayMediaAccessHandlerTest, WebContentsDestroyed) {
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
web_contents()->GetPrimaryMainFrame()->GetRoutingID(), 0,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{},
blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
@ -594,7 +596,8 @@ TEST_F(DisplayMediaAccessHandlerTest, MultipleRequests) {
web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID(),
web_contents()->GetPrimaryMainFrame()->GetRoutingID(), 0,
url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM, /*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{},
blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
/*disable_local_echo=*/false,
@ -791,7 +794,7 @@ TEST_F(DisplayMediaAccessHandlerTest,
{
content::MediaStreamRequest request =
MakeMediaDeviceUpdateRequest(false /* request_audio */);
request.requested_video_device_id = "MALFORMED";
request.requested_video_device_ids = {"MALFORMED"};
HandleRequest(request, &wait_loop[1], &results[1], devices[1]);
}
HandleRequest(MakeMediaDeviceUpdateRequest(false /* request_audio */),

@ -335,8 +335,8 @@ TEST_P(MediaStreamCaptureIndicatorStreamTypeTest,
/*url_origin=*/url::Origin(),
/*user_gesture=*/false,
blink::MediaStreamRequestType::MEDIA_GENERATE_STREAM,
/*requested_audio_device_id=*/"",
/*requested_video_device_id=*/"fake_device",
/*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{"fake_device"},
blink::mojom::MediaStreamType::NO_SERVICE, video_stream_type,
/*disable_local_echo=*/false,
/*request_pan_tilt_zoom_permission=*/false),

@ -178,8 +178,9 @@ class MediaStreamDevicesControllerTest : public WebRtcTestBase {
GetWebContents()->GetPrimaryMainFrame()->GetRoutingID();
return content::MediaStreamRequest(
render_process_id, render_frame_id, 0,
url::Origin::Create(example_url()), false, request_type, audio_id,
video_id, audio_type, video_type,
url::Origin::Create(example_url()), false, request_type,
/*requested_audio_device_ids=*/{audio_id},
/*requested_video_device_ids=*/{video_id}, audio_type, video_type,
/*disable_local_echo=*/false, request_pan_tilt_zoom_permission);
}

@ -79,8 +79,6 @@ void UpdatePageSpecificContentSettings(
content_settings::PageSpecificContentSettings::MicrophoneCameraState
microphone_camera_state;
std::string requested_audio_device = request.requested_audio_device_id;
std::string requested_video_device = request.requested_video_device_id;
if (audio_setting != CONTENT_SETTING_DEFAULT) {
microphone_camera_state.Put(

@ -54,8 +54,8 @@ class TabCaptureAccessHandlerTest : public ChromeRenderViewHostTestHarness {
web_contents()->GetPrimaryMainFrame()->GetRoutingID(),
/*page_request_id=*/0, url::Origin::Create(GURL(kOrigin)),
/*user_gesture=*/false, blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_id=*/std::string(),
/*requested_video_device_id=*/std::string(),
/*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{},
blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE,
/*disable_local_echo=*/false,

@ -72,7 +72,8 @@ class MediaStreamDevicesControllerBrowserTest
return content::MediaStreamRequest(
render_process_id, render_frame_id, 0,
url::Origin::Create(request_url_), false, blink::MEDIA_DEVICE_ACCESS,
std::string(), std::string(), audio_request_type, video_request_type,
/*requested_audio_device_ids=*/{}, /*requested_video_device_ids=*/{},
audio_request_type, video_request_type,
/*disable_local_echo=*/false,
/*request_pan_tilt_zoom_permission=*/false);
}

@ -217,9 +217,9 @@ bool CastWebViewDefault::DidAddMessageToConsole(
const blink::MediaStreamDevice* GetRequestedDeviceOrDefault(
const blink::MediaStreamDevices& devices,
const std::string& requested_device_id) {
if (!requested_device_id.empty()) {
auto it = base::ranges::find(devices, requested_device_id,
const std::vector<std::string>& requested_device_ids) {
if (!requested_device_ids.empty() && !requested_device_ids.front().empty()) {
auto it = base::ranges::find(devices, requested_device_ids.front(),
&blink::MediaStreamDevice::id);
return it != devices.end() ? &(*it) : nullptr;
}
@ -258,7 +258,7 @@ void CastWebViewDefault::RequestMediaAccessPermission(
if (request.audio_type ==
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
const blink::MediaStreamDevice* device = GetRequestedDeviceOrDefault(
audio_devices, request.requested_audio_device_id);
audio_devices, request.requested_audio_device_ids);
if (device) {
DVLOG(1) << __func__ << "Using audio device: id=" << device->id
<< " name=" << device->name;
@ -269,7 +269,7 @@ void CastWebViewDefault::RequestMediaAccessPermission(
if (request.video_type ==
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
const blink::MediaStreamDevice* device = GetRequestedDeviceOrDefault(
video_devices, request.requested_video_device_id);
video_devices, request.requested_video_device_ids);
if (device) {
DVLOG(1) << __func__ << "Using video device: id=" << device->id
<< " name=" << device->name;

@ -166,8 +166,7 @@ TEST_F(BackgroundLoaderContentsTest, DoesNotGiveMediaAccessPermission) {
0 /* page_request_id */, url::Origin::Create(GURL()) /* url_origin */,
false /* user_gesture */,
blink::MediaStreamRequestType::MEDIA_DEVICE_ACCESS /* request_type */,
std::string() /* requested_audio_device_id */,
std::string() /* requested_video_device_id */,
{} /* requested_audio_device_ids */, {} /* requested_video_device_ids */,
blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE /* audio_type */,
blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE /* video_type */,
false /* disable_local_echo */,

@ -116,7 +116,7 @@ void MediaStreamDevicesController::RequestPermissions(
}
permission_types.push_back(blink::PermissionType::AUDIO_CAPTURE);
requested_audio_capture_device_ids = {request.requested_audio_device_id};
requested_audio_capture_device_ids = request.requested_audio_device_ids;
}
if (controller->ShouldRequestVideo()) {
content::PermissionResult permission_status =
@ -135,11 +135,11 @@ void MediaStreamDevicesController::RequestPermissions(
}
permission_types.push_back(blink::PermissionType::VIDEO_CAPTURE);
requested_video_capture_device_ids = {request.requested_video_device_id};
requested_video_capture_device_ids = request.requested_video_device_ids;
bool has_pan_tilt_zoom_camera = controller->HasAvailableDevices(
blink::PermissionType::CAMERA_PAN_TILT_ZOOM,
request.requested_video_device_id);
request.requested_video_device_ids);
// Request CAMERA_PAN_TILT_ZOOM only if the website requested the
// pan-tilt-zoom permission and there are suitable PTZ capable devices
@ -245,9 +245,9 @@ blink::mojom::StreamDevicesSetPtr MediaStreamDevicesController::GetDevices(
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
DCHECK_EQ(blink::mojom::MediaStreamType::NO_SERVICE,
request_.video_type);
if (!request_.requested_audio_device_id.empty()) {
if (!request_.requested_audio_device_ids.empty()) {
devices.audio_device = *enumerator_->GetRequestedAudioDevice(
request_.requested_audio_device_id);
request_.requested_audio_device_ids.front());
} else {
const blink::MediaStreamDevices& audio_devices =
enumerator_->GetAudioCaptureDevices();
@ -260,9 +260,9 @@ blink::mojom::StreamDevicesSetPtr MediaStreamDevicesController::GetDevices(
DCHECK_EQ(blink::mojom::MediaStreamType::NO_SERVICE,
request_.audio_type);
// Pepper API opens only one device at a time.
if (!request_.requested_video_device_id.empty()) {
if (!request_.requested_video_device_ids.empty()) {
devices.video_device = *enumerator_->GetRequestedVideoDevice(
request_.requested_video_device_id);
request_.requested_video_device_ids.front());
} else {
const blink::MediaStreamDevices& video_devices =
enumerator_->GetVideoCaptureDevices();
@ -277,19 +277,19 @@ blink::mojom::StreamDevicesSetPtr MediaStreamDevicesController::GetDevices(
bool get_default_video_device = video_allowed;
// Get the exact audio or video device if an id is specified.
if (audio_allowed && !request_.requested_audio_device_id.empty()) {
if (audio_allowed && !request_.requested_audio_device_ids.empty()) {
const blink::MediaStreamDevice* audio_device =
enumerator_->GetRequestedAudioDevice(
request_.requested_audio_device_id);
request_.requested_audio_device_ids.front());
if (audio_device) {
devices.audio_device = *audio_device;
get_default_audio_device = false;
}
}
if (video_allowed && !request_.requested_video_device_id.empty()) {
if (video_allowed && !request_.requested_video_device_ids.empty()) {
const blink::MediaStreamDevice* video_device =
enumerator_->GetRequestedVideoDevice(
request_.requested_video_device_id);
request_.requested_video_device_ids.front());
if (video_device) {
devices.video_device = *video_device;
get_default_video_device = false;
@ -372,12 +372,12 @@ ContentSetting MediaStreamDevicesController::GetContentSetting(
return CONTENT_SETTING_DEFAULT;
}
std::string device_id;
std::vector<std::string> device_ids;
if (permission == blink::PermissionType::AUDIO_CAPTURE)
device_id = request.requested_audio_device_id;
device_ids = request.requested_audio_device_ids;
else
device_id = request.requested_video_device_id;
if (!HasAvailableDevices(permission, device_id)) {
device_ids = request.requested_video_device_ids;
if (!HasAvailableDevices(permission, device_ids)) {
*denial_reason = blink::mojom::MediaStreamRequestResult::NO_HARDWARE;
return CONTENT_SETTING_BLOCK;
}
@ -511,7 +511,7 @@ void MediaStreamDevicesController::PromptAnsweredGroupedRequest(
bool MediaStreamDevicesController::HasAvailableDevices(
blink::PermissionType permission,
const std::string& device_id) const {
const std::vector<std::string>& device_ids) const {
const MediaStreamDevices* devices = nullptr;
if (permission == blink::PermissionType::AUDIO_CAPTURE) {
devices = &enumerator_->GetAudioCaptureDevices();
@ -531,14 +531,15 @@ bool MediaStreamDevicesController::HasAvailableDevices(
return false;
// If there are no particular device requirements, all devices will do.
if (device_id.empty() &&
if (device_ids.empty() &&
permission != blink::PermissionType::CAMERA_PAN_TILT_ZOOM) {
return true;
}
// Try to find a device which fulfils all device requirements.
for (const blink::MediaStreamDevice& device : *devices) {
if (!device_id.empty() && device.id != device_id) {
if (!device_ids.empty() && std::find(device_ids.begin(), device_ids.end(),
device.id) == device_ids.end()) {
continue;
}
if (permission == blink::PermissionType::CAMERA_PAN_TILT_ZOOM &&

@ -101,7 +101,7 @@ class MediaStreamDevicesController {
const std::vector<blink::mojom::PermissionStatus>& permissions_status);
bool HasAvailableDevices(blink::PermissionType permission,
const std::string& device_id) const;
const std::vector<std::string>& device_ids) const;
// The current state of the audio/video content settings which may be updated
// through the lifetime of the request.

@ -157,8 +157,8 @@ TEST_F(MediaStreamDevicesControllerTest, RequestPermissions) {
/*page_request_id=*/0, /*url_origin=*/origin_, /*user_gesture=*/false,
/*request_type=*/
blink::MediaStreamRequestType::MEDIA_GENERATE_STREAM,
/*requested_audio_device_id=*/kRequestedAudioCaptureDevice.id,
/*requested_video_device_id=*/kRequestedVideoCaptureDevice.id,
/*requested_audio_device_ids=*/{kRequestedAudioCaptureDevice.id},
/*requested_video_device_ids=*/{kRequestedVideoCaptureDevice.id},
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
/*disable_local_echo=*/false,

@ -71,7 +71,7 @@ void VerifyHMACDeviceID(MediaDeviceType device_type,
blink::StreamControls GetAudioStreamControls(std::string hmac_device_id) {
blink::StreamControls stream_controls{/*request_audio=*/true,
/*request_video=*/false};
stream_controls.audio.device_id = hmac_device_id;
stream_controls.audio.device_ids = {hmac_device_id};
return stream_controls;
}

@ -698,7 +698,7 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateStreamWithDepthVideo) {
// video stream using GenerateStreamAndWaitForResult, we use
// controls.video.source_id to specify that the stream is depth video.
// See also MediaStreamManager::GenerateStream and other tests here.
controls.video.device_id = source_id;
controls.video.device_ids = {source_id};
blink::mojom::StreamDevicesSet expectation;
expectation.stream_devices.emplace_back(blink::mojom::StreamDevices::New(
@ -976,7 +976,7 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateStreamsWithSourceId) {
GetHMACForRawMediaDeviceID(salt_and_origin_, audio_it->unique_id);
ASSERT_FALSE(source_id.empty());
blink::StreamControls controls(true, true);
controls.audio.device_id = source_id;
controls.audio.device_ids = {source_id};
SetupFakeUI(true);
GenerateStreamAndWaitForResult(kPageRequestId, controls, expectation);
@ -988,7 +988,7 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateStreamsWithSourceId) {
GetHMACForRawMediaDeviceID(salt_and_origin_, device_id);
ASSERT_FALSE(source_id.empty());
blink::StreamControls controls(true, true);
controls.video.device_id = source_id;
controls.video.device_ids = {source_id};
GenerateStreamAndWaitForResult(kPageRequestId, controls, expectation);
EXPECT_EQ(video_device(/*stream_index=*/0u).value().id, source_id);
@ -998,7 +998,7 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateStreamsWithSourceId) {
// Test that generating a stream with an invalid video source id fail.
TEST_F(MediaStreamDispatcherHostTest, GenerateStreamsWithInvalidVideoSourceId) {
blink::StreamControls controls(true, true);
controls.video.device_id = "invalid source id";
controls.video.device_ids = {"invalid source id"};
GenerateStreamAndWaitForFailure(
kPageRequestId, controls,
@ -1008,7 +1008,7 @@ TEST_F(MediaStreamDispatcherHostTest, GenerateStreamsWithInvalidVideoSourceId) {
// Test that generating a stream with an invalid audio source id fail.
TEST_F(MediaStreamDispatcherHostTest, GenerateStreamsWithInvalidAudioSourceId) {
blink::StreamControls controls(true, true);
controls.audio.device_id = "invalid source id";
controls.audio.device_ids = {"invalid source id"};
GenerateStreamAndWaitForFailure(
kPageRequestId, controls,

@ -673,20 +673,21 @@ class MediaStreamManager::DeviceRequest {
// 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) {
void CreateUIRequest(
const std::vector<std::string>& requested_audio_device_ids,
const std::vector<std::string>& requested_video_device_ids) {
DCHECK(!ui_request_);
SendLogMessage(base::StringPrintf(
"DR::CreateUIRequest([requester_id=%d] {requested_audio_device_id=%s}, "
"{requested_video_device_id=%s})",
requester_id, requested_audio_device_id.c_str(),
requested_video_device_id.c_str()));
requester_id, base::JoinString(requested_audio_device_ids, ",").c_str(),
base::JoinString(requested_video_device_ids, "").c_str()));
target_render_frame_host_id_ = requesting_render_frame_host_id;
ui_request_ = std::make_unique<MediaStreamRequest>(
requesting_render_frame_host_id.child_id,
requesting_render_frame_host_id.frame_routing_id, page_request_id,
salt_and_origin.origin(), user_gesture, request_type_,
requested_audio_device_id, requested_video_device_id, audio_type_,
requested_audio_device_ids, requested_video_device_ids, audio_type_,
video_type_, stream_controls_.disable_local_echo,
stream_controls_.request_pan_tilt_zoom_permission);
ui_request_->suppress_local_audio_playback =
@ -709,8 +710,9 @@ class MediaStreamManager::DeviceRequest {
ui_request_ = std::make_unique<MediaStreamRequest>(
target_render_frame_host_id_.child_id,
target_render_frame_host_id_.frame_routing_id, page_request_id,
salt_and_origin.origin(), user_gesture, request_type_, "", "",
audio_type_, video_type_, stream_controls_.disable_local_echo,
salt_and_origin.origin(), user_gesture, request_type_,
std::vector<std::string>{}, std::vector<std::string>{}, audio_type_,
video_type_, stream_controls_.disable_local_echo,
/*request_pan_tilt_zoom_permission=*/false);
ui_request_->exclude_system_audio = stream_controls_.exclude_system_audio;
}
@ -2134,10 +2136,10 @@ void MediaStreamManager::OpenDevice(
StreamControls controls;
if (blink::IsAudioInputMediaType(type)) {
controls.audio.stream_type = type;
controls.audio.device_id = device_id;
controls.audio.device_ids.push_back(device_id);
} else if (blink::IsVideoInputMediaType(type)) {
controls.video.stream_type = type;
controls.video.device_id = device_id;
controls.video.device_ids.push_back(device_id);
} else {
NOTREACHED();
}
@ -2217,34 +2219,44 @@ void MediaStreamManager::StopRemovedDevice(
}
}
bool MediaStreamManager::PickDeviceId(
bool MediaStreamManager::RemoveInvalidDeviceIds(
const MediaDeviceSaltAndOrigin& salt_and_origin,
const TrackControls& controls,
const blink::WebMediaDeviceInfoArray& devices,
std::string* device_id) const {
if (controls.device_id.empty()) {
std::vector<std::string>* device_ids) const {
if (controls.device_ids.empty()) {
return true;
}
if (!GetDeviceIDAndGroupIDFromHMAC(salt_and_origin, controls.device_id,
devices, device_id,
/*group_id=*/nullptr)) {
LOG(WARNING) << "Invalid device ID = " << controls.device_id;
for (const auto& controls_device_id : controls.device_ids) {
std::string device_id;
if (!GetDeviceIDAndGroupIDFromHMAC(salt_and_origin, controls_device_id,
devices, &device_id,
/*group_id=*/nullptr)) {
LOG(WARNING) << "Invalid device ID = " << controls_device_id;
continue;
}
device_ids->push_back(device_id);
}
if (device_ids->empty()) {
LOG(WARNING) << "No valid device IDs";
return false;
}
return true;
}
bool MediaStreamManager::GetRequestedDeviceCaptureId(
bool MediaStreamManager::GetEligibleCaptureDeviceids(
const DeviceRequest* request,
MediaStreamType type,
const blink::WebMediaDeviceInfoArray& devices,
std::string* device_id) const {
std::vector<std::string>* device_ids) const {
if (type == MediaStreamType::DEVICE_AUDIO_CAPTURE) {
return PickDeviceId(request->salt_and_origin,
request->stream_controls().audio, devices, device_id);
return RemoveInvalidDeviceIds(request->salt_and_origin,
request->stream_controls().audio, devices,
device_ids);
} else if (type == MediaStreamType::DEVICE_VIDEO_CAPTURE) {
return PickDeviceId(request->salt_and_origin,
request->stream_controls().video, devices, device_id);
return RemoveInvalidDeviceIds(request->salt_and_origin,
request->stream_controls().video, devices,
device_ids);
} else {
NOTREACHED();
}
@ -2701,14 +2713,15 @@ bool MediaStreamManager::SetUpDisplayCaptureRequest(DeviceRequest* request) {
// selection of a source, see
// https://w3c.github.io/mediacapture-screen-share/#constraints.
if (!request->stream_controls().video.requested() ||
!request->stream_controls().video.device_id.empty() ||
!request->stream_controls().audio.device_id.empty()) {
!request->stream_controls().video.device_ids.empty() ||
!request->stream_controls().audio.device_ids.empty()) {
LOG(ERROR) << "Invalid display media request.";
return false;
}
request->CreateUIRequest(std::string() /* requested_audio_device_id */,
std::string() /* requested_video_device_id */);
request->CreateUIRequest(
std::vector<std::string>{} /* requested_audio_device_id */,
std::vector<std::string>{} /* requested_video_device_id */);
DVLOG(3) << "Audio requested " << request->stream_controls().audio.requested()
<< " Video requested "
<< request->stream_controls().video.requested();
@ -2724,28 +2737,28 @@ bool MediaStreamManager::SetUpDeviceCaptureRequest(
request->video_type() == MediaStreamType::NO_SERVICE));
SendLogMessage(base::StringPrintf(
"SetUpDeviceCaptureRequest([requester_id=%d])", request->requester_id));
std::string audio_device_id;
std::vector<std::string> audio_device_ids;
if (request->stream_controls().audio.requested() &&
!GetRequestedDeviceCaptureId(
!GetEligibleCaptureDeviceids(
request, request->audio_type(),
enumeration[static_cast<size_t>(MediaDeviceType::kMediaAudioInput)],
&audio_device_id)) {
&audio_device_ids)) {
return false;
}
std::string video_device_id;
std::vector<std::string> video_device_ids;
if (request->stream_controls().video.requested() &&
!GetRequestedDeviceCaptureId(
!GetEligibleCaptureDeviceids(
request, request->video_type(),
enumeration[static_cast<size_t>(MediaDeviceType::kMediaVideoInput)],
&video_device_id)) {
&video_device_ids)) {
return false;
}
request->CreateUIRequest(audio_device_id, video_device_id);
request->CreateUIRequest(audio_device_ids, video_device_ids);
DVLOG(3) << "Audio requested " << request->stream_controls().audio.requested()
<< " device id = " << audio_device_id << "Video requested "
<< " device id = " << audio_device_ids.size() << "Video requested "
<< request->stream_controls().video.requested()
<< " device id = " << video_device_id;
<< " device id = " << video_device_ids.size();
return true;
}
@ -2755,10 +2768,11 @@ bool MediaStreamManager::SetUpTabCaptureRequest(DeviceRequest* request,
request->video_type() == MediaStreamType::GUM_TAB_VIDEO_CAPTURE);
std::string capture_device_id;
if (!request->stream_controls().audio.device_id.empty()) {
capture_device_id = request->stream_controls().audio.device_id;
} else if (!request->stream_controls().video.device_id.empty()) {
capture_device_id = request->stream_controls().video.device_id;
if (!request->stream_controls().audio.device_ids.empty() &&
!request->stream_controls().audio.device_ids.front().empty()) {
capture_device_id = request->stream_controls().audio.device_ids.front();
} else if (!request->stream_controls().video.device_ids.empty()) {
capture_device_id = request->stream_controls().video.device_ids.front();
} else {
return false;
}
@ -2846,18 +2860,18 @@ bool MediaStreamManager::SetUpScreenCaptureRequest(DeviceRequest* request) {
return false;
}
std::string video_device_id;
std::vector<std::string> video_device_ids;
if (request->video_type() == MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE &&
!request->stream_controls().video.device_id.empty()) {
video_device_id = request->stream_controls().video.device_id;
!request->stream_controls().video.device_ids.empty()) {
video_device_ids = request->stream_controls().video.device_ids;
}
const std::string audio_device_id =
const std::vector<std::string> audio_device_ids =
request->audio_type() == MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE
? video_device_id
: "";
? video_device_ids
: std::vector<std::string>{};
request->CreateUIRequest(audio_device_id, video_device_id);
request->CreateUIRequest(audio_device_ids, video_device_ids);
return true;
}
@ -2874,9 +2888,10 @@ void MediaStreamManager::SetUpDesktopCaptureChangeSourceRequest(
request->set_request_type(blink::MEDIA_DEVICE_UPDATE);
request->CreateUIRequest(
std::string() /* requested_audio_device_id */,
media_id.is_null() ? std::string()
: media_id.ToString() /* requested_video_device_id */);
std::vector<std::string>{} /* requested_audio_device_id */,
media_id.is_null()
? std::vector<std::string>{}
: std::vector{media_id.ToString()} /* requested_video_device_id */);
ReadOutputParamsAndPostRequestToUI(label, request, MediaDeviceEnumeration());
}

@ -660,18 +660,18 @@ class CONTENT_EXPORT MediaStreamManager
// valid alternate device ID.
// Returns false if the required device ID is present and invalid.
// Otherwise, if no valid device is found, device_id is unchanged.
bool PickDeviceId(const MediaDeviceSaltAndOrigin& salt_and_origin,
const blink::TrackControls& controls,
const blink::WebMediaDeviceInfoArray& devices,
std::string* device_id) const;
bool RemoveInvalidDeviceIds(const MediaDeviceSaltAndOrigin& salt_and_origin,
const blink::TrackControls& controls,
const blink::WebMediaDeviceInfoArray& devices,
std::vector<std::string>* device_id) const;
// Finds the requested device id from request. The requested device type
// must be MEDIA_DEVICE_AUDIO_CAPTURE or MEDIA_DEVICE_VIDEO_CAPTURE.
bool GetRequestedDeviceCaptureId(
bool GetEligibleCaptureDeviceids(
const DeviceRequest* request,
blink::mojom::MediaStreamType type,
const blink::WebMediaDeviceInfoArray& devices,
std::string* device_id) const;
std::vector<std::string>* device_id) const;
void TranslateDeviceIdToSourceId(const DeviceRequest* request,
blink::MediaStreamDevice* device) const;

@ -368,14 +368,14 @@ class TestVideoCaptureHost : public media::mojom::VideoCaptureHost {
blink::StreamControls GetVideoStreamControls(std::string hmac_device_id) {
blink::StreamControls stream_controls{/*request_audio=*/false,
/*request_video=*/true};
stream_controls.video.device_id = hmac_device_id;
stream_controls.video.device_ids = {hmac_device_id};
return stream_controls;
}
blink::StreamControls GetAudioStreamControls(std::string hmac_device_id) {
blink::StreamControls stream_controls{/*request_audio=*/true,
/*request_video=*/false};
stream_controls.audio.device_id = hmac_device_id;
stream_controls.audio.device_ids = {hmac_device_id};
return stream_controls;
}

@ -543,14 +543,16 @@ void FakeMediaStreamUIProxy::RequestAccess(
if (!devices_to_use.audio_device.has_value() &&
blink::IsAudioInputMediaType(request->audio_type) &&
blink::IsAudioInputMediaType(device.type) &&
(request->requested_audio_device_id.empty() ||
request->requested_audio_device_id == device.id)) {
(request->requested_audio_device_ids.empty() ||
request->requested_audio_device_ids.front().empty() ||
request->requested_audio_device_ids.front() == device.id)) {
devices_to_use.audio_device = device;
} else if (!devices_to_use.video_device.has_value() &&
blink::IsVideoInputMediaType(request->video_type) &&
blink::IsVideoInputMediaType(device.type) &&
(request->requested_video_device_id.empty() ||
request->requested_video_device_id == device.id)) {
(request->requested_video_device_ids.empty() ||
request->requested_video_device_ids.front().empty() ||
request->requested_video_device_ids.front() == device.id)) {
devices_to_use.video_device = device;
}
}

@ -125,21 +125,24 @@ class MediaStreamUIProxyTest : public testing::Test {
};
MATCHER_P(SameRequest, expected, "") {
return
expected->render_process_id == arg.render_process_id &&
expected->render_frame_id == arg.render_frame_id &&
expected->security_origin == arg.security_origin &&
expected->request_type == arg.request_type &&
expected->requested_audio_device_id == arg.requested_audio_device_id &&
expected->requested_video_device_id == arg.requested_video_device_id &&
expected->audio_type == arg.audio_type &&
expected->video_type == arg.video_type;
return expected->render_process_id == arg.render_process_id &&
expected->render_frame_id == arg.render_frame_id &&
expected->security_origin == arg.security_origin &&
expected->request_type == arg.request_type &&
expected->requested_audio_device_ids ==
arg.requested_audio_device_ids &&
expected->requested_video_device_ids ==
arg.requested_video_device_ids &&
expected->audio_type == arg.audio_type &&
expected->video_type == arg.video_type;
}
TEST_F(MediaStreamUIProxyTest, Deny) {
auto request = std::make_unique<MediaStreamRequest>(
0, 0, 0, url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_ids=*/std::vector<std::string>{},
/*requested_video_device_ids=*/std::vector<std::string>{},
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
@ -178,7 +181,9 @@ TEST_F(MediaStreamUIProxyTest, Deny) {
TEST_F(MediaStreamUIProxyTest, AcceptAndStart) {
auto request = std::make_unique<MediaStreamRequest>(
0, 0, 0, url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_ids=*/std::vector<std::string>{},
/*requested_video_device_ids=*/std::vector<std::string>{},
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
@ -229,7 +234,9 @@ TEST_F(MediaStreamUIProxyTest, AcceptAndStart) {
TEST_F(MediaStreamUIProxyTest, DeleteBeforeAccepted) {
auto request = std::make_unique<MediaStreamRequest>(
0, 0, 0, url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_ids=*/std::vector<std::string>{},
/*requested_video_device_ids=*/std::vector<std::string>{},
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
@ -261,7 +268,9 @@ TEST_F(MediaStreamUIProxyTest, DeleteBeforeAccepted) {
TEST_F(MediaStreamUIProxyTest, StopFromUI) {
auto request = std::make_unique<MediaStreamRequest>(
0, 0, 0, url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_ids=*/std::vector<std::string>{},
/*requested_video_device_ids=*/std::vector<std::string>{},
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
@ -326,7 +335,9 @@ TEST_F(MediaStreamUIProxyTest, StopFromUI) {
TEST_F(MediaStreamUIProxyTest, WindowIdCallbackCalled) {
auto request = std::make_unique<MediaStreamRequest>(
0, 0, 0, url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_ids=*/std::vector<std::string>{},
/*requested_video_device_ids=*/std::vector<std::string>{},
blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
@ -372,7 +383,9 @@ TEST_F(MediaStreamUIProxyTest, WindowIdCallbackCalled) {
TEST_F(MediaStreamUIProxyTest, ChangeSourceFromUI) {
auto request = std::make_unique<MediaStreamRequest>(
0, 0, 0, url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_ids=*/std::vector<std::string>{},
/*requested_video_device_ids=*/std::vector<std::string>{},
blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
@ -438,7 +451,9 @@ TEST_F(MediaStreamUIProxyTest, ChangeSourceFromUI) {
TEST_F(MediaStreamUIProxyTest, ChangeTabSourceFromUI) {
auto request = std::make_unique<MediaStreamRequest>(
0, 0, 0, url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_ids=*/std::vector<std::string>{},
/*requested_video_device_ids=*/std::vector<std::string>{},
blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
@ -506,7 +521,9 @@ TEST_F(MediaStreamUIProxyTest, ChangeTabSourceFromUI) {
// Switching source tab will trigger another MediaStreamRequest
request = std::make_unique<MediaStreamRequest>(
0, 0, 0, url::Origin::Create(GURL("http://origin/")), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(),
blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_ids=*/std::vector<std::string>{},
/*requested_video_device_ids=*/std::vector<std::string>{},
blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);
@ -589,7 +606,9 @@ class MediaStreamUIProxyPermissionsPolicyTest
return std::make_unique<MediaStreamRequest>(
rfh->GetProcess()->GetID(), rfh->GetRoutingID(), 0,
url::Origin::Create(rfh->GetLastCommittedURL()), false,
blink::MEDIA_GENERATE_STREAM, std::string(), std::string(), mic_type,
blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_ids=*/std::vector<std::string>{},
/*requested_video_device_ids=*/std::vector<std::string>{}, mic_type,
cam_type,
/*disable_local_echo=*/false,
/*request_pan_tilt_zoom_permission=*/false);

@ -3118,8 +3118,8 @@ TEST_F(WebContentsImplTest, RequestMediaAccessPermissionNoDelegate) {
/*render_process_id=*/0, /*render_frame_id=*/0, /*page_request_id=*/0,
/*url_origin=*/url::Origin::Create(GURL("")), /*user_gesture=*/false,
blink::MediaStreamRequestType::MEDIA_GENERATE_STREAM,
/*requested_audio_device_id=*/"",
/*requested_video_device_id=*/"",
/*requested_audio_device_ids=*/{},
/*requested_video_device_ids=*/{},
blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE,
/*disable_local_echo=*/false, /*request_pan_tilt_zoom_permission=*/false);

@ -13,8 +13,8 @@ MediaStreamRequest::MediaStreamRequest(
const url::Origin& url_origin,
bool user_gesture,
blink::MediaStreamRequestType request_type,
const std::string& requested_audio_device_id,
const std::string& requested_video_device_id,
const std::vector<std::string>& requested_audio_device_ids,
const std::vector<std::string>& requested_video_device_ids,
blink::mojom::MediaStreamType audio_type,
blink::mojom::MediaStreamType video_type,
bool disable_local_echo,
@ -26,8 +26,8 @@ MediaStreamRequest::MediaStreamRequest(
url_origin(url_origin),
user_gesture(user_gesture),
request_type(request_type),
requested_audio_device_id(requested_audio_device_id),
requested_video_device_id(requested_video_device_id),
requested_audio_device_ids(requested_audio_device_ids),
requested_video_device_ids(requested_video_device_ids),
audio_type(audio_type),
video_type(video_type),
disable_local_echo(disable_local_echo),

@ -31,8 +31,8 @@ struct CONTENT_EXPORT MediaStreamRequest {
const url::Origin& url_origin,
bool user_gesture,
blink::MediaStreamRequestType request_type,
const std::string& requested_audio_device_id,
const std::string& requested_video_device_id,
const std::vector<std::string>& requested_audio_device_ids,
const std::vector<std::string>& requested_video_device_ids,
blink::mojom::MediaStreamType audio_type,
blink::mojom::MediaStreamType video_type,
bool disable_local_echo,
@ -73,8 +73,8 @@ struct CONTENT_EXPORT MediaStreamRequest {
blink::MediaStreamRequestType request_type;
// Stores the requested raw device id for physical audio or video devices.
std::string requested_audio_device_id;
std::string requested_video_device_id;
std::vector<std::string> requested_audio_device_ids;
std::vector<std::string> requested_video_device_ids;
// Flag to indicate if the request contains audio.
blink::mojom::MediaStreamType audio_type;

@ -26,17 +26,26 @@ namespace extensions {
namespace {
// Return the first device from `requested_device_ids` that's found in the id
// list. If none of the ids are found then return the first device.
const MediaStreamDevice* GetRequestedDeviceOrDefault(
const MediaStreamDevices& devices,
const std::string& requested_device_id) {
if (!requested_device_id.empty()) {
const std::vector<std::string>& requested_device_ids) {
for (const auto& requested_device_id : requested_device_ids) {
if (requested_device_id.empty()) {
continue;
}
auto it = base::ranges::find(devices, requested_device_id,
&MediaStreamDevice::id);
return it != devices.end() ? &(*it) : nullptr;
if (it != devices.end()) {
return &(*it);
}
}
if (!devices.empty())
if (!devices.empty()) {
return &devices[0];
}
return nullptr;
}
@ -67,7 +76,7 @@ void GrantMediaStreamRequest(content::WebContents* web_contents,
VerifyMediaAccessPermission(request.audio_type, extension);
const MediaStreamDevice* device = GetRequestedDeviceOrDefault(
MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(),
request.requested_audio_device_id);
request.requested_audio_device_ids);
if (device)
devices.audio_device = *device;
}
@ -77,7 +86,7 @@ void GrantMediaStreamRequest(content::WebContents* web_contents,
VerifyMediaAccessPermission(request.video_type, extension);
const MediaStreamDevice* device = GetRequestedDeviceOrDefault(
MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(),
request.requested_video_device_id);
request.requested_video_device_ids);
if (device)
devices.video_device = *device;
}

@ -227,7 +227,10 @@ void HandleMediaPermissionsRequestResult(
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
if (result[result_pos] == blink::mojom::PermissionStatus::GRANTED) {
devices->audio_device = blink::MediaStreamDevice(
request.audio_type, request.requested_audio_device_id,
request.audio_type,
request.requested_audio_device_ids.empty()
? ""
: request.requested_audio_device_ids.front(),
/*name=*/"");
}
result_pos++;
@ -237,7 +240,10 @@ void HandleMediaPermissionsRequestResult(
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
if (result[result_pos] == blink::mojom::PermissionStatus::GRANTED) {
devices->video_device = blink::MediaStreamDevice(
request.video_type, request.requested_video_device_id,
request.video_type,
request.requested_video_device_ids.empty()
? ""
: request.requested_video_device_ids.front(),
/*name=*/"");
}
}

@ -49,8 +49,9 @@ bool StructTraits<blink::mojom::TrackControlsDataView, blink::TrackControls>::
Read(blink::mojom::TrackControlsDataView input, blink::TrackControls* out) {
if (!input.ReadStreamType(&out->stream_type))
return false;
if (!input.ReadDeviceId(&out->device_id))
if (!input.ReadDeviceIds(&out->device_ids)) {
return false;
}
return true;
}

@ -36,7 +36,7 @@ struct BLINK_COMMON_EXPORT TrackControls {
// An empty string represents the default device.
// A nonempty string represents a specific device.
std::string device_id;
std::vector<std::string> device_ids;
};
// StreamControls describes what is sent to the browser process

@ -75,8 +75,9 @@ struct BLINK_COMMON_EXPORT
return controls.stream_type;
}
static const std::string& device_id(const blink::TrackControls& controls) {
return controls.device_id;
static const std::vector<std::string>& device_ids(
const blink::TrackControls& controls) {
return controls.device_ids;
}
static bool Read(blink::mojom::TrackControlsDataView input,

@ -124,7 +124,7 @@ struct MediaStreamDevice {
// See common/mediastream/media_stream_controls.h.
struct TrackControls {
MediaStreamType stream_type;
string device_id;
array<string> device_ids;
};
// See common/mediastream/media_stream_controls.h.

@ -34,7 +34,8 @@ void MockMojoMediaStreamDispatcherHost::GenerateStreams(
strategy == blink::mojom::StreamSelectionStrategy::FORCE_NEW_STREAM)) {
stream_devices_.audio_device = MediaStreamDevice(
controls.audio.stream_type,
controls.audio.device_id + session_id_.ToString(), "microphone");
controls.audio.device_ids.front() + session_id_.ToString(),
"microphone");
stream_devices_.audio_device.value().set_session_id(session_id_);
stream_devices_.audio_device.value().matched_output_device_id =
"associated_output_device_id" + session_id_.ToString();
@ -43,7 +44,8 @@ void MockMojoMediaStreamDispatcherHost::GenerateStreams(
if (controls.video.requested()) {
stream_devices_.video_device = MediaStreamDevice(
controls.video.stream_type,
controls.video.device_id + session_id_.ToString(), "usb video camera");
controls.video.device_ids.front() + session_id_.ToString(),
"usb video camera");
stream_devices_.video_device.value().video_facing =
media::MEDIA_VIDEO_FACING_USER;
stream_devices_.video_device.value().set_session_id(session_id_);

@ -14,6 +14,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/types/optional_util.h"
@ -731,8 +732,8 @@ void UserMediaProcessor::SelectAudioSettings(
}
if (current_request_info_->stream_controls()->audio.stream_type !=
MediaStreamType::DISPLAY_AUDIO_CAPTURE) {
current_request_info_->stream_controls()->audio.device_id =
settings.device_id();
current_request_info_->stream_controls()->audio.device_ids = {
settings.device_id()};
current_request_info_->stream_controls()->disable_local_echo =
settings.disable_local_echo();
}
@ -904,8 +905,8 @@ void UserMediaProcessor::SelectVideoDeviceSettings(
GetUserMediaRequestFailed(result, failed_constraint_name);
return;
}
current_request_info_->stream_controls()->video.device_id =
settings.device_id();
current_request_info_->stream_controls()->video.device_ids = {
settings.device_id()};
current_request_info_->SetVideoCaptureSettings(
settings, false /* is_content_capture */);
@ -948,8 +949,8 @@ void UserMediaProcessor::SelectVideoContentSettings() {
if (stream_type != MediaStreamType::DISPLAY_VIDEO_CAPTURE &&
stream_type != MediaStreamType::DISPLAY_VIDEO_CAPTURE_THIS_TAB &&
stream_type != MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET) {
current_request_info_->stream_controls()->video.device_id =
settings.device_id();
current_request_info_->stream_controls()->video.device_ids = {
settings.device_id()};
}
current_request_info_->SetVideoCaptureSettings(settings,
@ -964,10 +965,14 @@ void UserMediaProcessor::GenerateStreamForCurrentRequestInfo(
DCHECK(current_request_info_);
SendLogMessage(base::StringPrintf(
"GenerateStreamForCurrentRequestInfo({request_id=%d}, "
"{audio.device_id=%s}, {video.device_id=%s})",
"{audio.device_ids=%s}, {video.device_ids=%s})",
current_request_info_->request_id(),
current_request_info_->stream_controls()->audio.device_id.c_str(),
current_request_info_->stream_controls()->video.device_id.c_str()));
base::JoinString(
current_request_info_->stream_controls()->audio.device_ids, ",")
.c_str(),
base::JoinString(
current_request_info_->stream_controls()->video.device_ids, ",")
.c_str()));
current_request_info_->set_state(RequestInfo::State::kSentForGeneration);
// If SessionId is set, this request is for a transferred MediaStreamTrack and