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"); transceiver.setDirectionInternal("sendonly");
} }
} else { } else {
transceiver = this._addTransceiverNoEvents(track, { transceiver = this._addTransceiverNoEvents(
track,
{
streams, streams,
direction: "sendrecv", direction: "sendrecv",
}); },
transceiver.setAddTrackMagic(); true
);
} }
this.updateNegotiationNeeded(); this.updateNegotiationNeeded();
@ -1431,7 +1434,7 @@ export class RTCPeerConnection {
this.updateNegotiationNeeded(); this.updateNegotiationNeeded();
} }
_addTransceiverNoEvents(sendTrackOrKind, init) { _addTransceiverNoEvents(sendTrackOrKind, init, addTrackMagic) {
let sendTrack = null; let sendTrack = null;
let kind; let kind;
if (typeof sendTrackOrKind == "string") { if (typeof sendTrackOrKind == "string") {
@ -1449,7 +1452,7 @@ export class RTCPeerConnection {
} }
try { try {
return this._pc.addTransceiver(init, kind, sendTrack); return this._pc.addTransceiver(init, kind, sendTrack, addTrackMagic);
} catch (e) { } catch (e) {
// Exceptions thrown by c++ code do not propagate. In most cases, that's // 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 // fine because we're using Promises, which can be copied. But this is

View file

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

View file

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

View file

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

View file

@ -74,10 +74,6 @@ RTCRtpSender::RTCRtpSender(nsPIDOMWindowInner* aWindow, PeerConnectionImpl* aPc,
if (aConduit->type() == MediaSessionConduit::AUDIO) { if (aConduit->type() == MediaSessionConduit::AUDIO) {
mDtmf = new RTCDTMFSender(aWindow, mTransceiver); mDtmf = new RTCDTMFSender(aWindow, mTransceiver);
GetJsepTransceiver().mSendTrack.SetMaxEncodings(1);
} else {
GetJsepTransceiver().mSendTrack.SetMaxEncodings(
webrtc::kMaxSimulcastStreams);
} }
mPipeline->SetTrack(mSenderTrack); mPipeline->SetTrack(mSenderTrack);
@ -91,7 +87,7 @@ RTCRtpSender::RTCRtpSender(nsPIDOMWindowInner* aWindow, PeerConnectionImpl* aPc,
if (aEncodings.Length()) { if (aEncodings.Length()) {
// This sender was created by addTransceiver with sendEncodings. // This sender was created by addTransceiver with sendEncodings.
mParameters.mEncodings = aEncodings; mParameters.mEncodings = aEncodings;
SetJsepRids(mParameters); mSimulcastEnvelopeSet = true;
mozilla::glean::rtcrtpsender::used_sendencodings.AddToNumerator(1); mozilla::glean::rtcrtpsender::used_sendencodings.AddToNumerator(1);
} else { } else {
// This sender was created by addTrack, sRD(offer), or addTransceiver // This sender was created by addTrack, sRD(offer), or addTransceiver
@ -724,7 +720,7 @@ already_AddRefed<Promise> RTCRtpSender::SetParameters(
MaybeUpdateConduit(); MaybeUpdateConduit();
if (compatModeAllowedRidChange) { if (compatModeAllowedRidChange) {
SetJsepRids(paramsCopy); mSimulcastEnvelopeSet = true;
} }
// If the media stack is successfully configured with parameters, // 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) { void RTCRtpSender::GetParameters(RTCRtpSendParameters& aParameters) {
MOZ_ASSERT(mParameters.mEncodings.Length()); MOZ_ASSERT(mParameters.mEncodings.Length());
// If sender.[[LastReturnedParameters]] is not null, return // If sender.[[LastReturnedParameters]] is not null, return
@ -1206,10 +1186,10 @@ void RTCRtpSender::SetTrack(const RefPtr<MediaStreamTrack>& aTrack) {
mSenderTrack = aTrack; mSenderTrack = aTrack;
SeamlessTrackSwitch(aTrack); SeamlessTrackSwitch(aTrack);
if (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 // However, an RtpTransceiver MUST NOT be removed if a track was attached
// to the RtpTransceiver via the addTrack method. // 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); 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() { Maybe<RTCRtpSender::VideoConfig> RTCRtpSender::GetNewVideoConfig() {
@ -1579,7 +1582,7 @@ RefPtr<MediaPipelineTransmit> RTCRtpSender::GetPipeline() const {
std::string RTCRtpSender::GetMid() const { return mTransceiver->GetMidAscii(); } std::string RTCRtpSender::GetMid() const { return mTransceiver->GetMidAscii(); }
JsepTransceiver& RTCRtpSender::GetJsepTransceiver() { JsepTransceiver& RTCRtpSender::GetJsepTransceiver() {
return *mTransceiver->GetJsepTransceiver(); return mTransceiver->GetJsepTransceiver();
} }
void RTCRtpSender::UpdateDtmfSender() { void RTCRtpSender::UpdateDtmfSender() {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -13,8 +13,6 @@
#include "jsep/JsepTransport.h" #include "jsep/JsepTransport.h"
#include "jsep/JsepTrack.h" #include "jsep/JsepTrack.h"
#include <mozilla/OwningNonNull.h>
#include "nsISupportsImpl.h"
#include "nsError.h" #include "nsError.h"
namespace mozilla { namespace mozilla {
@ -27,9 +25,6 @@ class JsepUuidGenerator {
}; };
class JsepTransceiver { class JsepTransceiver {
private:
~JsepTransceiver(){};
public: public:
explicit JsepTransceiver(SdpMediaSection::MediaType type, explicit JsepTransceiver(SdpMediaSection::MediaType type,
JsepUuidGenerator& aUuidGen, 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) = default;
JsepTransceiver(const JsepTransceiver& orig) JsepTransceiver(JsepTransceiver&& orig) = default;
: mJsDirection(orig.mJsDirection), JsepTransceiver& operator=(const JsepTransceiver& aRhs) = default;
mSendTrack(orig.mSendTrack), JsepTransceiver& operator=(JsepTransceiver&& aRhs) = default;
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) {}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JsepTransceiver); ~JsepTransceiver() = default;
void Rollback(JsepTransceiver& oldTransceiver, bool aRemote) { void Rollback(JsepTransceiver& oldTransceiver, bool aRemote) {
MOZ_ASSERT(oldTransceiver.GetMediaType() == GetMediaType()); MOZ_ASSERT(oldTransceiver.GetMediaType() == GetMediaType());
@ -130,6 +113,7 @@ class JsepTransceiver {
void RestartDatachannelTransceiver() { void RestartDatachannelTransceiver() {
MOZ_RELEASE_ASSERT(GetMediaType() == SdpMediaSection::kApplication); MOZ_RELEASE_ASSERT(GetMediaType() == SdpMediaSection::kApplication);
mStopped = false; mStopped = false;
mCanRecycle = false;
} }
void SetRemoved() { mRemoved = true; } void SetRemoved() { mRemoved = true; }

View file

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

View file

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

File diff suppressed because it is too large Load diff