Commit 848ce57f authored by Guido Urdaneta's avatar Guido Urdaneta Committed by Commit Bot

Use heuristic to group ID for video-input devices in enumerateDevices

This CL adds a heuristic to find associations between video-input and
audio-input devices.

The heuristic considers an association to exist if the label for the
video device (minus the VID:PID suffix, if it exists) appears as a
substring of the audio label.

If zero or more than one association is found then the video device ID
is used as its group ID. This also fixes the problem of having all
video devices with empty group IDs.

We expect this heuristic to have a relatively high false-negative rate
and a low false-positive rate.

Bug: 627793
Change-Id: Iebc9228ca6a8acd5f0e740f3120ef851fecc1cb1
Reviewed-on: https://chromium-review.googlesource.com/883534Reviewed-by: default avatarHenrik Boström <hbos@chromium.org>
Reviewed-by: default avatarHarald Alvestrand <hta@chromium.org>
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
Cr-Commit-Position: refs/heads/master@{#532119}
parent 77aa1ef8
......@@ -111,14 +111,7 @@ class WebRtcGetMediaDevicesBrowserTest
found_video_input = true;
}
// enumerateDevices doesn't have group ID support for video input devices.
// TODO(guidou): remove this once http://crbug.com/627793 is fixed.
if (device.kind == kDeviceKindVideoInput) {
EXPECT_TRUE(device.group_id.empty());
} else {
EXPECT_FALSE(device.group_id.empty());
}
EXPECT_FALSE(device.group_id.empty());
devices->push_back(device);
}
......
......@@ -73,8 +73,49 @@ MediaDeviceInfoArray GetFakeAudioDevices(bool is_input) {
return result;
}
std::string VideoLabelWithoutModelID(const std::string& label) {
if (label.rfind(")") != label.size() - 1)
return label;
auto idx = label.rfind(" (");
if (idx == std::string::npos)
return label;
return label.substr(0, idx - 1);
}
} // namespace
std::string GuessVideoGroupID(const MediaDeviceInfoArray& audio_infos,
const MediaDeviceInfo& video_info) {
std::string video_label = VideoLabelWithoutModelID(video_info.label);
// If |video_label| is very small, do not guess in order to avoid false
// positives.
if (video_label.size() <= 3)
return video_info.device_id;
auto equals_lambda = [&video_label](const MediaDeviceInfo& info) {
return info.label.find(video_label) != std::string::npos;
};
auto it_first =
std::find_if(audio_infos.begin(), audio_infos.end(), equals_lambda);
if (it_first == audio_infos.end())
return video_info.device_id;
auto it = it_first;
while ((it = std::find_if(it + 1, audio_infos.end(), equals_lambda)) !=
audio_infos.end()) {
// If the label appears with more than one group ID, it is impossible to
// know which group ID is the correct one.
if (it->group_id != it_first->group_id)
return video_info.device_id;
}
return it_first->group_id;
}
struct MediaDevicesManager::EnumerationRequest {
EnumerationRequest(const BoolDeviceTypes& requested_types,
const EnumerationCallback& callback)
......@@ -407,12 +448,23 @@ void MediaDevicesManager::OnDevicesEnumerated(
const MediaDeviceEnumeration& enumeration) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::string group_id_salt = group_id_salt_base + device_id_salt;
MediaDeviceInfoArray video_device_infos =
enumeration[MEDIA_DEVICE_TYPE_VIDEO_INPUT];
for (auto& video_device_info : video_device_infos) {
video_device_info.group_id = GuessVideoGroupID(
enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT], video_device_info);
}
std::vector<MediaDeviceInfoArray> result(NUM_MEDIA_DEVICE_TYPES);
for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
if (!requested_types[i])
continue;
for (const auto& device_info : enumeration[i]) {
const MediaDeviceInfoArray& device_infos =
i == MEDIA_DEVICE_TYPE_VIDEO_INPUT ? video_device_infos
: enumeration[i];
for (const auto& device_info : device_infos) {
result[i].push_back(TranslateMediaDeviceInfo(
has_permissions[i], device_id_salt, group_id_salt, security_origin,
device_info));
......
......@@ -6,6 +6,9 @@
#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_DEVICES_MANAGER_H_
#include <array>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/callback.h"
......@@ -257,6 +260,19 @@ class CONTENT_EXPORT MediaDevicesManager
DISALLOW_COPY_AND_ASSIGN(MediaDevicesManager);
};
// This function uses a heuristic to guess the group ID for a video device with
// label |video_label| based on appearance of |video_label| as a substring in
// the label of any of the audio devices in |audio_infos|. The heuristic tries
// to minimize the probability of false positives.
// If the heuristic fails to find an association, the |video_info.device_id| is
// returned to be used as group ID. This group ID and the device ID are later
// obfuscated with different salts before being sent to the renderer process.
// TODO(crbug.com/627793): Replace the heuristic with proper associations
// provided by the OS.
CONTENT_EXPORT std::string GuessVideoGroupID(
const MediaDeviceInfoArray& audio_infos,
const MediaDeviceInfo& video_info);
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_MEDIA_DEVICES_MANAGER_H_
......@@ -628,4 +628,57 @@ TEST_F(MediaDevicesManagerTest, SubscribeDeviceChanges) {
EXPECT_EQ(num_audio_output_devices, notification_all_audio_output.size());
}
TEST_F(MediaDevicesManagerTest, GuessVideoGroupID) {
MediaDeviceInfoArray audio_devices = {
{"device_id_1", "Microphone (Logitech Webcam C930e)", "group_1"},
{"device_id_2", "HD Pro Webcam C920", "group_2"},
{"device_id_3", "Microsoft® LifeCam Cinema(TM)", "group_3"},
{"device_id_4", "Repeated webcam", "group_4"},
{"device_id_5", "Repeated webcam", "group_5"},
{"device_id_6", "Dual-mic webcam device", "group_6"},
{"device_id_7", "Dual-mic webcam device", "group_6"},
{"device_id_8", "Repeated dual-mic webcam device", "group_7"},
{"device_id_9", "Repeated dual-mic webcam device", "group_7"},
{"device_id_10", "Repeated dual-mic webcam device", "group_8"},
{"device_id_11", "Repeated dual-mic webcam device", "group_8"},
};
MediaDeviceInfo logitech_video("logitech_video",
"Logitech Webcam C930e (046d:0843)", "");
MediaDeviceInfo hd_pro_video("hd_pro_video", "HD Pro Webcam C920 (046d:082d)",
"");
MediaDeviceInfo lifecam_video(
"lifecam_video", "Microsoft® LifeCam Cinema(TM) (045e:075d)", "");
MediaDeviceInfo repeated_webcam1_video("repeated_webcam_1", "Repeated webcam",
"");
MediaDeviceInfo repeated_webcam2_video("repeated_webcam_2", "Repeated webcam",
"");
MediaDeviceInfo dual_mic_video("dual_mic_video",
"Dual-mic webcam device (1111:1111)", "");
MediaDeviceInfo webcam_only_video("webcam_only_video", "Webcam-only device",
"");
MediaDeviceInfo repeated_dual_mic1_video(
"repeated_dual_mic1_video", "Repeated dual-mic webcam device", "");
MediaDeviceInfo repeated_dual_mic2_video(
"repeated_dual_mic2_video", "Repeated dual-mic webcam device", "");
MediaDeviceInfo short_label_video("short_label_video", " ()", "");
EXPECT_EQ(GuessVideoGroupID(audio_devices, logitech_video), "group_1");
EXPECT_EQ(GuessVideoGroupID(audio_devices, hd_pro_video), "group_2");
EXPECT_EQ(GuessVideoGroupID(audio_devices, lifecam_video), "group_3");
EXPECT_EQ(GuessVideoGroupID(audio_devices, repeated_webcam1_video),
repeated_webcam1_video.device_id);
EXPECT_EQ(GuessVideoGroupID(audio_devices, repeated_webcam2_video),
repeated_webcam2_video.device_id);
EXPECT_EQ(GuessVideoGroupID(audio_devices, dual_mic_video), "group_6");
EXPECT_EQ(GuessVideoGroupID(audio_devices, webcam_only_video),
webcam_only_video.device_id);
EXPECT_EQ(GuessVideoGroupID(audio_devices, repeated_dual_mic1_video),
repeated_dual_mic1_video.device_id);
EXPECT_EQ(GuessVideoGroupID(audio_devices, repeated_dual_mic2_video),
repeated_dual_mic2_video.device_id);
EXPECT_EQ(GuessVideoGroupID(audio_devices, short_label_video),
short_label_video.device_id);
}
} // namespace content
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment