Bug 1829667: Make JsepTransceiver non-refcounted. r=mjf,webidl,mccr8

Involves substantial refactoring to make most access by-value (or through
functional-style in-place modifications) for safety.

Differential Revision: https://phabricator.services.mozilla.com/D176423
This commit is contained in:
Byron Campen 2023-04-28 14:53:57 +00:00
parent e533a4c3dd
commit c7504aeccf
15 changed files with 1207 additions and 1207 deletions

View File

@ -1391,11 +1391,14 @@ export class RTCPeerConnection {
transceiver.setDirectionInternal("sendonly");
}
} else {
transceiver = this._addTransceiverNoEvents(track, {
streams,
direction: "sendrecv",
});
transceiver.setAddTrackMagic();
transceiver = this._addTransceiverNoEvents(
track,
{
streams,
direction: "sendrecv",
},
true
);
}
this.updateNegotiationNeeded();
@ -1431,7 +1434,7 @@ export class RTCPeerConnection {
this.updateNegotiationNeeded();
}
_addTransceiverNoEvents(sendTrackOrKind, init) {
_addTransceiverNoEvents(sendTrackOrKind, init, addTrackMagic) {
let sendTrack = null;
let kind;
if (typeof sendTrackOrKind == "string") {
@ -1449,7 +1452,7 @@ export class RTCPeerConnection {
}
try {
return this._pc.addTransceiver(init, kind, sendTrack);
return this._pc.addTransceiver(init, kind, sendTrack, addTrackMagic);
} 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

View File

@ -877,17 +877,14 @@ nsresult PeerConnectionImpl::GetDatachannelParameters(
*mmsset = false;
transportId->clear();
RefPtr<JsepTransceiver> datachannelTransceiver;
for (const auto& transceiver : mJsepSession->GetTransceivers()) {
if ((transceiver->GetMediaType() == SdpMediaSection::kApplication) &&
transceiver->mSendTrack.GetNegotiatedDetails()) {
datachannelTransceiver = transceiver;
break;
}
}
Maybe<const JsepTransceiver> datachannelTransceiver =
mJsepSession->FindTransceiver([](const JsepTransceiver& aTransceiver) {
return aTransceiver.GetMediaType() == SdpMediaSection::kApplication;
});
if (!datachannelTransceiver ||
!datachannelTransceiver->mTransport.mComponents) {
!datachannelTransceiver->mTransport.mComponents ||
!datachannelTransceiver->mSendTrack.GetNegotiatedDetails()) {
return NS_ERROR_FAILURE;
}
@ -942,7 +939,7 @@ nsresult PeerConnectionImpl::GetDatachannelParameters(
}
nsresult PeerConnectionImpl::AddRtpTransceiverToJsepSession(
RefPtr<JsepTransceiver>& transceiver) {
JsepTransceiver& transceiver) {
nsresult res = ConfigureJsepSessionCodecs();
if (NS_FAILED(res)) {
CSFLogError(LOGTAG, "Failed to configure codecs");
@ -965,7 +962,7 @@ static Maybe<SdpMediaSection::MediaType> ToSdpMediaType(
already_AddRefed<RTCRtpTransceiver> PeerConnectionImpl::AddTransceiver(
const dom::RTCRtpTransceiverInit& aInit, const nsAString& aKind,
dom::MediaStreamTrack* aSendTrack, ErrorResult& aRv) {
dom::MediaStreamTrack* aSendTrack, bool aAddTrackMagic, ErrorResult& aRv) {
// Copy, because we might need to modify
RTCRtpTransceiverInit init(aInit);
@ -976,9 +973,8 @@ already_AddRefed<RTCRtpTransceiver> PeerConnectionImpl::AddTransceiver(
return nullptr;
}
RefPtr<JsepTransceiver> jsepTransceiver =
new JsepTransceiver(*type, *mUuidGen);
jsepTransceiver->SetRtxIsAllowed(mRtxIsAllowed);
JsepTransceiver jsepTransceiver(*type, *mUuidGen);
jsepTransceiver.SetRtxIsAllowed(mRtxIsAllowed);
// Do this last, since it is not possible to roll back.
nsresult rv = AddRtpTransceiverToJsepSession(jsepTransceiver);
@ -1074,9 +1070,9 @@ already_AddRefed<RTCRtpTransceiver> PeerConnectionImpl::AddTransceiver(
}
RefPtr<RTCRtpTransceiver> transceiver = CreateTransceiver(
jsepTransceiver->GetUuid(),
jsepTransceiver->GetMediaType() == SdpMediaSection::kVideo, init,
aSendTrack, aRv);
jsepTransceiver.GetUuid(),
jsepTransceiver.GetMediaType() == SdpMediaSection::kVideo, init,
aSendTrack, aAddTrackMagic, aRv);
if (aRv.Failed()) {
// Would be nice if we could peek at the rv without stealing it, so we
@ -1177,22 +1173,19 @@ PeerConnectionImpl::CreateDataChannel(
CSFLogDebug(LOGTAG, "%s: making DOMDataChannel", __FUNCTION__);
RefPtr<JsepTransceiver> dcTransceiver;
for (auto& transceiver : mJsepSession->GetTransceivers()) {
if (transceiver->GetMediaType() == SdpMediaSection::kApplication) {
dcTransceiver = transceiver;
break;
}
}
Maybe<JsepTransceiver> dcTransceiver =
mJsepSession->FindTransceiver([](const JsepTransceiver& aTransceiver) {
return aTransceiver.GetMediaType() == SdpMediaSection::kApplication;
});
if (!dcTransceiver) {
dcTransceiver = new JsepTransceiver(
SdpMediaSection::MediaType::kApplication, *mUuidGen);
mJsepSession->AddTransceiver(dcTransceiver);
if (dcTransceiver) {
dcTransceiver->RestartDatachannelTransceiver();
mJsepSession->SetTransceiver(*dcTransceiver);
} else {
mJsepSession->AddTransceiver(
JsepTransceiver(SdpMediaSection::MediaType::kApplication, *mUuidGen));
}
dcTransceiver->RestartDatachannelTransceiver();
RefPtr<nsDOMDataChannel> retval;
rv = NS_NewDOMDataChannel(dataChannel.forget(), mWindow,
getter_AddRefs(retval));
@ -1364,47 +1357,49 @@ void PeerConnectionImpl::RunNextOperation(ErrorResult& aError) {
void PeerConnectionImpl::SyncToJsep() {
for (const auto& transceiver : mTransceivers) {
transceiver->SyncToJsep();
transceiver->SyncToJsep(*mJsepSession);
}
}
void PeerConnectionImpl::SyncFromJsep() {
CSFLogDebug(LOGTAG, "%s", __FUNCTION__);
for (const auto& jsepTransceiver : mJsepSession->GetTransceivers()) {
if (jsepTransceiver->GetMediaType() ==
SdpMediaSection::MediaType::kApplication) {
continue;
}
mJsepSession->ForEachTransceiver(
[this, self = RefPtr<PeerConnectionImpl>(this)](
const JsepTransceiver& jsepTransceiver) {
if (jsepTransceiver.GetMediaType() ==
SdpMediaSection::MediaType::kApplication) {
return;
}
CSFLogDebug(LOGTAG, "%s: Looking for match", __FUNCTION__);
RefPtr<RTCRtpTransceiver> transceiver;
for (auto& temp : mTransceivers) {
if (temp->GetJsepTransceiverId() == jsepTransceiver->GetUuid()) {
CSFLogDebug(LOGTAG, "%s: Found match", __FUNCTION__);
transceiver = temp;
break;
}
}
CSFLogDebug(LOGTAG, "%s: Looking for match", __FUNCTION__);
RefPtr<RTCRtpTransceiver> transceiver;
for (auto& temp : mTransceivers) {
if (temp->GetJsepTransceiverId() == jsepTransceiver.GetUuid()) {
CSFLogDebug(LOGTAG, "%s: Found match", __FUNCTION__);
transceiver = temp;
break;
}
}
if (!transceiver) {
CSFLogDebug(LOGTAG, "%s: No match, making new", __FUNCTION__);
dom::RTCRtpTransceiverInit init;
init.mDirection = RTCRtpTransceiverDirection::Recvonly;
IgnoredErrorResult rv;
transceiver = CreateTransceiver(
jsepTransceiver->GetUuid(),
jsepTransceiver->GetMediaType() == SdpMediaSection::kVideo, init,
nullptr, rv);
if (NS_WARN_IF(rv.Failed())) {
MOZ_ASSERT(false);
return;
}
mTransceivers.AppendElement(transceiver);
}
if (!transceiver) {
CSFLogDebug(LOGTAG, "%s: No match, making new", __FUNCTION__);
dom::RTCRtpTransceiverInit init;
init.mDirection = RTCRtpTransceiverDirection::Recvonly;
IgnoredErrorResult rv;
transceiver = CreateTransceiver(
jsepTransceiver.GetUuid(),
jsepTransceiver.GetMediaType() == SdpMediaSection::kVideo, init,
nullptr, false, rv);
if (NS_WARN_IF(rv.Failed())) {
MOZ_ASSERT(false);
return;
}
mTransceivers.AppendElement(transceiver);
}
CSFLogDebug(LOGTAG, "%s: Syncing transceiver", __FUNCTION__);
transceiver->SyncFromJsep();
}
CSFLogDebug(LOGTAG, "%s: Syncing transceiver", __FUNCTION__);
transceiver->SyncFromJsep(*mJsepSession);
});
}
already_AddRefed<dom::Promise> PeerConnectionImpl::MakePromise(
@ -1592,9 +1587,6 @@ PeerConnectionImpl::CreateOffer(const JsepOfferOptions& aOptions) {
*buildJSErrorData(result, errorString), rv);
} else {
mJsepSession = std::move(uncommittedJsepSession);
for (auto& transceiver : mTransceivers) {
transceiver->SetJsepSession(mJsepSession.get());
}
mPCObserver->OnCreateOfferSuccess(ObString(offer.c_str()), rv);
}
}));
@ -1631,9 +1623,6 @@ PeerConnectionImpl::CreateAnswer() {
*buildJSErrorData(result, errorString), rv);
} else {
mJsepSession = std::move(uncommittedJsepSession);
for (auto& transceiver : mTransceivers) {
transceiver->SetJsepSession(mJsepSession.get());
}
mPCObserver->OnCreateAnswerSuccess(ObString(answer.c_str()), rv);
}
}));
@ -3012,9 +3001,6 @@ void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing(
}
mJsepSession = std::move(mUncommittedJsepSession);
for (auto& transceiver : mTransceivers) {
transceiver->SetJsepSession(mJsepSession.get());
}
auto newSignalingState = GetSignalingState();
SyncFromJsep();
@ -3981,30 +3967,33 @@ bool PeerConnectionImpl::ShouldForceProxy() const {
}
void PeerConnectionImpl::EnsureTransports(const JsepSession& aSession) {
for (const auto& transceiver : aSession.GetTransceivers()) {
if (transceiver->HasOwnTransport()) {
mJsepSession->ForEachTransceiver([this,
self = RefPtr<PeerConnectionImpl>(this)](
const JsepTransceiver& transceiver) {
if (transceiver.HasOwnTransport()) {
mTransportHandler->EnsureProvisionalTransport(
transceiver->mTransport.mTransportId,
transceiver->mTransport.mLocalUfrag,
transceiver->mTransport.mLocalPwd,
transceiver->mTransport.mComponents);
transceiver.mTransport.mTransportId,
transceiver.mTransport.mLocalUfrag, transceiver.mTransport.mLocalPwd,
transceiver.mTransport.mComponents);
}
}
});
GatherIfReady();
}
void PeerConnectionImpl::UpdateRTCDtlsTransports(bool aMarkAsStable) {
for (const auto& jsepTransceiver : mJsepSession->GetTransceivers()) {
std::string transportId = jsepTransceiver->mTransport.mTransportId;
if (transportId.empty()) {
continue;
}
if (!mTransportIdToRTCDtlsTransport.count(transportId)) {
mTransportIdToRTCDtlsTransport.emplace(
transportId, new RTCDtlsTransport(GetParentObject()));
}
}
mJsepSession->ForEachTransceiver(
[this, self = RefPtr<PeerConnectionImpl>(this)](
const JsepTransceiver& jsepTransceiver) {
std::string transportId = jsepTransceiver.mTransport.mTransportId;
if (transportId.empty()) {
return;
}
if (!mTransportIdToRTCDtlsTransport.count(transportId)) {
mTransportIdToRTCDtlsTransport.emplace(
transportId, new RTCDtlsTransport(GetParentObject()));
}
});
for (auto& transceiver : mTransceivers) {
std::string transportId = transceiver->GetTransportId();
@ -4042,17 +4031,19 @@ nsresult PeerConnectionImpl::UpdateTransports(const JsepSession& aSession,
const bool forceIceTcp) {
std::set<std::string> finalTransports;
Maybe<std::string> sctpTransport;
for (const auto& transceiver : aSession.GetTransceivers()) {
if (transceiver->GetMediaType() == SdpMediaSection::kApplication &&
transceiver->HasTransport()) {
sctpTransport = Some(transceiver->mTransport.mTransportId);
}
mJsepSession->ForEachTransceiver(
[&, this, self = RefPtr<PeerConnectionImpl>(this)](
const JsepTransceiver& transceiver) {
if (transceiver.GetMediaType() == SdpMediaSection::kApplication &&
transceiver.HasTransport()) {
sctpTransport = Some(transceiver.mTransport.mTransportId);
}
if (transceiver->HasOwnTransport()) {
finalTransports.insert(transceiver->mTransport.mTransportId);
UpdateTransport(*transceiver, forceIceTcp);
}
}
if (transceiver.HasOwnTransport()) {
finalTransports.insert(transceiver.mTransport.mTransportId);
UpdateTransport(transceiver, forceIceTcp);
}
});
// clean up the unused RTCDtlsTransports
RemoveRTCDtlsTransportsExcept(finalTransports);
@ -4447,7 +4438,7 @@ void PeerConnectionImpl::EnsureIceGathering(bool aDefaultRouteOnly,
already_AddRefed<dom::RTCRtpTransceiver> PeerConnectionImpl::CreateTransceiver(
const std::string& aId, bool aIsVideo, const RTCRtpTransceiverInit& aInit,
dom::MediaStreamTrack* aSendTrack, ErrorResult& aRv) {
dom::MediaStreamTrack* aSendTrack, bool aAddTrackMagic, ErrorResult& aRv) {
PeerConnectionCtx* ctx = PeerConnectionCtx::GetInstance();
if (!mCall) {
mCall = WebrtcCallWrapper::Create(
@ -4458,6 +4449,12 @@ already_AddRefed<dom::RTCRtpTransceiver> PeerConnectionImpl::CreateTransceiver(
ctx->GetSharedWebrtcState());
}
if (aAddTrackMagic) {
mJsepSession->ApplyToTransceiver(aId, [](JsepTransceiver& aTransceiver) {
aTransceiver.SetAddTrackMagic();
});
}
RefPtr<RTCRtpTransceiver> transceiver = new RTCRtpTransceiver(
mWindow, PrivacyRequested(), this, mTransportHandler, mJsepSession.get(),
aId, aIsVideo, mSTSThread.get(), aSendTrack, mCall.get(), mIdGenerator);
@ -4467,8 +4464,6 @@ already_AddRefed<dom::RTCRtpTransceiver> PeerConnectionImpl::CreateTransceiver(
return nullptr;
}
transceiver->SyncToJsep();
if (aSendTrack) {
// implement checking for peerIdentity (where failure == black/silence)
Document* doc = mWindow->GetExtantDoc();

View File

@ -296,7 +296,7 @@ class PeerConnectionImpl final
already_AddRefed<dom::RTCRtpTransceiver> AddTransceiver(
const dom::RTCRtpTransceiverInit& aInit, const nsAString& aKind,
dom::MediaStreamTrack* aSendTrack, ErrorResult& aRv);
dom::MediaStreamTrack* aSendTrack, bool aAddTrackMagic, ErrorResult& aRv);
bool CheckNegotiationNeeded();
bool CreatedSender(const dom::RTCRtpSender& aSender) const;
@ -614,7 +614,7 @@ class PeerConnectionImpl final
std::string* transportId,
bool* client) const;
nsresult AddRtpTransceiverToJsepSession(RefPtr<JsepTransceiver>& transceiver);
nsresult AddRtpTransceiverToJsepSession(JsepTransceiver& transceiver);
void RecordIceRestartStatistics(JsepSdpType type);
@ -825,7 +825,7 @@ class PeerConnectionImpl final
already_AddRefed<dom::RTCRtpTransceiver> CreateTransceiver(
const std::string& aId, bool aIsVideo,
const dom::RTCRtpTransceiverInit& aInit,
dom::MediaStreamTrack* aSendTrack, ErrorResult& aRv);
dom::MediaStreamTrack* aSendTrack, bool aAddTrackMagic, ErrorResult& aRv);
std::string GetTransportIdMatchingSendTrack(
const dom::MediaStreamTrack& aTrack) const;

View File

@ -892,12 +892,12 @@ std::string RTCRtpReceiver::GetMid() const {
JsepTransceiver& RTCRtpReceiver::GetJsepTransceiver() {
MOZ_ASSERT(mTransceiver);
return *mTransceiver->GetJsepTransceiver();
return mTransceiver->GetJsepTransceiver();
}
const JsepTransceiver& RTCRtpReceiver::GetJsepTransceiver() const {
MOZ_ASSERT(mTransceiver);
return *mTransceiver->GetJsepTransceiver();
return mTransceiver->GetJsepTransceiver();
}
} // namespace mozilla::dom

View File

@ -74,10 +74,6 @@ 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);
@ -91,7 +87,7 @@ RTCRtpSender::RTCRtpSender(nsPIDOMWindowInner* aWindow, PeerConnectionImpl* aPc,
if (aEncodings.Length()) {
// This sender was created by addTransceiver with sendEncodings.
mParameters.mEncodings = aEncodings;
SetJsepRids(mParameters);
mSimulcastEnvelopeSet = true;
mozilla::glean::rtcrtpsender::used_sendencodings.AddToNumerator(1);
} else {
// This sender was created by addTrack, sRD(offer), or addTransceiver
@ -724,7 +720,7 @@ already_AddRefed<Promise> RTCRtpSender::SetParameters(
MaybeUpdateConduit();
if (compatModeAllowedRidChange) {
SetJsepRids(paramsCopy);
mSimulcastEnvelopeSet = true;
}
// If the media stack is successfully configured with parameters,
@ -841,22 +837,6 @@ void RTCRtpSender::CheckAndRectifyEncodings(
}
}
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
@ -1206,10 +1186,10 @@ void RTCRtpSender::SetTrack(const RefPtr<MediaStreamTrack>& aTrack) {
mSenderTrack = aTrack;
SeamlessTrackSwitch(aTrack);
if (aTrack) {
// RFC says:
// RFC says (in the section on remote rollback):
// However, an RtpTransceiver MUST NOT be removed if a track was attached
// to the RtpTransceiver via the addTrack method.
GetJsepTransceiver().SetOnlyExistsBecauseOfSetRemote(false);
mAddTrackCalled = true;
}
}
@ -1314,6 +1294,29 @@ void RTCRtpSender::SyncToJsep(JsepTransceiver& aJsepTransceiver) const {
}
aJsepTransceiver.mSendTrack.UpdateStreamIds(streamIds);
if (mSimulcastEnvelopeSet) {
std::vector<std::string> rids;
for (const auto& encoding : mParameters.mEncodings) {
if (encoding.mRid.WasPassed()) {
rids.push_back(NS_ConvertUTF16toUTF8(encoding.mRid.Value()).get());
} else {
rids.push_back("");
}
}
aJsepTransceiver.mSendTrack.SetRids(rids);
}
if (mTransceiver->IsVideo()) {
aJsepTransceiver.mSendTrack.SetMaxEncodings(webrtc::kMaxSimulcastStreams);
} else {
aJsepTransceiver.mSendTrack.SetMaxEncodings(1);
}
if (mAddTrackCalled) {
aJsepTransceiver.SetOnlyExistsBecauseOfSetRemote(false);
}
}
Maybe<RTCRtpSender::VideoConfig> RTCRtpSender::GetNewVideoConfig() {
@ -1579,7 +1582,7 @@ RefPtr<MediaPipelineTransmit> RTCRtpSender::GetPipeline() const {
std::string RTCRtpSender::GetMid() const { return mTransceiver->GetMidAscii(); }
JsepTransceiver& RTCRtpSender::GetJsepTransceiver() {
return *mTransceiver->GetJsepTransceiver();
return mTransceiver->GetJsepTransceiver();
}
void RTCRtpSender::UpdateDtmfSender() {

View File

@ -138,7 +138,6 @@ class RTCRtpSender : public nsISupports,
std::string GetMid() const;
JsepTransceiver& GetJsepTransceiver();
void SetJsepRids(const RTCRtpSendParameters& aParameters);
static void ApplyJsEncodingToConduitEncoding(
const RTCRtpEncodingParameters& aJsEncoding,
VideoCodecConfig::Encoding* aConduitEncoding);
@ -157,6 +156,7 @@ class RTCRtpSender : public nsISupports,
nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<PeerConnectionImpl> mPc;
RefPtr<dom::MediaStreamTrack> mSenderTrack;
bool mAddTrackCalled = false;
RTCRtpSendParameters mParameters;
Maybe<RTCRtpSendParameters> mPendingParameters;
uint32_t mNumSetParametersCalls = 0;

View File

@ -164,7 +164,7 @@ RTCRtpTransceiver::RTCRtpTransceiver(
mPc(aPc),
mTransportHandler(aTransportHandler),
mTransceiverId(aTransceiverId),
mJsepTransceiver(aJsepSession->GetTransceiver(mTransceiverId)),
mJsepTransceiver(*aJsepSession->GetTransceiver(mTransceiverId)),
mStsThread(aStsThread),
mCallWrapper(aCallWrapper),
mSendTrack(aSendTrack),
@ -449,7 +449,7 @@ bool RTCRtpTransceiver::ConduitHasPluginID(uint64_t aPluginID) {
return mConduit && mConduit->HasCodecPluginID(aPluginID);
}
void RTCRtpTransceiver::SyncFromJsep() {
void RTCRtpTransceiver::SyncFromJsep(const JsepSession& aSession) {
MOZ_MTLOG(ML_DEBUG, mPc->GetHandle()
<< "[" << mMid.Ref() << "]: " << __FUNCTION__
<< " Syncing from JSEP transceiver");
@ -459,35 +459,35 @@ void RTCRtpTransceiver::SyncFromJsep() {
return;
}
auto jsepTransceiver = GetJsepTransceiver();
mJsepTransceiver = *aSession.GetTransceiver(mTransceiverId);
// Transceivers can stop due to JSEP negotiation, so we need to check that
if (jsepTransceiver->IsStopped()) {
if (mJsepTransceiver.IsStopped()) {
StopImpl();
}
mReceiver->SyncFromJsep(*jsepTransceiver);
mSender->SyncFromJsep(*jsepTransceiver);
mReceiver->SyncFromJsep(mJsepTransceiver);
mSender->SyncFromJsep(mJsepTransceiver);
// mid from JSEP
if (jsepTransceiver->IsAssociated()) {
mMid = jsepTransceiver->GetMid();
if (mJsepTransceiver.IsAssociated()) {
mMid = mJsepTransceiver.GetMid();
} else {
mMid = std::string();
}
// currentDirection from JSEP, but not if "this transceiver has never been
// represented in an offer/answer exchange"
if (jsepTransceiver->HasLevel() && jsepTransceiver->IsNegotiated()) {
if (jsepTransceiver->mRecvTrack.GetActive()) {
if (jsepTransceiver->mSendTrack.GetActive()) {
if (mJsepTransceiver.HasLevel() && mJsepTransceiver.IsNegotiated()) {
if (mJsepTransceiver.mRecvTrack.GetActive()) {
if (mJsepTransceiver.mSendTrack.GetActive()) {
mCurrentDirection.SetValue(dom::RTCRtpTransceiverDirection::Sendrecv);
mHasBeenUsedToSend = true;
} else {
mCurrentDirection.SetValue(dom::RTCRtpTransceiverDirection::Recvonly);
}
} else {
if (jsepTransceiver->mSendTrack.GetActive()) {
if (mJsepTransceiver.mSendTrack.GetActive()) {
mCurrentDirection.SetValue(dom::RTCRtpTransceiverDirection::Sendonly);
mHasBeenUsedToSend = true;
} else {
@ -496,28 +496,25 @@ void RTCRtpTransceiver::SyncFromJsep() {
}
}
mShouldRemove = jsepTransceiver->IsRemoved();
mHasTransport = jsepTransceiver->HasLevel() && !jsepTransceiver->IsStopped();
mShouldRemove = mJsepTransceiver.IsRemoved();
mHasTransport = mJsepTransceiver.HasLevel() && !mJsepTransceiver.IsStopped();
}
void RTCRtpTransceiver::SyncToJsep() const {
void RTCRtpTransceiver::SyncToJsep(JsepSession& aSession) const {
MOZ_MTLOG(ML_DEBUG, mPc->GetHandle()
<< "[" << mMid.Ref() << "]: " << __FUNCTION__
<< " Syncing to JSEP transceiver");
auto jsepTransceiver = GetJsepTransceiver();
mReceiver->SyncToJsep(*jsepTransceiver);
mSender->SyncToJsep(*jsepTransceiver);
jsepTransceiver->mJsDirection = ToSdpDirection(mDirection);
if (mStopped) {
jsepTransceiver->Stop();
}
}
// TODO: Unify with SyncFromJsep
void RTCRtpTransceiver::SetJsepSession(JsepSession* aJsepSession) {
mJsepTransceiver = aJsepSession->GetTransceiver(mTransceiverId);
MOZ_RELEASE_ASSERT(mJsepTransceiver);
aSession.ApplyToTransceiver(
mTransceiverId, [this, self = RefPtr<const RTCRtpTransceiver>(this)](
JsepTransceiver& aTransceiver) {
mReceiver->SyncToJsep(aTransceiver);
mSender->SyncToJsep(aTransceiver);
aTransceiver.mJsDirection = ToSdpDirection(mDirection);
if (mStopped) {
aTransceiver.Stop();
}
});
}
void RTCRtpTransceiver::GetKind(nsAString& aKind) const {
@ -565,12 +562,6 @@ void RTCRtpTransceiver::SetDirectionInternal(
mDirection = aDirection;
}
void RTCRtpTransceiver::SetAddTrackMagic() {
// We do this immediately, without waiting for a SyncToJsep, because this is
// set at transceiver creation time.
GetJsepTransceiver()->SetAddTrackMagic();
}
bool RTCRtpTransceiver::ShouldRemove() const { return mShouldRemove; }
bool RTCRtpTransceiver::CanSendDTMF() const {
@ -589,8 +580,8 @@ bool RTCRtpTransceiver::CanSendDTMF() const {
// Ok, it looks like the connection is up and sending. Did we negotiate
// telephone-event?
JsepTrackNegotiatedDetails* details =
GetJsepTransceiver()->mSendTrack.GetNegotiatedDetails();
const JsepTrackNegotiatedDetails* details =
mJsepTransceiver.mSendTrack.GetNegotiatedDetails();
if (NS_WARN_IF(!details || !details->GetEncodingCount())) {
// What?
return false;
@ -648,12 +639,11 @@ static void JsepCodecDescToAudioCodecConfig(
// TODO: This and the next function probably should move to JsepTransceiver
Maybe<const std::vector<UniquePtr<JsepCodecDescription>>&>
RTCRtpTransceiver::GetNegotiatedSendCodecs() const {
auto jsepTransceiver = GetJsepTransceiver();
if (!jsepTransceiver->mSendTrack.GetActive()) {
if (!mJsepTransceiver.mSendTrack.GetActive()) {
return Nothing();
}
const auto* details = jsepTransceiver->mSendTrack.GetNegotiatedDetails();
const auto* details = mJsepTransceiver.mSendTrack.GetNegotiatedDetails();
if (!details) {
return Nothing();
}
@ -667,12 +657,11 @@ RTCRtpTransceiver::GetNegotiatedSendCodecs() const {
Maybe<const std::vector<UniquePtr<JsepCodecDescription>>&>
RTCRtpTransceiver::GetNegotiatedRecvCodecs() const {
auto jsepTransceiver = GetJsepTransceiver();
if (!jsepTransceiver->mRecvTrack.GetActive()) {
if (!mJsepTransceiver.mRecvTrack.GetActive()) {
return Nothing();
}
const auto* details = jsepTransceiver->mRecvTrack.GetNegotiatedDetails();
const auto* details = mJsepTransceiver.mRecvTrack.GetNegotiatedDetails();
if (!details) {
return Nothing();
}

View File

@ -102,25 +102,21 @@ class RTCRtpTransceiver : public nsISupports, public nsWrapperCache {
void Stop(ErrorResult& aRv);
void SetDirectionInternal(RTCRtpTransceiverDirection aDirection);
bool HasBeenUsedToSend() const { return mHasBeenUsedToSend; }
void SetAddTrackMagic();
bool CanSendDTMF() const;
bool Stopped() const { return mStopped; }
void SyncToJsep() const;
void SyncFromJsep();
void SetJsepSession(JsepSession* aJsepSession);
void SyncToJsep(JsepSession& aSession) const;
void SyncFromJsep(const JsepSession& aSession);
std::string GetMidAscii() const;
void SetDtlsTransport(RTCDtlsTransport* aDtlsTransport, bool aStable);
void RollbackToStableDtlsTransport();
std::string GetTransportId() const {
return mJsepTransceiver->mTransport.mTransportId;
return mJsepTransceiver.mTransport.mTransportId;
}
RefPtr<JsepTransceiver> GetJsepTransceiver() const {
return mJsepTransceiver;
}
JsepTransceiver& GetJsepTransceiver() { return mJsepTransceiver; }
bool IsVideo() const;
@ -193,7 +189,8 @@ class RTCRtpTransceiver : public nsISupports, public nsWrapperCache {
RefPtr<PeerConnectionImpl> mPc;
RefPtr<MediaTransportHandler> mTransportHandler;
const std::string mTransceiverId;
RefPtr<JsepTransceiver> mJsepTransceiver;
// Copy of latest from the JSEP engine.
JsepTransceiver mJsepTransceiver;
nsCOMPtr<nsISerialEventTarget> mStsThread;
// state for webrtc.org that is shared between all transceivers
RefPtr<WebrtcCallWrapper> mCallWrapper;

View File

@ -113,8 +113,8 @@ class JsepSession {
void ForEachCodec(UnaryFunction& function) {
std::for_each(Codecs().begin(), Codecs().end(), function);
for (auto& transceiver : GetTransceivers()) {
transceiver->mSendTrack.ForEachCodec(function);
transceiver->mRecvTrack.ForEachCodec(function);
transceiver.mSendTrack.ForEachCodec(function);
transceiver.mRecvTrack.ForEachCodec(function);
}
}
@ -122,24 +122,63 @@ class JsepSession {
void SortCodecs(BinaryPredicate& sorter) {
std::stable_sort(Codecs().begin(), Codecs().end(), sorter);
for (auto& transceiver : GetTransceivers()) {
transceiver->mSendTrack.SortCodecs(sorter);
transceiver->mRecvTrack.SortCodecs(sorter);
transceiver.mSendTrack.SortCodecs(sorter);
transceiver.mRecvTrack.SortCodecs(sorter);
}
}
// Returns transceivers in the order they were added.
virtual const std::vector<RefPtr<JsepTransceiver>>& GetTransceivers()
const = 0;
virtual std::vector<RefPtr<JsepTransceiver>>& GetTransceivers() = 0;
RefPtr<JsepTransceiver> GetTransceiver(const std::string& aId) const {
for (const auto& transceiver : GetTransceivers()) {
if (transceiver->GetUuid() == aId) {
return transceiver;
// Would be nice to have this return a Maybe containing the return of
// |aFunction|, but Maybe cannot contain a void.
template <typename UnaryFunction>
bool ApplyToTransceiver(const std::string& aId, UnaryFunction&& aFunction) {
for (auto& transceiver : GetTransceivers()) {
if (transceiver.GetUuid() == aId) {
std::forward<UnaryFunction>(aFunction)(transceiver);
return true;
}
}
return nullptr;
return false;
}
virtual void AddTransceiver(RefPtr<JsepTransceiver> transceiver) = 0;
template <typename UnaryFunction>
void ForEachTransceiver(UnaryFunction&& aFunction) {
for (auto& transceiver : GetTransceivers()) {
std::forward<UnaryFunction>(aFunction)(transceiver);
}
}
template <typename UnaryFunction>
void ForEachTransceiver(UnaryFunction&& aFunction) const {
for (const auto& transceiver : GetTransceivers()) {
std::forward<UnaryFunction>(aFunction)(transceiver);
}
}
Maybe<const JsepTransceiver> GetTransceiver(const std::string& aId) const {
for (const auto& transceiver : GetTransceivers()) {
if (transceiver.GetUuid() == aId) {
return Some(transceiver);
}
}
return Nothing();
}
template <typename MatchFunction>
Maybe<const JsepTransceiver> FindTransceiver(MatchFunction&& aFunc) const {
for (const auto& transceiver : GetTransceivers()) {
if (std::forward<MatchFunction>(aFunc)(transceiver)) {
return Some(transceiver);
}
}
return Nothing();
}
bool SetTransceiver(const JsepTransceiver& aNew) {
return ApplyToTransceiver(aNew.GetUuid(),
[aNew](JsepTransceiver& aOld) { aOld = aNew; });
}
virtual void AddTransceiver(const JsepTransceiver& transceiver) = 0;
class Result {
public:
@ -211,14 +250,14 @@ class JsepSession {
memset(sending, 0, sizeof(sending));
for (const auto& transceiver : GetTransceivers()) {
if (transceiver->mRecvTrack.GetActive() ||
transceiver->GetMediaType() == SdpMediaSection::kApplication) {
receiving[transceiver->mRecvTrack.GetMediaType()]++;
if (transceiver.mRecvTrack.GetActive() ||
transceiver.GetMediaType() == SdpMediaSection::kApplication) {
receiving[transceiver.mRecvTrack.GetMediaType()]++;
}
if (transceiver->mSendTrack.GetActive() ||
transceiver->GetMediaType() == SdpMediaSection::kApplication) {
sending[transceiver->mSendTrack.GetMediaType()]++;
if (transceiver.mSendTrack.GetActive() ||
transceiver.GetMediaType() == SdpMediaSection::kApplication) {
sending[transceiver.mSendTrack.GetMediaType()]++;
}
}
}
@ -230,6 +269,11 @@ class JsepSession {
void SetRtxIsAllowed(bool aRtxIsAllowed) { mRtxIsAllowed = aRtxIsAllowed; }
protected:
friend class JsepSessionTest;
// Returns transceivers in the order they were added.
virtual std::vector<JsepTransceiver>& GetTransceivers() = 0;
virtual const std::vector<JsepTransceiver>& GetTransceivers() const = 0;
const std::string mName;
JsepSignalingState mState;
uint32_t mNegotiations;

View File

@ -91,14 +91,6 @@ JsepSessionImpl::JsepSessionImpl(const JsepSessionImpl& aOrig)
: nullptr),
mSdpHelper(&mLastError),
mParser(new HybridSdpParser()) {
for (const auto& transceiver : aOrig.mTransceivers) {
// Deep copy
mTransceivers.push_back(new JsepTransceiver(*transceiver));
}
for (const auto& transceiver : aOrig.mOldTransceivers) {
// Deep copy
mOldTransceivers.push_back(new JsepTransceiver(*transceiver));
}
for (const auto& codec : aOrig.mSupportedCodecs) {
mSupportedCodecs.emplace_back(codec->Clone());
}
@ -145,20 +137,20 @@ JsepSessionImpl::GetLocalIceCredentials() const {
return result;
}
void JsepSessionImpl::AddTransceiver(RefPtr<JsepTransceiver> aTransceiver) {
void JsepSessionImpl::AddTransceiver(const JsepTransceiver& aTransceiver) {
mLastError.clear();
MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: Adding transceiver "
<< aTransceiver->GetUuid());
InitTransceiver(*aTransceiver);
MOZ_MTLOG(ML_DEBUG,
"[" << mName << "]: Adding transceiver " << aTransceiver.GetUuid());
#ifdef DEBUG
if (aTransceiver->GetMediaType() == SdpMediaSection::kApplication) {
if (aTransceiver.GetMediaType() == SdpMediaSection::kApplication) {
// Make sure we don't add more than one DataChannel transceiver
for (const auto& transceiver : mTransceivers) {
MOZ_ASSERT(transceiver->GetMediaType() != SdpMediaSection::kApplication);
MOZ_ASSERT(transceiver.GetMediaType() != SdpMediaSection::kApplication);
}
}
#endif
mTransceivers.push_back(aTransceiver);
InitTransceiver(mTransceivers.back());
}
void JsepSessionImpl::InitTransceiver(JsepTransceiver& aTransceiver) {
@ -409,9 +401,11 @@ JsepSession::Result JsepSessionImpl::CreateOffer(
NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
for (size_t level = 0;
JsepTransceiver* transceiver = GetTransceiverForLocal(level); ++level) {
Maybe<JsepTransceiver> transceiver = GetTransceiverForLocal(level);
++level) {
rv = CreateOfferMsection(options, *transceiver, sdp.get());
NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
SetTransceiver(*transceiver);
}
SetupBundle(sdp.get());
@ -568,7 +562,7 @@ JsepSession::Result JsepSessionImpl::CreateAnswer(
for (size_t i = 0; i < offer.GetMediaSectionCount(); ++i) {
// The transceivers are already in place, due to setRemote
JsepTransceiver* transceiver(GetTransceiverForLevel(i));
Maybe<JsepTransceiver> transceiver(GetTransceiverForLevel(i));
if (!transceiver) {
JSEP_SET_ERROR("No transceiver for level " << i);
MOZ_ASSERT(false);
@ -577,6 +571,7 @@ JsepSession::Result JsepSessionImpl::CreateAnswer(
rv = CreateAnswerMsection(options, *transceiver, offer.GetMediaSection(i),
sdp.get());
NS_ENSURE_SUCCESS(rv, dom::PCError::OperationError);
SetTransceiver(*transceiver);
}
// Ensure that each bundle-group starts with a mid that has a transport, in
@ -814,10 +809,7 @@ JsepSession::Result JsepSessionImpl::SetLocalDescription(
if (type == kJsepSdpOffer) {
// Save in case we need to rollback
mOldTransceivers.clear();
for (const auto& transceiver : mTransceivers) {
mOldTransceivers.push_back(new JsepTransceiver(*transceiver));
}
mOldTransceivers = mTransceivers;
}
SdpHelper::BundledMids bundledMids;
@ -832,7 +824,7 @@ JsepSession::Result JsepSessionImpl::SetLocalDescription(
}
for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) {
JsepTransceiver* transceiver(GetTransceiverForLevel(i));
Maybe<JsepTransceiver> transceiver(GetTransceiverForLevel(i));
if (!transceiver) {
MOZ_ASSERT(false);
JSEP_SET_ERROR("No transceiver for level " << i);
@ -845,6 +837,7 @@ JsepSession::Result JsepSessionImpl::SetLocalDescription(
if (mSdpHelper.MsectionIsDisabled(msection)) {
transceiver->mTransport.Close();
SetTransceiver(*transceiver);
continue;
}
@ -860,7 +853,7 @@ JsepSession::Result JsepSessionImpl::SetLocalDescription(
}
if (hasOwnTransport) {
EnsureHasOwnTransport(parsed->GetMediaSection(i), transceiver);
EnsureHasOwnTransport(parsed->GetMediaSection(i), *transceiver);
}
if (type == kJsepSdpOffer) {
@ -876,6 +869,7 @@ JsepSession::Result JsepSessionImpl::SetLocalDescription(
transceiver->SetBundleLevel(it->second->GetLevel());
}
}
SetTransceiver(*transceiver);
}
CopyBundleTransports();
@ -1024,15 +1018,14 @@ JsepSession::Result JsepSessionImpl::SetRemoteDescription(
iceOptions = parsed->GetAttributeList().GetIceOptions().mValues;
}
// Save in case we need to rollback.
if (type == kJsepSdpOffer) {
mOldTransceivers.clear();
for (const auto& transceiver : mTransceivers) {
mOldTransceivers.push_back(new JsepTransceiver(*transceiver));
if (!transceiver->IsNegotiated()) {
// Save in case we need to rollback.
mOldTransceivers = mTransceivers;
for (auto& transceiver : mTransceivers) {
if (!transceiver.IsNegotiated()) {
// We chose a level for this transceiver, but never negotiated it.
// Discard this state.
transceiver->ClearLevel();
transceiver.ClearLevel();
}
}
}
@ -1085,18 +1078,19 @@ nsresult JsepSessionImpl::HandleNegotiatedSession(
// First, set the bundle level on the transceivers
for (auto& [mid, transportOwner] : bundledMids) {
JsepTransceiver* bundledTransceiver = GetTransceiverForMid(mid);
Maybe<JsepTransceiver> bundledTransceiver = GetTransceiverForMid(mid);
if (!bundledTransceiver) {
JSEP_SET_ERROR("No transceiver for bundled mid " << mid);
return NS_ERROR_INVALID_ARG;
}
bundledTransceiver->SetBundleLevel(transportOwner->GetLevel());
SetTransceiver(*bundledTransceiver);
}
// Now walk through the m-sections, perform negotiation, and update the
// transceivers.
for (size_t i = 0; i < local->GetMediaSectionCount(); ++i) {
JsepTransceiver* transceiver(GetTransceiverForLevel(i));
Maybe<JsepTransceiver> transceiver(GetTransceiverForLevel(i));
if (!transceiver) {
MOZ_ASSERT(false);
JSEP_SET_ERROR("No transceiver for level " << i);
@ -1112,20 +1106,22 @@ nsresult JsepSessionImpl::HandleNegotiatedSession(
transceiver->mSendTrack.SetActive(false);
transceiver->mRecvTrack.SetActive(false);
transceiver->SetCanRecycle();
SetTransceiver(*transceiver);
// Do not clear mLevel yet! That will happen on the next negotiation.
continue;
}
rv = MakeNegotiatedTransceiver(remote->GetMediaSection(i),
local->GetMediaSection(i), transceiver);
local->GetMediaSection(i), *transceiver);
NS_ENSURE_SUCCESS(rv, rv);
SetTransceiver(*transceiver);
}
CopyBundleTransports();
std::vector<JsepTrack*> remoteTracks;
for (const auto& transceiver : mTransceivers) {
remoteTracks.push_back(&transceiver->mRecvTrack);
for (auto& transceiver : mTransceivers) {
remoteTracks.push_back(&transceiver.mRecvTrack);
}
JsepTrack::SetUniquePayloadTypes(remoteTracks);
@ -1139,14 +1135,14 @@ nsresult JsepSessionImpl::HandleNegotiatedSession(
nsresult JsepSessionImpl::MakeNegotiatedTransceiver(
const SdpMediaSection& remote, const SdpMediaSection& local,
JsepTransceiver* transceiver) {
JsepTransceiver& transceiver) {
const SdpMediaSection& answer = *mIsPendingOfferer ? remote : local;
bool sending = false;
bool receiving = false;
// JS could stop the transceiver after the answer was created.
if (!transceiver->IsStopped()) {
if (!transceiver.IsStopped()) {
if (*mIsPendingOfferer) {
receiving = answer.IsSending();
sending = answer.IsReceiving();
@ -1161,23 +1157,23 @@ nsresult JsepSessionImpl::MakeNegotiatedTransceiver(
<< local.GetMediaType() << " sending=" << sending
<< " receiving=" << receiving);
transceiver->SetNegotiated();
transceiver.SetNegotiated();
// Ensure that this is finalized in case we need to copy it below
nsresult rv =
FinalizeTransport(remote.GetAttributeList(), answer.GetAttributeList(),
&transceiver->mTransport);
&transceiver.mTransport);
NS_ENSURE_SUCCESS(rv, rv);
transceiver->mSendTrack.SetActive(sending);
rv = transceiver->mSendTrack.Negotiate(answer, remote, local);
transceiver.mSendTrack.SetActive(sending);
rv = transceiver.mSendTrack.Negotiate(answer, remote, local);
if (NS_FAILED(rv)) {
JSEP_SET_ERROR("Answer had no codecs in common with offer in m-section "
<< local.GetLevel());
return rv;
}
JsepTrack& recvTrack = transceiver->mRecvTrack;
JsepTrack& recvTrack = transceiver.mRecvTrack;
recvTrack.SetActive(receiving);
rv = recvTrack.Negotiate(answer, remote, local);
if (NS_FAILED(rv)) {
@ -1186,7 +1182,7 @@ nsresult JsepSessionImpl::MakeNegotiatedTransceiver(
return rv;
}
if (transceiver->HasBundleLevel() && recvTrack.GetSsrcs().empty() &&
if (transceiver.HasBundleLevel() && recvTrack.GetSsrcs().empty() &&
recvTrack.GetMediaType() != SdpMediaSection::kApplication) {
// TODO(bug 1105005): Once we have urn:ietf:params:rtp-hdrext:sdes:mid
// support, we should only fire this warning if that extension was not
@ -1197,7 +1193,7 @@ nsresult JsepSessionImpl::MakeNegotiatedTransceiver(
"dropped.");
}
if (transceiver->mTransport.mComponents == 2) {
if (transceiver.mTransport.mComponents == 2) {
// RTCP MUX or not.
// TODO(bug 1095743): verify that the PTs are consistent with mux.
MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: RTCP-MUX is off");
@ -1234,10 +1230,10 @@ nsresult JsepSessionImpl::MakeNegotiatedTransceiver(
}
void JsepSessionImpl::EnsureHasOwnTransport(const SdpMediaSection& msection,
JsepTransceiver* transceiver) {
JsepTransport& transport = transceiver->mTransport;
JsepTransceiver& transceiver) {
JsepTransport& transport = transceiver.mTransport;
if (!transceiver->HasOwnTransport()) {
if (!transceiver.HasOwnTransport()) {
// Transceiver didn't own this transport last time, it won't now either
transport.Close();
}
@ -1245,7 +1241,7 @@ void JsepSessionImpl::EnsureHasOwnTransport(const SdpMediaSection& msection,
transport.mLocalUfrag = msection.GetAttributeList().GetIceUfrag();
transport.mLocalPwd = msection.GetAttributeList().GetIcePwd();
transceiver->ClearBundleLevel();
transceiver.ClearBundleLevel();
if (!transport.mComponents) {
if (mSdpHelper.HasRtcp(msection.GetProtocol())) {
@ -1266,34 +1262,34 @@ void JsepSessionImpl::EnsureHasOwnTransport(const SdpMediaSection& msection,
void JsepSessionImpl::CopyBundleTransports() {
for (auto& transceiver : mTransceivers) {
if (transceiver->HasBundleLevel()) {
if (transceiver.HasBundleLevel()) {
MOZ_MTLOG(ML_DEBUG,
"[" << mName << "] Transceiver " << transceiver->GetLevel()
"[" << mName << "] Transceiver " << transceiver.GetLevel()
<< " is in a bundle; transceiver "
<< transceiver->BundleLevel() << " owns the transport.");
const JsepTransceiver* transportOwner =
GetTransceiverForLevel(transceiver->BundleLevel());
<< transceiver.BundleLevel() << " owns the transport.");
Maybe<const JsepTransceiver> transportOwner =
GetTransceiverForLevel(transceiver.BundleLevel());
MOZ_ASSERT(transportOwner);
if (transportOwner) {
transceiver->mTransport = transportOwner->mTransport;
transceiver.mTransport = transportOwner->mTransport;
}
} else if (transceiver->HasLevel()) {
} else if (transceiver.HasLevel()) {
MOZ_MTLOG(ML_DEBUG, "[" << mName << "] Transceiver "
<< transceiver->GetLevel()
<< transceiver.GetLevel()
<< " is not necessarily in a bundle.");
}
if (transceiver->HasLevel()) {
if (transceiver.HasLevel()) {
MOZ_MTLOG(ML_DEBUG,
"[" << mName << "] Transceiver " << transceiver->GetLevel()
<< " transport-id: " << transceiver->mTransport.mTransportId
<< " components: " << transceiver->mTransport.mComponents);
"[" << mName << "] Transceiver " << transceiver.GetLevel()
<< " transport-id: " << transceiver.mTransport.mTransportId
<< " components: " << transceiver.mTransport.mComponents);
}
}
}
nsresult JsepSessionImpl::FinalizeTransport(const SdpAttributeList& remote,
const SdpAttributeList& answer,
JsepTransport* transport) {
JsepTransport* transport) const {
if (!transport->mComponents) {
return NS_OK;
}
@ -1382,7 +1378,7 @@ nsresult JsepSessionImpl::CopyPreviousTransportParams(
// If newLocal is an offer, this will be the number of components we used
// last time, and if it is an answer, this will be the number of
// components we've decided we're using now.
JsepTransceiver* transceiver(GetTransceiverForLevel(i));
Maybe<const JsepTransceiver> transceiver(GetTransceiverForLevel(i));
if (!transceiver) {
MOZ_ASSERT(false);
JSEP_SET_ERROR("No transceiver for level " << i);
@ -1545,43 +1541,39 @@ nsresult JsepSessionImpl::SetRemoteDescriptionAnswer(JsepSdpType type,
return NS_OK;
}
JsepTransceiver* JsepSessionImpl::GetTransceiverForLevel(size_t level) const {
for (const auto& transceiver : mTransceivers) {
if (transceiver->HasLevel() && (transceiver->GetLevel() == level)) {
return transceiver.get();
}
}
return nullptr;
Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForLevel(
size_t level) const {
return FindTransceiver([level](const JsepTransceiver& transceiver) {
return transceiver.HasLevel() && (transceiver.GetLevel() == level);
});
}
JsepTransceiver* JsepSessionImpl::GetTransceiverForMid(
Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForMid(
const std::string& mid) const {
for (const auto& transceiver : mTransceivers) {
if (transceiver->IsAssociated() && (transceiver->GetMid() == mid)) {
return transceiver.get();
}
}
return nullptr;
return FindTransceiver([mid](const JsepTransceiver& transceiver) {
return transceiver.IsAssociated() && (transceiver.GetMid() == mid);
});
}
JsepTransceiver* JsepSessionImpl::GetTransceiverForLocal(size_t level) {
if (JsepTransceiver* transceiver = GetTransceiverForLevel(level)) {
Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForLocal(size_t level) {
if (Maybe<JsepTransceiver> transceiver = GetTransceiverForLevel(level)) {
if (transceiver->CanRecycle() &&
transceiver->GetMediaType() != SdpMediaSection::kApplication) {
// Attempt to recycle. If this fails, the old transceiver stays put.
transceiver->Disassociate();
JsepTransceiver* newTransceiver =
Maybe<JsepTransceiver> newTransceiver =
FindUnassociatedTransceiver(transceiver->GetMediaType(), false);
if (newTransceiver) {
newTransceiver->SetLevel(level);
transceiver->ClearLevel();
transceiver->mSendTrack.ClearRids();
SetTransceiver(*newTransceiver);
SetTransceiver(*transceiver);
return newTransceiver;
}
}
SetTransceiver(*transceiver);
return transceiver;
}
@ -1589,66 +1581,67 @@ JsepTransceiver* JsepSessionImpl::GetTransceiverForLocal(size_t level) {
// Look for an RTP transceiver
for (auto& transceiver : mTransceivers) {
if (transceiver->GetMediaType() != SdpMediaSection::kApplication &&
!transceiver->IsStopped() && !transceiver->HasLevel()) {
transceiver->SetLevel(level);
return transceiver.get();
if (transceiver.GetMediaType() != SdpMediaSection::kApplication &&
!transceiver.IsStopped() && !transceiver.HasLevel()) {
transceiver.SetLevel(level);
return Some(transceiver);
}
}
// Ok, look for a datachannel
for (auto& transceiver : mTransceivers) {
if (!transceiver->IsStopped() && !transceiver->HasLevel()) {
transceiver->SetLevel(level);
return transceiver.get();
if (!transceiver.IsStopped() && !transceiver.HasLevel()) {
transceiver.SetLevel(level);
return Some(transceiver);
}
}
return nullptr;
return Nothing();
}
JsepTransceiver* JsepSessionImpl::GetTransceiverForRemote(
Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverForRemote(
const SdpMediaSection& msection) {
size_t level = msection.GetLevel();
if (JsepTransceiver* transceiver = GetTransceiverForLevel(level)) {
Maybe<JsepTransceiver> transceiver = GetTransceiverForLevel(level);
if (transceiver) {
if (!transceiver->CanRecycle()) {
return transceiver;
}
transceiver->Disassociate();
transceiver->ClearLevel();
transceiver->mSendTrack.ClearRids();
SetTransceiver(*transceiver);
}
// No transceiver for |level|
JsepTransceiver* transceiver =
FindUnassociatedTransceiver(msection.GetMediaType(), true /*magic!*/);
transceiver = FindUnassociatedTransceiver(msection.GetMediaType(), true);
if (transceiver) {
transceiver->SetLevel(level);
SetTransceiver(*transceiver);
return transceiver;
}
// Make a new transceiver
RefPtr<JsepTransceiver> newTransceiver(new JsepTransceiver(
msection.GetMediaType(), *mUuidGen, SdpDirectionAttribute::kRecvonly));
newTransceiver->SetLevel(level);
newTransceiver->SetOnlyExistsBecauseOfSetRemote(true);
JsepTransceiver newTransceiver(msection.GetMediaType(), *mUuidGen,
SdpDirectionAttribute::kRecvonly);
newTransceiver.SetLevel(level);
newTransceiver.SetOnlyExistsBecauseOfSetRemote(true);
AddTransceiver(newTransceiver);
return newTransceiver.get();
return Some(mTransceivers.back());
}
JsepTransceiver* JsepSessionImpl::GetTransceiverWithTransport(
Maybe<JsepTransceiver> JsepSessionImpl::GetTransceiverWithTransport(
const std::string& transportId) const {
for (const auto& transceiver : mTransceivers) {
if (transceiver->HasOwnTransport() &&
(transceiver->mTransport.mTransportId == transportId)) {
MOZ_ASSERT(transceiver->HasLevel(),
if (transceiver.HasOwnTransport() &&
(transceiver.mTransport.mTransportId == transportId)) {
MOZ_ASSERT(transceiver.HasLevel(),
"Transceiver has a transport, but no level!");
return transceiver.get();
return Some(transceiver);
}
}
return nullptr;
return Nothing();
}
nsresult JsepSessionImpl::UpdateTransceiversFromRemoteDescription(
@ -1657,7 +1650,7 @@ nsresult JsepSessionImpl::UpdateTransceiversFromRemoteDescription(
for (size_t i = 0; i < remote.GetMediaSectionCount(); ++i) {
const SdpMediaSection& msection = remote.GetMediaSection(i);
JsepTransceiver* transceiver(GetTransceiverForRemote(msection));
Maybe<JsepTransceiver> transceiver(GetTransceiverForRemote(msection));
if (!transceiver) {
return NS_ERROR_FAILURE;
}
@ -1677,10 +1670,12 @@ nsresult JsepSessionImpl::UpdateTransceiversFromRemoteDescription(
transceiver->Disassociate();
// This cannot be rolled back.
transceiver->Stop();
SetTransceiver(*transceiver);
continue;
}
if (msection.GetMediaType() == SdpMediaSection::MediaType::kApplication) {
SetTransceiver(*transceiver);
continue;
}
@ -1696,43 +1691,43 @@ nsresult JsepSessionImpl::UpdateTransceiversFromRemoteDescription(
// msection is not sending. If the msection is sending, and there are no
// a=msid, the previously set default will stay.
transceiver->mRecvTrack.RecvTrackSetRemote(remote, msection);
SetTransceiver(*transceiver);
}
return NS_OK;
}
JsepTransceiver* JsepSessionImpl::FindUnassociatedTransceiver(
Maybe<JsepTransceiver> JsepSessionImpl::FindUnassociatedTransceiver(
SdpMediaSection::MediaType type, bool magic) {
// Look through transceivers that are not mapped to an m-section
for (auto& transceiver : mTransceivers) {
if (type == SdpMediaSection::kApplication &&
type == transceiver->GetMediaType()) {
transceiver->RestartDatachannelTransceiver();
return transceiver.get();
type == transceiver.GetMediaType()) {
transceiver.RestartDatachannelTransceiver();
return Some(transceiver);
}
if (!transceiver->IsStopped() && !transceiver->HasLevel() &&
(!magic || transceiver->HasAddTrackMagic()) &&
(transceiver->GetMediaType() == type)) {
return transceiver.get();
if (!transceiver.IsStopped() && !transceiver.HasLevel() &&
(!magic || transceiver.HasAddTrackMagic()) &&
(transceiver.GetMediaType() == type)) {
return Some(transceiver);
}
}
return nullptr;
return Nothing();
}
void JsepSessionImpl::RollbackLocalOffer() {
for (size_t i = 0; i < mTransceivers.size(); ++i) {
auto transceiver = mTransceivers[i];
auto& transceiver = mTransceivers[i];
if (mOldTransceivers.size() > i) {
transceiver->Rollback(*mOldTransceivers[i], false);
transceiver.Rollback(mOldTransceivers[i], false);
mOldTransceivers[i] = transceiver;
continue;
}
RefPtr<JsepTransceiver> temp(
new JsepTransceiver(transceiver->GetMediaType(), *mUuidGen));
InitTransceiver(*temp);
transceiver->Rollback(*temp, false);
JsepTransceiver temp(transceiver.GetMediaType(), *mUuidGen);
InitTransceiver(temp);
transceiver.Rollback(temp, false);
mOldTransceivers.push_back(transceiver);
}
@ -1741,10 +1736,10 @@ void JsepSessionImpl::RollbackLocalOffer() {
void JsepSessionImpl::RollbackRemoteOffer() {
for (size_t i = 0; i < mTransceivers.size(); ++i) {
auto transceiver = mTransceivers[i];
auto& transceiver = mTransceivers[i];
if (mOldTransceivers.size() > i) {
// Some stuff cannot be rolled back. Save this information.
transceiver->Rollback(*mOldTransceivers[i], true);
transceiver.Rollback(mOldTransceivers[i], true);
mOldTransceivers[i] = transceiver;
continue;
}
@ -1752,14 +1747,13 @@ void JsepSessionImpl::RollbackRemoteOffer() {
// New transceiver!
// We rollback even for transceivers we will remove, just to ensure we end
// up at the starting state.
RefPtr<JsepTransceiver> temp(
new JsepTransceiver(transceiver->GetMediaType(), *mUuidGen));
InitTransceiver(*temp);
transceiver->Rollback(*temp, true);
JsepTransceiver temp(transceiver.GetMediaType(), *mUuidGen);
InitTransceiver(temp);
transceiver.Rollback(temp, true);
if (transceiver->OnlyExistsBecauseOfSetRemote()) {
transceiver->Stop();
transceiver->SetRemoved();
if (transceiver.OnlyExistsBecauseOfSetRemote()) {
transceiver.Stop();
transceiver.SetRemoved();
}
mOldTransceivers.push_back(transceiver);
}
@ -2197,7 +2191,7 @@ JsepSession::Result JsepSessionImpl::AddRemoteIceCandidate(
return Result();
}
JsepTransceiver* transceiver = nullptr;
Maybe<JsepTransceiver> transceiver;
if (!mid.empty()) {
transceiver = GetTransceiverForMid(mid);
} else if (level.isSome()) {
@ -2249,7 +2243,8 @@ nsresult JsepSessionImpl::AddLocalIceCandidate(const std::string& candidate,
return NS_ERROR_UNEXPECTED;
}
JsepTransceiver* transceiver = GetTransceiverWithTransport(transportId);
Maybe<const JsepTransceiver> transceiver =
GetTransceiverWithTransport(transportId);
if (!transceiver || !transceiver->IsAssociated()) {
// mainly here to make some testing less complicated, but also just in case
return NS_OK;
@ -2292,20 +2287,20 @@ nsresult JsepSessionImpl::UpdateDefaultCandidate(
for (const auto& transceiver : mTransceivers) {
// We set the default address for bundled m-sections, but not candidate
// attributes. Ugh.
if (transceiver->mTransport.mTransportId == transportId) {
MOZ_ASSERT(transceiver->HasLevel(),
if (transceiver.mTransport.mTransportId == transportId) {
MOZ_ASSERT(transceiver.HasLevel(),
"Transceiver has a transport, but no level! "
"This should never happen.");
std::string defaultRtcpCandidateAddrCopy(defaultRtcpCandidateAddr);
if (mState == kJsepStateStable) {
if (transceiver->mTransport.mComponents == 1) {
if (transceiver.mTransport.mComponents == 1) {
// We know we're doing rtcp-mux by now. Don't create an rtcp attr.
defaultRtcpCandidateAddrCopy = "";
defaultRtcpCandidatePort = 0;
}
}
size_t level = transceiver->GetLevel();
size_t level = transceiver.GetLevel();
if (level >= sdp->GetMediaSectionCount()) {
MOZ_ASSERT(false, "Transceiver's level is too large!");
JSEP_SET_ERROR("Transceiver's level is too large!");
@ -2404,8 +2399,8 @@ bool JsepSessionImpl::CheckNegotiationNeeded() const {
MOZ_ASSERT(mState == kJsepStateStable);
for (const auto& transceiver : mTransceivers) {
if (transceiver->IsStopped()) {
if (transceiver->IsAssociated()) {
if (transceiver.IsStopped()) {
if (transceiver.IsAssociated()) {
MOZ_MTLOG(ML_DEBUG, "[" << mName
<< "]: Negotiation needed because of "
"stopped transceiver that still has a mid.");
@ -2414,7 +2409,7 @@ bool JsepSessionImpl::CheckNegotiationNeeded() const {
continue;
}
if (!transceiver->IsAssociated()) {
if (!transceiver.IsAssociated()) {
MOZ_MTLOG(ML_DEBUG, "[" << mName
<< "]: Negotiation needed because of "
"unassociated (but not stopped) transceiver.");
@ -2428,16 +2423,16 @@ bool JsepSessionImpl::CheckNegotiationNeeded() const {
continue;
}
if (!transceiver->HasLevel()) {
if (!transceiver.HasLevel()) {
MOZ_CRASH("Associated transceivers should always have a level.");
continue;
}
if (transceiver->GetMediaType() == SdpMediaSection::kApplication) {
if (transceiver.GetMediaType() == SdpMediaSection::kApplication) {
continue;
}
size_t level = transceiver->GetLevel();
size_t level = transceiver.GetLevel();
if (NS_WARN_IF(mCurrentLocalDescription->GetMediaSectionCount() <= level) ||
NS_WARN_IF(mCurrentRemoteDescription->GetMediaSectionCount() <=
level)) {
@ -2450,7 +2445,7 @@ bool JsepSessionImpl::CheckNegotiationNeeded() const {
const SdpMediaSection& remote =
mCurrentRemoteDescription->GetMediaSection(level);
if (transceiver->mJsDirection & sdp::kSend) {
if (transceiver.mJsDirection & sdp::kSend) {
std::vector<std::string> sdpMsids;
if (local.GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) {
for (const auto& msidAttr : local.GetAttributeList().GetMsid().mMsids) {
@ -2462,7 +2457,7 @@ bool JsepSessionImpl::CheckNegotiationNeeded() const {
std::sort(sdpMsids.begin(), sdpMsids.end());
std::vector<std::string> jsepMsids;
for (const auto& jsepMsid : transceiver->mSendTrack.GetStreamIds()) {
for (const auto& jsepMsid : transceiver.mSendTrack.GetStreamIds()) {
jsepMsids.push_back(jsepMsid);
}
std::sort(jsepMsids.begin(), jsepMsids.end());
@ -2489,8 +2484,8 @@ bool JsepSessionImpl::CheckNegotiationNeeded() const {
}
if (mIsCurrentOfferer.isSome() && *mIsCurrentOfferer) {
if ((local.GetDirection() != transceiver->mJsDirection) &&
reverse(remote.GetDirection()) != transceiver->mJsDirection) {
if ((local.GetDirection() != transceiver.mJsDirection) &&
reverse(remote.GetDirection()) != transceiver.mJsDirection) {
MOZ_MTLOG(ML_DEBUG, "[" << mName
<< "]: Negotiation needed because "
"the direction on our offer, and the remote "
@ -2499,7 +2494,7 @@ bool JsepSessionImpl::CheckNegotiationNeeded() const {
return true;
}
} else if (local.GetDirection() !=
(transceiver->mJsDirection & reverse(remote.GetDirection()))) {
(transceiver.mJsDirection & reverse(remote.GetDirection()))) {
MOZ_MTLOG(
ML_DEBUG,
"[" << mName

View File

@ -57,6 +57,11 @@ class JsepSessionCopyableStuff {
std::vector<std::pair<size_t, std::string>> mLastSdpParsingErrors;
bool mEncodeTrackId = true;
SsrcGenerator mSsrcGenerator;
// !!!NOT INDEXED BY LEVEL!!! The level mapping is done with
// JsepTransceiver::mLevel. The keys are UUIDs.
std::vector<JsepTransceiver> mTransceivers;
// So we can rollback. Not as simple as just going back to the old, though...
std::vector<JsepTransceiver> mOldTransceivers;
};
class JsepSessionImpl : public JsepSession, public JsepSessionCopyableStuff {
@ -167,16 +172,7 @@ class JsepSessionImpl : public JsepSession, public JsepSessionCopyableStuff {
virtual std::set<std::pair<std::string, std::string>> GetLocalIceCredentials()
const override;
virtual const std::vector<RefPtr<JsepTransceiver>>& GetTransceivers()
const override {
return mTransceivers;
}
virtual std::vector<RefPtr<JsepTransceiver>>& GetTransceivers() override {
return mTransceivers;
}
virtual void AddTransceiver(RefPtr<JsepTransceiver> transceiver) override;
virtual void AddTransceiver(const JsepTransceiver& transceiver) override;
virtual bool CheckNegotiationNeeded() const override;
@ -185,6 +181,15 @@ class JsepSessionImpl : public JsepSession, public JsepSessionCopyableStuff {
override;
private:
friend class JsepSessionTest;
virtual const std::vector<JsepTransceiver>& GetTransceivers() const override {
return mTransceivers;
}
virtual std::vector<JsepTransceiver>& GetTransceivers() override {
return mTransceivers;
}
// Non-const so it can set mLastError
nsresult CreateGenericSDP(UniquePtr<Sdp>* sdp);
void AddExtmap(SdpMediaSection* msection);
@ -208,16 +213,17 @@ class JsepSessionImpl : public JsepSession, public JsepSessionCopyableStuff {
nsresult ValidateOffer(const Sdp& offer);
nsresult ValidateAnswer(const Sdp& offer, const Sdp& answer);
nsresult UpdateTransceiversFromRemoteDescription(const Sdp& remote);
JsepTransceiver* GetTransceiverForLevel(size_t level) const;
JsepTransceiver* GetTransceiverForMid(const std::string& mid) const;
JsepTransceiver* GetTransceiverForLocal(size_t level);
JsepTransceiver* GetTransceiverForRemote(const SdpMediaSection& msection);
JsepTransceiver* GetTransceiverWithTransport(
Maybe<JsepTransceiver> GetTransceiverForLevel(size_t level) const;
Maybe<JsepTransceiver> GetTransceiverForMid(const std::string& mid) const;
Maybe<JsepTransceiver> GetTransceiverForLocal(size_t level);
Maybe<JsepTransceiver> GetTransceiverForRemote(
const SdpMediaSection& msection);
Maybe<JsepTransceiver> GetTransceiverWithTransport(
const std::string& transportId) const;
// The w3c and IETF specs have a lot of "magical" behavior that happens when
// addTrack is used. This was a deliberate design choice. Sadface.
JsepTransceiver* FindUnassociatedTransceiver(SdpMediaSection::MediaType type,
bool magic);
Maybe<JsepTransceiver> FindUnassociatedTransceiver(
SdpMediaSection::MediaType type, bool magic);
// Called for rollback of local description
void RollbackLocalOffer();
// Called for rollback of remote description
@ -241,14 +247,14 @@ class JsepSessionImpl : public JsepSession, public JsepSessionCopyableStuff {
SdpSetupAttribute::Role* rolep);
nsresult MakeNegotiatedTransceiver(const SdpMediaSection& remote,
const SdpMediaSection& local,
JsepTransceiver* transceiverOut);
JsepTransceiver& transceiverOut);
void EnsureHasOwnTransport(const SdpMediaSection& msection,
JsepTransceiver* transceiver);
JsepTransceiver& transceiver);
void CopyBundleTransports();
nsresult FinalizeTransport(const SdpAttributeList& remote,
const SdpAttributeList& answer,
JsepTransport* transport);
JsepTransport* transport) const;
nsresult GetNegotiatedBundledMids(SdpHelper::BundledMids* bundledMids);
@ -263,12 +269,6 @@ class JsepSessionImpl : public JsepSession, public JsepSessionCopyableStuff {
void InitTransceiver(JsepTransceiver& aTransceiver);
// !!!NOT INDEXED BY LEVEL!!! The level mapping is done with
// JsepTransceiver::mLevel. The keys are UUIDs.
std::vector<RefPtr<JsepTransceiver>> mTransceivers;
// So we can rollback. Not as simple as just going back to the old, though...
std::vector<RefPtr<JsepTransceiver>> mOldTransceivers;
UniquePtr<JsepUuidGenerator> mUuidGen;
UniquePtr<Sdp> mGeneratedOffer; // Created but not set.
UniquePtr<Sdp> mGeneratedAnswer; // Created but not set.

View File

@ -13,8 +13,6 @@
#include "jsep/JsepTransport.h"
#include "jsep/JsepTrack.h"
#include <mozilla/OwningNonNull.h>
#include "nsISupportsImpl.h"
#include "nsError.h"
namespace mozilla {
@ -27,9 +25,6 @@ class JsepUuidGenerator {
};
class JsepTransceiver {
private:
~JsepTransceiver(){};
public:
explicit JsepTransceiver(SdpMediaSection::MediaType type,
JsepUuidGenerator& aUuidGen,
@ -51,24 +46,12 @@ class JsepTransceiver {
}
}
// Can't use default copy c'tor because of the refcount members. Ugh.
JsepTransceiver(const JsepTransceiver& orig)
: mJsDirection(orig.mJsDirection),
mSendTrack(orig.mSendTrack),
mRecvTrack(orig.mRecvTrack),
mTransport(orig.mTransport),
mUuid(orig.mUuid),
mMid(orig.mMid),
mLevel(orig.mLevel),
mBundleLevel(orig.mBundleLevel),
mAddTrackMagic(orig.mAddTrackMagic),
mOnlyExistsBecauseOfSetRemote(orig.mOnlyExistsBecauseOfSetRemote),
mStopped(orig.mStopped),
mRemoved(orig.mRemoved),
mNegotiated(orig.mNegotiated),
mCanRecycle(orig.mCanRecycle) {}
JsepTransceiver(const JsepTransceiver& orig) = default;
JsepTransceiver(JsepTransceiver&& orig) = default;
JsepTransceiver& operator=(const JsepTransceiver& aRhs) = default;
JsepTransceiver& operator=(JsepTransceiver&& aRhs) = default;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JsepTransceiver);
~JsepTransceiver() = default;
void Rollback(JsepTransceiver& oldTransceiver, bool aRemote) {
MOZ_ASSERT(oldTransceiver.GetMediaType() == GetMediaType());
@ -130,6 +113,7 @@ class JsepTransceiver {
void RestartDatachannelTransceiver() {
MOZ_RELEASE_ASSERT(GetMediaType() == SdpMediaSection::kApplication);
mStopped = false;
mCanRecycle = false;
}
void SetRemoved() { mRemoved = true; }

View File

@ -45,7 +45,8 @@ interface PeerConnectionImpl {
[Throws]
RTCRtpTransceiver addTransceiver(RTCRtpTransceiverInit init,
DOMString kind,
MediaStreamTrack? sendTrack);
MediaStreamTrack? sendTrack,
boolean addTrackMagic);
sequence<RTCRtpTransceiver> getTransceivers();
[Throws]

View File

@ -38,8 +38,6 @@ interface RTCRtpTransceiver {
// TODO: bug 1396922
// undefined setCodecPreferences(sequence<RTCRtpCodecCapability> codecs);
[ChromeOnly]
undefined setAddTrackMagic();
[ChromeOnly]
undefined setDirectionInternal(RTCRtpTransceiverDirection direction);

File diff suppressed because it is too large Load Diff