Bug 1401592: Add a config option to imitate the old setParameters behavior. r=jib,chutten,webidl,smaug

When the compat mode is used, we log warnings to the JS console instead of
throwing. We also add a bunch of Glean metrics for tracking how often we
see these warnings (or failures) in the field.

Differential Revision: https://phabricator.services.mozilla.com/D161523
This commit is contained in:
Byron Campen 2022-12-13 16:32:25 +00:00
Родитель 4d5c35000c
Коммит e1da52d829
24 изменённых файлов: 1877 добавлений и 53 удалений

Просмотреть файл

@ -477,6 +477,11 @@ nsresult PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
// Initialize the media object.
mForceProxy = ShouldForceProxy();
// We put this here, in case we later want to set this based on a non-standard
// param in RTCConfiguration.
mAllowOldSetParameters = Preferences::GetBool(
"media.peerconnection.allow_old_setParameters", false);
// setup the stun local addresses IPC async call
InitLocalAddrs();
@ -2079,6 +2084,12 @@ void PeerConnectionImpl::StampTimecard(const char* aEvent) {
STAMP_TIMECARD(mTimeCard, aEvent);
}
void PeerConnectionImpl::SendWarningToConsole(const nsCString& aWarning) {
nsAutoString msg = NS_ConvertASCIItoUTF16(aWarning);
nsContentUtils::ReportToConsoleByWindowID(msg, nsIScriptError::warningFlag,
"WebRTC"_ns, mWindow->WindowID());
}
nsresult PeerConnectionImpl::CalculateFingerprint(
const std::string& algorithm, std::vector<uint8_t>* fingerprint) const {
DtlsDigest digest(algorithm);
@ -2372,6 +2383,7 @@ nsresult PeerConnectionImpl::SetConfiguration(
// Store the configuration for about:webrtc
StoreConfigurationForAboutWebrtc(aConfiguration);
return NS_OK;
}

Просмотреть файл

@ -519,6 +519,10 @@ class PeerConnectionImpl final
return NS_ConvertUTF8toUTF16(result.c_str());
}
bool ShouldAllowOldSetParameters() const { return mAllowOldSetParameters; }
void SendWarningToConsole(const nsCString& aWarning);
private:
virtual ~PeerConnectionImpl();
PeerConnectionImpl(const PeerConnectionImpl& rhs);
@ -821,6 +825,9 @@ class PeerConnectionImpl final
// Used to store the mDNS hostnames that we have registered
std::set<std::string> mRegisteredMDNSHostnames;
// web-compat stopgap
bool mAllowOldSetParameters = false;
// Used to store the mDNS hostnames that we have queried
struct PendingIceCandidate {
std::vector<std::string> mTokenizedCandidate;

Просмотреть файл

@ -6,6 +6,7 @@
#include "transport/logging.h"
#include "mozilla/dom/MediaStreamTrack.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/glean/GleanMetrics.h"
#include "transportbridge/MediaPipeline.h"
#include "nsPIDOMWindow.h"
#include "nsString.h"
@ -78,10 +79,18 @@ RTCRtpSender::RTCRtpSender(nsPIDOMWindowInner* aWindow, PeerConnectionImpl* aPc,
}
mPipeline->SetTrack(mSenderTrack);
mozilla::glean::rtcrtpsender::count.Add(1);
if (mPc->ShouldAllowOldSetParameters()) {
mAllowOldSetParameters = true;
mozilla::glean::rtcrtpsender::count_setparameters_compat.Add(1);
}
if (aEncodings.Length()) {
// This sender was created by addTransceiver with sendEncodings.
mParameters.mEncodings = aEncodings;
SetJsepRids(mParameters);
mozilla::glean::rtcrtpsender::used_sendencodings.AddToNumerator(1);
} else {
// This sender was created by addTrack, sRD(offer), or addTransceiver
// without sendEncodings.
@ -419,6 +428,16 @@ nsTArray<RefPtr<dom::RTCStatsPromise>> RTCRtpSender::GetStatsInternal() {
return promises;
}
void RTCRtpSender::WarnAboutBadSetParameters(const nsCString& aError) {
nsCString warning(
"WARNING! Invalid setParameters call detected! The good news? Firefox "
"supports sendEncodings in addTransceiver now, so we ask that you switch "
"over to using the parameters code you use for other browsers. Thank you "
"for your patience and support. The specific error was: ");
warning += aError;
mPc->SendWarningToConsole(warning);
}
already_AddRefed<Promise> RTCRtpSender::SetParameters(
const dom::RTCRtpSendParameters& aParameters, ErrorResult& aError) {
dom::RTCRtpSendParameters paramsCopy(aParameters);
@ -449,11 +468,47 @@ already_AddRefed<Promise> RTCRtpSender::SetParameters(
// If sender.[[LastReturnedParameters]] is null, return a promise rejected
// with a newly created InvalidStateError.
if (!mLastReturnedParameters.isSome()) {
p->MaybeRejectWithInvalidStateError(
nsCString error(
"Cannot call setParameters without first calling getParameters");
return p.forget();
if (mAllowOldSetParameters) {
if (!mHaveWarnedBecauseNoGetParameters) {
mHaveWarnedBecauseNoGetParameters = true;
mozilla::glean::rtcrtpsender_setparameters::warn_no_getparameters
.AddToNumerator(1);
}
WarnAboutBadSetParameters(error);
} else {
if (!mHaveFailedBecauseNoGetParameters) {
mHaveFailedBecauseNoGetParameters = true;
mozilla::glean::rtcrtpsender_setparameters::fail_no_getparameters
.AddToNumerator(1);
}
p->MaybeRejectWithInvalidStateError(error);
return p.forget();
}
}
// According to the spec, our consistency checking is based on
// [[LastReturnedParameters]], but if we're letting
// [[LastReturnedParameters]]==null slide, we still want to do
// consistency checking on _something_ so we can warn implementers if they
// are messing that up also. Just find something, _anything_, to do that
// checking with.
// TODO(bug 1803388): Remove this stuff once it is no longer needed.
// TODO(bug 1803389): Remove the glean errors once they are no longer needed.
Maybe<RTCRtpSendParameters> oldParams;
if (mAllowOldSetParameters) {
if (mPendingParameters.isSome()) {
oldParams = mPendingParameters;
} else {
oldParams = Some(mParameters);
}
MOZ_ASSERT(oldParams.isSome());
} else {
oldParams = mLastReturnedParameters;
}
MOZ_ASSERT(oldParams.isSome());
// Validate parameters by running the following steps:
// Let encodings be parameters.encodings.
// Let codecs be parameters.codecs.
@ -463,32 +518,104 @@ already_AddRefed<Promise> RTCRtpSender::SetParameters(
// return a promise rejected with a newly created InvalidModificationError:
// encodings.length is different from N.
if (paramsCopy.mEncodings.Length() !=
mLastReturnedParameters->mEncodings.Length()) {
p->MaybeRejectWithInvalidModificationError(
"Cannot change the number of encodings with setParameters");
return p.forget();
}
// encodings has been re-ordered.
for (size_t i = 0; i < paramsCopy.mEncodings.Length(); ++i) {
const auto& oldEncoding = mLastReturnedParameters->mEncodings[i];
const auto& newEncoding = paramsCopy.mEncodings[i];
if (oldEncoding.mRid != newEncoding.mRid) {
p->MaybeRejectWithInvalidModificationError(
"Cannot change rid, or reorder encodings");
if (paramsCopy.mEncodings.Length() != oldParams->mEncodings.Length()) {
nsCString error("Cannot change the number of encodings with setParameters");
if (!mAllowOldSetParameters) {
if (!mHaveFailedBecauseEncodingCountChange) {
mHaveFailedBecauseEncodingCountChange = true;
mozilla::glean::rtcrtpsender_setparameters::fail_length_changed
.AddToNumerator(1);
}
p->MaybeRejectWithInvalidModificationError(error);
return p.forget();
}
if (!mHaveWarnedBecauseEncodingCountChange) {
mHaveWarnedBecauseEncodingCountChange = true;
mozilla::glean::rtcrtpsender_setparameters::warn_length_changed
.AddToNumerator(1);
}
WarnAboutBadSetParameters(error);
} else {
// encodings has been re-ordered.
for (size_t i = 0; i < paramsCopy.mEncodings.Length(); ++i) {
const auto& oldEncoding = oldParams->mEncodings[i];
const auto& newEncoding = paramsCopy.mEncodings[i];
if (oldEncoding.mRid != newEncoding.mRid) {
nsCString error("Cannot change rid, or reorder encodings");
if (!mAllowOldSetParameters) {
if (!mHaveFailedBecauseRidChange) {
mHaveFailedBecauseRidChange = true;
mozilla::glean::rtcrtpsender_setparameters::fail_rid_changed
.AddToNumerator(1);
}
p->MaybeRejectWithInvalidModificationError(error);
return p.forget();
}
if (!mHaveWarnedBecauseRidChange) {
mHaveWarnedBecauseRidChange = true;
mozilla::glean::rtcrtpsender_setparameters::warn_rid_changed
.AddToNumerator(1);
}
WarnAboutBadSetParameters(error);
}
}
}
// Any parameter in parameters is marked as a Read-only parameter (such as
// RID) and has a value that is different from the corresponding parameter
// value in sender.[[LastReturnedParameters]]. Note that this also applies to
// transactionId.
if (mLastReturnedParameters->mTransactionId != paramsCopy.mTransactionId) {
p->MaybeRejectWithInvalidModificationError(
// TODO(bug 1803388): Handle this in webidl, once we stop allowing the old
// setParameters style.
if (!paramsCopy.mTransactionId.WasPassed()) {
nsCString error("transactionId is not set!");
if (!mAllowOldSetParameters) {
if (!mHaveFailedBecauseNoTransactionId) {
mHaveFailedBecauseNoTransactionId = true;
mozilla::glean::rtcrtpsender_setparameters::fail_no_transactionid
.AddToNumerator(1);
}
p->MaybeRejectWithTypeError(error);
return p.forget();
}
if (!mHaveWarnedBecauseNoTransactionId) {
mHaveWarnedBecauseNoTransactionId = true;
mozilla::glean::rtcrtpsender_setparameters::warn_no_transactionid
.AddToNumerator(1);
}
WarnAboutBadSetParameters(error);
} else if (oldParams->mTransactionId != paramsCopy.mTransactionId) {
// Any parameter in parameters is marked as a Read-only parameter (such as
// RID) and has a value that is different from the corresponding parameter
// value in sender.[[LastReturnedParameters]]. Note that this also applies
// to transactionId.
nsCString error(
"Cannot change transaction id: call getParameters, modify the result, "
"and then call setParameters");
if (!mAllowOldSetParameters) {
if (!mHaveFailedBecauseStaleTransactionId) {
mHaveFailedBecauseStaleTransactionId = true;
mozilla::glean::rtcrtpsender_setparameters::fail_stale_transactionid
.AddToNumerator(1);
}
p->MaybeRejectWithInvalidModificationError(error);
return p.forget();
}
if (!mHaveWarnedBecauseStaleTransactionId) {
mHaveWarnedBecauseStaleTransactionId = true;
mozilla::glean::rtcrtpsender_setparameters::warn_stale_transactionid
.AddToNumerator(1);
}
WarnAboutBadSetParameters(error);
}
// This could conceivably happen if we are allowing the old setParameters
// behavior.
if (!paramsCopy.mEncodings.Length()) {
if (!mHaveFailedBecauseNoEncodings) {
mHaveFailedBecauseNoEncodings = true;
mozilla::glean::rtcrtpsender_setparameters::fail_no_encodings
.AddToNumerator(1);
}
p->MaybeRejectWithInvalidModificationError(
"Cannot set an empty encodings array");
return p.forget();
}
@ -512,6 +639,10 @@ already_AddRefed<Promise> RTCRtpSender::SetParameters(
ErrorResult rv;
CheckAndRectifyEncodings(paramsCopy.mEncodings, mTransceiver->IsVideo(), rv);
if (rv.Failed()) {
if (!mHaveFailedBecauseOtherError) {
mHaveFailedBecauseOtherError = true;
mozilla::glean::rtcrtpsender_setparameters::fail_other.AddToNumerator(1);
}
p->MaybeReject(std::move(rv));
return p.forget();
}
@ -541,6 +672,10 @@ already_AddRefed<Promise> RTCRtpSender::SetParameters(
uint32_t serialNumber = ++mNumSetParametersCalls;
MaybeUpdateConduit();
if (mAllowOldSetParameters) {
SetJsepRids(paramsCopy);
}
// If the media stack is successfully configured with parameters,
// queue a task to run the following steps:
GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
@ -676,7 +811,7 @@ void RTCRtpSender::GetParameters(RTCRtpSendParameters& aParameters) {
// Let result be a new RTCRtpSendParameters dictionary constructed as follows:
// transactionId is set to a new unique identifier
aParameters.mTransactionId = mPc->GenerateUUID();
aParameters.mTransactionId.Construct(mPc->GenerateUUID());
// encodings is set to the value of the [[SendEncodings]] internal slot.
aParameters.mEncodings = mParameters.mEncodings;
@ -693,9 +828,11 @@ void RTCRtpSender::GetParameters(RTCRtpSendParameters& aParameters) {
// rtcp.reducedSize is set to true if reduced-size RTCP has been negotiated
// for sending, and false otherwise.
// TODO(bug 1765852): We do not support this yet
// aParameters.mRtcp.Construct();
aParameters.mRtcp.mCname.Construct();
aParameters.mRtcp.mReducedSize.Construct(false);
aParameters.mRtcp.Construct();
aParameters.mRtcp.Value().mCname.Construct();
aParameters.mRtcp.Value().mReducedSize.Construct(false);
aParameters.mHeaderExtensions.Construct();
aParameters.mCodecs.Construct();
// Set sender.[[LastReturnedParameters]] to result.
mLastReturnedParameters = Some(aParameters);
@ -786,8 +923,7 @@ Sequence<RTCRtpEncodingParameters> RTCRtpSender::ToSendEncodings(
void RTCRtpSender::MaybeGetJsepRids() {
MOZ_ASSERT(!mSimulcastEnvelopeSet);
MOZ_ASSERT(mParameters.mEncodings.Length() == 1);
MOZ_ASSERT(!mParameters.mEncodings[0].mRid.WasPassed());
MOZ_ASSERT(mParameters.mEncodings.Length());
auto jsepRids = GetJsepTransceiver().mSendTrack.GetRids();
if (!jsepRids.empty()) {
@ -796,7 +932,6 @@ void RTCRtpSender::MaybeGetJsepRids() {
// JSEP is using at least one rid. Stomp our single ridless encoding
mParameters.mEncodings = ToSendEncodings(jsepRids);
}
GetJsepTransceiver().mSendTrack.SetMaxEncodings(jsepRids.size());
mSimulcastEnvelopeSet = true;
}
}
@ -805,7 +940,27 @@ Sequence<RTCRtpEncodingParameters> RTCRtpSender::GetMatchingEncodings(
const std::vector<std::string>& aRids) const {
Sequence<RTCRtpEncodingParameters> result;
if (aRids.empty() || (aRids.size() == 1 && aRids[0].empty())) {
if (!aRids.empty() && !aRids[0].empty()) {
// Simulcast, or unicast with rid
for (const auto& encoding : mParameters.mEncodings) {
for (const auto& rid : aRids) {
auto utf16Rid = NS_ConvertUTF8toUTF16(rid.c_str());
if (!encoding.mRid.WasPassed() || (utf16Rid == encoding.mRid.Value())) {
auto encodingCopy(encoding);
if (!encodingCopy.mRid.WasPassed()) {
encodingCopy.mRid.Construct(NS_ConvertUTF8toUTF16(rid.c_str()));
}
Unused << result.AppendElement(encodingCopy, fallible);
break;
}
}
}
}
// If we're allowing the old setParameters behavior, we _might_ be able to
// get into this situation even if there were rids above. Be extra careful.
// Under normal circumstances, this just handles the ridless case.
if (!result.Length()) {
// Unicast with no specified rid. Restore mUnicastEncoding, if
// it exists, otherwise pick the first encoding.
if (mUnicastEncoding.isSome()) {
@ -813,22 +968,6 @@ Sequence<RTCRtpEncodingParameters> RTCRtpSender::GetMatchingEncodings(
} else {
Unused << result.AppendElement(mParameters.mEncodings[0], fallible);
}
return result;
}
for (const auto& encoding : mParameters.mEncodings) {
for (const auto& rid : aRids) {
MOZ_ASSERT(!rid.empty());
auto utf16Rid = NS_ConvertUTF8toUTF16(rid.c_str());
if (!encoding.mRid.WasPassed() || (utf16Rid == encoding.mRid.Value())) {
auto encodingCopy(encoding);
if (!encodingCopy.mRid.WasPassed()) {
encodingCopy.mRid.Construct(NS_ConvertUTF8toUTF16(rid.c_str()));
}
Unused << result.AppendElement(encodingCopy, fallible);
break;
}
}
}
return result;

Просмотреть файл

@ -147,6 +147,8 @@ class RTCRtpSender : public nsISupports, public nsWrapperCache {
const std::vector<std::string>& aRids) const;
void MaybeGetJsepRids();
void WarnAboutBadSetParameters(const nsCString& aError);
nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<PeerConnectionImpl> mPc;
RefPtr<dom::MediaStreamTrack> mSenderTrack;
@ -163,6 +165,23 @@ class RTCRtpSender : public nsISupports, public nsWrapperCache {
RefPtr<RTCRtpTransceiver> mTransceiver;
nsTArray<RefPtr<DOMMediaStream>> mStreams;
bool mHaveSetupTransport = false;
// TODO(bug 1803388): Remove this stuff once it is no longer needed.
bool mAllowOldSetParameters = false;
// TODO(bug 1803388): Remove the glean warnings once they are no longer needed
bool mHaveWarnedBecauseNoGetParameters = false;
bool mHaveWarnedBecauseEncodingCountChange = false;
bool mHaveWarnedBecauseRidChange = false;
bool mHaveWarnedBecauseNoTransactionId = false;
bool mHaveWarnedBecauseStaleTransactionId = false;
// TODO(bug 1803389): Remove the glean errors once they are no longer needed.
bool mHaveFailedBecauseNoGetParameters = false;
bool mHaveFailedBecauseEncodingCountChange = false;
bool mHaveFailedBecauseRidChange = false;
bool mHaveFailedBecauseNoTransactionId = false;
bool mHaveFailedBecauseStaleTransactionId = false;
bool mHaveFailedBecauseNoEncodings = false;
bool mHaveFailedBecauseOtherError = false;
RefPtr<dom::RTCDTMFSender> mDtmf;

Просмотреть файл

@ -109,8 +109,9 @@ void JsepTrack::AddToAnswer(const SdpMediaSection& offer,
void JsepTrack::SetRids(const std::vector<std::string>& aRids) {
MOZ_ASSERT(!aRids.empty());
MOZ_ASSERT(mRids.empty());
MOZ_ASSERT(aRids.size() <= mMaxEncodings);
if (!mRids.empty()) {
return;
}
mRids = aRids;
}

Просмотреть файл

@ -0,0 +1,264 @@
# 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/.
# Adding a new metric? We have docs for that!
# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html
---
$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
$tags:
- 'Core :: WebRTC'
rtcrtpsender:
count:
type: counter
description: >
The number of RTCRtpSenders created.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
count_setparameters_compat:
type: counter
description: >
The number of RTCRtpSenders created that use the compatibility mode for
setParameters.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
used_sendencodings:
type: rate
description: >
The proportion of RTCRtpSenders that were created by an addTransceivers
call that was passed a sendEncodings.
denominator_metric: rtcrtpsender.count
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
rtcrtpsender.setparameters:
warn_no_getparameters:
type: rate
description: >
The proportion of RTCRtpSenders configured with the setParameters compat
mode that have warned at least once about a setParameters call because
[[LastReturnedParameters]] was not set. (ie; there was not a recent
enough call to getParameters)
denominator_metric: rtcrtpsender.count_setparameters_compat
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
warn_length_changed:
type: rate
description: >
The proportion of RTCRtpSenders configured with the setParameters compat
mode that have warned at least once about a setParameters call that
attempted to change the number of encodings.
denominator_metric: rtcrtpsender.count_setparameters_compat
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
warn_rid_changed:
type: rate
description: >
The proportion of RTCRtpSenders configured with the setParameters compat
mode that have warned at least once about a setParameters call that
attempted to change the rid on an encoding (note that we only check this
if the encoding count did not change, see warn_length_changed).
denominator_metric: rtcrtpsender.count_setparameters_compat
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
warn_no_transactionid:
type: rate
description: >
The proportion of RTCRtpSenders configured with the setParameters compat
mode that have warned at least once about a setParameters call that did
not set the transactionId field.
denominator_metric: rtcrtpsender.count_setparameters_compat
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
warn_stale_transactionid:
type: rate
description: >
The proportion of RTCRtpSenders configured with the setParameters compat
mode that have warned at least once about a setParameters call that used
a stale transaction id.
denominator_metric: rtcrtpsender.count_setparameters_compat
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
fail_length_changed:
type: rate
description: >
The proportion of RTCRtpSenders that have thrown an error at least once
about a setParameters call that attempted to change the number of
encodings.
denominator_metric: rtcrtpsender.count
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
fail_rid_changed:
type: rate
description: >
The proportion of RTCRtpSenders that have thrown an error at least once
about a setParameters call that attempted to change the rid on an
encoding (note that we only check this if the encoding count did not
change, see fail_length_changed).
denominator_metric: rtcrtpsender.count
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
fail_no_getparameters:
type: rate
description: >
The proportion of RTCRtpSenders that have thrown an error at least once
about a setParameters call because [[LastReturnedParameters]] was not set.
(ie; there was not a recent enough call to getParameters)
denominator_metric: rtcrtpsender.count
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
fail_no_transactionid:
type: rate
description: >
The proportion of RTCRtpSenders that have thrown an error at least once
about a setParameters call that did not set the transactionId field.
denominator_metric: rtcrtpsender.count
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
fail_stale_transactionid:
type: rate
description: >
The proportion of RTCRtpSenders that have thrown an error at least once
about a setParameters call that used a stale transaction id.
denominator_metric: rtcrtpsender.count
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
fail_no_encodings:
type: rate
description: >
The proportion of RTCRtpSenders configured with the setParameters compat
mode that have thrown an error at least once about a setParameters call
that had no encodings (we do not measure this against the general
population of RTCRtpSenders, since without the compat mode this failure
is never observed, because it fails the length change check).
denominator_metric: rtcrtpsender.count_setparameters_compat
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116
fail_other:
type: rate
description: >
The proportion of RTCRtpSenders that have thrown an error at least once
about a setParameters call that had no encodings.
denominator_metric: rtcrtpsender.count
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1401592
data_sensitivity:
- technical
notification_emails:
- webrtc-telemetry-alerts@mozilla.com
expires: 116

Просмотреть файл

@ -991,6 +991,38 @@ const getTurnHostname = turnUrl => {
return hostAndMaybePort.split(":")[0];
};
// Yo dawg I heard you like Proxies
// Example: let value = await GleanTest.category.metric.testGetValue();
const GleanTest = new Proxy(
{},
{
get(target, categoryName, receiver) {
return new Proxy(
{},
{
get(target, metricName, receiver) {
return {
// The only API we actually implement right now.
async testGetValue() {
return SpecialPowers.spawnChrome(
[categoryName, metricName],
async (categoryName, metricName) => {
await Services.fog.testFlushAllChildren();
const window = this.browsingContext.topChromeWindow;
return window.Glean[categoryName][
metricName
].testGetValue();
}
);
},
};
},
}
);
},
}
);
/**
* This class executes a series of functions in a continuous sequence.
* Promise-bearing functions are executed after the previous promise completes.

Просмотреть файл

@ -280,3 +280,21 @@ scheme=http
fail-if = 1
[test_peerConnection_telephoneEventFirst.html]
[test_peerConnection_rtcp_rsize.html]
[test_peerConnection_scaleResolution_oldSetParameters.html]
[test_peerConnection_setParameters_maxFramerate_oldSetParameters.html]
[test_peerConnection_setParameters_oldSetParameters.html]
[test_peerConnection_setParameters_scaleResolutionDownBy_oldSetParameters.html]
skip-if = (os == 'win' && processor == 'aarch64') # aarch64 due to bug 1537567
[test_peerConnection_simulcastAnswer_lowResFirst_oldSetParameters.html]
skip-if = toolkit == 'android' # no simulcast support on android
[test_peerConnection_simulcastAnswer_oldSetParameters.html]
skip-if = toolkit == 'android' # no simulcast support on android
[test_peerConnection_simulcastOddResolution_oldSetParameters.html]
skip-if = toolkit == 'android' # no simulcast support on android
[test_peerConnection_simulcastOffer_lowResFirst_oldSetParameters.html]
skip-if = toolkit == 'android' # no simulcast support on android
[test_peerConnection_simulcastOffer_oldSetParameters.html]
skip-if = toolkit == 'android' # no simulcast support on android
[test_peerConnection_glean.html]

Просмотреть файл

@ -0,0 +1,370 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1401592",
title: "Test that glean is recording stats as expected",
visible: true
});
const tests = [
async function checkRTCRtpSenderCount() {
const pc = new RTCPeerConnection();
const oldCount = await GleanTest.rtcrtpsender.count.testGetValue() ?? 0;
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
const countDiff = await GleanTest.rtcrtpsender.count.testGetValue() - oldCount;
is(countDiff, 1, "Glean should have recorded the creation of a single RTCRtpSender");
},
async function checkRTCRtpSenderSetParametersCompatCount() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const oldCount = await GleanTest.rtcrtpsender.countSetparametersCompat.testGetValue() ?? 0;
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
const countDiff = await GleanTest.rtcrtpsender.countSetparametersCompat.testGetValue() - oldCount;
is(countDiff, 1, "Glean should have recorded the creation of a single RTCRtpSender that uses the setParameters compat mode");
},
async function checkSendEncodings() {
const pc = new RTCPeerConnection();
const oldRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
const newRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
is(newRate.denominator, oldRate.denominator + 1, "Glean should have recorded the creation of a single RTCRtpSender");
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded the use of sendEncodings");
},
async function checkAddTransceiverNoSendEncodings() {
const pc = new RTCPeerConnection();
const oldRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
const {sender} = pc.addTransceiver('video');
const newRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
is(newRate.denominator, oldRate.denominator + 1, "Glean should have recorded the creation of a single RTCRtpSender");
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded a use of sendEncodings");
},
async function checkAddTrack() {
const pc = new RTCPeerConnection();
const oldRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
const stream = await navigator.mediaDevices.getUserMedia({video: true});
const sender = pc.addTrack(stream.getTracks()[0]);
const newRate = await GleanTest.rtcrtpsender.usedSendencodings.testGetValue();
is(newRate.denominator, oldRate.denominator + 1, "Glean should have recorded the creation of a single RTCRtpSender");
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded a use of sendEncodings");
},
async function checkBadSetParametersNoGetParametersWarning() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue();
await sender.setParameters({encodings: [{rid: "0"},{rid: "1"},{rid: "2"}]});
let newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning in setParameters due to lack of a getParameters call");
// Glean should only record the warning once per sender!
oldRate = newRate;
await sender.setParameters({encodings: [{rid: "0"},{rid: "1"},{rid: "2"}]});
newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoGetparameters.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning in setParameters due to lack of a getParameters call");
},
async function checkBadSetParametersLengthChangedWarning() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue();
let params = sender.getParameters();
params.encodings.pop();
await sender.setParameters(params);
let newRate = await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning due to a length change in encodings");
// Glean should only record the warning once per sender!
params = sender.getParameters();
params.encodings.pop();
oldRate = newRate;
await sender.setParameters(params);
newRate = await GleanTest.rtcrtpsenderSetparameters.warnLengthChanged.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning due to a length change in encodings");
},
async function checkBadSetParametersRidChangedWarning() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnRidChanged.testGetValue();
let params = sender.getParameters();
params.encodings[1].rid = "foo";
await sender.setParameters(params);
let newRate = await GleanTest.rtcrtpsenderSetparameters.warnRidChanged.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning due to a rid change in encodings");
// Glean should only record the warning once per sender!
params = sender.getParameters();
params.encodings[1].rid = "bar";
oldRate = newRate;
await sender.setParameters(params);
newRate = await GleanTest.rtcrtpsenderSetparameters.warnRidChanged.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning due to a rid change in encodings");
},
async function checkBadSetParametersNoTransactionIdWarning() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue();
await sender.setParameters({encodings: [{rid: "0"},{rid: "1"},{rid: "2"}]});
let newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning due to missing transactionId in setParameters");
// Glean should only record the warning once per sender!
oldRate = newRate;
await sender.setParameters({encodings: [{rid: "0"},{rid: "1"},{rid: "2"}]});
newRate = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning due to missing transactionId in setParameters");
},
async function checkBadSetParametersStaleTransactionIdWarning() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.warnStaleTransactionid.testGetValue();
let params = sender.getParameters();
// Cause transactionId to be stale
await pc.createOffer();
// ...but make sure there is a recent getParameters call
sender.getParameters();
await sender.setParameters(params);
let newRate = await GleanTest.rtcrtpsenderSetparameters.warnStaleTransactionid.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded a warning due to stale transactionId in setParameters");
// Glean should only record the warning once per sender!
oldRate = newRate;
params = sender.getParameters();
// Cause transactionId to be stale
await pc.createOffer();
// ...but make sure there is a recent getParameters call
sender.getParameters();
await sender.setParameters(params);
newRate = await GleanTest.rtcrtpsenderSetparameters.warnStaleTransactionid.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another warning due to stale transactionId in setParameters");
},
async function checkBadSetParametersLengthChangedError() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", false]);
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failLengthChanged.testGetValue();
let params = sender.getParameters();
params.encodings.pop();
try {
await sender.setParameters(params);
} catch(e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failLengthChanged.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to a length change in encodings");
// Glean should only record the error once per sender!
params = sender.getParameters();
params.encodings.pop();
oldRate = newRate;
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failLengthChanged.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to a length change in encodings");
},
async function checkBadSetParametersRidChangedError() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", false]);
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue();
let params = sender.getParameters();
params.encodings[1].rid = "foo";
try {
await sender.setParameters(params);
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to a rid change in encodings");
// Glean should only record the error once per sender!
params = sender.getParameters();
params.encodings[1].rid = "bar";
oldRate = newRate;
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failRidChanged.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to a rid change in encodings");
},
async function checkBadSetParametersNoGetParametersError() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", false]);
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failNoGetparameters.testGetValue();
try {
await sender.setParameters({encodings: [{rid: "0"},{rid: "1"},{rid: "2"}]});
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failNoGetparameters.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error in setParameters due to lack of a getParameters call");
// Glean should only record the error once per sender!
oldRate = newRate;
try {
await sender.setParameters({encodings: [{rid: "0"},{rid: "1"},{rid: "2"}]});
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failNoGetparameters.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error in setParameters due to lack of a getParameters call");
},
async function checkBadSetParametersStaleTransactionIdError() {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", false]);
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failStaleTransactionid.testGetValue();
let params = sender.getParameters();
// Cause transactionId to be stale
await pc.createOffer();
// ...but make sure there is a recent getParameters call
sender.getParameters();
try {
await sender.setParameters(params);
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failStaleTransactionid.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to stale transactionId in setParameters");
// Glean should only record the error once per sender!
oldRate = newRate;
params = sender.getParameters();
// Cause transactionId to be stale
await pc.createOffer();
// ...but make sure there is a recent getParameters call
sender.getParameters();
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failStaleTransactionid.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to stale transactionId in setParameters");
},
async function checkBadSetParametersNoEncodingsError() {
// If we do not allow the old setParameters, this will fail the length check
// instead.
await pushPrefs(
["media.peerconnection.allow_old_setParameters", true]);
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failNoEncodings.testGetValue();
let params = sender.getParameters();
params.encodings = [];
try {
await sender.setParameters(params);
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failNoEncodings.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to stale transactionId in setParameters");
// Glean should only record the error once per sender!
oldRate = newRate;
params = sender.getParameters();
params.encodings = [];
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failNoEncodings.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to stale transactionId in setParameters");
},
async function checkBadSetParametersOtherError() {
const pc = new RTCPeerConnection();
const {sender} = pc.addTransceiver('video', {
sendEncodings: [{rid: "0"},{rid: "1"},{rid: "2"}]
});
let oldRate = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue();
let params = sender.getParameters();
params.encodings[0].scaleResolutionDownBy = 0.5;
try {
await sender.setParameters(params);
} catch (e) {
}
let newRate = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue();
is(newRate.numerator, oldRate.numerator + 1, "Glean should have recorded an error due to some other failure");
// Glean should only record the error once per sender!
oldRate = newRate;
params = sender.getParameters();
params.encodings[0].scaleResolutionDownBy = 0.5;
try {
await sender.setParameters(params);
} catch (e) {
}
newRate = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue();
is(newRate.numerator, oldRate.numerator, "Glean should not have recorded another error due to some other failure");
},
];
runNetworkTest(async () => {
for (const test of tests) {
info(`Running test: ${test.name}`);
await test();
info(`Done running test: ${test.name}`);
}
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,101 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1244913",
title: "Scale resolution down on a PeerConnection",
visible: true
});
var mustRejectWith = (msg, reason, f) =>
f().then(() => ok(false, msg),
e => is(e.name, reason, msg));
async function testScale(codec) {
var pc1 = new RTCPeerConnection();
var pc2 = new RTCPeerConnection();
var add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
pc1.onicecandidate = e => add(pc2, e.candidate, generateErrorCallback());
pc2.onicecandidate = e => add(pc1, e.candidate, generateErrorCallback());
info("testing scaling with " + codec);
let stream = await navigator.mediaDevices.getUserMedia({ video: true });
var v1 = createMediaElement('video', 'v1');
var v2 = createMediaElement('video', 'v2');
var ontrackfired = new Promise(resolve => pc2.ontrack = e => resolve(e));
var v2loadedmetadata = new Promise(resolve => v2.onloadedmetadata = resolve);
is(v2.currentTime, 0, "v2.currentTime is zero at outset");
v1.srcObject = stream;
var sender = pc1.addTrack(stream.getVideoTracks()[0], stream);
const otherErrorStart = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue();
const noTransactionIdWarningStart = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue();
await mustRejectWith(
"Invalid scaleResolutionDownBy must reject", "RangeError",
() => sender.setParameters(
{ encodings:[{ scaleResolutionDownBy: 0.5 } ] })
);
const otherErrorEnd = await GleanTest.rtcrtpsenderSetparameters.failOther.testGetValue();
const noTransactionIdWarningEnd = await GleanTest.rtcrtpsenderSetparameters.warnNoTransactionid.testGetValue();
// Make sure Glean is recording these statistics
is(otherErrorEnd.denominator, otherErrorStart.denominator, "No new RTCRtpSenders were created during this time");
is(otherErrorEnd.numerator, otherErrorStart.numerator + 1, "RTCRtpSender.setParameters reported a failure via Glean");
is(noTransactionIdWarningEnd.denominator, noTransactionIdWarningStart.denominator, "No new RTCRtpSenders were created during this time");
is(noTransactionIdWarningEnd.numerator, noTransactionIdWarningStart.numerator + 1, "Glean should have recorded a warning due to missing transactionId");
await sender.setParameters({ encodings: [{ maxBitrate: 60000,
scaleResolutionDownBy: 2 }] });
let offer = await pc1.createOffer();
if (codec == "VP8") {
offer.sdp = sdputils.removeAllButPayloadType(offer.sdp, 126);
}
await pc1.setLocalDescription(offer);
await pc2.setRemoteDescription(pc1.localDescription);
let answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(pc2.localDescription);
let trackevent = await ontrackfired;
v2.srcObject = trackevent.streams[0];
await v2loadedmetadata;
await waitUntil(() => v2.currentTime > 0);
ok(v2.currentTime > 0, "v2.currentTime is moving (" + v2.currentTime + ")");
ok(v1.videoWidth > 0, "source width is positive");
ok(v1.videoHeight > 0, "source height is positive");
is(v2.videoWidth, v1.videoWidth / 2, "sink is half the width of source");
is(v2.videoHeight, v1.videoHeight / 2, "sink is half the height of source");
stream.getTracks().forEach(track => track.stop());
v1.srcObject = v2.srcObject = null;
pc1.close()
pc2.close()
}
runNetworkTest(async () => {
await matchPlatformH264CodecPrefs();
await pushPrefs(['media.peerconnection.video.lock_scaling', true]);
await testScale("VP8");
await testScale("H264");
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -157,6 +157,8 @@ const tests = [
];
runNetworkTest(async () => {
await pushPrefs(
["media.peerconnection.allow_old_setParameters", false]);
for (const test of tests) {
info(`Running test: ${test.name}`);
await test();

Просмотреть файл

@ -0,0 +1,60 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1611957",
title: "Live-updating maxFramerate"
});
let sender, receiver;
async function checkMaxFrameRate(rate) {
sender.setParameters({ encodings: [{ maxFramerate: rate }] });
await wait(2000);
const stats = Array.from((await receiver.getStats()).values());
const inboundRtp = stats.find(stat => stat.type == "inbound-rtp");
info(`inbound-rtp stats: ${JSON.stringify(inboundRtp)}`);
const fps = inboundRtp.framesPerSecond;
ok(fps <= (rate * 1.1) + 1, `fps is an appropriate value (${fps}) for rate (${rate})`);
}
runNetworkTest(async function (options) {
let test = new PeerConnectionTest(options);
test.setMediaConstraints([{video: true}], []);
test.chain.append([
function CHECK_PRECONDITIONS() {
is(test.pcLocal._pc.getSenders().length, 1,
"Should have 1 local sender");
is(test.pcRemote._pc.getReceivers().length, 1,
"Should have 1 remote receiver");
sender = test.pcLocal._pc.getSenders()[0];
receiver = test.pcRemote._pc.getReceivers()[0];
},
function PC_LOCAL_SET_MAX_FRAMERATE_2() {
return checkMaxFrameRate(2);
},
function PC_LOCAL_SET_MAX_FRAMERATE_4() {
return checkMaxFrameRate(4);
},
function PC_LOCAL_SET_MAX_FRAMERATE_15() {
return checkMaxFrameRate(15);
},
function PC_LOCAL_SET_MAX_FRAMERATE_8() {
return checkMaxFrameRate(8);
},
function PC_LOCAL_SET_MAX_FRAMERATE_1() {
return checkMaxFrameRate(1);
},
]);
await test.run();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,86 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1230184",
title: "Set parameters on sender",
visible: true
});
function parameterstest(pc) {
ok(pc.getSenders().length, "have senders");
var sender = pc.getSenders()[0];
var testParameters = (params, errorName, errorMsg) => {
info("Trying to set " + JSON.stringify(params));
var validateParameters = (a, b) => {
var validateEncoding = (a, b) => {
is(a.rid, b.rid, "same rid");
is(a.maxBitrate, b.maxBitrate, "same maxBitrate");
is(a.maxFramerate, b.maxFramerate, "same maxFramerate");
is(a.scaleResolutionDownBy, b.scaleResolutionDownBy,
"same scaleResolutionDownBy");
};
is(a.encodings.length, (b.encodings || []).length, "same encodings");
a.encodings.forEach((en, i) => validateEncoding(en, b.encodings[i]));
};
var before = JSON.stringify(sender.getParameters());
isnot(JSON.stringify(params), before, "starting condition");
var p = sender.setParameters(params)
.then(() => {
isnot(JSON.stringify(sender.getParameters()), before, "parameters changed");
validateParameters(sender.getParameters(), params);
is(null, errorName || null, "is success expected");
}, e => {
is(e.name, errorName, "correct error name");
is(e.message, errorMsg, "correct error message");
});
is(JSON.stringify(sender.getParameters()), before, "parameters not set yet");
return p;
};
return [
[{ encodings: [ { rid: "foo", maxBitrate: 40000, scaleResolutionDownBy: 2 },
{ rid: "bar", maxBitrate: 10000, scaleResolutionDownBy: 4 }]
}],
[{ encodings: [{ maxBitrate: 10000, scaleResolutionDownBy: 4 }]}],
[{ encodings: [{ maxFramerate: 0.0, scaleResolutionDownBy: 1 }]}],
[{ encodings: [{ maxFramerate: 30.5, scaleResolutionDownBy: 1 }]}],
[{ encodings: [{ maxFramerate: -1, scaleResolutionDownBy: 1 }]}, "RangeError", "maxFramerate must be non-negative"],
[{ encodings: [{ maxBitrate: 40000 },
{ rid: "bar", maxBitrate: 10000 }] }, "TypeError", "Missing rid"],
[{ encodings: [{ rid: "foo", maxBitrate: 40000 },
{ rid: "bar", maxBitrate: 10000 },
{ rid: "bar", maxBitrate: 20000 }] }, "TypeError", "Duplicate rid"],
[{}, "TypeError", `RTCRtpSender.setParameters: Missing required 'encodings' member of RTCRtpSendParameters.`]
].reduce((p, args) => p.then(() => testParameters.apply(this, args)),
Promise.resolve());
}
runNetworkTest(() => {
const test = new PeerConnectionTest();
test.setMediaConstraints([{video: true}], [{video: true}]);
test.chain.removeAfter("PC_REMOTE_WAIT_FOR_MEDIA_FLOW");
// Test sender parameters.
test.chain.append([
function PC_LOCAL_SET_PARAMETERS(test) {
return parameterstest(test.pcLocal._pc);
}
]);
return test.run();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,83 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1253499",
title: "Live-updating scaleResolutionDownBy"
});
let sender, localElem, remoteElem;
let originalWidth, originalHeight;
async function checkScaleDownBy(scale) {
sender.setParameters({ encodings: [{ scaleResolutionDownBy: scale }] });
await haveEvent(remoteElem, "resize", wait(5000, new Error("Timeout")));
// Find the expected resolution. Internally we pick the closest lower
// resolution with an identical aspect ratio.
let expectedWidth = Math.floor(originalWidth / scale);
let expectedHeight = Math.floor(originalHeight / scale);
is(remoteElem.videoWidth, expectedWidth,
`Width should have scaled down by ${scale}`);
is(remoteElem.videoHeight, expectedHeight,
`Height should have scaled down by ${scale}`);
}
runNetworkTest(async function (options) {
await pushPrefs(['media.peerconnection.video.lock_scaling', true]);
// [TODO] re-enable HW decoder after bug 1526207 is fixed.
if (navigator.userAgent.includes("Android")) {
await pushPrefs(["media.navigator.mediadatadecoder_vpx_enabled", false],
["media.webrtc.hw.h264.enabled", false]);
}
let test = new PeerConnectionTest(options);
test.setMediaConstraints([{video: true}], []);
test.chain.append([
function CHECK_PRECONDITIONS() {
is(test.pcLocal._pc.getSenders().length, 1,
"Should have 1 local sender");
is(test.pcLocal.localMediaElements.length, 1,
"Should have 1 local sending media element");
is(test.pcRemote.remoteMediaElements.length, 1,
"Should have 1 remote media element");
sender = test.pcLocal._pc.getSenders()[0];
localElem = test.pcLocal.localMediaElements[0];
remoteElem = test.pcRemote.remoteMediaElements[0];
remoteElem.addEventListener("resize", () =>
info(`Video resized to ${remoteElem.videoWidth}x${remoteElem.videoHeight}`));
originalWidth = localElem.videoWidth;
originalHeight = localElem.videoHeight;
info(`Original width is ${originalWidth}`);
},
function PC_LOCAL_SCALEDOWNBY_2() {
return checkScaleDownBy(2);
},
function PC_LOCAL_SCALEDOWNBY_4() {
return checkScaleDownBy(4);
},
function PC_LOCAL_SCALEDOWNBY_15() {
return checkScaleDownBy(15);
},
function PC_LOCAL_SCALEDOWNBY_8() {
return checkScaleDownBy(8);
},
function PC_LOCAL_SCALEDOWNBY_1() {
return checkScaleDownBy(1);
},
]);
await test.run();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,115 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
<script type="application/javascript" src="helpers_from_wpt/sdp.js"></script>
<script type="application/javascript" src="simulcast.js"></script>
<script type="application/javascript" src="stats.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1231507",
title: "Basic video-only peer connection with Simulcast answer, first rid has lowest resolution",
visible: true
});
runNetworkTest(async () => {
await pushPrefs(
['media.peerconnection.simulcast', true],
// 180Kbps was determined empirically, set well-higher than
// the 80Kbps+overhead needed for the two simulcast streams.
// 100Kbps was apparently too low.
['media.peerconnection.video.min_bitrate_estimate', 180*1000]);
const offerer = new RTCPeerConnection();
const answerer = new RTCPeerConnection();
const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
offerer.onicecandidate = e => add(answerer, e.candidate, generateErrorCallback());
answerer.onicecandidate = e => add(offerer, e.candidate, generateErrorCallback());
const metadataToBeLoaded = [];
offerer.ontrack = (e) => {
metadataToBeLoaded.push(getPlaybackWithLoadedMetadata(e.track));
};
// Two recv transceivers, one for each simulcast stream
offerer.addTransceiver('video', { direction: 'recvonly' });
offerer.addTransceiver('video', { direction: 'recvonly' });
// One send transceiver, that will be used to send both simulcast streams
const emitter = new VideoFrameEmitter();
const videoStream = emitter.stream();
answerer.addTrack(videoStream.getVideoTracks()[0], videoStream);
emitter.start();
const offer = await offerer.createOffer();
const mungedOffer = midToRid(offer);
info(`Transformed recv offer to simulcast: ${offer.sdp} to ${mungedOffer}`);
await answerer.setRemoteDescription({type: "offer", sdp: mungedOffer});
await offerer.setLocalDescription(offer);
const rids = offerer.getTransceivers().map(t => t.mid);
is(rids.length, 2, 'Should have 2 mids in offer');
ok(rids[0] != '', 'First mid should be non-empty');
ok(rids[1] != '', 'Second mid should be non-empty');
info(`rids: ${JSON.stringify(rids)}`);
const sender = answerer.getSenders()[0];
sender.setParameters({
encodings: [
{ rid: rids[0], maxBitrate: 40000, scaleResolutionDownBy: 2 },
{ rid: rids[1], maxBitrate: 40000 }
]
});
const answer = await answerer.createAnswer();
const mungedAnswer = ridToMid(answer);
info(`Transformed send simulcast answer to multiple m-sections: ${answer.sdp} to ${mungedAnswer}`);
await offerer.setRemoteDescription({type: "answer", sdp: mungedAnswer});
await answerer.setLocalDescription(answer);
is(metadataToBeLoaded.length, 2, 'Offerer should have gotten 2 ontrack events');
info('Waiting for 2 loadedmetadata events');
const videoElems = await Promise.all(metadataToBeLoaded);
const statsReady =
Promise.all([waitForSyncedRtcp(offerer), waitForSyncedRtcp(answerer)]);
const helper = new VideoStreamHelper();
info('Waiting for first video element to start playing');
await helper.checkVideoPlaying(videoElems[0]);
info('Waiting for second video element to start playing');
await helper.checkVideoPlaying(videoElems[1]);
is(videoElems[1].videoWidth, 50,
"sink is same width as source, modulo our cropping algorithm");
is(videoElems[1].videoHeight, 50,
"sink is same height as source, modulo our cropping algorithm");
is(videoElems[0].videoWidth, 25,
"sink is 1/2 width of source, modulo our cropping algorithm");
is(videoElems[0].videoHeight, 25,
"sink is 1/2 height of source, modulo our cropping algorithm");
await statsReady;
const senderStats = await sender.getStats();
checkSenderStats(senderStats, 2);
checkExpectedFields(senderStats);
pedanticChecks(senderStats);
emitter.stop();
videoStream.getVideoTracks()[0].stop();
offerer.close();
answerer.close();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,115 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
<script type="application/javascript" src="helpers_from_wpt/sdp.js"></script>
<script type="application/javascript" src="simulcast.js"></script>
<script type="application/javascript" src="stats.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1231507",
title: "Basic video-only peer connection with Simulcast answer",
visible: true
});
runNetworkTest(async () => {
await pushPrefs(
['media.peerconnection.simulcast', true],
// 180Kbps was determined empirically, set well-higher than
// the 80Kbps+overhead needed for the two simulcast streams.
// 100Kbps was apparently too low.
['media.peerconnection.video.min_bitrate_estimate', 180*1000]);
const offerer = new RTCPeerConnection();
const answerer = new RTCPeerConnection();
const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
offerer.onicecandidate = e => add(answerer, e.candidate, generateErrorCallback());
answerer.onicecandidate = e => add(offerer, e.candidate, generateErrorCallback());
const metadataToBeLoaded = [];
offerer.ontrack = (e) => {
metadataToBeLoaded.push(getPlaybackWithLoadedMetadata(e.track));
};
// Two recv transceivers, one for each simulcast stream
offerer.addTransceiver('video', { direction: 'recvonly' });
offerer.addTransceiver('video', { direction: 'recvonly' });
// One send transceiver, that will be used to send both simulcast streams
const emitter = new VideoFrameEmitter();
const videoStream = emitter.stream();
answerer.addTrack(videoStream.getVideoTracks()[0], videoStream);
emitter.start();
const offer = await offerer.createOffer();
const mungedOffer = midToRid(offer);
info(`Transformed recv offer to simulcast: ${offer.sdp} to ${mungedOffer}`);
await answerer.setRemoteDescription({type: "offer", sdp: mungedOffer});
await offerer.setLocalDescription(offer);
const rids = offerer.getTransceivers().map(t => t.mid);
is(rids.length, 2, 'Should have 2 mids in offer');
ok(rids[0] != '', 'First mid should be non-empty');
ok(rids[1] != '', 'Second mid should be non-empty');
info(`rids: ${JSON.stringify(rids)}`);
const sender = answerer.getSenders()[0];
await sender.setParameters({
encodings: [
{ rid: rids[0], maxBitrate: 40000 },
{ rid: rids[1], maxBitrate: 40000, scaleResolutionDownBy: 2 }
]
});
const answer = await answerer.createAnswer();
const mungedAnswer = ridToMid(answer);
info(`Transformed send simulcast answer to multiple m-sections: ${answer.sdp} to ${mungedAnswer}`);
await offerer.setRemoteDescription({type: "answer", sdp: mungedAnswer});
await answerer.setLocalDescription(answer);
is(metadataToBeLoaded.length, 2, 'Offerer should have gotten 2 ontrack events');
info('Waiting for 2 loadedmetadata events');
const videoElems = await Promise.all(metadataToBeLoaded);
const statsReady =
Promise.all([waitForSyncedRtcp(offerer), waitForSyncedRtcp(answerer)]);
const helper = new VideoStreamHelper();
info('Waiting for first video element to start playing');
await helper.checkVideoPlaying(videoElems[0]);
info('Waiting for second video element to start playing');
await helper.checkVideoPlaying(videoElems[1]);
is(videoElems[0].videoWidth, 50,
"sink is same width as source, modulo our cropping algorithm");
is(videoElems[0].videoHeight, 50,
"sink is same height as source, modulo our cropping algorithm");
is(videoElems[1].videoWidth, 25,
"sink is 1/2 width of source, modulo our cropping algorithm");
is(videoElems[1].videoHeight, 25,
"sink is 1/2 height of source, modulo our cropping algorithm");
await statsReady;
const senderStats = await sender.getStats();
checkSenderStats(senderStats, 2);
checkExpectedFields(senderStats);
pedanticChecks(senderStats);
emitter.stop();
videoStream.getVideoTracks()[0].stop();
offerer.close();
answerer.close();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,172 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
<script type="application/javascript" src="helpers_from_wpt/sdp.js"></script>
<script type="application/javascript" src="simulcast.js"></script>
<script type="application/javascript" src="stats.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1432793",
title: "Simulcast with odd resolution",
visible: true
});
runNetworkTest(async () => {
const helper = new VideoStreamHelper();
const emitter = new VideoFrameEmitter(helper.green, helper.red, 705, 528);
async function checkVideoElement(senderElement, receiverElement, encoding) {
info(`Waiting for receiver video element ${encoding.rid} to start playing`);
await helper.checkVideoPlaying(receiverElement);
const srcWidth = senderElement.videoWidth;
const srcHeight = senderElement.videoHeight;
info(`Source resolution is ${srcWidth}x${srcHeight}`);
const scaleDownBy = encoding.scaleResolutionDownBy;
const expectedWidth = srcWidth / scaleDownBy;
const expectedHeight = srcHeight / scaleDownBy;
const margin = srcWidth * 0.1;
const width = receiverElement.videoWidth;
const height = receiverElement.videoHeight;
const rid = encoding.rid;
ok(width >= expectedWidth - margin && width <= expectedWidth + margin,
`Width ${width} should be within 10% of ${expectedWidth} for rid '${rid}'`);
ok(height >= expectedHeight - margin && height <= expectedHeight + margin,
`Height ${height} should be within 10% of ${expectedHeight} for rid '${rid}'`);
}
async function checkVideoElements(senderElement, receiverElements, encodings) {
is(receiverElements.length, encodings.length, 'Number of video elements should match number of encodings');
info('Waiting for sender video element to start playing');
await helper.checkVideoPlaying(senderElement);
for (let i = 0; i < encodings.length; i++) {
await checkVideoElement(senderElement, receiverElements[i], encodings[i]);
}
}
async function checkSenderStats(sender) {
const senderStats = await sender.getStats();
checkSenderStats(senderStats, encodings.length);
checkExpectedFields(senderStats);
pedanticChecks(senderStats);
}
async function waitForResizeEvents(elements) {
return Promise.all(elements.map(elem => haveEvent(elem, 'resize')));
}
const encodings = [{ rid: "0", maxBitrate: 40000, scaleResolutionDownBy: 1.9 },
{ rid: "1", maxBitrate: 40000, scaleResolutionDownBy: 3.5 },
{ rid: "2", maxBitrate: 40000, scaleResolutionDownBy: 17.8 }];
await pushPrefs(
['media.peerconnection.simulcast', true],
// 180Kbps was determined empirically, set well-higher than
// the 80Kbps+overhead needed for the two simulcast streams.
// 100Kbps was apparently too low.
['media.peerconnection.video.min_bitrate_estimate', 180*1000]);
const offerer = new RTCPeerConnection();
const answerer = new RTCPeerConnection();
const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
offerer.onicecandidate = e => add(answerer, e.candidate, generateErrorCallback());
answerer.onicecandidate = e => add(offerer, e.candidate, generateErrorCallback());
const metadataToBeLoaded = [];
answerer.ontrack = (e) => {
metadataToBeLoaded.push(getPlaybackWithLoadedMetadata(e.track));
};
// One send transceiver, that will be used to send both simulcast streams
const videoStream = emitter.stream();
offerer.addTrack(videoStream.getVideoTracks()[0], videoStream);
const senderElement = document.createElement('video');
senderElement.autoplay = true;
senderElement.srcObject = videoStream;
senderElement.id = videoStream.id
const sender = offerer.getSenders()[0];
sender.setParameters({encodings});
const offer = await offerer.createOffer();
const mungedOffer = ridToMid(offer);
info(`Transformed send simulcast offer to multiple m-sections: ${offer.sdp} to ${mungedOffer}`);
await answerer.setRemoteDescription({type: "offer", sdp: mungedOffer});
await offerer.setLocalDescription(offer);
const rids = answerer.getTransceivers().map(t => t.mid);
is(rids.length, 3, 'Should have 3 mids in offer');
ok(rids[0], 'First mid should be non-empty');
ok(rids[1], 'Second mid should be non-empty');
ok(rids[2], 'Third mid should be non-empty');
info(`rids: ${JSON.stringify(rids)}`);
const answer = await answerer.createAnswer();
const mungedAnswer = midToRid(answer);
info(`Transformed recv answer to simulcast: ${answer.sdp} to ${mungedAnswer}`);
await offerer.setRemoteDescription({type: "answer", sdp: mungedAnswer});
await answerer.setLocalDescription(answer);
is(metadataToBeLoaded.length, 3, 'Offerer should have gotten 3 ontrack events');
emitter.start();
info('Waiting for 3 loadedmetadata events');
const videoElems = await Promise.all(metadataToBeLoaded);
await checkVideoElements(senderElement, videoElems, encodings);
emitter.stop();
await Promise.all([waitForSyncedRtcp(offerer), waitForSyncedRtcp(answerer)]);
info(`Changing source resolution to 1280x720`);
emitter.size(1280, 720);
emitter.start();
await waitForResizeEvents([senderElement, ...videoElems]);
await checkVideoElements(senderElement, videoElems, encodings);
await checkSenderStats(sender);
encodings[0].scaleResolutionDownBy = 1;
encodings[1].scaleResolutionDownBy = 2;
encodings[2].scaleResolutionDownBy = 3;
info(`Changing encodings to ${JSON.stringify(encodings)}`);
await sender.setParameters({encodings});
await waitForResizeEvents(videoElems);
await checkVideoElements(senderElement, videoElems, encodings);
await checkSenderStats(sender);
encodings[0].scaleResolutionDownBy = 6;
encodings[1].scaleResolutionDownBy = 5;
encodings[2].scaleResolutionDownBy = 4;
info(`Changing encodings to ${JSON.stringify(encodings)}`);
await sender.setParameters({encodings});
await waitForResizeEvents(videoElems);
await checkVideoElements(senderElement, videoElems, encodings);
await checkSenderStats(sender);
encodings[0].scaleResolutionDownBy = 4;
encodings[1].scaleResolutionDownBy = 1;
encodings[2].scaleResolutionDownBy = 2;
info(`Changing encodings to ${JSON.stringify(encodings)}`);
await sender.setParameters({encodings});
await waitForResizeEvents(videoElems);
await checkVideoElements(senderElement, videoElems, encodings);
await checkSenderStats(sender);
emitter.stop();
videoStream.getVideoTracks()[0].stop();
offerer.close();
answerer.close();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,112 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="parser_rtp.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
<script type="application/javascript" src="helpers_from_wpt/sdp.js"></script>
<script type="application/javascript" src="simulcast.js"></script>
<script type="application/javascript" src="stats.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1231507",
title: "Basic video-only peer connection with Simulcast offer, first rid has lowest resolution",
visible: true
});
runNetworkTest(async () => {
await pushPrefs(
['media.peerconnection.simulcast', true],
// 180Kbps was determined empirically, set well-higher than
// the 80Kbps+overhead needed for the two simulcast streams.
// 100Kbps was apparently too low.
['media.peerconnection.video.min_bitrate_estimate', 180*1000]);
const offerer = new RTCPeerConnection();
const answerer = new RTCPeerConnection();
const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
offerer.onicecandidate = e => add(answerer, e.candidate, generateErrorCallback());
answerer.onicecandidate = e => add(offerer, e.candidate, generateErrorCallback());
const metadataToBeLoaded = [];
answerer.ontrack = (e) => {
metadataToBeLoaded.push(getPlaybackWithLoadedMetadata(e.track));
};
// One send transceiver, that will be used to send both simulcast streams
const emitter = new VideoFrameEmitter();
const videoStream = emitter.stream();
offerer.addTrack(videoStream.getVideoTracks()[0], videoStream);
emitter.start();
const sender = offerer.getSenders()[0];
sender.setParameters({
encodings: [
{ rid: '0', maxBitrate: 40000, scaleResolutionDownBy: 2 },
{ rid: '1', maxBitrate: 40000 }
]
});
const offer = await offerer.createOffer();
const mungedOffer = ridToMid(offer);
info(`Transformed send simulcast offer to multiple m-sections: ${offer.sdp} to ${mungedOffer}`);
await answerer.setRemoteDescription({type: 'offer', sdp: mungedOffer});
await offerer.setLocalDescription(offer);
const rids = answerer.getTransceivers().map(t => t.mid);
is(rids.length, 2, 'Should have 2 mids in offer');
ok(rids[0] != '', 'First mid should be non-empty');
ok(rids[1] != '', 'Second mid should be non-empty');
info(`rids: ${JSON.stringify(rids)}`);
const answer = await answerer.createAnswer();
const mungedAnswer = midToRid(answer);
info(`Transformed recv answer to simulcast: ${answer.sdp} to ${mungedAnswer}`);
await offerer.setRemoteDescription({type: 'answer', sdp: mungedAnswer});
await answerer.setLocalDescription(answer);
is(metadataToBeLoaded.length, 2, 'Offerer should have gotten 2 ontrack events');
info('Waiting for 2 loadedmetadata events');
const videoElems = await Promise.all(metadataToBeLoaded);
const statsReady =
Promise.all([waitForSyncedRtcp(offerer), waitForSyncedRtcp(answerer)]);
const helper = new VideoStreamHelper();
info('Waiting for first video element to start playing');
await helper.checkVideoPlaying(videoElems[0]);
info('Waiting for second video element to start playing');
await helper.checkVideoPlaying(videoElems[1]);
is(videoElems[1].videoWidth, 50,
"sink is same width as source, modulo our cropping algorithm");
is(videoElems[1].videoHeight, 50,
"sink is same height as source, modulo our cropping algorithm");
is(videoElems[0].videoWidth, 25,
"sink is 1/2 width of source, modulo our cropping algorithm");
is(videoElems[0].videoHeight, 25,
"sink is 1/2 height of source, modulo our cropping algorithm");
await statsReady;
const senderStats = await sender.getStats();
checkSenderStats(senderStats, 2);
checkExpectedFields(senderStats);
pedanticChecks(senderStats);
emitter.stop();
videoStream.getVideoTracks()[0].stop();
offerer.close();
answerer.close();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,112 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
<script type="application/javascript" src="parser_rtp.js"></script>
<script type="application/javascript" src="/tests/dom/canvas/test/captureStream_common.js"></script>
<script type="application/javascript" src="helpers_from_wpt/sdp.js"></script>
<script type="application/javascript" src="simulcast.js"></script>
<script type="application/javascript" src="stats.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1231507",
title: "Basic video-only peer connection with Simulcast offer",
visible: true
});
runNetworkTest(async () => {
await pushPrefs(
['media.peerconnection.simulcast', true],
// 180Kbps was determined empirically, set well-higher than
// the 80Kbps+overhead needed for the two simulcast streams.
// 100Kbps was apparently too low.
['media.peerconnection.video.min_bitrate_estimate', 180*1000]);
const offerer = new RTCPeerConnection();
const answerer = new RTCPeerConnection();
const add = (pc, can, failed) => can && pc.addIceCandidate(can).catch(failed);
offerer.onicecandidate = e => add(answerer, e.candidate, generateErrorCallback());
answerer.onicecandidate = e => add(offerer, e.candidate, generateErrorCallback());
const metadataToBeLoaded = [];
answerer.ontrack = (e) => {
metadataToBeLoaded.push(getPlaybackWithLoadedMetadata(e.track));
};
// One send transceiver, that will be used to send both simulcast streams
const emitter = new VideoFrameEmitter();
const videoStream = emitter.stream();
offerer.addTrack(videoStream.getVideoTracks()[0], videoStream);
emitter.start();
const sender = offerer.getSenders()[0];
sender.setParameters({
encodings: [
{ rid: '0', maxBitrate: 40000 },
{ rid: '1', maxBitrate: 40000, scaleResolutionDownBy: 2 }
]
});
const offer = await offerer.createOffer();
const mungedOffer = ridToMid(offer);
info(`Transformed send simulcast offer to multiple m-sections: ${offer.sdp} to ${mungedOffer}`);
await answerer.setRemoteDescription({type: "offer", sdp: mungedOffer});
await offerer.setLocalDescription(offer);
const rids = answerer.getTransceivers().map(t => t.mid);
is(rids.length, 2, 'Should have 2 mids in offer');
ok(rids[0] != '', 'First mid should be non-empty');
ok(rids[1] != '', 'Second mid should be non-empty');
info(`rids: ${JSON.stringify(rids)}`);
const answer = await answerer.createAnswer();
const mungedAnswer = midToRid(answer);
info(`Transformed recv answer to simulcast: ${answer.sdp} to ${mungedAnswer}`);
await offerer.setRemoteDescription({type: "answer", sdp: mungedAnswer});
await answerer.setLocalDescription(answer);
is(metadataToBeLoaded.length, 2, 'Offerer should have gotten 2 ontrack events');
info('Waiting for 2 loadedmetadata events');
const videoElems = await Promise.all(metadataToBeLoaded);
const statsReady =
Promise.all([waitForSyncedRtcp(offerer), waitForSyncedRtcp(answerer)]);
const helper = new VideoStreamHelper();
info('Waiting for first video element to start playing');
await helper.checkVideoPlaying(videoElems[0]);
info('Waiting for second video element to start playing');
await helper.checkVideoPlaying(videoElems[1]);
is(videoElems[0].videoWidth, 50,
"sink is same width as source, modulo our cropping algorithm");
is(videoElems[0].videoHeight, 50,
"sink is same height as source, modulo our cropping algorithm");
is(videoElems[1].videoWidth, 25,
"sink is 1/2 width of source, modulo our cropping algorithm");
is(videoElems[1].videoHeight, 25,
"sink is 1/2 height of source, modulo our cropping algorithm");
await statsReady;
const senderStats = await sender.getStats();
checkSenderStats(senderStats, 2);
checkExpectedFields(senderStats);
pedanticChecks(senderStats);
emitter.stop();
videoStream.getVideoTracks()[0].stop();
offerer.close();
answerer.close();
});
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -65,12 +65,12 @@ dictionary RTCRtpParameters {
// We do not support these, but every wpt test involving parameters insists
// that these be present, regardless of whether the test-case has anything to
// do with these in particular (see validateRtpParameters).
required sequence<RTCRtpHeaderExtensionParameters> headerExtensions;
required RTCRtcpParameters rtcp;
required sequence<RTCRtpCodecParameters> codecs;
sequence<RTCRtpHeaderExtensionParameters> headerExtensions;
RTCRtcpParameters rtcp;
sequence<RTCRtpCodecParameters> codecs;
};
dictionary RTCRtpSendParameters : RTCRtpParameters {
required DOMString transactionId;
DOMString transactionId;
required sequence<RTCRtpEncodingParameters> encodings;
};

Просмотреть файл

@ -353,6 +353,7 @@ pref("media.videocontrols.keyboard-tab-to-all-controls", true);
pref("media.navigator.permission.disabled", false);
pref("media.navigator.streams.fake", false);
pref("media.peerconnection.default_iceservers", "[]");
pref("media.peerconnection.allow_old_setParameters", true);
pref("media.peerconnection.ice.loopback", false); // Set only for testing in offline environments.
pref("media.peerconnection.ice.tcp", true);
pref("media.peerconnection.ice.tcp_so_sock_count", 0); // Disable SO gathering

Просмотреть файл

@ -59,6 +59,8 @@ function androidStartup() {
windowTracker.init();
}
Services.fog.initializeFOG();
}
// ///// Desktop ///////

Просмотреть файл

@ -1,3 +1,3 @@
prefs: [media.navigator.permission.disabled:true, media.navigator.streams.fake:true, privacy.resistFingerprinting.reduceTimerPrecision.jitter:false, privacy.reduceTimerPrecision:false, media.peerconnection.ice.trickle_grace_period:10000, media.peerconnection.ice.obfuscate_host_addresses:false]
prefs: [media.navigator.permission.disabled:true, media.navigator.streams.fake:true, privacy.resistFingerprinting.reduceTimerPrecision.jitter:false, privacy.reduceTimerPrecision:false, media.peerconnection.ice.trickle_grace_period:10000, media.peerconnection.ice.obfuscate_host_addresses:false, media.peerconnection.allow_old_setParameters:false]
lsan-allowed: [Alloc, MakeAndAddRef, Malloc, NS_NewDOMDataChannel, NS_NewRunnableFunction, PR_Realloc, ParentContentActorCreateFunc, allocate, mozilla::DataChannelConnection::Create, mozilla::DataChannelConnection::HandleOpenRequestMessage, mozilla::DataChannelConnection::Open, mozilla::MediaPacket::Copy, mozilla::MediaPipeline::MediaPipeline, mozilla::WeakPtr, mozilla::dom::DocGroup::Create, mozilla::dom::DocGroup::DocGroup, mozilla::runnable_args_func, nsRefPtrDeque, sctp_add_vtag_to_timewait, sctp_hashinit_flags]
leak-threshold: [default:3020800, rdd:51200, tab:51200]

Просмотреть файл

@ -17,6 +17,7 @@
gecko_metrics = [
"browser/base/content/metrics.yaml",
"dom/media/metrics.yaml",
"dom/media/webrtc/metrics.yaml",
"dom/metrics.yaml",
"gfx/metrics.yaml",
"netwerk/metrics.yaml",