Bug 1213453 - Correlate video device group id based on device name. r=pehrsons

Differential Revision: https://phabricator.services.mozilla.com/D20372

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Alex Chronopoulos 2019-03-08 11:52:18 +00:00
parent 6efd82fb6f
commit abfb29171d
4 changed files with 405 additions and 0 deletions

View File

@ -1779,6 +1779,70 @@ class GetUserMediaRunnableWrapper : public Runnable {
};
#endif
// This function tries to guess the group id for a video device
// based on the device name. If only one audio device's name contains
// the name of the video device, then, this video device will take
// the group id of the audio device. Since this is a guess we try
// to minimize the probability of false positive. If we fail to find
// a correlation we leave the video group id untouched. In that case the
// group id will be the video device name.
/* static */
void MediaManager::GuessVideoDeviceGroupIDs(MediaDeviceSet& aDevices) {
// Run the logic in a lambda to avoid duplication.
auto updateGroupIdIfNeeded = [&](RefPtr<MediaDevice>& aVideo,
const dom::MediaDeviceKind aKind) -> bool {
MOZ_ASSERT(aVideo->mKind == dom::MediaDeviceKind::Videoinput);
MOZ_ASSERT(aKind == dom::MediaDeviceKind::Audioinput ||
aKind == dom::MediaDeviceKind::Audiooutput);
// This will store the new group id if a match is found.
nsString newVideoGroupID;
// If the group id needs to be updated this will become true. It is
// necessary when the new group id is an empty string. Without this extra
// variable to signal the update, we would resort to test if
// `newVideoGroupId` is empty. However,
// that check does not work when the new group id is an empty string.
bool updateGroupId = false;
for (const RefPtr<MediaDevice>& dev : aDevices) {
if (dev->mKind != aKind) {
continue;
}
if (!FindInReadable(aVideo->mName, dev->mName)) {
continue;
}
if (newVideoGroupID.IsEmpty()) {
// This is only expected on first match. If that's the only match group
// id will be updated to this one at the end of the loop.
updateGroupId = true;
newVideoGroupID = dev->mGroupID;
} else {
// More than one device found, it is impossible to know which group id
// is the correct one.
updateGroupId = false;
newVideoGroupID = NS_LITERAL_STRING("");
break;
}
}
if (updateGroupId) {
aVideo =
new MediaDevice(aVideo, aVideo->mID, newVideoGroupID, aVideo->mRawID);
return true;
}
return false;
};
for (RefPtr<MediaDevice>& video : aDevices) {
if (video->mKind != dom::MediaDeviceKind::Videoinput) {
continue;
}
if (updateGroupIdIfNeeded(video, dom::MediaDeviceKind::Audioinput)) {
// GroupId has been updated, continue to the next video device
continue;
}
// GroupId has not been updated, check among the outputs
updateGroupIdIfNeeded(video, dom::MediaDeviceKind::Audiooutput);
}
}
/**
* EnumerateRawDevices - Enumerate a list of audio & video devices that
* satisfy passed-in constraints. List contains raw id's.
@ -1891,6 +1955,9 @@ RefPtr<MediaManager::MgrPromise> MediaManager::EnumerateRawDevices(
MediaSinkEnum::Speaker, &outputs);
aOutDevices->AppendElements(outputs);
}
if (hasVideo) {
GuessVideoDeviceGroupIDs(*aOutDevices);
}
holder->Resolve(false, __func__);
});

View File

@ -280,6 +280,7 @@ class MediaManager final : public nsIMediaManagerService,
const uint64_t aWindowId);
static already_AddRefed<nsIWritableVariant> ToJSArray(
MediaDeviceSet& aDevices);
static void GuessVideoDeviceGroupIDs(MediaManager::MediaDeviceSet& aDevices);
private:
enum class DeviceEnumerationType : uint8_t {

View File

@ -0,0 +1,336 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AudioDeviceInfo.h"
#include "MediaManager.h"
#include "gmock/gmock.h"
#include "gtest/gtest-printers.h"
#include "gtest/gtest.h"
#include "mozilla/Attributes.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
#include "webrtc/MediaEngineSource.h"
using ::testing::Return;
void PrintTo(const nsString& aValue, ::std::ostream* aStream) {
NS_ConvertUTF16toUTF8 str(aValue);
(*aStream) << str.get();
}
void PrintTo(const nsCString& aValue, ::std::ostream* aStream) {
(*aStream) << aValue.get();
}
class MockMediaEngineSource : public MediaEngineSource {
public:
MOCK_CONST_METHOD0(GetMediaSource, dom::MediaSourceEnum());
/* Unused overrides */
MOCK_CONST_METHOD0(GetName, nsString());
MOCK_CONST_METHOD0(GetUUID, nsCString());
MOCK_CONST_METHOD0(GetGroupId, nsString());
MOCK_METHOD6(Allocate, nsresult(const dom::MediaTrackConstraints&,
const MediaEnginePrefs&, const nsString&,
const ipc::PrincipalInfo&, AllocationHandle**,
const char**));
MOCK_METHOD4(SetTrack, void(const RefPtr<const AllocationHandle>&,
const RefPtr<SourceMediaStream>&, TrackID,
const PrincipalHandle&));
MOCK_METHOD1(Start, nsresult(const RefPtr<const AllocationHandle>&));
MOCK_METHOD5(Reconfigure, nsresult(const RefPtr<AllocationHandle>&,
const dom::MediaTrackConstraints&,
const MediaEnginePrefs&, const nsString&,
const char**));
MOCK_METHOD1(Stop, nsresult(const RefPtr<const AllocationHandle>&));
MOCK_METHOD1(Deallocate, nsresult(const RefPtr<const AllocationHandle>&));
MOCK_CONST_METHOD2(GetBestFitnessDistance,
uint32_t(const nsTArray<const NormalizedConstraintSet*>&,
const nsString&));
MOCK_METHOD6(Pull,
void(const RefPtr<const AllocationHandle>& aHandle,
const RefPtr<SourceMediaStream>& aStream, TrackID aTrackID,
StreamTime aEndOfAppendedData, StreamTime aDesiredTime,
const PrincipalHandle& aPrincipalHandle));
};
RefPtr<AudioDeviceInfo> MakeAudioDeviceInfo(const nsString aName) {
return MakeRefPtr<AudioDeviceInfo>(
nullptr, aName, NS_LITERAL_STRING("GroupId"), NS_LITERAL_STRING("Vendor"),
AudioDeviceInfo::TYPE_OUTPUT, AudioDeviceInfo::STATE_ENABLED,
AudioDeviceInfo::PREF_NONE, AudioDeviceInfo::FMT_F32LE,
AudioDeviceInfo::FMT_F32LE, 2u, 44100u, 44100u, 44100u, 0, 0);
}
RefPtr<MediaDevice> MakeCameraDevice(const nsString& aName,
const nsString& aGroupId) {
auto v = MakeRefPtr<MockMediaEngineSource>();
EXPECT_CALL(*v, GetMediaSource())
.WillRepeatedly(Return(dom::MediaSourceEnum::Camera));
return MakeRefPtr<MediaDevice>(v, aName, NS_LITERAL_STRING(""), aGroupId,
NS_LITERAL_STRING(""));
}
RefPtr<MediaDevice> MakeMicDevice(const nsString& aName,
const nsString& aGroupId) {
auto a = MakeRefPtr<MockMediaEngineSource>();
EXPECT_CALL(*a, GetMediaSource())
.WillRepeatedly(Return(dom::MediaSourceEnum::Microphone));
return MakeRefPtr<MediaDevice>(a, aName, NS_LITERAL_STRING(""), aGroupId,
NS_LITERAL_STRING(""));
}
RefPtr<MediaDevice> MakeSpeakerDevice(const nsString& aName,
const nsString& aGroupId) {
return MakeRefPtr<MediaDevice>(MakeAudioDeviceInfo(aName),
NS_LITERAL_STRING("ID"), aGroupId,
NS_LITERAL_STRING("RawID"));
}
/* Verify that when an audio input device name contains the video input device
* name the video device group id is updated to become equal to the audio
* device group id. */
TEST(TestGroupId, MatchInput_PartOfName) {
MediaManager::MediaDeviceSet devices;
devices.AppendElement(
MakeCameraDevice(NS_LITERAL_STRING("Vendor Model"),
NS_LITERAL_STRING("Cam-Model-GroupId")));
devices.AppendElement(
MakeMicDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
MediaManager::GuessVideoDeviceGroupIDs(devices);
EXPECT_EQ(devices[0]->mGroupID, devices[1]->mGroupID)
<< "Video group id is the same as audio input group id.";
}
/* Verify that when an audio input device name is the same as the video input
* device name the video device group id is updated to become equal to the audio
* device group id. */
TEST(TestGroupId, MatchInput_FullName) {
MediaManager::MediaDeviceSet devices;
devices.AppendElement(
MakeCameraDevice(NS_LITERAL_STRING("Vendor Model"),
NS_LITERAL_STRING("Cam-Model-GroupId")));
devices.AppendElement(MakeMicDevice(NS_LITERAL_STRING("Vendor Model"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
MediaManager::GuessVideoDeviceGroupIDs(devices);
EXPECT_EQ(devices[0]->mGroupID, devices[1]->mGroupID)
<< "Video group id is the same as audio input group id.";
}
/* Verify that when an audio input device name does not contain the video input
* device name the video device group id does not change. */
TEST(TestGroupId, NoMatchInput) {
MediaManager::MediaDeviceSet devices;
nsString Cam_Model_GroupId = NS_LITERAL_STRING("Cam-Model-GroupId");
devices.AppendElement(
MakeCameraDevice(NS_LITERAL_STRING("Vendor Model"), Cam_Model_GroupId));
devices.AppendElement(MakeMicDevice(NS_LITERAL_STRING("Model Analog Stereo"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
MediaManager::GuessVideoDeviceGroupIDs(devices);
EXPECT_EQ(devices[0]->mGroupID, Cam_Model_GroupId)
<< "Video group id has not been updated.";
EXPECT_NE(devices[0]->mGroupID, devices[1]->mGroupID)
<< "Video group id is different than audio input group id.";
}
/* Verify that when more that one audio input and more than one audio output
* device name contain the video input device name the video device group id
* does not change. */
TEST(TestGroupId, NoMatch_TwoIdenticalDevices) {
MediaManager::MediaDeviceSet devices;
nsString Cam_Model_GroupId = NS_LITERAL_STRING("Cam-Model-GroupId");
devices.AppendElement(
MakeCameraDevice(NS_LITERAL_STRING("Vendor Model"), Cam_Model_GroupId));
devices.AppendElement(
MakeMicDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
devices.AppendElement(
MakeMicDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
devices.AppendElement(
MakeSpeakerDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Speaker-Model-GroupId")));
devices.AppendElement(
MakeSpeakerDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Speaker-Model-GroupId")));
MediaManager::GuessVideoDeviceGroupIDs(devices);
EXPECT_EQ(devices[0]->mGroupID, Cam_Model_GroupId)
<< "Video group id has not been updated.";
EXPECT_NE(devices[0]->mGroupID, devices[1]->mGroupID)
<< "Video group id is different than audio input group id.";
EXPECT_NE(devices[0]->mGroupID, devices[3]->mGroupID)
<< "Video group id is different than audio output group id.";
}
/* Verify that when more that one audio input device name contain the video
* input device name the video device group id is not updated by audio input
* device group id but it continues looking at audio output devices where it
* finds a match so video input group id is updated by audio output group id. */
TEST(TestGroupId, Match_TwoIdenticalInputsMatchOutput) {
MediaManager::MediaDeviceSet devices;
nsString Cam_Model_GroupId = NS_LITERAL_STRING("Cam-Model-GroupId");
devices.AppendElement(
MakeCameraDevice(NS_LITERAL_STRING("Vendor Model"), Cam_Model_GroupId));
devices.AppendElement(
MakeMicDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
devices.AppendElement(
MakeMicDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
devices.AppendElement(
MakeSpeakerDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Speaker-Model-GroupId")));
MediaManager::GuessVideoDeviceGroupIDs(devices);
EXPECT_EQ(devices[0]->mGroupID, devices[3]->mGroupID)
<< "Video group id is the same as audio output group id.";
}
/* Verify that when more that one audio input and more than one audio output
* device names contain the video input device name the video device group id
* does not change. */
TEST(TestGroupId, NoMatch_ThreeIdenticalDevices) {
MediaManager::MediaDeviceSet devices;
nsString Cam_Model_GroupId = NS_LITERAL_STRING("Cam-Model-GroupId");
devices.AppendElement(
MakeCameraDevice(NS_LITERAL_STRING("Vendor Model"), Cam_Model_GroupId));
devices.AppendElement(
MakeMicDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
devices.AppendElement(
MakeMicDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
devices.AppendElement(
MakeMicDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
devices.AppendElement(
MakeSpeakerDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Speaker-Model-GroupId")));
devices.AppendElement(
MakeSpeakerDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Speaker-Model-GroupId")));
devices.AppendElement(
MakeSpeakerDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Speaker-Model-GroupId")));
MediaManager::GuessVideoDeviceGroupIDs(devices);
EXPECT_EQ(devices[0]->mGroupID, Cam_Model_GroupId)
<< "Video group id has not been updated.";
EXPECT_NE(devices[0]->mGroupID, devices[1]->mGroupID)
<< "Video group id is different than audio input group id.";
EXPECT_NE(devices[0]->mGroupID, devices[4]->mGroupID)
<< "Video group id is different than audio output group id.";
}
/* Verify that when an audio output device name contains the video input device
* name the video device group id is updated to become equal to the audio
* device group id. */
TEST(TestGroupId, MatchOutput) {
MediaManager::MediaDeviceSet devices;
devices.AppendElement(
MakeCameraDevice(NS_LITERAL_STRING("Vendor Model"),
NS_LITERAL_STRING("Cam-Model-GroupId")));
devices.AppendElement(MakeMicDevice(NS_LITERAL_STRING("Mic Analog Stereo"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
devices.AppendElement(
MakeSpeakerDevice(NS_LITERAL_STRING("Vendor Model Analog Stereo"),
NS_LITERAL_STRING("Speaker-Model-GroupId")));
MediaManager::GuessVideoDeviceGroupIDs(devices);
EXPECT_EQ(devices[0]->mGroupID, devices[2]->mGroupID)
<< "Video group id is the same as audio output group id.";
}
/* Verify that when an audio input device name is the same as audio output
* device and video input device name the video device group id is updated to
* become equal to the audio input device group id. */
TEST(TestGroupId, InputOutputSameName) {
MediaManager::MediaDeviceSet devices;
devices.AppendElement(
MakeCameraDevice(NS_LITERAL_STRING("Vendor Model"),
NS_LITERAL_STRING("Cam-Model-GroupId")));
devices.AppendElement(MakeMicDevice(NS_LITERAL_STRING("Vendor Model"),
NS_LITERAL_STRING("Mic-Model-GroupId")));
devices.AppendElement(
MakeSpeakerDevice(NS_LITERAL_STRING("Vendor Model"),
NS_LITERAL_STRING("Speaker-Model-GroupId")));
MediaManager::GuessVideoDeviceGroupIDs(devices);
EXPECT_EQ(devices[0]->mGroupID, devices[1]->mGroupID)
<< "Video input group id is the same as audio input group id.";
}
/* Verify that when an audio input device name contains the video input device
* and the audio input group id is an empty string, the video device group id
* is updated to become equal to the audio device group id. */
TEST(TestGroupId, InputEmptyGroupId) {
MediaManager::MediaDeviceSet devices;
devices.AppendElement(
MakeCameraDevice(NS_LITERAL_STRING("Vendor Model"),
NS_LITERAL_STRING("Cam-Model-GroupId")));
devices.AppendElement(
MakeMicDevice(NS_LITERAL_STRING("Vendor Model"), NS_LITERAL_STRING("")));
MediaManager::GuessVideoDeviceGroupIDs(devices);
EXPECT_EQ(devices[0]->mGroupID, devices[1]->mGroupID)
<< "Video input group id is the same as audio input group id.";
}
/* Verify that when an audio output device name contains the video input device
* and the audio output group id is an empty string, the video device group id
* is updated to become equal to the audio output device group id. */
TEST(TestGroupId, OutputEmptyGroupId) {
MediaManager::MediaDeviceSet devices;
devices.AppendElement(
MakeCameraDevice(NS_LITERAL_STRING("Vendor Model"),
NS_LITERAL_STRING("Cam-Model-GroupId")));
devices.AppendElement(MakeSpeakerDevice(NS_LITERAL_STRING("Vendor Model"),
NS_LITERAL_STRING("")));
MediaManager::GuessVideoDeviceGroupIDs(devices);
EXPECT_EQ(devices[0]->mGroupID, devices[1]->mGroupID)
<< "Video input group id is the same as audio output group id.";
}

View File

@ -28,6 +28,7 @@ UNIFIED_SOURCES += [
'TestGMPCrossOrigin.cpp',
'TestGMPRemoveAndDelete.cpp',
'TestGMPUtils.cpp',
'TestGroupId.cpp',
'TestIntervalSet.cpp',
'TestMediaDataDecoder.cpp',
'TestMediaDataEncoder.cpp',