diff options
Diffstat (limited to 'dom/media/webrtc/jsapi')
-rw-r--r-- | dom/media/webrtc/jsapi/MediaTransportHandler.cpp | 62 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/MediaTransportHandler.h | 15 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/MediaTransportHandlerIPC.cpp | 10 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/MediaTransportParent.cpp | 14 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/PeerConnectionImpl.cpp | 562 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/PeerConnectionImpl.h | 42 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/RTCDtlsTransport.cpp | 7 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/RTCDtlsTransport.h | 3 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/RTCIceTransport.cpp | 60 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/RTCIceTransport.h | 53 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/RTCRtpReceiver.cpp | 4 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/RTCRtpSender.cpp | 1 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/RTCRtpTransceiver.cpp | 33 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/RTCRtpTransceiver.h | 3 | ||||
-rw-r--r-- | dom/media/webrtc/jsapi/moz.build | 2 |
15 files changed, 643 insertions, 228 deletions
diff --git a/dom/media/webrtc/jsapi/MediaTransportHandler.cpp b/dom/media/webrtc/jsapi/MediaTransportHandler.cpp index 3ee95f36f6..cf95ef41d6 100644 --- a/dom/media/webrtc/jsapi/MediaTransportHandler.cpp +++ b/dom/media/webrtc/jsapi/MediaTransportHandler.cpp @@ -156,9 +156,9 @@ class MediaTransportHandlerSTS : public MediaTransportHandler, using MediaTransportHandler::OnRtcpStateChange; using MediaTransportHandler::OnStateChange; - void OnGatheringStateChange(NrIceCtx* aIceCtx, - NrIceCtx::GatheringState aState); - void OnConnectionStateChange(NrIceCtx* aIceCtx, + void OnGatheringStateChange(const std::string& aTransportId, + NrIceMediaStream::GatheringState aState); + void OnConnectionStateChange(NrIceMediaStream* aIceStream, NrIceCtx::ConnectionState aState); void OnCandidateFound(NrIceMediaStream* aStream, const std::string& aCandidate, @@ -589,8 +589,6 @@ void MediaTransportHandlerSTS::CreateIceCtx(const std::string& aName) { __func__); } - mIceCtx->SignalGatheringStateChange.connect( - this, &MediaTransportHandlerSTS::OnGatheringStateChange); mIceCtx->SignalConnectionStateChange.connect( this, &MediaTransportHandlerSTS::OnConnectionStateChange); @@ -784,6 +782,8 @@ void MediaTransportHandlerSTS::EnsureProvisionalTransport( stream->SignalCandidate.connect( this, &MediaTransportHandlerSTS::OnCandidateFound); + stream->SignalGatheringStateChange.connect( + this, &MediaTransportHandlerSTS::OnGatheringStateChange); } // Begins an ICE restart if this stream has a different ufrag/pwd @@ -1181,31 +1181,31 @@ void MediaTransportHandler::OnAlpnNegotiated(const std::string& aAlpn) { } void MediaTransportHandler::OnGatheringStateChange( - dom::RTCIceGatheringState aState) { + const std::string& aTransportId, dom::RTCIceGathererState aState) { if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) { mCallbackThread->Dispatch( // This is being called from sigslot, which does not hold a strong ref. WrapRunnable(this, &MediaTransportHandler::OnGatheringStateChange, - aState), + aTransportId, aState), NS_DISPATCH_NORMAL); return; } - SignalGatheringStateChange(aState); + SignalGatheringStateChange(aTransportId, aState); } void MediaTransportHandler::OnConnectionStateChange( - dom::RTCIceConnectionState aState) { + const std::string& aTransportId, dom::RTCIceTransportState aState) { if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) { mCallbackThread->Dispatch( // This is being called from sigslot, which does not hold a strong ref. WrapRunnable(this, &MediaTransportHandler::OnConnectionStateChange, - aState), + aTransportId, aState), NS_DISPATCH_NORMAL); return; } - SignalConnectionStateChange(aState); + SignalConnectionStateChange(aTransportId, aState); } void MediaTransportHandler::OnPacketReceived(const std::string& aTransportId, @@ -1583,48 +1583,48 @@ RefPtr<TransportFlow> MediaTransportHandlerSTS::CreateTransportFlow( return flow; } -static mozilla::dom::RTCIceGatheringState toDomIceGatheringState( - NrIceCtx::GatheringState aState) { +static mozilla::dom::RTCIceGathererState toDomIceGathererState( + NrIceMediaStream::GatheringState aState) { switch (aState) { - case NrIceCtx::ICE_CTX_GATHER_INIT: - return dom::RTCIceGatheringState::New; - case NrIceCtx::ICE_CTX_GATHER_STARTED: - return dom::RTCIceGatheringState::Gathering; - case NrIceCtx::ICE_CTX_GATHER_COMPLETE: - return dom::RTCIceGatheringState::Complete; + case NrIceMediaStream::ICE_STREAM_GATHER_INIT: + return dom::RTCIceGathererState::New; + case NrIceMediaStream::ICE_STREAM_GATHER_STARTED: + return dom::RTCIceGathererState::Gathering; + case NrIceMediaStream::ICE_STREAM_GATHER_COMPLETE: + return dom::RTCIceGathererState::Complete; } MOZ_CRASH(); } void MediaTransportHandlerSTS::OnGatheringStateChange( - NrIceCtx* aIceCtx, NrIceCtx::GatheringState aState) { - OnGatheringStateChange(toDomIceGatheringState(aState)); + const std::string& aTransportId, NrIceMediaStream::GatheringState aState) { + OnGatheringStateChange(aTransportId, toDomIceGathererState(aState)); } -static mozilla::dom::RTCIceConnectionState toDomIceConnectionState( +static mozilla::dom::RTCIceTransportState toDomIceTransportState( NrIceCtx::ConnectionState aState) { switch (aState) { case NrIceCtx::ICE_CTX_INIT: - return dom::RTCIceConnectionState::New; + return dom::RTCIceTransportState::New; case NrIceCtx::ICE_CTX_CHECKING: - return dom::RTCIceConnectionState::Checking; + return dom::RTCIceTransportState::Checking; case NrIceCtx::ICE_CTX_CONNECTED: - return dom::RTCIceConnectionState::Connected; + return dom::RTCIceTransportState::Connected; case NrIceCtx::ICE_CTX_COMPLETED: - return dom::RTCIceConnectionState::Completed; + return dom::RTCIceTransportState::Completed; case NrIceCtx::ICE_CTX_FAILED: - return dom::RTCIceConnectionState::Failed; + return dom::RTCIceTransportState::Failed; case NrIceCtx::ICE_CTX_DISCONNECTED: - return dom::RTCIceConnectionState::Disconnected; + return dom::RTCIceTransportState::Disconnected; case NrIceCtx::ICE_CTX_CLOSED: - return dom::RTCIceConnectionState::Closed; + return dom::RTCIceTransportState::Closed; } MOZ_CRASH(); } void MediaTransportHandlerSTS::OnConnectionStateChange( - NrIceCtx* aIceCtx, NrIceCtx::ConnectionState aState) { - OnConnectionStateChange(toDomIceConnectionState(aState)); + NrIceMediaStream* aIceStream, NrIceCtx::ConnectionState aState) { + OnConnectionStateChange(aIceStream->GetId(), toDomIceTransportState(aState)); } // The stuff below here will eventually go into the MediaTransportChild class diff --git a/dom/media/webrtc/jsapi/MediaTransportHandler.h b/dom/media/webrtc/jsapi/MediaTransportHandler.h index a776cb6fd7..100eff019e 100644 --- a/dom/media/webrtc/jsapi/MediaTransportHandler.h +++ b/dom/media/webrtc/jsapi/MediaTransportHandler.h @@ -12,7 +12,8 @@ #include "transport/dtlsidentity.h" // For DtlsDigest #include "mozilla/dom/RTCPeerConnectionBinding.h" #include "mozilla/dom/RTCConfigurationBinding.h" -#include "transport/nricectx.h" // Need some enums +#include "mozilla/dom/RTCIceTransportBinding.h" // RTCIceTransportState +#include "transport/nricectx.h" // Need some enums #include "common/CandidateInfo.h" #include "transport/nr_socket_proxy_config.h" #include "RTCStatsReport.h" @@ -122,8 +123,10 @@ class MediaTransportHandler { sigslot::signal2<const std::string&, const CandidateInfo&> SignalCandidate; sigslot::signal2<const std::string&, bool> SignalAlpnNegotiated; - sigslot::signal1<dom::RTCIceGatheringState> SignalGatheringStateChange; - sigslot::signal1<dom::RTCIceConnectionState> SignalConnectionStateChange; + sigslot::signal2<const std::string&, dom::RTCIceGathererState> + SignalGatheringStateChange; + sigslot::signal2<const std::string&, dom::RTCIceTransportState> + SignalConnectionStateChange; sigslot::signal2<const std::string&, const MediaPacket&> SignalPacketReceived; sigslot::signal2<const std::string&, const MediaPacket&> @@ -142,8 +145,10 @@ class MediaTransportHandler { void OnCandidate(const std::string& aTransportId, const CandidateInfo& aCandidateInfo); void OnAlpnNegotiated(const std::string& aAlpn); - void OnGatheringStateChange(dom::RTCIceGatheringState aState); - void OnConnectionStateChange(dom::RTCIceConnectionState aState); + void OnGatheringStateChange(const std::string& aTransportId, + dom::RTCIceGathererState aState); + void OnConnectionStateChange(const std::string& aTransportId, + dom::RTCIceTransportState aState); void OnPacketReceived(const std::string& aTransportId, const MediaPacket& aPacket); void OnEncryptedSending(const std::string& aTransportId, diff --git a/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.cpp b/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.cpp index 847e9fd3de..4d13ae0ac0 100644 --- a/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.cpp +++ b/dom/media/webrtc/jsapi/MediaTransportHandlerIPC.cpp @@ -406,21 +406,21 @@ mozilla::ipc::IPCResult MediaTransportChild::RecvOnAlpnNegotiated( } mozilla::ipc::IPCResult MediaTransportChild::RecvOnGatheringStateChange( - const int& state) { + const string& transportId, const int& state) { MutexAutoLock lock(mMutex); if (mUser) { - mUser->OnGatheringStateChange( - static_cast<dom::RTCIceGatheringState>(state)); + mUser->OnGatheringStateChange(transportId, + static_cast<dom::RTCIceGathererState>(state)); } return ipc::IPCResult::Ok(); } mozilla::ipc::IPCResult MediaTransportChild::RecvOnConnectionStateChange( - const int& state) { + const string& transportId, const int& state) { MutexAutoLock lock(mMutex); if (mUser) { mUser->OnConnectionStateChange( - static_cast<dom::RTCIceConnectionState>(state)); + transportId, static_cast<dom::RTCIceTransportState>(state)); } return ipc::IPCResult::Ok(); } diff --git a/dom/media/webrtc/jsapi/MediaTransportParent.cpp b/dom/media/webrtc/jsapi/MediaTransportParent.cpp index 31d0b9f30f..4bbd75b41c 100644 --- a/dom/media/webrtc/jsapi/MediaTransportParent.cpp +++ b/dom/media/webrtc/jsapi/MediaTransportParent.cpp @@ -49,14 +49,16 @@ class MediaTransportParent::Impl : public sigslot::has_slots<> { NS_ENSURE_TRUE_VOID(mParent->SendOnAlpnNegotiated(aAlpn)); } - void OnGatheringStateChange(dom::RTCIceGatheringState aState) { - NS_ENSURE_TRUE_VOID( - mParent->SendOnGatheringStateChange(static_cast<int>(aState))); + void OnGatheringStateChange(const std::string& aTransportId, + dom::RTCIceGathererState aState) { + NS_ENSURE_TRUE_VOID(mParent->SendOnGatheringStateChange( + aTransportId, static_cast<int>(aState))); } - void OnConnectionStateChange(dom::RTCIceConnectionState aState) { - NS_ENSURE_TRUE_VOID( - mParent->SendOnConnectionStateChange(static_cast<int>(aState))); + void OnConnectionStateChange(const std::string& aTransportId, + dom::RTCIceTransportState aState) { + NS_ENSURE_TRUE_VOID(mParent->SendOnConnectionStateChange( + aTransportId, static_cast<int>(aState))); } void OnPacketReceived(const std::string& aTransportId, diff --git a/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp b/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp index 985100153a..9afa7e5dd2 100644 --- a/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp +++ b/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp @@ -85,6 +85,7 @@ #include "mozilla/dom/RTCCertificate.h" #include "mozilla/dom/RTCSctpTransportBinding.h" // RTCSctpTransportState #include "mozilla/dom/RTCDtlsTransportBinding.h" // RTCDtlsTransportState +#include "mozilla/dom/RTCIceTransportBinding.h" // RTCIceTransportState #include "mozilla/dom/RTCRtpReceiverBinding.h" #include "mozilla/dom/RTCRtpSenderBinding.h" #include "mozilla/dom/RTCStatsReportBinding.h" @@ -249,19 +250,41 @@ void PeerConnectionAutoTimer::UnregisterConnection(bool aContainedAV) { bool PeerConnectionAutoTimer::IsStopped() { return mRefCnt == 0; } +// There is not presently an implementation of these for nsTHashMap :( +inline void ImplCycleCollectionUnlink( + PeerConnectionImpl::RTCDtlsTransportMap& aMap) { + for (auto& tableEntry : aMap) { + ImplCycleCollectionUnlink(*tableEntry.GetModifiableData()); + } + aMap.Clear(); +} + +inline void ImplCycleCollectionTraverse( + nsCycleCollectionTraversalCallback& aCallback, + PeerConnectionImpl::RTCDtlsTransportMap& aMap, const char* aName, + uint32_t aFlags = 0) { + for (auto& tableEntry : aMap) { + ImplCycleCollectionTraverse(aCallback, *tableEntry.GetModifiableData(), + aName, aFlags); + } +} + NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(PeerConnectionImpl) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PeerConnectionImpl) tmp->Close(); tmp->BreakCycles(); - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPCObserver, mWindow, mCertificate, - mSTSThread, mReceiveStreams, mOperations, - mSctpTransport, mKungFuDeathGrip) + NS_IMPL_CYCLE_COLLECTION_UNLINK( + mPCObserver, mWindow, mCertificate, mSTSThread, mReceiveStreams, + mOperations, mTransportIdToRTCDtlsTransport, mSctpTransport, + mLastStableSctpTransport, mLastStableSctpDtlsTransport, mKungFuDeathGrip) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PeerConnectionImpl) NS_IMPL_CYCLE_COLLECTION_TRAVERSE( mPCObserver, mWindow, mCertificate, mSTSThread, mReceiveStreams, - mOperations, mTransceivers, mSctpTransport, mKungFuDeathGrip) + mOperations, mTransceivers, mTransportIdToRTCDtlsTransport, + mSctpTransport, mLastStableSctpTransport, mLastStableSctpDtlsTransport, + mKungFuDeathGrip) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTING_ADDREF(PeerConnectionImpl) @@ -849,6 +872,7 @@ nsresult PeerConnectionImpl::GetDatachannelParameters( if (!datachannelTransceiver || !datachannelTransceiver->mTransport.mComponents || + !datachannelTransceiver->mTransport.mDtls || !datachannelTransceiver->mSendTrack.GetNegotiatedDetails()) { return NS_ERROR_FAILURE; } @@ -1061,7 +1085,7 @@ bool PeerConnectionImpl::CreatedSender(const dom::RTCRtpSender& aSender) const { return aSender.IsMyPc(this); } -nsresult PeerConnectionImpl::InitializeDataChannel() { +nsresult PeerConnectionImpl::MaybeInitializeDataChannel() { PC_AUTO_ENTER_API_CALL(false); CSFLogDebug(LOGTAG, "%s", __FUNCTION__); @@ -1440,6 +1464,16 @@ void PeerConnectionImpl::UpdateNegotiationNeeded() { })); } +RefPtr<dom::RTCRtpTransceiver> PeerConnectionImpl::GetTransceiver( + const std::string& aTransceiverId) { + for (const auto& transceiver : mTransceivers) { + if (transceiver->GetJsepTransceiverId() == aTransceiverId) { + return transceiver; + } + } + return nullptr; +} + void PeerConnectionImpl::NotifyDataChannel( already_AddRefed<DataChannel> aChannel) { PC_AUTO_ENTER_API_CALL_NO_CHECK(); @@ -1637,7 +1671,6 @@ JsepSdpType ToJsepSdpType(dom::RTCSdpType aType) { return kJsepSdpAnswer; case dom::RTCSdpType::Rollback: return kJsepSdpRollback; - case dom::RTCSdpType::EndGuard_:; } MOZ_CRASH("Nonexistent dom::RTCSdpType"); @@ -1989,10 +2022,14 @@ nsresult PeerConnectionImpl::OnAlpnNegotiated(bool aPrivacyRequested) { void PeerConnectionImpl::OnDtlsStateChange(const std::string& aTransportId, TransportLayer::State aState) { - auto it = mTransportIdToRTCDtlsTransport.find(aTransportId); - if (it != mTransportIdToRTCDtlsTransport.end()) { - it->second->UpdateState(aState); + nsCString key(aTransportId.data(), aTransportId.size()); + RefPtr<RTCDtlsTransport> dtlsTransport = + mTransportIdToRTCDtlsTransport.Get(key); + if (!dtlsTransport) { + return; } + + dtlsTransport->UpdateState(aState); // Whenever the state of an RTCDtlsTransport changes or when the [[IsClosed]] // slot turns true, the user agent MUST update the connection state by // queueing a task that runs the following steps: @@ -2024,9 +2061,9 @@ RTCPeerConnectionState PeerConnectionImpl::GetNewConnectionState() const { // Would use a bitset, but that requires lots of static_cast<size_t> // Oh well. std::set<RTCDtlsTransportState> statesFound; - for (const auto& [id, dtlsTransport] : mTransportIdToRTCDtlsTransport) { - Unused << id; - statesFound.insert(dtlsTransport->State()); + std::set<RefPtr<RTCDtlsTransport>> transports(GetActiveTransports()); + for (const auto& transport : transports) { + statesFound.insert(transport->State()); } // failed The previous state doesn't apply, and either @@ -2075,9 +2112,9 @@ RTCPeerConnectionState PeerConnectionImpl::GetNewConnectionState() const { bool PeerConnectionImpl::UpdateConnectionState() { auto newState = GetNewConnectionState(); if (newState != mConnectionState) { - CSFLogDebug(LOGTAG, "%s: %d -> %d (%p)", __FUNCTION__, - static_cast<int>(mConnectionState), static_cast<int>(newState), - this); + CSFLogInfo(LOGTAG, "%s: %d -> %d (%p)", __FUNCTION__, + static_cast<int>(mConnectionState), static_cast<int>(newState), + this); mConnectionState = newState; if (mConnectionState != RTCPeerConnectionState::Closed) { return true; @@ -2569,7 +2606,7 @@ PeerConnectionImpl::Close() { transceiver->Close(); } - mTransportIdToRTCDtlsTransport.clear(); + mTransportIdToRTCDtlsTransport.Clear(); mQueuedIceCtxOperations.clear(); @@ -2964,18 +3001,25 @@ void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing( InvalidateLastReturnedParameters(); } + if (aSdpType == dom::RTCSdpType::Offer && + mSignalingState == RTCSignalingState::Stable) { + // If description is of type "offer" and + // connection.[[SignalingState]] is "stable" then for each + // transceiver in connection's set of transceivers, run the following + // steps: + SaveStateForRollback(); + } + // Section 4.4.1.5 Set the RTCSessionDescription: if (aSdpType == dom::RTCSdpType::Rollback) { // - step 4.5.10, type is rollback - RollbackRTCDtlsTransports(); + RestoreStateForRollback(); } else if (!(aRemote && aSdpType == dom::RTCSdpType::Offer)) { // - step 4.5.9 type is not rollback // - step 4.5.9.1 when remote is false // - step 4.5.9.2.13 when remote is true, type answer or pranswer // More simply: not rollback, and not for remote offers. - bool markAsStable = aSdpType == dom::RTCSdpType::Offer && - mSignalingState == RTCSignalingState::Stable; - UpdateRTCDtlsTransports(markAsStable); + UpdateRTCDtlsTransports(); } // Did we just apply a local description? @@ -2992,11 +3036,6 @@ void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing( } if (mJsepSession->GetState() == kJsepStateStable) { - if (aSdpType != dom::RTCSdpType::Rollback) { - // We need this initted for UpdateTransports - InitializeDataChannel(); - } - // If we're rolling back a local offer, we might need to remove some // transports, and stomp some MediaPipeline setup, but nothing further // needs to be done. @@ -3071,6 +3110,10 @@ void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing( // Spec does not actually tell us to do this, but that is probably a // spec bug. // https://github.com/w3c/webrtc-pc/issues/2817 + bool gatheringStateChanged = UpdateIceGatheringState(); + + bool iceConnectionStateChanged = UpdateIceConnectionState(); + bool connectionStateChanged = UpdateConnectionState(); // This only gets populated for remote descriptions @@ -3104,6 +3147,16 @@ void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing( pcObserver->OnStateChange(PCObserverStateType::SignalingState, jrv); } + if (gatheringStateChanged) { + pcObserver->OnStateChange(PCObserverStateType::IceGatheringState, + jrv); + } + + if (iceConnectionStateChanged) { + pcObserver->OnStateChange(PCObserverStateType::IceConnectionState, + jrv); + } + if (connectionStateChanged) { pcObserver->OnStateChange(PCObserverStateType::ConnectionState, jrv); } @@ -3290,61 +3343,162 @@ void PeerConnectionImpl::SendLocalIceCandidateToContent( } void PeerConnectionImpl::IceConnectionStateChange( - dom::RTCIceConnectionState domState) { + const std::string& aTransportId, dom::RTCIceTransportState domState) { + // If connection.[[IsClosed]] is true, abort these steps. PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); - CSFLogDebug(LOGTAG, "%s: %d -> %d", __FUNCTION__, - static_cast<int>(mIceConnectionState), - static_cast<int>(domState)); + CSFLogDebug(LOGTAG, "IceConnectionStateChange: %s %d (%p)", + aTransportId.c_str(), static_cast<int>(domState), this); - if (domState == mIceConnectionState) { - // no work to be done since the states are the same. - // this can happen during ICE rollback situations. + // Let transport be the RTCIceTransport whose state is changing. + nsCString key(aTransportId.data(), aTransportId.size()); + RefPtr<RTCDtlsTransport> dtlsTransport = + mTransportIdToRTCDtlsTransport.Get(key); + if (!dtlsTransport) { return; } + RefPtr<RTCIceTransport> transport = dtlsTransport->IceTransport(); - mIceConnectionState = domState; + if (domState == RTCIceTransportState::Closed) { + mTransportIdToRTCDtlsTransport.Remove(key); + } - // Would be nice if we had a means of converting one of these dom enums - // to a string that wasn't almost as much text as this switch statement... - switch (mIceConnectionState) { - case RTCIceConnectionState::New: - STAMP_TIMECARD(mTimeCard, "Ice state: new"); - break; - case RTCIceConnectionState::Checking: - // For telemetry - mIceStartTime = TimeStamp::Now(); - STAMP_TIMECARD(mTimeCard, "Ice state: checking"); - break; - case RTCIceConnectionState::Connected: - STAMP_TIMECARD(mTimeCard, "Ice state: connected"); - StartCallTelem(); - break; - case RTCIceConnectionState::Completed: - STAMP_TIMECARD(mTimeCard, "Ice state: completed"); - break; - case RTCIceConnectionState::Failed: - STAMP_TIMECARD(mTimeCard, "Ice state: failed"); - break; - case RTCIceConnectionState::Disconnected: - STAMP_TIMECARD(mTimeCard, "Ice state: disconnected"); - break; - case RTCIceConnectionState::Closed: - STAMP_TIMECARD(mTimeCard, "Ice state: closed"); - break; - default: - MOZ_ASSERT_UNREACHABLE("Unexpected mIceConnectionState!"); + // Let selectedCandidatePairChanged be false. + // TODO(bug 1307994) + + // Let transportIceConnectionStateChanged be false. + bool transportIceConnectionStateChanged = false; + + // Let connectionIceConnectionStateChanged be false. + bool connectionIceConnectionStateChanged = false; + + // Let connectionStateChanged be false. + bool connectionStateChanged = false; + + if (transport->State() == domState) { + return; + } + + // If transport's RTCIceTransportState was changed, run the following steps: + + // Set transport.[[IceTransportState]] to the new indicated + // RTCIceTransportState. + transport->SetState(domState); + + // Set transportIceConnectionStateChanged to true. + transportIceConnectionStateChanged = true; + + // Set connection.[[IceConnectionState]] to the value of deriving a new state + // value as described by the RTCIceConnectionState enum. + if (UpdateIceConnectionState()) { + // If connection.[[IceConnectionState]] changed in the previous step, set + // connectionIceConnectionStateChanged to true. + connectionIceConnectionStateChanged = true; + } + + // Set connection.[[ConnectionState]] to the value of deriving a new state + // value as described by the RTCPeerConnectionState enum. + if (UpdateConnectionState()) { + // If connection.[[ConnectionState]] changed in the previous step, set + // connectionStateChanged to true. + connectionStateChanged = true; + } + + // If selectedCandidatePairChanged is true, fire an event named + // selectedcandidatepairchange at transport. + // TODO(bug 1307994) + + // If transportIceConnectionStateChanged is true, fire an event named + // statechange at transport. + if (transportIceConnectionStateChanged) { + transport->FireStateChangeEvent(); } - bool connectionStateChanged = UpdateConnectionState(); WrappableJSErrorResult rv; RefPtr<PeerConnectionObserver> pcObserver(mPCObserver); - pcObserver->OnStateChange(PCObserverStateType::IceConnectionState, rv); + + // If connectionIceConnectionStateChanged is true, fire an event named + // iceconnectionstatechange at connection. + if (connectionIceConnectionStateChanged) { + pcObserver->OnStateChange(PCObserverStateType::IceConnectionState, rv); + } + + // If connectionStateChanged is true, fire an event named + // connectionstatechange at connection. if (connectionStateChanged) { pcObserver->OnStateChange(PCObserverStateType::ConnectionState, rv); } } +RTCIceConnectionState PeerConnectionImpl::GetNewIceConnectionState() const { + // closed The RTCPeerConnection object's [[IsClosed]] slot is true. + if (IsClosed()) { + return RTCIceConnectionState::Closed; + } + + // Would use a bitset, but that requires lots of static_cast<size_t> + // Oh well. + std::set<RTCIceTransportState> statesFound; + std::set<RefPtr<RTCDtlsTransport>> transports(GetActiveTransports()); + for (const auto& transport : transports) { + RefPtr<dom::RTCIceTransport> iceTransport = transport->IceTransport(); + CSFLogWarn(LOGTAG, "GetNewIceConnectionState: %p %d", iceTransport.get(), + static_cast<int>(iceTransport->State())); + statesFound.insert(iceTransport->State()); + } + + // failed None of the previous states apply and any RTCIceTransports are + // in the "failed" state. + if (statesFound.count(RTCIceTransportState::Failed)) { + return RTCIceConnectionState::Failed; + } + + // disconnected None of the previous states apply and any + // RTCIceTransports are in the "disconnected" state. + if (statesFound.count(RTCIceTransportState::Disconnected)) { + return RTCIceConnectionState::Disconnected; + } + + // new None of the previous states apply and all RTCIceTransports are + // in the "new" or "closed" state, or there are no transports. + if (!statesFound.count(RTCIceTransportState::Checking) && + !statesFound.count(RTCIceTransportState::Completed) && + !statesFound.count(RTCIceTransportState::Connected)) { + return RTCIceConnectionState::New; + } + + // checking None of the previous states apply and any RTCIceTransports are + // in the "new" or "checking" state. + if (statesFound.count(RTCIceTransportState::New) || + statesFound.count(RTCIceTransportState::Checking)) { + return RTCIceConnectionState::Checking; + } + + // completed None of the previous states apply and all RTCIceTransports are + // in the "completed" or "closed" state. + if (!statesFound.count(RTCIceTransportState::Connected)) { + return RTCIceConnectionState::Completed; + } + + // connected None of the previous states apply. + return RTCIceConnectionState::Connected; +} + +bool PeerConnectionImpl::UpdateIceConnectionState() { + auto newState = GetNewIceConnectionState(); + if (newState != mIceConnectionState) { + CSFLogInfo(LOGTAG, "%s: %d -> %d (%p)", __FUNCTION__, + static_cast<int>(mIceConnectionState), + static_cast<int>(newState), this); + mIceConnectionState = newState; + if (mIceConnectionState != RTCIceConnectionState::Closed) { + return true; + } + } + + return false; +} + void PeerConnectionImpl::OnCandidateFound(const std::string& aTransportId, const CandidateInfo& aCandidateInfo) { if (mStunAddrsRequest && !aCandidateInfo.mMDNSAddress.empty()) { @@ -3378,18 +3532,82 @@ void PeerConnectionImpl::OnCandidateFound(const std::string& aTransportId, } void PeerConnectionImpl::IceGatheringStateChange( - dom::RTCIceGatheringState state) { + const std::string& aTransportId, dom::RTCIceGathererState state) { + // If connection.[[IsClosed]] is true, abort these steps. PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); - CSFLogDebug(LOGTAG, "%s %d", __FUNCTION__, static_cast<int>(state)); - if (mIceGatheringState == state) { + CSFLogWarn(LOGTAG, "IceGatheringStateChange: %s %d (%p)", + aTransportId.c_str(), static_cast<int>(state), this); + + // Let transport be the RTCIceTransport for which candidate gathering + // began/finished. + nsCString key(aTransportId.data(), aTransportId.size()); + RefPtr<RTCDtlsTransport> dtlsTransport = + mTransportIdToRTCDtlsTransport.Get(key); + if (!dtlsTransport) { + return; + } + RefPtr<RTCIceTransport> transport = dtlsTransport->IceTransport(); + + if (transport->GatheringState() == state) { return; } - mIceGatheringState = state; + // Set transport.[[IceGathererState]] to gathering. + // or + // Set transport.[[IceGathererState]] to complete. + transport->SetGatheringState(state); + + // Set connection.[[IceGatheringState]] to the value of deriving a new state + // value as described by the RTCIceGatheringState enum. + // + // Let connectionIceGatheringStateChanged be true if + // connection.[[IceGatheringState]] changed in the previous step, otherwise + // false. + bool gatheringStateChanged = UpdateIceGatheringState(); + + // Do not read or modify state beyond this point. + + // Fire an event named gatheringstatechange at transport. + transport->FireGatheringStateChangeEvent(); + + // If connectionIceGatheringStateChanged is true, fire an event named + // icegatheringstatechange at connection. + if (gatheringStateChanged) { + // NOTE: If we're in the "complete" case, our JS code will fire a null + // icecandidate event after firing the icegatheringstatechange event. + // Fire an event named icecandidate using the RTCPeerConnectionIceEvent + // interface with the candidate attribute set to null at connection. + JSErrorResult rv; + mPCObserver->OnStateChange(PCObserverStateType::IceGatheringState, rv); + } +} + +bool PeerConnectionImpl::UpdateIceGatheringState() { + // If connection.[[IsClosed]] is true, abort these steps. + if (IsClosed()) { + return false; + } + + // Let newState be the value of deriving a new state value as + // described by the RTCIceGatheringState enum. + auto newState = GetNewIceGatheringState(); + + // If connection.[[IceGatheringState]] is equal to newState, abort + // these steps. + if (newState == mIceGatheringState) { + return false; + } + + CSFLogInfo(LOGTAG, "UpdateIceGatheringState: %d -> %d (%p)", + static_cast<int>(mIceGatheringState), static_cast<int>(newState), + this); + // Set connection.[[IceGatheringState]] to newState. + mIceGatheringState = newState; - // Would be nice if we had a means of converting one of these dom enums - // to a string that wasn't almost as much text as this switch statement... + // Would be nice if we had a means of converting one of these dom + // enums to a string that wasn't almost as much text as this switch + // statement... switch (mIceGatheringState) { case RTCIceGatheringState::New: STAMP_TIMECARD(mTimeCard, "Ice gathering state: new"); @@ -3404,8 +3622,42 @@ void PeerConnectionImpl::IceGatheringStateChange( MOZ_ASSERT_UNREACHABLE("Unexpected mIceGatheringState!"); } - JSErrorResult rv; - mPCObserver->OnStateChange(PCObserverStateType::IceGatheringState, rv); + return true; +} + +RTCIceGatheringState PeerConnectionImpl::GetNewIceGatheringState() const { + // new Any of the RTCIceTransports are in the "new" gathering state + // and none of the transports are in the "gathering" state, or there are no + // transports. + + // NOTE! This derives the RTCIce**Gathering**State from the individual + // RTCIce**Gatherer**State of the transports. These are different enums. + // But they have exactly the same values, in the same order. + // ¯\_(ツ)_/¯ + bool foundComplete = false; + std::set<RefPtr<RTCDtlsTransport>> transports(GetActiveTransports()); + for (const auto& transport : transports) { + RefPtr<dom::RTCIceTransport> iceTransport = transport->IceTransport(); + switch (iceTransport->GatheringState()) { + case RTCIceGathererState::New: + break; + case RTCIceGathererState::Gathering: + // gathering Any of the RTCIceTransports are in the "gathering" + // state. + return RTCIceGatheringState::Gathering; + case RTCIceGathererState::Complete: + foundComplete = true; + break; + } + } + + if (!foundComplete) { + return RTCIceGatheringState::New; + } + + // This could change depending on the outcome in + // https://github.com/w3c/webrtc-pc/issues/2914 + return RTCIceGatheringState::Complete; } void PeerConnectionImpl::UpdateDefaultCandidate( @@ -3878,11 +4130,8 @@ void PeerConnectionImpl::StunAddrsHandler::OnStunAddrsAvailable( pcw.impl()->mStunAddrs = addrs.Clone(); pcw.impl()->mLocalAddrsRequestState = STUN_ADDR_REQUEST_COMPLETE; pcw.impl()->FlushIceCtxOperationQueueIfReady(); - // If parent process returns 0 STUN addresses, change ICE connection - // state to failed. - if (!pcw.impl()->mStunAddrs.Length()) { - pcw.impl()->IceConnectionStateChange(dom::RTCIceConnectionState::Failed); - } + // If this fails, ICE cannot succeed, but we need to still go through the + // motions. } void PeerConnectionImpl::InitLocalAddrs() { @@ -3952,109 +4201,120 @@ void PeerConnectionImpl::EnsureTransports(const JsepSession& aSession) { GatherIfReady(); } -void PeerConnectionImpl::UpdateRTCDtlsTransports(bool aMarkAsStable) { +void PeerConnectionImpl::UpdateRTCDtlsTransports() { + // We use mDataConnection below, make sure it is initted if necessary + MaybeInitializeDataChannel(); + + // Make sure that the SCTP transport is unset if we do not see a DataChannel. + // We'll restore this if we do see a DataChannel. + RefPtr<dom::RTCSctpTransport> oldSctp = mSctpTransport.forget(); + mJsepSession->ForEachTransceiver( - [this, self = RefPtr<PeerConnectionImpl>(this)]( - const JsepTransceiver& jsepTransceiver) { + [this, self = RefPtr<PeerConnectionImpl>(this), + oldSctp](const JsepTransceiver& jsepTransceiver) { std::string transportId = jsepTransceiver.mTransport.mTransportId; - if (transportId.empty()) { - return; + RefPtr<dom::RTCDtlsTransport> dtlsTransport; + if (!transportId.empty()) { + nsCString key(transportId.data(), transportId.size()); + dtlsTransport = mTransportIdToRTCDtlsTransport.GetOrInsertNew( + key, GetParentObject()); } - if (!mTransportIdToRTCDtlsTransport.count(transportId)) { - mTransportIdToRTCDtlsTransport.emplace( - transportId, new RTCDtlsTransport(GetParentObject())); + + if (jsepTransceiver.GetMediaType() == SdpMediaSection::kApplication) { + // Spec says we only update the RTCSctpTransport when negotiation + // completes. This is probably a spec bug. + // https://github.com/w3c/webrtc-pc/issues/2898 + if (!dtlsTransport || !mDataConnection) { + return; + } + + // Why on earth does the spec use a floating point for this? + double maxMessageSize = + static_cast<double>(mDataConnection->GetMaxMessageSize()); + Nullable<uint16_t> maxChannels; + + if (!oldSctp) { + mSctpTransport = new RTCSctpTransport( + GetParentObject(), *dtlsTransport, maxMessageSize, maxChannels); + } else { + // Restore the SCTP transport we had before this function was called + oldSctp->SetTransport(*dtlsTransport); + oldSctp->SetMaxMessageSize(maxMessageSize); + oldSctp->SetMaxChannels(maxChannels); + mSctpTransport = oldSctp; + } + } else { + RefPtr<dom::RTCRtpTransceiver> domTransceiver = + GetTransceiver(jsepTransceiver.GetUuid()); + if (domTransceiver) { + domTransceiver->SetDtlsTransport(dtlsTransport); + } } }); +} - for (auto& transceiver : mTransceivers) { - std::string transportId = transceiver->GetTransportId(); - if (transportId.empty()) { - continue; - } - if (mTransportIdToRTCDtlsTransport.count(transportId)) { - transceiver->SetDtlsTransport(mTransportIdToRTCDtlsTransport[transportId], - aMarkAsStable); - } +void PeerConnectionImpl::SaveStateForRollback() { + // This could change depending on the outcome in + // https://github.com/w3c/webrtc-pc/issues/2899 + if (mSctpTransport) { + // We have to save both of these things, because the DTLS transport could + // change without the SCTP transport changing. + mLastStableSctpTransport = mSctpTransport; + mLastStableSctpDtlsTransport = mSctpTransport->Transport(); + } else { + mLastStableSctpTransport = nullptr; + mLastStableSctpDtlsTransport = nullptr; } - // Spec says we only update the RTCSctpTransport when negotiation completes + for (auto& transceiver : mTransceivers) { + transceiver->SaveStateForRollback(); + } } -void PeerConnectionImpl::RollbackRTCDtlsTransports() { +void PeerConnectionImpl::RestoreStateForRollback() { for (auto& transceiver : mTransceivers) { transceiver->RollbackToStableDtlsTransport(); } + + mSctpTransport = mLastStableSctpTransport; + if (mSctpTransport) { + mSctpTransport->SetTransport(*mLastStableSctpDtlsTransport); + } } -void PeerConnectionImpl::RemoveRTCDtlsTransportsExcept( - const std::set<std::string>& aTransportIds) { - for (auto iter = mTransportIdToRTCDtlsTransport.begin(); - iter != mTransportIdToRTCDtlsTransport.end();) { - if (!aTransportIds.count(iter->first)) { - iter = mTransportIdToRTCDtlsTransport.erase(iter); - } else { - ++iter; +std::set<RefPtr<dom::RTCDtlsTransport>> +PeerConnectionImpl::GetActiveTransports() const { + std::set<RefPtr<dom::RTCDtlsTransport>> result; + for (const auto& transceiver : mTransceivers) { + if (transceiver->GetDtlsTransport()) { + result.insert(transceiver->GetDtlsTransport()); } } + + if (mSctpTransport && mSctpTransport->Transport()) { + result.insert(mSctpTransport->Transport()); + } + return result; } nsresult PeerConnectionImpl::UpdateTransports(const JsepSession& aSession, const bool forceIceTcp) { std::set<std::string> finalTransports; - Maybe<std::string> sctpTransport; 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); } }); - // clean up the unused RTCDtlsTransports - RemoveRTCDtlsTransportsExcept(finalTransports); - mTransportHandler->RemoveTransportsExcept(finalTransports); for (const auto& transceiverImpl : mTransceivers) { transceiverImpl->UpdateTransport(); } - if (sctpTransport.isSome()) { - auto it = mTransportIdToRTCDtlsTransport.find(*sctpTransport); - if (it == mTransportIdToRTCDtlsTransport.end()) { - // What? - MOZ_ASSERT(false); - return NS_ERROR_FAILURE; - } - if (!mDataConnection) { - // What? - MOZ_ASSERT(false); - return NS_ERROR_FAILURE; - } - RefPtr<RTCDtlsTransport> dtlsTransport = it->second; - // Why on earth does the spec use a floating point for this? - double maxMessageSize = - static_cast<double>(mDataConnection->GetMaxMessageSize()); - Nullable<uint16_t> maxChannels; - - if (!mSctpTransport) { - mSctpTransport = new RTCSctpTransport(GetParentObject(), *dtlsTransport, - maxMessageSize, maxChannels); - } else { - mSctpTransport->SetTransport(*dtlsTransport); - mSctpTransport->SetMaxMessageSize(maxMessageSize); - mSctpTransport->SetMaxChannels(maxChannels); - } - } else { - mSctpTransport = nullptr; - } - return NS_OK; } @@ -4141,6 +4401,9 @@ nsresult PeerConnectionImpl::UpdateMediaPipelines() { void PeerConnectionImpl::StartIceChecks(const JsepSession& aSession) { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mJsepSession->GetState() == kJsepStateStable); + + auto transports = GetActiveTransports(); if (!mCanRegisterMDNSHostnamesDirectly) { for (auto& pair : mMDNSHostnamesToRegister) { @@ -4472,32 +4735,31 @@ std::string PeerConnectionImpl::GetTransportIdMatchingSendTrack( } void PeerConnectionImpl::SignalHandler::IceGatheringStateChange_s( - dom::RTCIceGatheringState aState) { + const std::string& aTransportId, dom::RTCIceGathererState aState) { ASSERT_ON_THREAD(mSTSThread); - GetMainThreadSerialEventTarget()->Dispatch( NS_NewRunnableFunction(__func__, - [handle = mHandle, aState] { + [handle = mHandle, aTransportId, aState] { PeerConnectionWrapper wrapper(handle); if (wrapper.impl()) { wrapper.impl()->IceGatheringStateChange( - aState); + aTransportId, aState); } }), NS_DISPATCH_NORMAL); } void PeerConnectionImpl::SignalHandler::IceConnectionStateChange_s( - dom::RTCIceConnectionState aState) { + const std::string& aTransportId, dom::RTCIceTransportState aState) { ASSERT_ON_THREAD(mSTSThread); GetMainThreadSerialEventTarget()->Dispatch( NS_NewRunnableFunction(__func__, - [handle = mHandle, aState] { + [handle = mHandle, aTransportId, aState] { PeerConnectionWrapper wrapper(handle); if (wrapper.impl()) { wrapper.impl()->IceConnectionStateChange( - aState); + aTransportId, aState); } }), NS_DISPATCH_NORMAL); diff --git a/dom/media/webrtc/jsapi/PeerConnectionImpl.h b/dom/media/webrtc/jsapi/PeerConnectionImpl.h index 085658b206..d7b54ad721 100644 --- a/dom/media/webrtc/jsapi/PeerConnectionImpl.h +++ b/dom/media/webrtc/jsapi/PeerConnectionImpl.h @@ -16,6 +16,7 @@ #include "nsPIDOMWindow.h" #include "nsIUUIDGenerator.h" #include "nsIThread.h" +#include "nsTHashSet.h" #include "mozilla/Mutex.h" #include "mozilla/Attributes.h" @@ -217,8 +218,10 @@ class PeerConnectionImpl final virtual const std::string& GetName(); // ICE events - void IceConnectionStateChange(dom::RTCIceConnectionState state); - void IceGatheringStateChange(dom::RTCIceGatheringState state); + void IceConnectionStateChange(const std::string& aTransportId, + dom::RTCIceTransportState state); + void IceGatheringStateChange(const std::string& aTransportId, + dom::RTCIceGathererState state); void OnCandidateFound(const std::string& aTransportId, const CandidateInfo& aCandidateInfo); void UpdateDefaultCandidate(const std::string& defaultAddr, @@ -411,7 +414,7 @@ class PeerConnectionImpl final void RecordEndOfCallTelemetry(); - nsresult InitializeDataChannel(); + nsresult MaybeInitializeDataChannel(); NS_IMETHODIMP_TO_ERRORRESULT_RETREF(nsDOMDataChannel, CreateDataChannel, ErrorResult& rv, const nsAString& aLabel, @@ -481,6 +484,9 @@ class PeerConnectionImpl final aTransceiversOut = mTransceivers.Clone(); } + RefPtr<dom::RTCRtpTransceiver> GetTransceiver( + const std::string& aTransceiverId); + // Gets the RTC Signaling State of the JSEP session dom::RTCSignalingState GetSignalingState() const; @@ -499,6 +505,12 @@ class PeerConnectionImpl final dom::RTCPeerConnectionState GetNewConnectionState() const; // Returns whether we need to fire a state change event bool UpdateConnectionState(); + dom::RTCIceConnectionState GetNewIceConnectionState() const; + // Returns whether we need to fire a state change event + bool UpdateIceConnectionState(); + dom::RTCIceGatheringState GetNewIceGatheringState() const; + // Returns whether we need to fire a state change event + bool UpdateIceGatheringState(); // initialize telemetry for when calls start void StartCallTelem(); @@ -585,6 +597,9 @@ class PeerConnectionImpl final void BreakCycles(); + using RTCDtlsTransportMap = + nsTHashMap<nsCStringHashKey, RefPtr<dom::RTCDtlsTransport>>; + private: virtual ~PeerConnectionImpl(); PeerConnectionImpl(const PeerConnectionImpl& rhs); @@ -805,10 +820,10 @@ class PeerConnectionImpl final // Ensure ICE transports exist that we might need when offer/answer concludes void EnsureTransports(const JsepSession& aSession); - void UpdateRTCDtlsTransports(bool aMarkAsStable); - void RollbackRTCDtlsTransports(); - void RemoveRTCDtlsTransportsExcept( - const std::set<std::string>& aTransportIds); + void UpdateRTCDtlsTransports(); + void SaveStateForRollback(); + void RestoreStateForRollback(); + std::set<RefPtr<dom::RTCDtlsTransport>> GetActiveTransports() const; // Activate ICE transports at the conclusion of offer/answer, // or when rollback occurs. @@ -861,9 +876,12 @@ class PeerConnectionImpl final std::set<std::pair<std::string, std::string>> mLocalIceCredentialsToReplace; nsTArray<RefPtr<dom::RTCRtpTransceiver>> mTransceivers; - std::map<std::string, RefPtr<dom::RTCDtlsTransport>> - mTransportIdToRTCDtlsTransport; + RTCDtlsTransportMap mTransportIdToRTCDtlsTransport; RefPtr<dom::RTCSctpTransport> mSctpTransport; + // This is similar to [[LastStableStateSender/ReceiverTransport]], but for + // DataChannel. + RefPtr<dom::RTCSctpTransport> mLastStableSctpTransport; + RefPtr<dom::RTCDtlsTransport> mLastStableSctpDtlsTransport; // Used whenever we need to dispatch a runnable to STS to tweak something // on our ICE ctx, but are not ready to do so at the moment (eg; we are @@ -924,8 +942,10 @@ class PeerConnectionImpl final void ConnectSignals(); // ICE events - void IceGatheringStateChange_s(dom::RTCIceGatheringState aState); - void IceConnectionStateChange_s(dom::RTCIceConnectionState aState); + void IceGatheringStateChange_s(const std::string& aTransportId, + dom::RTCIceGathererState aState); + void IceConnectionStateChange_s(const std::string& aTransportId, + dom::RTCIceTransportState aState); void OnCandidateFound_s(const std::string& aTransportId, const CandidateInfo& aCandidateInfo); void AlpnNegotiated_s(const std::string& aAlpn, bool aPrivacyRequested); diff --git a/dom/media/webrtc/jsapi/RTCDtlsTransport.cpp b/dom/media/webrtc/jsapi/RTCDtlsTransport.cpp index 243f06d2f1..83e0aeee82 100644 --- a/dom/media/webrtc/jsapi/RTCDtlsTransport.cpp +++ b/dom/media/webrtc/jsapi/RTCDtlsTransport.cpp @@ -9,7 +9,8 @@ namespace mozilla::dom { -NS_IMPL_CYCLE_COLLECTION_INHERITED(RTCDtlsTransport, DOMEventTargetHelper) +NS_IMPL_CYCLE_COLLECTION_INHERITED(RTCDtlsTransport, DOMEventTargetHelper, + mIceTransport) NS_IMPL_ADDREF_INHERITED(RTCDtlsTransport, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(RTCDtlsTransport, DOMEventTargetHelper) @@ -19,7 +20,9 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCDtlsTransport) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) RTCDtlsTransport::RTCDtlsTransport(nsPIDOMWindowInner* aWindow) - : DOMEventTargetHelper(aWindow), mState(RTCDtlsTransportState::New) {} + : DOMEventTargetHelper(aWindow), + mState(RTCDtlsTransportState::New), + mIceTransport(new RTCIceTransport(aWindow)) {} JSObject* RTCDtlsTransport::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { diff --git a/dom/media/webrtc/jsapi/RTCDtlsTransport.h b/dom/media/webrtc/jsapi/RTCDtlsTransport.h index 3800502154..3e744b5112 100644 --- a/dom/media/webrtc/jsapi/RTCDtlsTransport.h +++ b/dom/media/webrtc/jsapi/RTCDtlsTransport.h @@ -6,6 +6,7 @@ #define _RTCDtlsTransport_h_ #include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/dom/RTCIceTransport.h" #include "mozilla/RefPtr.h" #include "js/RootingAPI.h" #include "transport/transportlayer.h" @@ -30,6 +31,7 @@ class RTCDtlsTransport : public DOMEventTargetHelper { JS::Handle<JSObject*> aGivenProto) override; IMPL_EVENT_HANDLER(statechange) RTCDtlsTransportState State() const { return mState; } + RefPtr<RTCIceTransport> IceTransport() { return mIceTransport; } void UpdateStateNoEvent(TransportLayer::State aState); void UpdateState(TransportLayer::State aState); @@ -38,6 +40,7 @@ class RTCDtlsTransport : public DOMEventTargetHelper { virtual ~RTCDtlsTransport() = default; RTCDtlsTransportState mState; + RefPtr<RTCIceTransport> mIceTransport; }; } // namespace mozilla::dom diff --git a/dom/media/webrtc/jsapi/RTCIceTransport.cpp b/dom/media/webrtc/jsapi/RTCIceTransport.cpp new file mode 100644 index 0000000000..4c5e6eef4f --- /dev/null +++ b/dom/media/webrtc/jsapi/RTCIceTransport.cpp @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "RTCIceTransport.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/EventBinding.h" +#include "mozilla/dom/RTCIceTransportBinding.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(RTCIceTransport, DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(RTCIceTransport, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(RTCIceTransport, DOMEventTargetHelper) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCIceTransport) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +RTCIceTransport::RTCIceTransport(nsPIDOMWindowInner* aWindow) + : DOMEventTargetHelper(aWindow), + mState(RTCIceTransportState::New), + mGatheringState(RTCIceGathererState::New) {} + +JSObject* RTCIceTransport::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return RTCIceTransport_Binding::Wrap(aCx, this, aGivenProto); +} + +void RTCIceTransport::SetState(RTCIceTransportState aState) { mState = aState; } + +void RTCIceTransport::SetGatheringState(RTCIceGathererState aState) { + mGatheringState = aState; +} + +void RTCIceTransport::FireStateChangeEvent() { + EventInit init; + init.mBubbles = false; + init.mCancelable = false; + + RefPtr<Event> event = Event::Constructor(this, u"statechange"_ns, init); + + DispatchTrustedEvent(event); +} + +void RTCIceTransport::FireGatheringStateChangeEvent() { + EventInit init; + init.mBubbles = false; + init.mCancelable = false; + + RefPtr<Event> event = + Event::Constructor(this, u"gatheringstatechange"_ns, init); + + DispatchTrustedEvent(event); +} + +} // namespace mozilla::dom diff --git a/dom/media/webrtc/jsapi/RTCIceTransport.h b/dom/media/webrtc/jsapi/RTCIceTransport.h new file mode 100644 index 0000000000..931aa66bac --- /dev/null +++ b/dom/media/webrtc/jsapi/RTCIceTransport.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_DOM_MEDIA_WEBRTC_JSAPI_RTCICETRANSPORT_H_ +#define MOZILLA_DOM_MEDIA_WEBRTC_JSAPI_RTCICETRANSPORT_H_ + +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/RefPtr.h" +#include "js/RootingAPI.h" +#include "transport/transportlayer.h" + +class nsPIDOMWindowInner; + +namespace mozilla::dom { + +enum class RTCIceTransportState : uint8_t; +enum class RTCIceGathererState : uint8_t; + +class RTCIceTransport : public DOMEventTargetHelper { + public: + explicit RTCIceTransport(nsPIDOMWindowInner* aWindow); + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(RTCIceTransport, + DOMEventTargetHelper) + + // webidl + JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override; + IMPL_EVENT_HANDLER(statechange) + IMPL_EVENT_HANDLER(gatheringstatechange) + RTCIceTransportState State() const { return mState; } + RTCIceGathererState GatheringState() const { return mGatheringState; } + + void SetState(RTCIceTransportState aState); + void SetGatheringState(RTCIceGathererState aState); + + void FireStateChangeEvent(); + void FireGatheringStateChangeEvent(); + + private: + virtual ~RTCIceTransport() = default; + + RTCIceTransportState mState; + RTCIceGathererState mGatheringState; +}; + +} // namespace mozilla::dom +#endif // MOZILLA_DOM_MEDIA_WEBRTC_JSAPI_RTCICETRANSPORT_H_ diff --git a/dom/media/webrtc/jsapi/RTCRtpReceiver.cpp b/dom/media/webrtc/jsapi/RTCRtpReceiver.cpp index efe83fb782..96db9fc312 100644 --- a/dom/media/webrtc/jsapi/RTCRtpReceiver.cpp +++ b/dom/media/webrtc/jsapi/RTCRtpReceiver.cpp @@ -709,9 +709,9 @@ void RTCRtpReceiver::UpdateTransport() { // Add unique payload types as a last-ditch fallback auto uniquePts = GetJsepTransceiver() .mRecvTrack.GetNegotiatedDetails() - ->GetUniquePayloadTypes(); + ->GetUniqueReceivePayloadTypes(); for (unsigned char& uniquePt : uniquePts) { - filter->AddUniquePT(uniquePt); + filter->AddUniqueReceivePT(uniquePt); } } diff --git a/dom/media/webrtc/jsapi/RTCRtpSender.cpp b/dom/media/webrtc/jsapi/RTCRtpSender.cpp index 4ba0491521..da198c62e4 100644 --- a/dom/media/webrtc/jsapi/RTCRtpSender.cpp +++ b/dom/media/webrtc/jsapi/RTCRtpSender.cpp @@ -1502,7 +1502,6 @@ Maybe<RTCRtpSender::VideoConfig> RTCRtpSender::GetNewVideoConfig() { case dom::MediaSourceEnum::Microphone: case dom::MediaSourceEnum::AudioCapture: - case dom::MediaSourceEnum::EndGuard_: MOZ_ASSERT(false); break; } diff --git a/dom/media/webrtc/jsapi/RTCRtpTransceiver.cpp b/dom/media/webrtc/jsapi/RTCRtpTransceiver.cpp index b219619f87..6b7b456e46 100644 --- a/dom/media/webrtc/jsapi/RTCRtpTransceiver.cpp +++ b/dom/media/webrtc/jsapi/RTCRtpTransceiver.cpp @@ -239,7 +239,6 @@ SdpDirectionAttribute::Direction ToSdpDirection( case dom::RTCRtpTransceiverDirection::Inactive: case dom::RTCRtpTransceiverDirection::Stopped: return SdpDirectionAttribute::Direction::kInactive; - case dom::RTCRtpTransceiverDirection::EndGuard_:; } MOZ_CRASH("Invalid transceiver direction!"); } @@ -279,12 +278,13 @@ void RTCRtpTransceiver::Init(const RTCRtpTransceiverInit& aInit, mDirection = aInit.mDirection; } -void RTCRtpTransceiver::SetDtlsTransport(dom::RTCDtlsTransport* aDtlsTransport, - bool aStable) { +void RTCRtpTransceiver::SetDtlsTransport( + dom::RTCDtlsTransport* aDtlsTransport) { mDtlsTransport = aDtlsTransport; - if (aStable) { - mLastStableDtlsTransport = mDtlsTransport; - } +} + +void RTCRtpTransceiver::SaveStateForRollback() { + mLastStableDtlsTransport = mDtlsTransport; } void RTCRtpTransceiver::RollbackToStableDtlsTransport() { @@ -366,12 +366,17 @@ void RTCRtpTransceiver::InitConduitControl() { } void RTCRtpTransceiver::Close() { - // Called via PCImpl::Close -> PCImpl::CloseInt -> PCImpl::ShutdownMedia -> - // PCMedia::SelfDestruct. Satisfies step 7 of + // Called via PCImpl::Close + // Satisfies steps 7 and 9 of // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-close + // No events are fired for this. mShutdown = true; if (mDtlsTransport) { mDtlsTransport->UpdateStateNoEvent(TransportLayer::TS_CLOSED); + // Might not be set if we're cycle-collecting + if (mDtlsTransport->IceTransport()) { + mDtlsTransport->IceTransport()->SetState(RTCIceTransportState::Closed); + } } StopImpl(); } @@ -401,9 +406,9 @@ void RTCRtpTransceiver::Unlink() { // TODO: Only called from one place in PeerConnectionImpl, synchronously, when // the JSEP engine has successfully completed an offer/answer exchange. This is // a bit squirrely, since identity validation happens asynchronously in -// PeerConnection.jsm. This probably needs to happen once all the "in parallel" -// steps have succeeded, but before we queue the task for JS observable state -// updates. +// PeerConnection.sys.mjs. This probably needs to happen once all the "in +// parallel" steps have succeeded, but before we queue the task for JS +// observable state updates. nsresult RTCRtpTransceiver::UpdateTransport() { if (!mHasTransport) { return NS_OK; @@ -439,9 +444,9 @@ void RTCRtpTransceiver::ResetSync() { mSyncGroup = std::string(); } // TODO: Only called from one place in PeerConnectionImpl, synchronously, when // the JSEP engine has successfully completed an offer/answer exchange. This is // a bit squirrely, since identity validation happens asynchronously in -// PeerConnection.jsm. This probably needs to happen once all the "in parallel" -// steps have succeeded, but before we queue the task for JS observable state -// updates. +// PeerConnection.sys.mjs. This probably needs to happen once all the "in +// parallel" steps have succeeded, but before we queue the task for JS +// observable state updates. nsresult RTCRtpTransceiver::SyncWithMatchingVideoConduits( nsTArray<RefPtr<RTCRtpTransceiver>>& transceivers) { if (mStopped) { diff --git a/dom/media/webrtc/jsapi/RTCRtpTransceiver.h b/dom/media/webrtc/jsapi/RTCRtpTransceiver.h index afa6142d35..4fa7ef157c 100644 --- a/dom/media/webrtc/jsapi/RTCRtpTransceiver.h +++ b/dom/media/webrtc/jsapi/RTCRtpTransceiver.h @@ -119,7 +119,8 @@ class RTCRtpTransceiver : public nsISupports, public nsWrapperCache { void SyncFromJsep(const JsepSession& aSession); std::string GetMidAscii() const; - void SetDtlsTransport(RTCDtlsTransport* aDtlsTransport, bool aStable); + void SetDtlsTransport(RTCDtlsTransport* aDtlsTransport); + void SaveStateForRollback(); void RollbackToStableDtlsTransport(); std::string GetTransportId() const { diff --git a/dom/media/webrtc/jsapi/moz.build b/dom/media/webrtc/jsapi/moz.build index 78a6241cd6..05920fc151 100644 --- a/dom/media/webrtc/jsapi/moz.build +++ b/dom/media/webrtc/jsapi/moz.build @@ -31,6 +31,7 @@ UNIFIED_SOURCES += [ "RTCEncodedAudioFrame.cpp", "RTCEncodedFrameBase.cpp", "RTCEncodedVideoFrame.cpp", + "RTCIceTransport.cpp", "RTCRtpReceiver.cpp", "RTCRtpScriptTransform.cpp", "RTCRtpScriptTransformer.cpp", @@ -50,6 +51,7 @@ EXPORTS.mozilla.dom += [ "RTCEncodedAudioFrame.h", "RTCEncodedFrameBase.h", "RTCEncodedVideoFrame.h", + "RTCIceTransport.h", "RTCRtpReceiver.h", "RTCRtpScriptTransform.h", "RTCRtpScriptTransformer.h", |