mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1401592: Support sendEncodings in addTransceiver, and bring get/setParameters up to spec. r=mjf,webidl,smaug
Also, fix a pre-existing bug in JsepTrack::operator= Differential Revision: https://phabricator.services.mozilla.com/D156834
This commit is contained in:
parent
d69071ba92
commit
e3ee0cdf72
@ -1443,7 +1443,15 @@ class RTCPeerConnection {
|
||||
kind = sendTrack.kind;
|
||||
}
|
||||
|
||||
return this._pc.addTransceiver(init, kind, sendTrack);
|
||||
try {
|
||||
return this._pc.addTransceiver(init, kind, sendTrack);
|
||||
} catch (e) {
|
||||
// Exceptions thrown by c++ code do not propagate. In most cases, that's
|
||||
// fine because we're using Promises, which can be copied. But this is
|
||||
// not promise-based, so we have to do this sketchy stuff.
|
||||
const holder = new StructuredCloneHolder(new ClonedErrorHolder(e));
|
||||
throw holder.deserialize(this._win);
|
||||
}
|
||||
}
|
||||
|
||||
addTransceiver(sendTrackOrKind, init) {
|
||||
|
@ -927,7 +927,6 @@ nsresult PeerConnectionImpl::AddRtpTransceiverToJsepSession(
|
||||
}
|
||||
|
||||
mJsepSession->AddTransceiver(transceiver);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -944,6 +943,9 @@ static Maybe<SdpMediaSection::MediaType> ToSdpMediaType(
|
||||
already_AddRefed<RTCRtpTransceiver> PeerConnectionImpl::AddTransceiver(
|
||||
const dom::RTCRtpTransceiverInit& aInit, const nsAString& aKind,
|
||||
dom::MediaStreamTrack* aSendTrack, ErrorResult& aRv) {
|
||||
// Copy, because we might need to modify
|
||||
RTCRtpTransceiverInit init(aInit);
|
||||
|
||||
Maybe<SdpMediaSection::MediaType> type = ToSdpMediaType(aKind);
|
||||
if (NS_WARN_IF(!type.isSome())) {
|
||||
MOZ_ASSERT(false, "Invalid media kind");
|
||||
@ -964,9 +966,93 @@ already_AddRefed<RTCRtpTransceiver> PeerConnectionImpl::AddTransceiver(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& sendEncodings = init.mSendEncodings;
|
||||
|
||||
// CheckAndRectifyEncodings covers these six:
|
||||
// If any encoding contains a rid member whose value does not conform to the
|
||||
// grammar requirements specified in Section 10 of [RFC8851], throw a
|
||||
// TypeError.
|
||||
|
||||
// If some but not all encodings contain a rid member, throw a TypeError.
|
||||
|
||||
// If any encoding contains a rid member whose value is the same as that of a
|
||||
// rid contained in another encoding in sendEncodings, throw a TypeError.
|
||||
|
||||
// If kind is "audio", remove the scaleResolutionDownBy member from all
|
||||
// encodings that contain one.
|
||||
|
||||
// If any encoding contains a scaleResolutionDownBy member whose value is
|
||||
// less than 1.0, throw a RangeError.
|
||||
|
||||
// Verify that the value of each maxFramerate member in sendEncodings that is
|
||||
// defined is greater than 0.0. If one of the maxFramerate values does not
|
||||
// meet this requirement, throw a RangeError.
|
||||
RTCRtpSender::CheckAndRectifyEncodings(sendEncodings,
|
||||
*type == SdpMediaSection::kVideo, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If any encoding contains a read-only parameter other than rid, throw an
|
||||
// InvalidAccessError.
|
||||
// NOTE: We don't support any additional read-only params right now. Also,
|
||||
// spec shoehorns this in between checks that setParameters also performs
|
||||
// (between the rid checks and the scaleResolutionDownBy checks).
|
||||
|
||||
// If any encoding contains a scaleResolutionDownBy member, then for each
|
||||
// encoding without one, add a scaleResolutionDownBy member with the value
|
||||
// 1.0.
|
||||
for (const auto& constEncoding : sendEncodings) {
|
||||
if (constEncoding.mScaleResolutionDownBy.WasPassed()) {
|
||||
for (auto& encoding : sendEncodings) {
|
||||
if (!encoding.mScaleResolutionDownBy.WasPassed()) {
|
||||
encoding.mScaleResolutionDownBy.Construct(1.0f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Let maxN be the maximum number of total simultaneous encodings the user
|
||||
// agent may support for this kind, at minimum 1.This should be an optimistic
|
||||
// number since the codec to be used is not known yet.
|
||||
size_t maxN =
|
||||
(*type == SdpMediaSection::kVideo) ? webrtc::kMaxSimulcastStreams : 1;
|
||||
|
||||
// If the number of encodings stored in sendEncodings exceeds maxN, then trim
|
||||
// sendEncodings from the tail until its length is maxN.
|
||||
// NOTE: Spec has this after all validation steps; even if there are elements
|
||||
// that we will trim off, we still validate them.
|
||||
if (sendEncodings.Length() > maxN) {
|
||||
sendEncodings.TruncateLength(maxN);
|
||||
}
|
||||
|
||||
// If kind is "video" and none of the encodings contain a
|
||||
// scaleResolutionDownBy member, then for each encoding, add a
|
||||
// scaleResolutionDownBy member with the value 2^(length of sendEncodings -
|
||||
// encoding index - 1). This results in smaller-to-larger resolutions where
|
||||
// the last encoding has no scaling applied to it, e.g. 4:2:1 if the length
|
||||
// is 3.
|
||||
// NOTE: The code above ensures that these are all set, or all unset, so we
|
||||
// can just check the first one.
|
||||
if (sendEncodings.Length() && *type == SdpMediaSection::kVideo &&
|
||||
!sendEncodings[0].mScaleResolutionDownBy.WasPassed()) {
|
||||
double scale = 1.0f;
|
||||
for (auto it = sendEncodings.rbegin(); it != sendEncodings.rend(); ++it) {
|
||||
it->mScaleResolutionDownBy.Construct(scale);
|
||||
scale *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// If the number of encodings now stored in sendEncodings is 1, then remove
|
||||
// any rid member from the lone entry.
|
||||
if (sendEncodings.Length() == 1) {
|
||||
sendEncodings[0].mRid.Reset();
|
||||
}
|
||||
|
||||
RefPtr<RTCRtpTransceiver> transceiver = CreateTransceiver(
|
||||
jsepTransceiver->GetUuid(),
|
||||
jsepTransceiver->GetMediaType() == SdpMediaSection::kVideo, aInit,
|
||||
jsepTransceiver->GetMediaType() == SdpMediaSection::kVideo, init,
|
||||
aSendTrack, aRv);
|
||||
|
||||
if (aRv.Failed()) {
|
||||
|
@ -511,6 +511,14 @@ class PeerConnectionImpl final
|
||||
return mPacketDumper;
|
||||
}
|
||||
|
||||
nsString GenerateUUID() const {
|
||||
std::string result;
|
||||
if (!mUuidGen->Generate(&result)) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
return NS_ConvertUTF8toUTF16(result.c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~PeerConnectionImpl();
|
||||
PeerConnectionImpl(const PeerConnectionImpl& rhs);
|
||||
|
@ -50,6 +50,7 @@ RTCRtpSender::RTCRtpSender(nsPIDOMWindowInner* aWindow, PeerConnectionImpl* aPc,
|
||||
nsISerialEventTarget* aStsThread,
|
||||
MediaSessionConduit* aConduit,
|
||||
dom::MediaStreamTrack* aTrack,
|
||||
const Sequence<RTCRtpEncodingParameters>& aEncodings,
|
||||
RTCRtpTransceiver* aTransceiver)
|
||||
: mWindow(aWindow),
|
||||
mPc(aPc),
|
||||
@ -70,8 +71,28 @@ RTCRtpSender::RTCRtpSender(nsPIDOMWindowInner* aWindow, PeerConnectionImpl* aPc,
|
||||
|
||||
if (aConduit->type() == MediaSessionConduit::AUDIO) {
|
||||
mDtmf = new RTCDTMFSender(aWindow, mTransceiver);
|
||||
GetJsepTransceiver().mSendTrack.SetMaxEncodings(1);
|
||||
} else {
|
||||
GetJsepTransceiver().mSendTrack.SetMaxEncodings(
|
||||
webrtc::kMaxSimulcastStreams);
|
||||
}
|
||||
mPipeline->SetTrack(mSenderTrack);
|
||||
|
||||
if (aEncodings.Length()) {
|
||||
// This sender was created by addTransceiver with sendEncodings.
|
||||
mParameters.mEncodings = aEncodings;
|
||||
SetJsepRids(mParameters);
|
||||
} else {
|
||||
// This sender was created by addTrack, sRD(offer), or addTransceiver
|
||||
// without sendEncodings.
|
||||
RTCRtpEncodingParameters defaultEncoding;
|
||||
defaultEncoding.mActive = true;
|
||||
if (aConduit->type() == MediaSessionConduit::VIDEO) {
|
||||
defaultEncoding.mScaleResolutionDownBy.Construct(1.0f);
|
||||
}
|
||||
Unused << mParameters.mEncodings.AppendElement(defaultEncoding, fallible);
|
||||
MaybeGetJsepRids();
|
||||
}
|
||||
}
|
||||
|
||||
#undef INIT_CANONICAL
|
||||
@ -398,91 +419,290 @@ nsTArray<RefPtr<dom::RTCStatsPromise>> RTCRtpSender::GetStatsInternal() {
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> RTCRtpSender::SetParameters(
|
||||
const dom::RTCRtpParameters& aParameters, ErrorResult& aError) {
|
||||
// TODO(bug 1401592): transaction ids and other spec fixes
|
||||
const dom::RTCRtpSendParameters& aParameters, ErrorResult& aError) {
|
||||
dom::RTCRtpSendParameters paramsCopy(aParameters);
|
||||
// When the setParameters method is called, the user agent MUST run the
|
||||
// following steps:
|
||||
// Let parameters be the method's first argument.
|
||||
// Let sender be the RTCRtpSender object on which setParameters is invoked.
|
||||
// Let transceiver be the RTCRtpTransceiver object associated with sender
|
||||
// (i.e.sender is transceiver.[[Sender]]).
|
||||
|
||||
RefPtr<dom::Promise> p = MakePromise(aError);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mPc->IsClosed()) {
|
||||
p->MaybeRejectWithInvalidStateError("Peer connection is closed");
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
// If transceiver.[[Stopped]] is true, return a promise rejected with a newly
|
||||
// created InvalidStateError.
|
||||
if (mTransceiver->Stopped()) {
|
||||
p->MaybeRejectWithInvalidStateError("This sender's transceiver is stopped");
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
dom::RTCRtpParameters parameters(aParameters);
|
||||
|
||||
if (!parameters.mEncodings.WasPassed()) {
|
||||
parameters.mEncodings.Construct();
|
||||
// If sender.[[LastReturnedParameters]] is null, return a promise rejected
|
||||
// with a newly created InvalidStateError.
|
||||
if (!mLastReturnedParameters.isSome()) {
|
||||
p->MaybeRejectWithInvalidStateError(
|
||||
"Cannot call setParameters without first calling getParameters");
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
std::set<nsString> uniqueRids;
|
||||
for (const auto& encoding : parameters.mEncodings.Value()) {
|
||||
if (encoding.mScaleResolutionDownBy < 1.0f) {
|
||||
p->MaybeRejectWithRangeError("scaleResolutionDownBy must be >= 1.0");
|
||||
return p.forget();
|
||||
}
|
||||
if (parameters.mEncodings.Value().Length() > 1 &&
|
||||
!encoding.mRid.WasPassed()) {
|
||||
p->MaybeRejectWithTypeError("Missing rid");
|
||||
return p.forget();
|
||||
}
|
||||
if (encoding.mRid.WasPassed()) {
|
||||
if (uniqueRids.count(encoding.mRid.Value())) {
|
||||
p->MaybeRejectWithTypeError("Duplicate rid");
|
||||
return p.forget();
|
||||
}
|
||||
uniqueRids.insert(encoding.mRid.Value());
|
||||
}
|
||||
// Validate parameters by running the following steps:
|
||||
// Let encodings be parameters.encodings.
|
||||
// Let codecs be parameters.codecs.
|
||||
// Let N be the number of RTCRtpEncodingParameters stored in
|
||||
// sender.[[SendEncodings]].
|
||||
// If any of the following conditions are met,
|
||||
// return a promise rejected with a newly created InvalidModificationError:
|
||||
|
||||
if (encoding.mMaxFramerate.WasPassed()) {
|
||||
if (encoding.mMaxFramerate.Value() < 0.0f) {
|
||||
p->MaybeRejectWithRangeError("maxFramerate must be non-negative");
|
||||
return p.forget();
|
||||
// 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");
|
||||
return p.forget();
|
||||
}
|
||||
}
|
||||
|
||||
// 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(
|
||||
"Cannot change transaction id: call getParameters, modify the result, "
|
||||
"and then call setParameters");
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
// TODO: Verify remaining read-only parameters
|
||||
// headerExtensions (bug 1765851)
|
||||
// rtcp (bug 1765852)
|
||||
// codecs (bug 1534687)
|
||||
|
||||
// CheckAndRectifyEncodings handles the following steps:
|
||||
// If transceiver kind is "audio", remove the scaleResolutionDownBy member
|
||||
// from all encodings that contain one.
|
||||
//
|
||||
// If transceiver kind is "video", and any encoding in encodings contains a
|
||||
// scaleResolutionDownBy member whose value is less than 1.0, return a
|
||||
// promise rejected with a newly created RangeError.
|
||||
//
|
||||
// Verify that each encoding in encodings has a maxFramerate member whose
|
||||
// value is greater than or equal to 0.0. If one of the maxFramerate values
|
||||
// does not meet this requirement, return a promise rejected with a newly
|
||||
// created RangeError.
|
||||
ErrorResult rv;
|
||||
CheckAndRectifyEncodings(paramsCopy.mEncodings, mTransceiver->IsVideo(), rv);
|
||||
if (rv.Failed()) {
|
||||
p->MaybeReject(std::move(rv));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
// If transceiver kind is "video", then for each encoding in encodings that
|
||||
// doesn't contain a scaleResolutionDownBy member, add a
|
||||
// scaleResolutionDownBy member with the value 1.0.
|
||||
if (mTransceiver->IsVideo()) {
|
||||
for (auto& encoding : paramsCopy.mEncodings) {
|
||||
if (!encoding.mScaleResolutionDownBy.WasPassed()) {
|
||||
encoding.mScaleResolutionDownBy.Construct(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bug 1401592): transaction ids, timing changes
|
||||
// Let p be a new promise. (see above)
|
||||
|
||||
// In parallel, configure the media stack to use parameters to transmit
|
||||
// sender.[[SenderTrack]].
|
||||
// Right now this is infallible. That may change someday.
|
||||
|
||||
// We need to put this in a member variable, since MaybeUpdateConduit needs it
|
||||
// This also allows PeerConnectionImpl to detect when there is a pending
|
||||
// setParameters, which has implcations for the handling of
|
||||
// setRemoteDescription.
|
||||
mPendingParameters = Some(paramsCopy);
|
||||
uint32_t serialNumber = ++mNumSetParametersCalls;
|
||||
MaybeUpdateConduit();
|
||||
|
||||
// If the media stack is successfully configured with parameters,
|
||||
// queue a task to run the following steps:
|
||||
GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
|
||||
__func__, [this, self = RefPtr<RTCRtpSender>(this), p, parameters] {
|
||||
// p never resolves if the pc is closed. That's what the spec wants.
|
||||
if (!mPc->IsClosed()) {
|
||||
ApplyParameters(parameters);
|
||||
p->MaybeResolveWithUndefined();
|
||||
__func__,
|
||||
[this, self = RefPtr<RTCRtpSender>(this), p, paramsCopy, serialNumber] {
|
||||
// Set sender.[[LastReturnedParameters]] to null.
|
||||
mLastReturnedParameters = Nothing();
|
||||
// Set sender.[[SendEncodings]] to parameters.encodings.
|
||||
mParameters = paramsCopy;
|
||||
// Only clear mPendingParameters if it matches; there could have been
|
||||
// back-to-back calls to setParameters, and we only want to clear this
|
||||
// if no subsequent setParameters is pending.
|
||||
if (serialNumber == mNumSetParametersCalls) {
|
||||
mPendingParameters = Nothing();
|
||||
}
|
||||
MOZ_ASSERT(mParameters.mEncodings.Length());
|
||||
// Resolve p with undefined.
|
||||
p->MaybeResolveWithUndefined();
|
||||
}));
|
||||
|
||||
// Return p.
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
void RTCRtpSender::GetParameters(RTCRtpParameters& aParameters) const {
|
||||
// TODO(bug 1401592): transaction ids and other spec fixes
|
||||
aParameters = mParameters;
|
||||
}
|
||||
|
||||
void RTCRtpSender::ApplyParameters(const RTCRtpParameters& aParameters) {
|
||||
mParameters = aParameters;
|
||||
std::vector<std::string> rids;
|
||||
|
||||
if (aParameters.mEncodings.WasPassed()) {
|
||||
for (const auto& encoding : aParameters.mEncodings.Value()) {
|
||||
if (encoding.mRid.WasPassed()) {
|
||||
rids.push_back(NS_ConvertUTF16toUTF8(encoding.mRid.Value()).get());
|
||||
// static
|
||||
void RTCRtpSender::CheckAndRectifyEncodings(
|
||||
Sequence<RTCRtpEncodingParameters>& aEncodings, bool aVideo,
|
||||
ErrorResult& aRv) {
|
||||
// If any encoding contains a rid member whose value does not conform to the
|
||||
// grammar requirements specified in Section 10 of [RFC8851], throw a
|
||||
// TypeError.
|
||||
for (const auto& encoding : aEncodings) {
|
||||
if (encoding.mRid.WasPassed()) {
|
||||
std::string utf8Rid = NS_ConvertUTF16toUTF8(encoding.mRid.Value()).get();
|
||||
std::string error;
|
||||
if (!SdpRidAttributeList::CheckRidValidity(utf8Rid, &error)) {
|
||||
aRv.ThrowTypeError(nsCString(error));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!rids.empty()) {
|
||||
GetJsepTransceiver().mSendTrack.SetRids(rids);
|
||||
if (aEncodings.Length() > 1) {
|
||||
// If some but not all encodings contain a rid member, throw a TypeError.
|
||||
// rid must be set if there is more than one encoding
|
||||
// NOTE: Since rid is read-only, and the number of encodings cannot grow,
|
||||
// this should never happen in setParameters.
|
||||
for (const auto& encoding : aEncodings) {
|
||||
if (!encoding.mRid.WasPassed()) {
|
||||
aRv.ThrowTypeError("Missing rid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If any encoding contains a rid member whose value is the same as that of
|
||||
// a rid contained in another encoding in sendEncodings, throw a TypeError.
|
||||
// NOTE: Since rid is read-only, and the number of encodings cannot grow,
|
||||
// this should never happen in setParameters.
|
||||
std::set<nsString> uniqueRids;
|
||||
for (const auto& encoding : aEncodings) {
|
||||
if (uniqueRids.count(encoding.mRid.Value())) {
|
||||
aRv.ThrowTypeError("Duplicate rid");
|
||||
return;
|
||||
}
|
||||
uniqueRids.insert(encoding.mRid.Value());
|
||||
}
|
||||
}
|
||||
// Do not permit the set of encodings to increase
|
||||
GetJsepTransceiver().mSendTrack.SetMaxEncodings(rids.size());
|
||||
// TODO: ptime/adaptivePtime validation (bug 1733647)
|
||||
|
||||
// If kind is "audio", remove the scaleResolutionDownBy member from all
|
||||
// encodings that contain one.
|
||||
if (!aVideo) {
|
||||
for (auto& encoding : aEncodings) {
|
||||
if (encoding.mScaleResolutionDownBy.WasPassed()) {
|
||||
encoding.mScaleResolutionDownBy.Reset();
|
||||
}
|
||||
if (encoding.mMaxFramerate.WasPassed()) {
|
||||
encoding.mMaxFramerate.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If any encoding contains a scaleResolutionDownBy member whose value is
|
||||
// less than 1.0, throw a RangeError.
|
||||
for (const auto& encoding : aEncodings) {
|
||||
if (encoding.mScaleResolutionDownBy.WasPassed()) {
|
||||
if (encoding.mScaleResolutionDownBy.Value() < 1.0f) {
|
||||
aRv.ThrowRangeError("scaleResolutionDownBy must be >= 1.0");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the value of each maxFramerate member in sendEncodings that is
|
||||
// defined is greater than 0.0. If one of the maxFramerate values does not
|
||||
// meet this requirement, throw a RangeError.
|
||||
for (const auto& encoding : aEncodings) {
|
||||
if (encoding.mMaxFramerate.WasPassed()) {
|
||||
if (encoding.mMaxFramerate.Value() < 0.0f) {
|
||||
aRv.ThrowRangeError("maxFramerate must be non-negative");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTCRtpSender::SetJsepRids(const RTCRtpSendParameters& aParameters) {
|
||||
MOZ_ASSERT(aParameters.mEncodings.Length());
|
||||
|
||||
std::vector<std::string> rids;
|
||||
for (const auto& encoding : aParameters.mEncodings) {
|
||||
if (encoding.mRid.WasPassed()) {
|
||||
rids.push_back(NS_ConvertUTF16toUTF8(encoding.mRid.Value()).get());
|
||||
} else {
|
||||
rids.push_back("");
|
||||
}
|
||||
}
|
||||
|
||||
GetJsepTransceiver().mSendTrack.SetRids(rids);
|
||||
mSimulcastEnvelopeSet = true;
|
||||
}
|
||||
|
||||
void RTCRtpSender::GetParameters(RTCRtpSendParameters& aParameters) {
|
||||
MOZ_ASSERT(mParameters.mEncodings.Length());
|
||||
// If sender.[[LastReturnedParameters]] is not null, return
|
||||
// sender.[[LastReturnedParameters]], and abort these steps.
|
||||
if (mLastReturnedParameters.isSome()) {
|
||||
aParameters = *mLastReturnedParameters;
|
||||
return;
|
||||
}
|
||||
|
||||
// Let result be a new RTCRtpSendParameters dictionary constructed as follows:
|
||||
|
||||
// transactionId is set to a new unique identifier
|
||||
aParameters.mTransactionId = mPc->GenerateUUID();
|
||||
|
||||
// encodings is set to the value of the [[SendEncodings]] internal slot.
|
||||
aParameters.mEncodings = mParameters.mEncodings;
|
||||
|
||||
// The headerExtensions sequence is populated based on the header extensions
|
||||
// that have been negotiated for sending
|
||||
// TODO(bug 1765851): We do not support this yet
|
||||
// aParameters.mHeaderExtensions.Construct();
|
||||
|
||||
// codecs is set to the value of the [[SendCodecs]] internal slot
|
||||
// TODO(bug 1534687): We do not support this yet
|
||||
|
||||
// rtcp.cname is set to the CNAME of the associated RTCPeerConnection.
|
||||
// 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);
|
||||
|
||||
// Set sender.[[LastReturnedParameters]] to result.
|
||||
mLastReturnedParameters = Some(aParameters);
|
||||
|
||||
// Queue a task that sets sender.[[LastReturnedParameters]] to null.
|
||||
GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
|
||||
__func__, [this, self = RefPtr<RTCRtpSender>(this)] {
|
||||
mLastReturnedParameters = Nothing();
|
||||
}));
|
||||
}
|
||||
|
||||
bool operator==(const RTCRtpEncodingParameters& a1,
|
||||
@ -496,6 +716,7 @@ bool operator==(const RTCRtpEncodingParameters& a1,
|
||||
a1.mSsrc == a2.mSsrc;
|
||||
}
|
||||
|
||||
// static
|
||||
void RTCRtpSender::ApplyJsEncodingToConduitEncoding(
|
||||
const RTCRtpEncodingParameters& aJsEncoding,
|
||||
VideoCodecConfig::Encoding* aConduitEncoding) {
|
||||
@ -506,8 +727,91 @@ void RTCRtpSender::ApplyJsEncodingToConduitEncoding(
|
||||
aConduitEncoding->constraints.maxFps =
|
||||
Some(aJsEncoding.mMaxFramerate.Value());
|
||||
}
|
||||
aConduitEncoding->constraints.scaleDownBy =
|
||||
aJsEncoding.mScaleResolutionDownBy;
|
||||
if (aJsEncoding.mScaleResolutionDownBy.WasPassed()) {
|
||||
// Optional does not have a valueOr, despite being based on Maybe
|
||||
// :(
|
||||
aConduitEncoding->constraints.scaleDownBy =
|
||||
aJsEncoding.mScaleResolutionDownBy.Value();
|
||||
} else {
|
||||
aConduitEncoding->constraints.scaleDownBy = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
Sequence<RTCRtpEncodingParameters> RTCRtpSender::ToSendEncodings(
|
||||
const std::vector<std::string>& aRids) const {
|
||||
MOZ_ASSERT(!aRids.empty());
|
||||
|
||||
Sequence<RTCRtpEncodingParameters> result;
|
||||
// If sendEncodings is given as input to this algorithm, and is non-empty,
|
||||
// set the [[SendEncodings]] slot to sendEncodings.
|
||||
for (const auto& rid : aRids) {
|
||||
MOZ_ASSERT(!rid.empty());
|
||||
RTCRtpEncodingParameters encoding;
|
||||
encoding.mActive = true;
|
||||
encoding.mRid.Construct(NS_ConvertUTF8toUTF16(rid.c_str()));
|
||||
Unused << result.AppendElement(encoding, fallible);
|
||||
}
|
||||
|
||||
// If sendEncodings is non-empty, set each encoding's scaleResolutionDownBy
|
||||
// to 2^(length of sendEncodings - encoding index - 1).
|
||||
if (mTransceiver->IsVideo()) {
|
||||
double scale = 1.0f;
|
||||
for (auto it = result.rbegin(); it != result.rend(); ++it) {
|
||||
it->mScaleResolutionDownBy.Construct(scale);
|
||||
scale *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void RTCRtpSender::MaybeGetJsepRids() {
|
||||
MOZ_ASSERT(!mSimulcastEnvelopeSet);
|
||||
MOZ_ASSERT(mParameters.mEncodings.Length() == 1);
|
||||
MOZ_ASSERT(!mParameters.mEncodings[0].mRid.WasPassed());
|
||||
|
||||
auto jsepRids = GetJsepTransceiver().mSendTrack.GetRids();
|
||||
if (!jsepRids.empty()) {
|
||||
if (jsepRids.size() != 1 || !jsepRids[0].empty()) {
|
||||
// JSEP is using at least one rid. Stomp our single ridless encoding
|
||||
mParameters.mEncodings = ToSendEncodings(jsepRids);
|
||||
}
|
||||
GetJsepTransceiver().mSendTrack.SetMaxEncodings(jsepRids.size());
|
||||
mSimulcastEnvelopeSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
Sequence<RTCRtpEncodingParameters> RTCRtpSender::GetMatchingEncodings(
|
||||
const std::vector<std::string>& aRids) const {
|
||||
Sequence<RTCRtpEncodingParameters> result;
|
||||
if (!mParameters.mEncodings.Length()) {
|
||||
MOZ_ASSERT(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (aRids.empty() || (aRids.size() == 1 && aRids[0].empty())) {
|
||||
// Unicast with no specified rid. Select the first encoding, since that's
|
||||
// what the spec says to do.
|
||||
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;
|
||||
}
|
||||
|
||||
void RTCRtpSender::SetStreams(
|
||||
@ -739,7 +1043,22 @@ void RTCRtpSender::MaybeUpdateConduit() {
|
||||
}
|
||||
}
|
||||
|
||||
void RTCRtpSender::SyncFromJsep(const JsepTransceiver& aJsepTransceiver) {}
|
||||
void RTCRtpSender::SyncFromJsep(const JsepTransceiver& aJsepTransceiver) {
|
||||
if (!mSimulcastEnvelopeSet) {
|
||||
// JSEP is establishing the simulcast envelope for the first time, right now
|
||||
// This is the addTrack (or addTransceiver without sendEncodings) case.
|
||||
MaybeGetJsepRids();
|
||||
} else if (!aJsepTransceiver.mSendTrack.GetNegotiatedDetails() ||
|
||||
!aJsepTransceiver.mSendTrack.IsInHaveRemote()) {
|
||||
// Spec says that we do not update our encodings until we're in stable,
|
||||
// _unless_ this is the first negotiation.
|
||||
std::vector<std::string> rids = aJsepTransceiver.mSendTrack.GetRids();
|
||||
mParameters.mEncodings = GetMatchingEncodings(rids);
|
||||
MOZ_ASSERT(mParameters.mEncodings.Length());
|
||||
}
|
||||
|
||||
MaybeUpdateConduit();
|
||||
}
|
||||
|
||||
void RTCRtpSender::SyncToJsep(JsepTransceiver& aJsepTransceiver) const {
|
||||
std::vector<std::string> streamIds;
|
||||
@ -830,12 +1149,14 @@ Maybe<RTCRtpSender::VideoConfig> RTCRtpSender::GetNewVideoConfig() {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mParameters.mEncodings.WasPassed());
|
||||
newConfig.mVideoCodec = Some(configs[0]);
|
||||
// Spec says that we start using new parameters right away, _before_ we
|
||||
// update the parameters that are visible to JS (ie; mParameters).
|
||||
const RTCRtpSendParameters& parameters =
|
||||
mPendingParameters.isSome() ? *mPendingParameters : mParameters;
|
||||
for (VideoCodecConfig::Encoding& conduitEncoding :
|
||||
newConfig.mVideoCodec->mEncodings) {
|
||||
for (const RTCRtpEncodingParameters& jsEncoding :
|
||||
mParameters.mEncodings.Value()) {
|
||||
for (const RTCRtpEncodingParameters& jsEncoding : parameters.mEncodings) {
|
||||
std::string rid;
|
||||
if (jsEncoding.mRid.WasPassed()) {
|
||||
rid = NS_ConvertUTF16toUTF8(jsEncoding.mRid.Value()).get();
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "mozilla/dom/RTCStatsReportBinding.h"
|
||||
#include "mozilla/dom/RTCRtpParametersBinding.h"
|
||||
#include "RTCStatsReport.h"
|
||||
#include "jsep/JsepTrack.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
@ -41,6 +42,7 @@ class RTCRtpSender : public nsISupports, public nsWrapperCache {
|
||||
MediaTransportHandler* aTransportHandler,
|
||||
AbstractThread* aCallThread, nsISerialEventTarget* aStsThread,
|
||||
MediaSessionConduit* aConduit, dom::MediaStreamTrack* aTrack,
|
||||
const Sequence<RTCRtpEncodingParameters>& aEncodings,
|
||||
RTCRtpTransceiver* aTransceiver);
|
||||
|
||||
// nsISupports
|
||||
@ -59,8 +61,14 @@ class RTCRtpSender : public nsISupports, public nsWrapperCache {
|
||||
ErrorResult& aError);
|
||||
already_AddRefed<Promise> GetStats(ErrorResult& aError);
|
||||
already_AddRefed<Promise> SetParameters(
|
||||
const dom::RTCRtpParameters& aParameters, ErrorResult& aError);
|
||||
void GetParameters(RTCRtpParameters& aParameters) const;
|
||||
const dom::RTCRtpSendParameters& aParameters, ErrorResult& aError);
|
||||
// Not a simple getter, so not const
|
||||
// See https://w3c.github.io/webrtc-pc/#dom-rtcrtpsender-getparameters
|
||||
void GetParameters(RTCRtpSendParameters& aParameters);
|
||||
|
||||
static void CheckAndRectifyEncodings(
|
||||
Sequence<RTCRtpEncodingParameters>& aEncodings, bool aVideo,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const;
|
||||
nsTArray<RefPtr<RTCStatsPromise>> GetStatsInternal();
|
||||
@ -72,6 +80,7 @@ class RTCRtpSender : public nsISupports, public nsWrapperCache {
|
||||
void SetStreams(const Sequence<OwningNonNull<DOMMediaStream>>& aStreams);
|
||||
// ChromeOnly webidl
|
||||
void GetStreams(nsTArray<RefPtr<DOMMediaStream>>& aStreams);
|
||||
// ChromeOnly webidl
|
||||
void SetTrack(const RefPtr<MediaStreamTrack>& aTrack);
|
||||
void Shutdown();
|
||||
void BreakCycles();
|
||||
@ -120,15 +129,25 @@ class RTCRtpSender : public nsISupports, public nsWrapperCache {
|
||||
|
||||
std::string GetMid() const;
|
||||
JsepTransceiver& GetJsepTransceiver();
|
||||
void ApplyParameters(const RTCRtpParameters& aParameters);
|
||||
void ApplyJsEncodingToConduitEncoding(
|
||||
void ConfigureVideoCodecMode();
|
||||
void SetJsepRids(const RTCRtpSendParameters& aParameters);
|
||||
static void ApplyJsEncodingToConduitEncoding(
|
||||
const RTCRtpEncodingParameters& aJsEncoding,
|
||||
VideoCodecConfig::Encoding* aConduitEncoding);
|
||||
Sequence<RTCRtpEncodingParameters> GetMatchingEncodings(
|
||||
const std::vector<std::string>& aRids) const;
|
||||
Sequence<RTCRtpEncodingParameters> ToSendEncodings(
|
||||
const std::vector<std::string>& aRids) const;
|
||||
void MaybeGetJsepRids();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
RefPtr<PeerConnectionImpl> mPc;
|
||||
RefPtr<dom::MediaStreamTrack> mSenderTrack;
|
||||
RTCRtpParameters mParameters;
|
||||
RTCRtpSendParameters mParameters;
|
||||
Maybe<RTCRtpSendParameters> mPendingParameters;
|
||||
uint32_t mNumSetParametersCalls = 0;
|
||||
bool mSimulcastEnvelopeSet = false;
|
||||
Maybe<RTCRtpSendParameters> mLastReturnedParameters;
|
||||
RefPtr<MediaPipelineTransmit> mPipeline;
|
||||
RefPtr<RTCRtpTransceiver> mTransceiver;
|
||||
nsTArray<RefPtr<DOMMediaStream>> mStreams;
|
||||
|
@ -219,7 +219,7 @@ void RTCRtpTransceiver::Init(const RTCRtpTransceiverInit& aInit,
|
||||
|
||||
mSender = new RTCRtpSender(mWindow, mPc, mTransportHandler,
|
||||
mCallWrapper->mCallThread, mStsThread, mConduit,
|
||||
mSendTrack, this);
|
||||
mSendTrack, aInit.mSendEncodings, this);
|
||||
|
||||
if (mConduit) {
|
||||
InitConduitControl();
|
||||
@ -236,7 +236,6 @@ void RTCRtpTransceiver::Init(const RTCRtpTransceiverInit& aInit,
|
||||
self.get(), &RTCRtpTransceiver::UpdateDtlsTransportState);
|
||||
}));
|
||||
|
||||
// TODO(bug 1401592): apply aInit.mSendEncodings to mSender
|
||||
mSender->SetStreams(aInit.mStreams);
|
||||
mDirection = aInit.mDirection;
|
||||
}
|
||||
|
@ -1057,7 +1057,6 @@ JsepSession::Result JsepSessionImpl::SetRemoteDescription(
|
||||
// We chose a level for this transceiver, but never negotiated it.
|
||||
// Discard this state.
|
||||
transceiver->ClearLevel();
|
||||
transceiver->mSendTrack.ClearRids();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ 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);
|
||||
mRids = aRids;
|
||||
}
|
||||
@ -122,6 +123,7 @@ void JsepTrack::SetMaxEncodings(size_t aMax) {
|
||||
|
||||
void JsepTrack::RecvTrackSetRemote(const Sdp& aSdp,
|
||||
const SdpMediaSection& aMsection) {
|
||||
mInHaveRemote = true;
|
||||
MOZ_ASSERT(mDirection == sdp::kRecv);
|
||||
MOZ_ASSERT(aMsection.GetMediaType() !=
|
||||
SdpMediaSection::MediaType::kApplication);
|
||||
@ -173,6 +175,7 @@ void JsepTrack::RecvTrackSetRemote(const Sdp& aSdp,
|
||||
|
||||
void JsepTrack::SendTrackSetRemote(SsrcGenerator& aSsrcGenerator,
|
||||
const SdpMediaSection& aRemoteMsection) {
|
||||
mInHaveRemote = true;
|
||||
if (mType == SdpMediaSection::kApplication) {
|
||||
return;
|
||||
}
|
||||
@ -181,7 +184,8 @@ void JsepTrack::SendTrackSetRemote(SsrcGenerator& aSsrcGenerator,
|
||||
|
||||
// TODO: Current language in webrtc-pc is completely broken, and so I will
|
||||
// not be quoting it here.
|
||||
if (aRemoteMsection.GetAttributeList().HasAttribute(
|
||||
if ((mType == SdpMediaSection::kVideo) &&
|
||||
aRemoteMsection.GetAttributeList().HasAttribute(
|
||||
SdpAttribute::kSimulcastAttribute)) {
|
||||
// Note: webrtc-pc does not appear to support the full IETF simulcast
|
||||
// spec. In particular, the IETF simulcast spec supports requesting
|
||||
@ -594,6 +598,7 @@ void JsepTrack::Negotiate(const SdpMediaSection& answer,
|
||||
}
|
||||
}
|
||||
|
||||
mInHaveRemote = false;
|
||||
mNegotiatedDetails = std::move(negotiatedDetails);
|
||||
}
|
||||
|
||||
|
@ -131,6 +131,8 @@ class JsepTrack {
|
||||
mSsrcToRtxSsrc = rhs.mSsrcToRtxSsrc;
|
||||
mActive = rhs.mActive;
|
||||
mRemoteSetSendBit = rhs.mRemoteSetSendBit;
|
||||
mMaxEncodings = rhs.mMaxEncodings;
|
||||
mRtxIsAllowed = rhs.mRtxIsAllowed;
|
||||
|
||||
mPrototypeCodecs.clear();
|
||||
for (const auto& codec : rhs.mPrototypeCodecs) {
|
||||
@ -234,6 +236,7 @@ class JsepTrack {
|
||||
void SetRtxIsAllowed(bool aRtxIsAllowed) { mRtxIsAllowed = aRtxIsAllowed; }
|
||||
|
||||
void SetMaxEncodings(size_t aMax);
|
||||
bool IsInHaveRemote() const { return mInHaveRemote; }
|
||||
|
||||
private:
|
||||
std::vector<UniquePtr<JsepCodecDescription>> GetCodecClones() const;
|
||||
@ -277,6 +280,7 @@ class JsepTrack {
|
||||
bool mActive;
|
||||
bool mRemoteSetSendBit;
|
||||
size_t mMaxEncodings = 3;
|
||||
bool mInHaveRemote = false;
|
||||
|
||||
// See Bug 1642419, this can be removed when all sites are working with RTX.
|
||||
bool mRtxIsAllowed = true;
|
||||
|
@ -132,6 +132,7 @@ interface RTCPeerConnection : EventTarget {
|
||||
MediaStream... streams);
|
||||
undefined removeTrack(RTCRtpSender sender);
|
||||
|
||||
[Throws]
|
||||
RTCRtpTransceiver addTransceiver((MediaStreamTrack or DOMString) trackOrKind,
|
||||
optional RTCRtpTransceiverInit init = {});
|
||||
|
||||
|
@ -32,12 +32,12 @@ dictionary RTCRtpEncodingParameters {
|
||||
unsigned long ssrc;
|
||||
RTCRtxParameters rtx;
|
||||
RTCFecParameters fec;
|
||||
boolean active;
|
||||
RTCPriorityType priority;
|
||||
boolean active = true;
|
||||
// From https://www.w3.org/TR/webrtc-priority/
|
||||
RTCPriorityType priority = "low";
|
||||
unsigned long maxBitrate;
|
||||
RTCDegradationPreference degradationPreference = "balanced";
|
||||
DOMString rid;
|
||||
float scaleResolutionDownBy = 1.0;
|
||||
double scaleResolutionDownBy;
|
||||
// From https://w3c.github.io/webrtc-extensions/#rtcrtpencodingparameters-dictionary
|
||||
double maxFramerate;
|
||||
};
|
||||
@ -62,8 +62,15 @@ dictionary RTCRtpCodecParameters {
|
||||
};
|
||||
|
||||
dictionary RTCRtpParameters {
|
||||
sequence<RTCRtpEncodingParameters> encodings;
|
||||
sequence<RTCRtpHeaderExtensionParameters> headerExtensions;
|
||||
RTCRtcpParameters rtcp;
|
||||
sequence<RTCRtpCodecParameters> codecs;
|
||||
// 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;
|
||||
};
|
||||
|
||||
dictionary RTCRtpSendParameters : RTCRtpParameters {
|
||||
required DOMString transactionId;
|
||||
required sequence<RTCRtpEncodingParameters> encodings;
|
||||
};
|
||||
|
@ -13,8 +13,8 @@ interface RTCRtpSender {
|
||||
readonly attribute MediaStreamTrack? track;
|
||||
readonly attribute RTCDtlsTransport? transport;
|
||||
[NewObject]
|
||||
Promise<undefined> setParameters (optional RTCRtpParameters parameters = {});
|
||||
RTCRtpParameters getParameters();
|
||||
Promise<undefined> setParameters (RTCRtpSendParameters parameters);
|
||||
RTCRtpSendParameters getParameters();
|
||||
[Throws]
|
||||
Promise<undefined> replaceTrack(MediaStreamTrack? withTrack);
|
||||
[NewObject]
|
||||
|
@ -17,8 +17,7 @@ enum RTCRtpTransceiverDirection {
|
||||
dictionary RTCRtpTransceiverInit {
|
||||
RTCRtpTransceiverDirection direction = "sendrecv";
|
||||
sequence<MediaStream> streams = [];
|
||||
// TODO: bug 1396918
|
||||
// sequence<RTCRtpEncodingParameters> sendEncodings;
|
||||
sequence<RTCRtpEncodingParameters> sendEncodings = [];
|
||||
};
|
||||
|
||||
[Pref="media.peerconnection.enabled",
|
||||
|
@ -59,18 +59,17 @@ promise_test(async t => {
|
||||
const pc2 = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc2.close());
|
||||
|
||||
const {sender} = pc1.addTransceiver("video", {sendEncodings: [{rid: "foo"}, {rid: "bar"}, {rid: "baz"}]});
|
||||
const {sender} = pc1.addTransceiver("video", {sendEncodings: [{rid: "foo"}, {rid: "bar"}]});
|
||||
let {encodings} = sender.getParameters();
|
||||
assert_equals(encodings.length, 3, "This test requires maxN >= 3");
|
||||
|
||||
await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo", "bar"]);
|
||||
await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo"]);
|
||||
|
||||
assert_equals(pc1.getTransceivers().length, 1);
|
||||
encodings = sender.getParameters().encodings;
|
||||
const rids = encodings.map(({rid}) => rid);
|
||||
assert_array_equals(rids, ["foo", "bar"]);
|
||||
assert_array_equals(rids, ["foo"]);
|
||||
const scaleDownByValues = encodings.map(({scaleResolutionDownBy}) => scaleResolutionDownBy);
|
||||
assert_array_equals(scaleDownByValues, [4, 2]);
|
||||
assert_array_equals(scaleDownByValues, [2]);
|
||||
}, 'sRD(recv simulcast answer) can narrow the simulcast envelope specified by addTransceiver');
|
||||
|
||||
promise_test(async t => {
|
||||
@ -79,25 +78,24 @@ promise_test(async t => {
|
||||
const pc2 = new RTCPeerConnection();
|
||||
t.add_cleanup(() => pc2.close());
|
||||
|
||||
const {sender} = pc1.addTransceiver("video", {sendEncodings: [{rid: "foo"}, {rid: "bar"}, {rid: "baz"}]});
|
||||
const {sender} = pc1.addTransceiver("video", {sendEncodings: [{rid: "foo"}, {rid: "bar"}]});
|
||||
let {encodings} = sender.getParameters();
|
||||
assert_equals(encodings.length, 3, "This test requires maxN >= 3");
|
||||
|
||||
await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo", "bar", "baz"]);
|
||||
|
||||
assert_equals(pc1.getTransceivers().length, 1);
|
||||
encodings = sender.getParameters().encodings;
|
||||
let rids = encodings.map(({rid}) => rid);
|
||||
assert_array_equals(rids, ["foo", "bar", "baz"]);
|
||||
|
||||
await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo", "bar"]);
|
||||
|
||||
assert_equals(pc1.getTransceivers().length, 1);
|
||||
encodings = sender.getParameters().encodings;
|
||||
rids = encodings.map(({rid}) => rid);
|
||||
let rids = encodings.map(({rid}) => rid);
|
||||
assert_array_equals(rids, ["foo", "bar"]);
|
||||
|
||||
await doOfferToSendSimulcastAndAnswer(pc1, pc2, ["foo"]);
|
||||
|
||||
assert_equals(pc1.getTransceivers().length, 1);
|
||||
encodings = sender.getParameters().encodings;
|
||||
rids = encodings.map(({rid}) => rid);
|
||||
assert_array_equals(rids, ["foo"]);
|
||||
const scaleDownByValues = encodings.map(({scaleResolutionDownBy}) => scaleResolutionDownBy);
|
||||
assert_array_equals(scaleDownByValues, [4, 2]);
|
||||
assert_array_equals(scaleDownByValues, [2]);
|
||||
}, 'sRD(recv simulcast answer) can narrow the simulcast envelope from a previous negotiation');
|
||||
|
||||
promise_test(async t => {
|
||||
|
Loading…
Reference in New Issue
Block a user