Bug 1037389 - add support for deviceId in gUM constraints (merged 11 patches). r=smaug, r=jesup

This commit is contained in:
Jan-Ivar Bruaroey 2015-07-02 18:01:52 -04:00
parent eece65fcc3
commit ccf0b4baae
22 changed files with 886 additions and 614 deletions

File diff suppressed because it is too large Load Diff

View File

@ -465,13 +465,25 @@ public:
NS_DECL_NSIMEDIADEVICE
void SetId(const nsAString& aID);
virtual uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
protected:
virtual ~MediaDevice() {}
explicit MediaDevice(MediaEngineSource* aSource);
explicit MediaDevice(MediaEngineSource* aSource, bool aIsVideo);
static uint32_t FitnessDistance(nsString aN,
const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint);
private:
static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings,
nsString aN);
static uint32_t FitnessDistance(nsString aN,
const dom::ConstrainDOMStringParameters& aParams);
protected:
nsString mName;
nsString mID;
dom::MediaSourceEnum mMediaSource;
nsRefPtr<MediaEngineSource> mSource;
public:
bool mIsVideo;
};
class VideoDevice : public MediaDevice
@ -482,8 +494,8 @@ public:
explicit VideoDevice(Source* aSource);
NS_IMETHOD GetType(nsAString& aType);
Source* GetSource();
uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs);
};
class AudioDevice : public MediaDevice
@ -494,8 +506,8 @@ public:
explicit AudioDevice(Source* aSource);
NS_IMETHOD GetType(nsAString& aType);
Source* GetSource();
uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs);
};
// we could add MediaManager if needed
@ -571,21 +583,25 @@ public:
MediaEnginePrefs mPrefs;
private:
typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
private:
typedef media::Pledge<SourceSet*, dom::MediaStreamError> PledgeSourceSet;
static bool IsPrivileged();
static bool IsLoop(nsIURI* aDocURI);
static bool IsPrivateBrowsing(nsPIDOMWindow *window);
static nsresult GenerateUUID(nsAString& aResult);
static nsresult AnonymizeId(nsAString& aId, const nsACString& aOriginKey);
public: // TODO: make private once we upgrade to GCC 4.8+ on linux.
static void AnonymizeDevices(SourceSet& aDevices, const nsACString& aOriginKey);
static already_AddRefed<nsIWritableVariant> ToJSArray(SourceSet& aDevices);
private:
already_AddRefed<PledgeSourceSet>
EnumerateRawDevices(uint64_t aWindowId,
const dom::MediaStreamConstraints& aConstraints);
EnumerateRawDevices(uint64_t aWindowId, dom::MediaSourceEnum aSrcType,
bool aFake, bool aFakeTracks);
already_AddRefed<PledgeSourceSet>
EnumerateDevicesImpl(uint64_t aWindowId,
const dom::MediaStreamConstraints& aConstraints);
EnumerateDevicesImpl(uint64_t aWindowId, dom::MediaSourceEnum aSrcType,
bool aFake = false, bool aFakeTracks = false);
StreamListeners* AddWindowID(uint64_t aWindowId);
WindowTable *GetActiveWindows() {

View File

@ -503,16 +503,12 @@ Parent<Super>::Parent(bool aSameProcess)
if (!gMediaParentLog)
gMediaParentLog = PR_NewLogModule("MediaParent");
LOG(("media::Parent: %p", this));
MOZ_COUNT_CTOR(Parent);
}
template<class Super>
Parent<Super>::~Parent()
{
LOG(("~media::Parent: %p", this));
MOZ_COUNT_DTOR(Parent);
}
PMediaParent*

View File

@ -23,7 +23,14 @@ var getVideoImagePixelData = function(v) {
"a" + imgData[3];
}
navigator.mozGetUserMedia({video: true, fake: true}, function(stream) {
// This test does not appear to work with the "Dummy video source" provided on
// linux through the "media.video_loopback_dev" pref in the tree test environment.
// To force the built-in fake streams to always be used instead, we specify
// fakeTracks, a feature solely of the built-in fake streams (even though we
// don't use the extra tracks).
navigator.mozGetUserMedia({video: true, fake: true, fakeTracks: true },
function(stream) {
var stream = stream;
var video1 = document.getElementById('video1');
var video2 = document.getElementById('video2');

View File

@ -99,17 +99,13 @@ function createMediaElement(type, label) {
/**
* Wrapper function for mozGetUserMedia to allow a singular area of control
* for determining whether we run this with fake devices or not.
* Wrapper function for mediaDevices.getUserMedia used by some tests. Whether
* to use fake devices or not is now determined in pref further below instead.
*
* @param {Dictionary} constraints
* The constraints for this mozGetUserMedia callback
*/
function getUserMedia(constraints) {
if (!("fake" in constraints) && FAKE_ENABLED) {
constraints["fake"] = FAKE_ENABLED;
}
info("Call getUserMedia for " + JSON.stringify(constraints));
return navigator.mediaDevices.getUserMedia(constraints);
}
@ -138,6 +134,7 @@ function setupEnvironment() {
['media.peerconnection.ice.stun_client_maximum_transmits', 14],
['media.peerconnection.ice.trickle_grace_period', 30000],
['media.navigator.permission.disabled', true],
['media.navigator.streams.fake', FAKE_ENABLED],
['media.getusermedia.screensharing.enabled', true],
['media.getusermedia.screensharing.allowed_domains', "mochi.test"]
]

View File

@ -8,10 +8,24 @@
<script type="application/javascript">
createHTML({ title: "Run enumerateDevices code", bug: "1046245" });
/**
Tests covering enumerateDevices API. Exercise code.
Tests covering enumerateDevices API and deviceId constraint. Exercise code.
*/
runTest(() => navigator.mediaDevices.enumerateDevices()
function mustSucceed(msg, f) {
return f().then(() => ok(true, msg + " must succeed"),
e => is(e.name, null, msg + " must succeed: " + e.message));
}
function mustFailWith(msg, reason, f) {
return f().then(() => ok(false, msg + " must fail"),
e => is(e.name, reason, msg + " must fail: " + e.message));
}
var pushPrefs = dict => new Promise(res => SpecialPowers.pushPrefEnv(dict, res));
runTest(() =>
pushPrefs({ set : [["media.navigator.streams.fake", true]] })
.then(() => navigator.mediaDevices.enumerateDevices())
.then(devices => {
ok(devices.length > 0, "At least one device found");
devices.forEach(d => {
@ -20,7 +34,28 @@ runTest(() => navigator.mediaDevices.enumerateDevices()
ok(d.label.length !== undefined, "Device label: " + d.label);
is(d.groupId, "", "Don't support groupId yet");
});
}));
})
// Check deviceId failure paths for video.
.then(() => mustSucceed("unknown plain deviceId on video",
() => navigator.mediaDevices.getUserMedia({
video: { deviceId: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" },
fake: true,
})))
.then(() => mustSucceed("unknown plain deviceId on audio",
() => navigator.mediaDevices.getUserMedia({
audio: { deviceId: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" },
fake: true,
})))
.then(() => mustFailWith("unknown exact deviceId on video", "NotFoundError",
() => navigator.mediaDevices.getUserMedia({
video: { deviceId: { exact: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" } },
fake: true,
})))
.then(() => mustFailWith("unknown exact deviceId on audio", "NotFoundError",
() => navigator.mediaDevices.getUserMedia({
audio: { deviceId: { exact: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" } },
fake: true,
}))));
</script>
</pre>

View File

@ -164,6 +164,15 @@ public:
mHasFakeTracks = aHasFakeTracks;
}
/* This call reserves but does not start the device. */
virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId) = 0;
virtual uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId) = 0;
protected:
// Only class' own members can be initialized in constructor initializer list.
explicit MediaEngineSource(MediaEngineState aState)
@ -224,13 +233,6 @@ class MediaEngineVideoSource : public MediaEngineSource
public:
virtual ~MediaEngineVideoSource() {}
/* This call reserves but does not start the device. */
virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs) = 0;
virtual uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) = 0;
protected:
explicit MediaEngineVideoSource(MediaEngineState aState)
: MediaEngineSource(aState) {}
@ -246,10 +248,6 @@ class MediaEngineAudioSource : public MediaEngineSource
public:
virtual ~MediaEngineAudioSource() {}
/* This call reserves but does not start the device. */
virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs) = 0;
protected:
explicit MediaEngineAudioSource(MediaEngineState aState)
: MediaEngineSource(aState) {}

View File

@ -51,121 +51,17 @@ MediaEngineCameraVideoSource::GetCapability(size_t aIndex,
aOut = mHardcodedCapabilities[aIndex];
}
// The full algorithm for all cameras. Sources that don't list capabilities
// need to fake it and hardcode some by populating mHardcodedCapabilities above.
// Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX
template<class ValueType, class ConstrainRange>
/* static */ uint32_t
MediaEngineCameraVideoSource::FitnessDistance(ValueType aN,
const ConstrainRange& aRange)
{
if ((aRange.mExact.WasPassed() && aRange.mExact.Value() != aN) ||
(aRange.mMin.WasPassed() && aRange.mMin.Value() > aN) ||
(aRange.mMax.WasPassed() && aRange.mMax.Value() < aN)) {
return UINT32_MAX;
}
if (!aRange.mIdeal.WasPassed() || aN == aRange.mIdeal.Value()) {
return 0;
}
return uint32_t(ValueType((std::abs(aN - aRange.mIdeal.Value()) * 1000) /
std::max(std::abs(aN), std::abs(aRange.mIdeal.Value()))));
}
// Binding code doesn't templatize well...
/*static*/ uint32_t
MediaEngineCameraVideoSource::FitnessDistance(int32_t aN,
const OwningLongOrConstrainLongRange& aConstraint, bool aAdvanced)
{
if (aConstraint.IsLong()) {
ConstrainLongRange range;
(aAdvanced ? range.mExact : range.mIdeal).Construct(aConstraint.GetAsLong());
return FitnessDistance(aN, range);
} else {
return FitnessDistance(aN, aConstraint.GetAsConstrainLongRange());
}
}
/*static*/ uint32_t
MediaEngineCameraVideoSource::FitnessDistance(double aN,
const OwningDoubleOrConstrainDoubleRange& aConstraint,
bool aAdvanced)
{
if (aConstraint.IsDouble()) {
ConstrainDoubleRange range;
(aAdvanced ? range.mExact : range.mIdeal).Construct(aConstraint.GetAsDouble());
return FitnessDistance(aN, range);
} else {
return FitnessDistance(aN, aConstraint.GetAsConstrainDoubleRange());
}
}
// Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX
/* static */ uint32_t
MediaEngineCameraVideoSource::FitnessDistance(nsString aN,
const ConstrainDOMStringParameters& aParams)
{
struct Func
{
static bool
Contains(const OwningStringOrStringSequence& aStrings, nsString aN)
{
return aStrings.IsString() ? aStrings.GetAsString() == aN
: aStrings.GetAsStringSequence().Contains(aN);
}
};
if (aParams.mExact.WasPassed() && !Func::Contains(aParams.mExact.Value(), aN)) {
return UINT32_MAX;
}
if (aParams.mIdeal.WasPassed() && !Func::Contains(aParams.mIdeal.Value(), aN)) {
return 1000;
}
return 0;
}
/* static */ uint32_t
MediaEngineCameraVideoSource::FitnessDistance(nsString aN,
const OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint,
bool aAdvanced)
{
if (aConstraint.IsString()) {
ConstrainDOMStringParameters params;
if (aAdvanced) {
params.mExact.Construct();
params.mExact.Value().SetAsString() = aConstraint.GetAsString();
} else {
params.mIdeal.Construct();
params.mIdeal.Value().SetAsString() = aConstraint.GetAsString();
}
return FitnessDistance(aN, params);
} else if (aConstraint.IsStringSequence()) {
ConstrainDOMStringParameters params;
if (aAdvanced) {
params.mExact.Construct();
params.mExact.Value().SetAsStringSequence() = aConstraint.GetAsStringSequence();
} else {
params.mIdeal.Construct();
params.mIdeal.Value().SetAsStringSequence() = aConstraint.GetAsStringSequence();
}
return FitnessDistance(aN, params);
} else {
return FitnessDistance(aN, aConstraint.GetAsConstrainDOMStringParameters());
}
}
uint32_t
MediaEngineCameraVideoSource::GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
const MediaTrackConstraintSet &aConstraints,
bool aAdvanced)
bool aAdvanced,
const nsString& aDeviceId)
{
// Treat width|height|frameRate == 0 on capability as "can do any".
// This allows for orthogonal capabilities that are not in discrete steps.
uint64_t distance =
uint64_t(FitnessDistance(aDeviceId, aConstraints.mDeviceId, aAdvanced)) +
uint64_t(FitnessDistance(mFacingMode, aConstraints.mFacingMode, aAdvanced)) +
uint64_t(aCandidate.width? FitnessDistance(int32_t(aCandidate.width),
aConstraints.mWidth,
@ -209,7 +105,8 @@ MediaEngineCameraVideoSource::TrimLessFitCandidates(CapabilitySet& set) {
uint32_t
MediaEngineCameraVideoSource::GetBestFitnessDistance(
const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets)
const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId)
{
size_t num = NumCapabilities();
@ -224,7 +121,7 @@ MediaEngineCameraVideoSource::GetBestFitnessDistance(
auto& candidate = candidateSet[i];
webrtc::CaptureCapability cap;
GetCapability(candidate.mIndex, cap);
uint32_t distance = GetFitnessDistance(cap, *cs, !first);
uint32_t distance = GetFitnessDistance(cap, *cs, !first, aDeviceId);
if (distance == UINT32_MAX) {
candidateSet.RemoveElementAt(i);
} else {
@ -268,7 +165,8 @@ MediaEngineCameraVideoSource::LogConstraints(
bool
MediaEngineCameraVideoSource::ChooseCapability(
const MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs)
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId)
{
if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
LOG(("ChooseCapability: prefs: %dx%d @%d-%dfps",
@ -296,7 +194,7 @@ MediaEngineCameraVideoSource::ChooseCapability(
auto& candidate = candidateSet[i];
webrtc::CaptureCapability cap;
GetCapability(candidate.mIndex, cap);
candidate.mDistance = GetFitnessDistance(cap, aConstraints, false);
candidate.mDistance = GetFitnessDistance(cap, aConstraints, false, aDeviceId);
if (candidate.mDistance == UINT32_MAX) {
candidateSet.RemoveElementAt(i);
} else {
@ -313,7 +211,7 @@ MediaEngineCameraVideoSource::ChooseCapability(
auto& candidate = candidateSet[i];
webrtc::CaptureCapability cap;
GetCapability(candidate.mIndex, cap);
if (GetFitnessDistance(cap, cs, true) == UINT32_MAX) {
if (GetFitnessDistance(cap, cs, true, aDeviceId) == UINT32_MAX) {
rejects.AppendElement(candidate);
candidateSet.RemoveElementAt(i);
} else {
@ -345,7 +243,7 @@ MediaEngineCameraVideoSource::ChooseCapability(
for (auto& candidate : candidateSet) {
webrtc::CaptureCapability cap;
GetCapability(candidate.mIndex, cap);
candidate.mDistance = GetFitnessDistance(cap, prefs, false);
candidate.mDistance = GetFitnessDistance(cap, prefs, false, aDeviceId);
}
TrimLessFitCandidates(candidateSet);
}

View File

@ -16,7 +16,8 @@
namespace mozilla {
class MediaEngineCameraVideoSource : public MediaEngineVideoSource
class MediaEngineCameraVideoSource : public MediaEngineVideoSource,
private MediaConstraintsHelper
{
public:
explicit MediaEngineCameraVideoSource(int aIndex,
@ -59,7 +60,8 @@ public:
}
uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) override;
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId) override;
virtual void Shutdown() override {};
@ -80,28 +82,18 @@ protected:
layers::Image* aImage,
TrackID aID,
StreamTime delta);
template<class ValueType, class ConstrainRange>
static uint32_t FitnessDistance(ValueType aN, const ConstrainRange& aRange);
static uint32_t FitnessDistance(int32_t aN,
const dom::OwningLongOrConstrainLongRange& aConstraint, bool aAdvanced);
static uint32_t FitnessDistance(double aN,
const dom::OwningDoubleOrConstrainDoubleRange& aConstraint, bool aAdvanced);
static uint32_t FitnessDistance(nsString aN,
const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint,
bool aAdvanced);
static uint32_t FitnessDistance(nsString aN,
const dom::ConstrainDOMStringParameters& aParams);
uint32_t GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
const dom::MediaTrackConstraintSet &aConstraints,
bool aAdvanced);
bool aAdvanced,
const nsString& aDeviceId);
static void TrimLessFitCandidates(CapabilitySet& set);
static void LogConstraints(const dom::MediaTrackConstraintSet& aConstraints,
bool aAdvanced);
virtual size_t NumCapabilities();
virtual void GetCapability(size_t aIndex, webrtc::CaptureCapability& aOut);
bool ChooseCapability(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs);
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId);
void SetName(nsString aName);
void SetUUID(const char* aUUID);
const nsCString& GetUUID(); // protected access

View File

@ -68,9 +68,24 @@ MediaEngineDefaultVideoSource::GetUUID(nsACString& aUUID)
return;
}
uint32_t
MediaEngineDefaultVideoSource::GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId)
{
uint32_t distance = 0;
for (const MediaTrackConstraintSet* cs : aConstraintSets) {
distance = GetMinimumFitnessDistance(*cs, false, aDeviceId);
break; // distance is read from first entry only
}
return distance;
}
nsresult
MediaEngineDefaultVideoSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs)
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId)
{
if (mState != kReleased) {
return NS_ERROR_FAILURE;
@ -348,9 +363,24 @@ MediaEngineDefaultAudioSource::GetUUID(nsACString& aUUID)
return;
}
uint32_t
MediaEngineDefaultAudioSource::GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId)
{
uint32_t distance = 0;
for (const MediaTrackConstraintSet* cs : aConstraintSets) {
distance = GetMinimumFitnessDistance(*cs, false, aDeviceId);
break; // distance is read from first entry only
}
return distance;
}
nsresult
MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs)
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId)
{
if (mState != kReleased) {
return NS_ERROR_FAILURE;

View File

@ -31,7 +31,8 @@ class MediaEngineDefault;
* The default implementation of the MediaEngine interface.
*/
class MediaEngineDefaultVideoSource : public nsITimerCallback,
public MediaEngineVideoSource
public MediaEngineVideoSource,
private MediaConstraintsHelper
{
public:
MediaEngineDefaultVideoSource();
@ -42,7 +43,8 @@ public:
virtual void GetUUID(nsACString&) override;
virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs) override;
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId) override;
virtual nsresult Deallocate() override;
virtual nsresult Start(SourceMediaStream*, TrackID) override;
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
@ -56,10 +58,8 @@ public:
TrackID aId,
StreamTime aDesiredTime) override;
virtual uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) override
{
return true;
}
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId) override;
virtual bool IsFake() override {
return true;
@ -101,7 +101,8 @@ protected:
class SineWaveGenerator;
class MediaEngineDefaultAudioSource : public nsITimerCallback,
public MediaEngineAudioSource
public MediaEngineAudioSource,
private MediaConstraintsHelper
{
public:
MediaEngineDefaultAudioSource();
@ -112,7 +113,8 @@ public:
virtual void GetUUID(nsACString&) override;
virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs) override;
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId) override;
virtual nsresult Deallocate() override;
virtual nsresult Start(SourceMediaStream*, TrackID) override;
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
@ -139,6 +141,10 @@ public:
return NS_ERROR_NOT_IMPLEMENTED;
}
virtual uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId) override;
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITIMERCALLBACK

View File

@ -147,13 +147,14 @@ MediaEngineGonkVideoSource::NumCapabilities()
nsresult
MediaEngineGonkVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
const MediaEnginePrefs& aPrefs)
const MediaEnginePrefs& aPrefs,
const nsString& aDeviceId)
{
LOG((__FUNCTION__));
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
if (mState == kReleased && mInitDone) {
ChooseCapability(aConstraints, aPrefs);
ChooseCapability(aConstraints, aPrefs, aDeviceId);
NS_DispatchToMainThread(WrapRunnable(nsRefPtr<MediaEngineGonkVideoSource>(this),
&MediaEngineGonkVideoSource::AllocImpl));
mCallbackMonitor.Wait();

View File

@ -61,7 +61,8 @@ public:
}
virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs) override;
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId) override;
virtual nsresult Deallocate() override;
virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;

View File

@ -120,7 +120,8 @@ MediaEngineTabVideoSource::GetUUID(nsACString_internal& aUuid)
nsresult
MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
const MediaEnginePrefs& aPrefs)
const MediaEnginePrefs& aPrefs,
const nsString& aDeviceId)
{
// windowId and scrollWithPage are not proper constraints, so just read them.
// They have no well-defined behavior in advanced, so ignore them there.

View File

@ -22,7 +22,8 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList
virtual void GetName(nsAString_internal&) override;
virtual void GetUUID(nsACString_internal&) override;
virtual nsresult Allocate(const dom::MediaTrackConstraints &,
const mozilla::MediaEnginePrefs&) override;
const mozilla::MediaEnginePrefs&,
const nsString& aDeviceId) override;
virtual nsresult Deallocate() override;
virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID) override;
virtual void SetDirectListeners(bool aHasDirectListeners) override {};
@ -34,7 +35,8 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList
return dom::MediaSourceEnum::Browser;
}
virtual uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) override
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId) override
{
return 0;
}

View File

@ -86,11 +86,13 @@ public:
, mMediaSource(aMediaSource)
{
MOZ_ASSERT(aVideoEnginePtr);
MOZ_ASSERT(aMediaSource != dom::MediaSourceEnum::Other);
Init();
}
virtual nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
const MediaEnginePrefs& aPrefs) override;
const MediaEnginePrefs& aPrefs,
const nsString& aDeviceId) override;
virtual nsresult Deallocate() override;
virtual nsresult Start(SourceMediaStream*, TrackID) override;
virtual nsresult Stop(SourceMediaStream*, TrackID) override;
@ -132,7 +134,8 @@ private:
};
class MediaEngineWebRTCAudioSource : public MediaEngineAudioSource,
public webrtc::VoEMediaProcess
public webrtc::VoEMediaProcess,
private MediaConstraintsHelper
{
public:
MediaEngineWebRTCAudioSource(nsIThread* aThread, webrtc::VoiceEngine* aVoiceEnginePtr,
@ -161,7 +164,8 @@ public:
virtual void GetUUID(nsACString& aUUID) override;
virtual nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
const MediaEnginePrefs& aPrefs) override;
const MediaEnginePrefs& aPrefs,
const nsString& aDeviceId) override;
virtual nsresult Deallocate() override;
virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
@ -189,6 +193,10 @@ public:
return NS_ERROR_NOT_IMPLEMENTED;
}
virtual uint32_t GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId) override;
// VoEMediaProcess.
void Process(int channel, webrtc::ProcessingTypes type,
int16_t audio10ms[], int length,

View File

@ -259,9 +259,31 @@ MediaEngineWebRTCAudioSource::Config(bool aEchoOn, uint32_t aEcho,
return NS_OK;
}
// GetBestFitnessDistance returns the best distance the capture device can offer
// as a whole, given an accumulated number of ConstraintSets.
// Ideal values are considered in the first ConstraintSet only.
// Plain values are treated as Ideal in the first ConstraintSet.
// Plain values are treated as Exact in subsequent ConstraintSets.
// Infinity = UINT32_MAX e.g. device cannot satisfy accumulated ConstraintSets.
// A finite result may be used to calculate this device's ranking as a choice.
uint32_t MediaEngineWebRTCAudioSource::GetBestFitnessDistance(
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets,
const nsString& aDeviceId)
{
uint32_t distance = 0;
for (const MediaTrackConstraintSet* cs : aConstraintSets) {
distance = GetMinimumFitnessDistance(*cs, false, aDeviceId);
break; // distance is read from first entry only
}
return distance;
}
nsresult
MediaEngineWebRTCAudioSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs)
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId)
{
if (mState == kReleased) {
if (mInitDone) {

View File

@ -208,14 +208,15 @@ MediaEngineWebRTCVideoSource::GetCapability(size_t aIndex,
nsresult
MediaEngineWebRTCVideoSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs)
const MediaEnginePrefs &aPrefs,
const nsString& aDeviceId)
{
LOG((__FUNCTION__));
if (mState == kReleased && mInitDone) {
// Note: if shared, we don't allow a later opener to affect the resolution.
// (This may change depending on spec changes for Constraints/settings)
if (!ChooseCapability(aConstraints, aPrefs)) {
if (!ChooseCapability(aConstraints, aPrefs, aDeviceId)) {
return NS_ERROR_UNEXPECTED;
}
if (mViECapture->AllocateCaptureDevice(GetUUID().get(),

View File

@ -81,4 +81,130 @@ FlattenedConstraints::FlattenedConstraints(const dom::MediaTrackConstraints& aOt
}
}
// MediaEngine helper
//
// The full algorithm for all devices. Sources that don't list capabilities
// need to fake it and hardcode some by populating mHardcodedCapabilities above.
//
// Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX
// First, all devices have a minimum distance based on their deviceId.
// If you have no other constraints, use this one. Reused by all device types.
uint32_t
MediaConstraintsHelper::GetMinimumFitnessDistance(
const dom::MediaTrackConstraintSet &aConstraints,
bool aAdvanced,
const nsString& aDeviceId)
{
uint64_t distance =
uint64_t(FitnessDistance(aDeviceId, aConstraints.mDeviceId, aAdvanced));
// This function is modeled on MediaEngineCameraVideoSource::GetFitnessDistance
// and will make more sense once more audio constraints are added.
return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
}
template<class ValueType, class ConstrainRange>
/* static */ uint32_t
MediaConstraintsHelper::FitnessDistance(ValueType aN,
const ConstrainRange& aRange)
{
if ((aRange.mExact.WasPassed() && aRange.mExact.Value() != aN) ||
(aRange.mMin.WasPassed() && aRange.mMin.Value() > aN) ||
(aRange.mMax.WasPassed() && aRange.mMax.Value() < aN)) {
return UINT32_MAX;
}
if (!aRange.mIdeal.WasPassed() || aN == aRange.mIdeal.Value()) {
return 0;
}
return uint32_t(ValueType((std::abs(aN - aRange.mIdeal.Value()) * 1000) /
std::max(std::abs(aN), std::abs(aRange.mIdeal.Value()))));
}
// Binding code doesn't templatize well...
/*static*/ uint32_t
MediaConstraintsHelper::FitnessDistance(int32_t aN,
const OwningLongOrConstrainLongRange& aConstraint, bool aAdvanced)
{
if (aConstraint.IsLong()) {
ConstrainLongRange range;
(aAdvanced ? range.mExact : range.mIdeal).Construct(aConstraint.GetAsLong());
return FitnessDistance(aN, range);
} else {
return FitnessDistance(aN, aConstraint.GetAsConstrainLongRange());
}
}
/*static*/ uint32_t
MediaConstraintsHelper::FitnessDistance(double aN,
const OwningDoubleOrConstrainDoubleRange& aConstraint,
bool aAdvanced)
{
if (aConstraint.IsDouble()) {
ConstrainDoubleRange range;
(aAdvanced ? range.mExact : range.mIdeal).Construct(aConstraint.GetAsDouble());
return FitnessDistance(aN, range);
} else {
return FitnessDistance(aN, aConstraint.GetAsConstrainDoubleRange());
}
}
// Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX
/* static */ uint32_t
MediaConstraintsHelper::FitnessDistance(nsString aN,
const ConstrainDOMStringParameters& aParams)
{
struct Func
{
static bool
Contains(const OwningStringOrStringSequence& aStrings, nsString aN)
{
return aStrings.IsString() ? aStrings.GetAsString() == aN
: aStrings.GetAsStringSequence().Contains(aN);
}
};
if (aParams.mExact.WasPassed() && !Func::Contains(aParams.mExact.Value(), aN)) {
return UINT32_MAX;
}
if (aParams.mIdeal.WasPassed() && !Func::Contains(aParams.mIdeal.Value(), aN)) {
return 1000;
}
return 0;
}
/* static */ uint32_t
MediaConstraintsHelper::FitnessDistance(nsString aN,
const OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint,
bool aAdvanced)
{
if (aConstraint.IsString()) {
ConstrainDOMStringParameters params;
if (aAdvanced) {
params.mExact.Construct();
params.mExact.Value().SetAsString() = aConstraint.GetAsString();
} else {
params.mIdeal.Construct();
params.mIdeal.Value().SetAsString() = aConstraint.GetAsString();
}
return FitnessDistance(aN, params);
} else if (aConstraint.IsStringSequence()) {
ConstrainDOMStringParameters params;
if (aAdvanced) {
params.mExact.Construct();
params.mExact.Value().SetAsStringSequence() = aConstraint.GetAsStringSequence();
} else {
params.mIdeal.Construct();
params.mIdeal.Value().SetAsStringSequence() = aConstraint.GetAsStringSequence();
}
return FitnessDistance(aN, params);
} else {
return FitnessDistance(aN, aConstraint.GetAsConstrainDOMStringParameters());
}
}
}

View File

@ -83,6 +83,29 @@ struct FlattenedConstraints : public NormalizedConstraintSet
explicit FlattenedConstraints(const dom::MediaTrackConstraints& aOther);
};
// A helper class for MediaEngines
class MediaConstraintsHelper
{
protected:
template<class ValueType, class ConstrainRange>
static uint32_t FitnessDistance(ValueType aN, const ConstrainRange& aRange);
static uint32_t FitnessDistance(int32_t aN,
const dom::OwningLongOrConstrainLongRange& aConstraint, bool aAdvanced);
static uint32_t FitnessDistance(double aN,
const dom::OwningDoubleOrConstrainDoubleRange& aConstraint, bool aAdvanced);
static uint32_t FitnessDistance(nsString aN,
const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint,
bool aAdvanced);
static uint32_t FitnessDistance(nsString aN,
const dom::ConstrainDOMStringParameters& aParams);
static uint32_t
GetMinimumFitnessDistance(const dom::MediaTrackConstraintSet &aConstraints,
bool aAdvanced,
const nsString& aDeviceId);
};
}
#endif /* MEDIATRACKCONSTRAINTS_H_ */

View File

@ -17,13 +17,13 @@ dictionary MediaStreamConstraints {
(boolean or MediaTrackConstraints) audio = false;
(boolean or MediaTrackConstraints) video = false;
boolean picture = false; // Mozilla legacy
boolean fake = false; // For testing purpose. Generates frames of solid
// colors if video is enabled, and sound of 1Khz sine
// wave if audio is enabled.
boolean fakeTracks = false; // For testing purpose, works only if fake is
// enabled. Enable fakeTracks returns a stream
// with two extra empty video tracks and three
// extra empty audio tracks.
boolean fake; // For testing purpose. Generates frames of solid
// colors if video is enabled, and sound of 1Khz sine
// wave if audio is enabled.
boolean fakeTracks; // For testing purpose, works only if fake is
// enabled. Enable fakeTracks returns a stream
// with two extra empty video tracks and three
// extra empty audio tracks.
DOMString? peerIdentity = null;
};

View File

@ -15,7 +15,8 @@ enum SupportedVideoConstraints {
"frameRate",
"mediaSource",
"browserWindow",
"scrollWithPage"
"scrollWithPage",
"deviceId"
};
enum SupportedAudioConstraints {
@ -30,6 +31,7 @@ dictionary MediaTrackConstraintSet {
DOMString mediaSource = "camera";
long long browserWindow;
boolean scrollWithPage;
ConstrainDOMString deviceId;
};
typedef (long or ConstrainLongRange) ConstrainLong;