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:
Byron Campen 2022-12-13 23:22:00 +00:00
parent d69071ba92
commit e3ee0cdf72
14 changed files with 551 additions and 97 deletions

View File

@ -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) {

View File

@ -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()) {

View File

@ -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);

View File

@ -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();

View File

@ -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;

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -132,6 +132,7 @@ interface RTCPeerConnection : EventTarget {
MediaStream... streams);
undefined removeTrack(RTCRtpSender sender);
[Throws]
RTCRtpTransceiver addTransceiver((MediaStreamTrack or DOMString) trackOrKind,
optional RTCRtpTransceiverInit init = {});

View File

@ -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;
};

View File

@ -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]

View File

@ -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",

View File

@ -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 => {