From 40a355a42d4a9444dc753c04c6608dade2f06a23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:13:27 +0200 Subject: Adding upstream version 125.0.1. Signed-off-by: Daniel Baumann --- dom/media/webrtc/MediaEngineFake.cpp | 6 +- dom/media/webrtc/MediaEngineRemoteVideoSource.cpp | 3 +- dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 6 +- dom/media/webrtc/MediaTrackConstraints.h | 11 - dom/media/webrtc/MediaTransportChild.h | 6 +- dom/media/webrtc/PMediaTransport.ipdl | 4 +- dom/media/webrtc/WebrtcGlobal.h | 33 +- dom/media/webrtc/WebrtcIPCTraits.h | 11 +- dom/media/webrtc/jsapi/MediaTransportHandler.cpp | 62 +-- dom/media/webrtc/jsapi/MediaTransportHandler.h | 15 +- .../webrtc/jsapi/MediaTransportHandlerIPC.cpp | 10 +- dom/media/webrtc/jsapi/MediaTransportParent.cpp | 14 +- dom/media/webrtc/jsapi/PeerConnectionImpl.cpp | 562 +++++++++++++++------ dom/media/webrtc/jsapi/PeerConnectionImpl.h | 42 +- dom/media/webrtc/jsapi/RTCDtlsTransport.cpp | 7 +- dom/media/webrtc/jsapi/RTCDtlsTransport.h | 3 + dom/media/webrtc/jsapi/RTCIceTransport.cpp | 60 +++ dom/media/webrtc/jsapi/RTCIceTransport.h | 53 ++ dom/media/webrtc/jsapi/RTCRtpReceiver.cpp | 4 +- dom/media/webrtc/jsapi/RTCRtpSender.cpp | 1 - dom/media/webrtc/jsapi/RTCRtpTransceiver.cpp | 33 +- dom/media/webrtc/jsapi/RTCRtpTransceiver.h | 3 +- dom/media/webrtc/jsapi/moz.build | 2 + dom/media/webrtc/jsep/JsepSessionImpl.cpp | 12 +- dom/media/webrtc/jsep/JsepTrack.cpp | 4 +- dom/media/webrtc/jsep/JsepTrack.h | 10 +- dom/media/webrtc/metrics.yaml | 80 ++- dom/media/webrtc/tests/mochitests/head.js | 1 + dom/media/webrtc/tests/mochitests/iceTestUtils.js | 126 +++++ dom/media/webrtc/tests/mochitests/pc.js | 21 +- .../test_peerConnection_RTCIceTransport.html | 197 ++++++++ .../test_peerConnection_basicAudioNATRelay.html | 2 + .../test_peerConnection_basicAudioNATRelayTCP.html | 2 + .../test_peerConnection_basicAudioNATRelayTLS.html | 2 + .../test_peerConnection_basicAudioNATSrflx.html | 2 + ...est_peerConnection_basicAudioNoisyUDPBlock.html | 2 + .../test_peerConnection_basicAudioRelayPolicy.html | 5 +- .../mochitests/test_peerConnection_bug825703.html | 79 +-- .../mochitests/test_peerConnection_callbacks.html | 29 +- .../test_peerConnection_localRollback.html | 2 +- .../test_peerConnection_portRestrictions.html | 4 + .../mochitests/test_peerConnection_restartIce.html | 12 +- ...onnection_restartIceLocalAndRemoteRollback.html | 10 - ...est_peerConnection_restartIceLocalRollback.html | 9 - .../test_peerConnection_restartIceNoBundle.html | 12 +- ...peerConnection_restartIceNoBundleNoRtcpMux.html | 12 +- .../test_peerConnection_restartIceNoRtcpMux.html | 13 +- .../test_peerConnection_sillyCodecPriorities.html | 12 +- .../test_peerConnection_stats_relayProtocol.html | 2 + ...t_peerConnection_threeUnbundledConnections.html | 4 +- .../test_peerConnection_throwInCallbacks.html | 7 +- .../test_peerConnection_verifyDescriptions.html | 89 ++-- .../webrtc/third_party_build/default_config_env | 20 +- .../webrtc/third_party_build/filter_git_changes.py | 2 +- dom/media/webrtc/third_party_build/prep_repo.sh | 8 +- .../webrtc/third_party_build/save_patch_stack.py | 2 +- .../webrtc/third_party_build/verify_vendoring.sh | 16 +- dom/media/webrtc/transport/nricectx.cpp | 180 ++++--- dom/media/webrtc/transport/nricectx.h | 39 +- dom/media/webrtc/transport/nricemediastream.cpp | 55 ++ dom/media/webrtc/transport/nricemediastream.h | 14 + dom/media/webrtc/transport/test/ice_unittest.cpp | 134 +++-- .../transport/test/test_nr_socket_ice_unittest.cpp | 3 +- .../webrtc/transport/test/transport_unittests.cpp | 16 +- .../transport/third_party/nICEr/src/ice/ice_ctx.c | 38 +- .../transport/third_party/nICEr/src/ice/ice_ctx.h | 21 +- .../third_party/nICEr/src/ice/ice_handler.h | 6 + .../third_party/nICEr/src/ice/ice_media_stream.c | 38 ++ .../third_party/nICEr/src/ice/ice_media_stream.h | 1 + .../third_party/nICEr/src/ice/ice_peer_ctx.c | 6 + .../webrtc/transportbridge/MediaPipelineFilter.cpp | 34 +- .../webrtc/transportbridge/MediaPipelineFilter.h | 4 +- 72 files changed, 1698 insertions(+), 652 deletions(-) create mode 100644 dom/media/webrtc/jsapi/RTCIceTransport.cpp create mode 100644 dom/media/webrtc/jsapi/RTCIceTransport.h create mode 100644 dom/media/webrtc/tests/mochitests/test_peerConnection_RTCIceTransport.html (limited to 'dom/media/webrtc') diff --git a/dom/media/webrtc/MediaEngineFake.cpp b/dom/media/webrtc/MediaEngineFake.cpp index 8c69ec5e47..b14d80ffbb 100644 --- a/dom/media/webrtc/MediaEngineFake.cpp +++ b/dom/media/webrtc/MediaEngineFake.cpp @@ -136,10 +136,8 @@ MediaEngineFakeVideoSource::MediaEngineFakeVideoSource() mSettings->mHeight.Construct( int32_t(MediaEnginePrefs::DEFAULT_43_VIDEO_HEIGHT)); mSettings->mFrameRate.Construct(double(MediaEnginePrefs::DEFAULT_VIDEO_FPS)); - mSettings->mFacingMode.Construct( - NS_ConvertASCIItoUTF16(dom::VideoFacingModeEnumValues::strings - [uint8_t(VideoFacingModeEnum::Environment)] - .value)); + mSettings->mFacingMode.Construct(NS_ConvertASCIItoUTF16( + dom::GetEnumString(VideoFacingModeEnum::Environment))); } nsString MediaEngineFakeVideoSource::GetGroupId() { diff --git a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp index a6648b68c7..dbb5e7ff70 100644 --- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp @@ -108,8 +108,7 @@ MediaEngineRemoteVideoSource::MediaEngineRemoteVideoSource( Maybe facingMode = GetFacingMode(mMediaDevice->mRawName); if (facingMode.isSome()) { - NS_ConvertASCIItoUTF16 facingString( - dom::VideoFacingModeEnumValues::GetString(*facingMode)); + NS_ConvertASCIItoUTF16 facingString(dom::GetEnumString(*facingMode)); mSettings->mFacingMode.Construct(facingString); mFacingMode.emplace(facingString); } diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index ee45bb0317..9d778d411d 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -105,9 +105,9 @@ nsresult MediaEngineWebRTCMicrophoneSource::EvaluateSettings( prefs.mChannels = c.mChannelCount.Get(std::min(prefs.mChannels, maxChannels)); prefs.mChannels = std::max(1, std::min(prefs.mChannels, maxChannels)); - LOG("Audio config: agc: %d, noise: %d, channels: %d", - prefs.mAgcOn ? prefs.mAgc : -1, prefs.mNoiseOn ? prefs.mNoise : -1, - prefs.mChannels); + LOG("Mic source %p Audio config: aec: %s, agc: %s, noise: %s, channels: %d", + this, prefs.mAecOn ? "on" : "off", prefs.mAgcOn ? "on" : "off", + prefs.mNoiseOn ? "on" : "off", prefs.mChannels); *aOutPrefs = prefs; diff --git a/dom/media/webrtc/MediaTrackConstraints.h b/dom/media/webrtc/MediaTrackConstraints.h index 61e0ed85ea..a948bf1499 100644 --- a/dom/media/webrtc/MediaTrackConstraints.h +++ b/dom/media/webrtc/MediaTrackConstraints.h @@ -20,17 +20,6 @@ namespace mozilla { class LocalMediaDevice; class MediaDevice; -template -static Enum StringToEnum(const EnumValuesStrings& aStrings, - const nsAString& aValue, Enum aDefaultValue) { - for (size_t i = 0; aStrings[i].value; i++) { - if (aValue.EqualsASCII(aStrings[i].value)) { - return Enum(i); - } - } - return aDefaultValue; -} - // Helper classes for orthogonal constraints without interdependencies. // Instead of constraining values, constrain the constraints themselves. class NormalizedConstraintSet { diff --git a/dom/media/webrtc/MediaTransportChild.h b/dom/media/webrtc/MediaTransportChild.h index ef08fc55aa..0f9f5e8714 100644 --- a/dom/media/webrtc/MediaTransportChild.h +++ b/dom/media/webrtc/MediaTransportChild.h @@ -21,8 +21,10 @@ class MediaTransportChild : public dom::PMediaTransportChild { mozilla::ipc::IPCResult RecvOnCandidate(const string& transportId, const CandidateInfo& candidateInfo); mozilla::ipc::IPCResult RecvOnAlpnNegotiated(const string& alpn); - mozilla::ipc::IPCResult RecvOnGatheringStateChange(const int& state); - mozilla::ipc::IPCResult RecvOnConnectionStateChange(const int& state); + mozilla::ipc::IPCResult RecvOnGatheringStateChange(const string& transportId, + const int& state); + mozilla::ipc::IPCResult RecvOnConnectionStateChange(const string& transportId, + const int& state); mozilla::ipc::IPCResult RecvOnPacketReceived(const string& transportId, const MediaPacket& packet); mozilla::ipc::IPCResult RecvOnEncryptedSending(const string& transportId, diff --git a/dom/media/webrtc/PMediaTransport.ipdl b/dom/media/webrtc/PMediaTransport.ipdl index e7501ed814..ae6580f768 100644 --- a/dom/media/webrtc/PMediaTransport.ipdl +++ b/dom/media/webrtc/PMediaTransport.ipdl @@ -88,8 +88,8 @@ parent: child: async OnCandidate(string transportId, CandidateInfo candidateInfo); async OnAlpnNegotiated(string alpn); - async OnGatheringStateChange(int state); - async OnConnectionStateChange(int state); + async OnGatheringStateChange(string transportId, int state); + async OnConnectionStateChange(string transportId, int state); async OnPacketReceived(string transportId, MediaPacket packet); async OnEncryptedSending(string transportId, MediaPacket packet); async OnStateChange(string transportId, int state); diff --git a/dom/media/webrtc/WebrtcGlobal.h b/dom/media/webrtc/WebrtcGlobal.h index d610ee5d10..e9ec2ced51 100644 --- a/dom/media/webrtc/WebrtcGlobal.h +++ b/dom/media/webrtc/WebrtcGlobal.h @@ -8,6 +8,7 @@ #include "WebrtcIPCTraits.h" #include "ipc/EnumSerializer.h" #include "ipc/IPCMessageUtilsSpecializations.h" +#include "mozilla/dom/BindingIPCUtils.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/RTCDataChannelBinding.h" #include "mozilla/dom/RTCStatsReportBinding.h" @@ -62,30 +63,22 @@ namespace IPC { template <> struct ParamTraits - : public ContiguousEnumSerializer {}; + : public mozilla::dom::WebIDLEnumSerializer {}; template <> struct ParamTraits - : public ContiguousEnumSerializer< - mozilla::dom::RTCStatsIceCandidatePairState, - mozilla::dom::RTCStatsIceCandidatePairState::Frozen, - mozilla::dom::RTCStatsIceCandidatePairState::EndGuard_> {}; + : public mozilla::dom::WebIDLEnumSerializer< + mozilla::dom::RTCStatsIceCandidatePairState> {}; template <> struct ParamTraits - : public ContiguousEnumSerializer< - mozilla::dom::RTCIceCandidateType, - mozilla::dom::RTCIceCandidateType::Host, - mozilla::dom::RTCIceCandidateType::EndGuard_> {}; + : public mozilla::dom::WebIDLEnumSerializer< + mozilla::dom::RTCIceCandidateType> {}; template <> struct ParamTraits - : public ContiguousEnumSerializer< - mozilla::dom::RTCBundlePolicy, - mozilla::dom::RTCBundlePolicy::Balanced, - mozilla::dom::RTCBundlePolicy::EndGuard_> {}; + : public mozilla::dom::WebIDLEnumSerializer { +}; DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::RTCIceServerInternal, mUrls, mCredentialProvided, mUserNameProvided); @@ -218,10 +211,8 @@ DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::RTCDataChannelStats, mId, template <> struct ParamTraits - : public ContiguousEnumSerializer< - mozilla::dom::RTCDataChannelState, - mozilla::dom::RTCDataChannelState::Connecting, - mozilla::dom::RTCDataChannelState::EndGuard_> {}; + : public mozilla::dom::WebIDLEnumSerializer< + mozilla::dom::RTCDataChannelState> {}; DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::RTCCodecStats, mTimestamp, mType, mId, mPayloadType, mCodecType, @@ -230,9 +221,7 @@ DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::RTCCodecStats, mTimestamp, template <> struct ParamTraits - : public ContiguousEnumSerializer {}; + : public mozilla::dom::WebIDLEnumSerializer {}; } // namespace IPC #endif // _WEBRTC_GLOBAL_H_ diff --git a/dom/media/webrtc/WebrtcIPCTraits.h b/dom/media/webrtc/WebrtcIPCTraits.h index b076745608..5b526da67f 100644 --- a/dom/media/webrtc/WebrtcIPCTraits.h +++ b/dom/media/webrtc/WebrtcIPCTraits.h @@ -9,6 +9,7 @@ #include "ipc/IPCMessageUtils.h" #include "ipc/IPCMessageUtilsSpecializations.h" #include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/BindingIPCUtils.h" #include "mozilla/dom/RTCConfigurationBinding.h" #include "mozilla/media/webrtc/WebrtcGlobal.h" #include "mozilla/dom/CandidateInfo.h" @@ -62,17 +63,15 @@ struct ParamTraits { } }; -template -struct WebidlEnumSerializer - : public ContiguousEnumSerializer {}; - template <> struct ParamTraits - : public WebidlEnumSerializer {}; + : public mozilla::dom::WebIDLEnumSerializer< + mozilla::dom::RTCIceCredentialType> {}; template <> struct ParamTraits - : public WebidlEnumSerializer {}; + : public mozilla::dom::WebIDLEnumSerializer< + mozilla::dom::RTCIceTransportPolicy> {}; DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::RTCIceServer, mCredential, mCredentialType, mUrl, mUrls, mUsername) 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 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 SignalCandidate; sigslot::signal2 SignalAlpnNegotiated; - sigslot::signal1 SignalGatheringStateChange; - sigslot::signal1 SignalConnectionStateChange; + sigslot::signal2 + SignalGatheringStateChange; + sigslot::signal2 + SignalConnectionStateChange; sigslot::signal2 SignalPacketReceived; sigslot::signal2 @@ -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(state)); + mUser->OnGatheringStateChange(transportId, + static_cast(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(state)); + transportId, static_cast(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(aState))); + void OnGatheringStateChange(const std::string& aTransportId, + dom::RTCIceGathererState aState) { + NS_ENSURE_TRUE_VOID(mParent->SendOnGatheringStateChange( + aTransportId, static_cast(aState))); } - void OnConnectionStateChange(dom::RTCIceConnectionState aState) { - NS_ENSURE_TRUE_VOID( - mParent->SendOnConnectionStateChange(static_cast(aState))); + void OnConnectionStateChange(const std::string& aTransportId, + dom::RTCIceTransportState aState) { + NS_ENSURE_TRUE_VOID(mParent->SendOnConnectionStateChange( + aTransportId, static_cast(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 PeerConnectionImpl::GetTransceiver( + const std::string& aTransceiverId) { + for (const auto& transceiver : mTransceivers) { + if (transceiver->GetJsepTransceiverId() == aTransceiverId) { + return transceiver; + } + } + return nullptr; +} + void PeerConnectionImpl::NotifyDataChannel( already_AddRefed 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 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 // Oh well. std::set statesFound; - for (const auto& [id, dtlsTransport] : mTransportIdToRTCDtlsTransport) { - Unused << id; - statesFound.insert(dtlsTransport->State()); + std::set> 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(mConnectionState), static_cast(newState), - this); + CSFLogInfo(LOGTAG, "%s: %d -> %d (%p)", __FUNCTION__, + static_cast(mConnectionState), static_cast(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(mIceConnectionState), - static_cast(domState)); + CSFLogDebug(LOGTAG, "IceConnectionStateChange: %s %d (%p)", + aTransportId.c_str(), static_cast(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 dtlsTransport = + mTransportIdToRTCDtlsTransport.Get(key); + if (!dtlsTransport) { return; } + RefPtr 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 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 + // Oh well. + std::set statesFound; + std::set> transports(GetActiveTransports()); + for (const auto& transport : transports) { + RefPtr iceTransport = transport->IceTransport(); + CSFLogWarn(LOGTAG, "GetNewIceConnectionState: %p %d", iceTransport.get(), + static_cast(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(mIceConnectionState), + static_cast(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(state)); - if (mIceGatheringState == state) { + CSFLogWarn(LOGTAG, "IceGatheringStateChange: %s %d (%p)", + aTransportId.c_str(), static_cast(state), this); + + // Let transport be the RTCIceTransport for which candidate gathering + // began/finished. + nsCString key(aTransportId.data(), aTransportId.size()); + RefPtr dtlsTransport = + mTransportIdToRTCDtlsTransport.Get(key); + if (!dtlsTransport) { + return; + } + RefPtr 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(mIceGatheringState), static_cast(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> transports(GetActiveTransports()); + for (const auto& transport : transports) { + RefPtr 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 oldSctp = mSctpTransport.forget(); + mJsepSession->ForEachTransceiver( - [this, self = RefPtr(this)]( - const JsepTransceiver& jsepTransceiver) { + [this, self = RefPtr(this), + oldSctp](const JsepTransceiver& jsepTransceiver) { std::string transportId = jsepTransceiver.mTransport.mTransportId; - if (transportId.empty()) { - return; + RefPtr 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(mDataConnection->GetMaxMessageSize()); + Nullable 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 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& aTransportIds) { - for (auto iter = mTransportIdToRTCDtlsTransport.begin(); - iter != mTransportIdToRTCDtlsTransport.end();) { - if (!aTransportIds.count(iter->first)) { - iter = mTransportIdToRTCDtlsTransport.erase(iter); - } else { - ++iter; +std::set> +PeerConnectionImpl::GetActiveTransports() const { + std::set> 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 finalTransports; - Maybe sctpTransport; mJsepSession->ForEachTransceiver( [&, this, self = RefPtr(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 dtlsTransport = it->second; - // Why on earth does the spec use a floating point for this? - double maxMessageSize = - static_cast(mDataConnection->GetMaxMessageSize()); - Nullable 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 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>; + 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& aTransportIds); + void UpdateRTCDtlsTransports(); + void SaveStateForRollback(); + void RestoreStateForRollback(); + std::set> GetActiveTransports() const; // Activate ICE transports at the conclusion of offer/answer, // or when rollback occurs. @@ -861,9 +876,12 @@ class PeerConnectionImpl final std::set> mLocalIceCredentialsToReplace; nsTArray> mTransceivers; - std::map> - mTransportIdToRTCDtlsTransport; + RTCDtlsTransportMap mTransportIdToRTCDtlsTransport; RefPtr mSctpTransport; + // This is similar to [[LastStableStateSender/ReceiverTransport]], but for + // DataChannel. + RefPtr mLastStableSctpTransport; + RefPtr 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 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 aGivenProto) override; IMPL_EVENT_HANDLER(statechange) RTCDtlsTransportState State() const { return mState; } + RefPtr 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 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 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::Constructor(this, u"statechange"_ns, init); + + DispatchTrustedEvent(event); +} + +void RTCIceTransport::FireGatheringStateChangeEvent() { + EventInit init; + init.mBubbles = false; + init.mCancelable = false; + + RefPtr 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 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::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>& 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", diff --git a/dom/media/webrtc/jsep/JsepSessionImpl.cpp b/dom/media/webrtc/jsep/JsepSessionImpl.cpp index 50def6eb6c..c091b6d618 100644 --- a/dom/media/webrtc/jsep/JsepSessionImpl.cpp +++ b/dom/media/webrtc/jsep/JsepSessionImpl.cpp @@ -1121,11 +1121,17 @@ nsresult JsepSessionImpl::HandleNegotiatedSession( CopyBundleTransports(); - std::vector remoteTracks; + std::vector receiveTracks; for (auto& transceiver : mTransceivers) { - remoteTracks.push_back(&transceiver.mRecvTrack); + // Do not count payload types for non-active recv tracks as duplicates. If + // we receive an RTP packet with a payload type that is used by both a + // sendrecv and a sendonly m-section, there is no ambiguity; it is for the + // sendrecv m-section. + if (transceiver.mRecvTrack.GetActive()) { + receiveTracks.push_back(&transceiver.mRecvTrack); + } } - JsepTrack::SetUniquePayloadTypes(remoteTracks); + JsepTrack::SetUniqueReceivePayloadTypes(receiveTracks); mNegotiations++; diff --git a/dom/media/webrtc/jsep/JsepTrack.cpp b/dom/media/webrtc/jsep/JsepTrack.cpp index 59c038cfc0..2498200ab2 100644 --- a/dom/media/webrtc/jsep/JsepTrack.cpp +++ b/dom/media/webrtc/jsep/JsepTrack.cpp @@ -662,7 +662,7 @@ nsresult JsepTrack::Negotiate(const SdpMediaSection& answer, // works, however, if that payload type appeared in only one m-section. // We figure that out here. /* static */ -void JsepTrack::SetUniquePayloadTypes(std::vector& tracks) { +void JsepTrack::SetUniqueReceivePayloadTypes(std::vector& tracks) { // Maps to track details if no other track contains the payload type, // otherwise maps to nullptr. std::map payloadTypeToDetailsMap; @@ -697,7 +697,7 @@ void JsepTrack::SetUniquePayloadTypes(std::vector& tracks) { auto trackDetails = ptAndDetails.second; if (trackDetails) { - trackDetails->mUniquePayloadTypes.push_back( + trackDetails->mUniqueReceivePayloadTypes.push_back( static_cast(uniquePt)); } } diff --git a/dom/media/webrtc/jsep/JsepTrack.h b/dom/media/webrtc/jsep/JsepTrack.h index d11735f43a..74a0d2396c 100644 --- a/dom/media/webrtc/jsep/JsepTrack.h +++ b/dom/media/webrtc/jsep/JsepTrack.h @@ -31,7 +31,7 @@ class JsepTrackNegotiatedDetails { JsepTrackNegotiatedDetails(const JsepTrackNegotiatedDetails& orig) : mExtmap(orig.mExtmap), - mUniquePayloadTypes(orig.mUniquePayloadTypes), + mUniqueReceivePayloadTypes(orig.mUniqueReceivePayloadTypes), mTias(orig.mTias), mRtpRtcpConf(orig.mRtpRtcpConf) { for (const auto& encoding : orig.mEncodings) { @@ -71,8 +71,8 @@ class JsepTrackNegotiatedDetails { } } - std::vector GetUniquePayloadTypes() const { - return mUniquePayloadTypes; + std::vector GetUniqueReceivePayloadTypes() const { + return mUniqueReceivePayloadTypes; } uint32_t GetTias() const { return mTias; } @@ -83,7 +83,7 @@ class JsepTrackNegotiatedDetails { friend class JsepTrack; std::map mExtmap; - std::vector mUniquePayloadTypes; + std::vector mUniqueReceivePayloadTypes; std::vector> mEncodings; uint32_t mTias; // bits per second RtpRtcpConfig mRtpRtcpConf; @@ -214,7 +214,7 @@ class JsepTrack { virtual nsresult Negotiate(const SdpMediaSection& answer, const SdpMediaSection& remote, const SdpMediaSection& local); - static void SetUniquePayloadTypes(std::vector& tracks); + static void SetUniqueReceivePayloadTypes(std::vector& tracks); virtual void GetNegotiatedPayloadTypes( std::vector* payloadTypes) const; diff --git a/dom/media/webrtc/metrics.yaml b/dom/media/webrtc/metrics.yaml index da3d077f2d..aea5cf17fb 100644 --- a/dom/media/webrtc/metrics.yaml +++ b/dom/media/webrtc/metrics.yaml @@ -18,14 +18,16 @@ rtcrtpsender: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 count_setparameters_compat: type: counter @@ -35,14 +37,16 @@ rtcrtpsender: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 used_sendencodings: type: rate @@ -53,14 +57,16 @@ rtcrtpsender: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 rtcrtpsender.setparameters: warn_no_getparameters: @@ -74,14 +80,16 @@ rtcrtpsender.setparameters: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 blame_no_getparameters: type: labeled_counter @@ -92,13 +100,15 @@ rtcrtpsender.setparameters: enough call to `getParameters`) Collected only on EARLY_BETA_OR_EARLIER. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1831343 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1831343 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - web_activity notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 warn_length_changed: type: rate @@ -110,14 +120,16 @@ rtcrtpsender.setparameters: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 blame_length_changed: type: labeled_counter @@ -128,13 +140,15 @@ rtcrtpsender.setparameters: EARLY_BETA_OR_EARLIER. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1831343 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1831343 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - web_activity notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 warn_no_transactionid: type: rate @@ -146,14 +160,16 @@ rtcrtpsender.setparameters: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 blame_no_transactionid: type: labeled_counter @@ -163,13 +179,15 @@ rtcrtpsender.setparameters: by the eTLD+1 of the site. Collected only on EARLY_BETA_OR_EARLIER. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1831343 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1831343 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - web_activity notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 fail_length_changed: type: rate @@ -181,14 +199,16 @@ rtcrtpsender.setparameters: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 fail_rid_changed: type: rate @@ -201,14 +221,16 @@ rtcrtpsender.setparameters: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 fail_no_getparameters: type: rate @@ -220,14 +242,16 @@ rtcrtpsender.setparameters: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 fail_no_transactionid: type: rate @@ -238,14 +262,16 @@ rtcrtpsender.setparameters: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 fail_stale_transactionid: type: rate @@ -256,14 +282,16 @@ rtcrtpsender.setparameters: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 fail_no_encodings: type: rate @@ -277,14 +305,16 @@ rtcrtpsender.setparameters: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 fail_other: type: rate @@ -295,14 +325,16 @@ rtcrtpsender.setparameters: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1401592 - https://bugzilla.mozilla.org/show_bug.cgi?id=1832459 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881403 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 codec.stats: ulpfec_negotiated: @@ -312,13 +344,15 @@ codec.stats: on the first negotiation for each video transceiver. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1858213 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881396 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1858213 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881396 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 labels: - negotiated - not_negotiated @@ -329,13 +363,15 @@ codec.stats: Count how many other fec options are being offered. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1858213 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881396 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1858213 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881396 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 video_preferred_codec: type: labeled_counter @@ -343,13 +379,15 @@ codec.stats: Counts the preferred video codec being signaled to us to identify preferred video codec. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1858213 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881396 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1858213 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881396 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 audio_preferred_codec: type: labeled_counter @@ -357,10 +395,12 @@ codec.stats: Counts the preferred audio codec being signaled to us to identify preferred audio codec. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1858213 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881396 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1858213 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1881396 data_sensitivity: - technical notification_emails: - webrtc-telemetry-alerts@mozilla.com - expires: 126 + expires: 132 diff --git a/dom/media/webrtc/tests/mochitests/head.js b/dom/media/webrtc/tests/mochitests/head.js index 9a4e5682a3..1fd559217a 100644 --- a/dom/media/webrtc/tests/mochitests/head.js +++ b/dom/media/webrtc/tests/mochitests/head.js @@ -424,6 +424,7 @@ function setupEnvironment() { ["media.peerconnection.nat_simulator.block_udp", false], ["media.peerconnection.nat_simulator.redirect_address", ""], ["media.peerconnection.nat_simulator.redirect_targets", ""], + ["media.peerconnection.treat_warnings_as_errors", true], ], }; diff --git a/dom/media/webrtc/tests/mochitests/iceTestUtils.js b/dom/media/webrtc/tests/mochitests/iceTestUtils.js index d4d1f5c4b4..9e76e3f7df 100644 --- a/dom/media/webrtc/tests/mochitests/iceTestUtils.js +++ b/dom/media/webrtc/tests/mochitests/iceTestUtils.js @@ -300,3 +300,129 @@ async function checkNoRelay(iceServers) { ); pc.close(); } + +function gatheringStateReached(object, state) { + if (object instanceof RTCIceTransport) { + return new Promise(r => + object.addEventListener("gatheringstatechange", function listener() { + if (object.gatheringState == state) { + object.removeEventListener("gatheringstatechange", listener); + r(state); + } + }) + ); + } else if (object instanceof RTCPeerConnection) { + return new Promise(r => + object.addEventListener("icegatheringstatechange", function listener() { + if (object.iceGatheringState == state) { + object.removeEventListener("icegatheringstatechange", listener); + r(state); + } + }) + ); + } else { + throw "First parameter is neither an RTCIceTransport nor an RTCPeerConnection"; + } +} + +function nextGatheringState(object) { + if (object instanceof RTCIceTransport) { + return new Promise(resolve => + object.addEventListener( + "gatheringstatechange", + () => resolve(object.gatheringState), + { once: true } + ) + ); + } else if (object instanceof RTCPeerConnection) { + return new Promise(resolve => + object.addEventListener( + "icegatheringstatechange", + () => resolve(object.iceGatheringState), + { once: true } + ) + ); + } else { + throw "First parameter is neither an RTCIceTransport nor an RTCPeerConnection"; + } +} + +function emptyCandidate(pc) { + return new Promise(r => + pc.addEventListener("icecandidate", function listener(e) { + if (e.candidate && e.candidate.candidate == "") { + pc.removeEventListener("icecandidate", listener); + r(e); + } + }) + ); +} + +function nullCandidate(pc) { + return new Promise(r => + pc.addEventListener("icecandidate", function listener(e) { + if (!e.candidate) { + pc.removeEventListener("icecandidate", listener); + r(e); + } + }) + ); +} + +function connectionStateReached(object, state) { + if (object instanceof RTCIceTransport || object instanceof RTCDtlsTransport) { + return new Promise(resolve => + object.addEventListener("statechange", function listener() { + if (object.state == state) { + object.removeEventListener("statechange", listener); + resolve(state); + } + }) + ); + } else if (object instanceof RTCPeerConnection) { + return new Promise(resolve => + object.addEventListener("connectionstatechange", function listener() { + if (object.connectionState == state) { + object.removeEventListener("connectionstatechange", listener); + resolve(state); + } + }) + ); + } else { + throw "First parameter is neither an RTCIceTransport, an RTCDtlsTransport, nor an RTCPeerConnection"; + } +} + +function nextConnectionState(object) { + if (object instanceof RTCIceTransport || object instanceof RTCDtlsTransport) { + return new Promise(resolve => + object.addEventListener("statechange", () => resolve(object.state), { + once: true, + }) + ); + } else if (object instanceof RTCPeerConnection) { + return new Promise(resolve => + object.addEventListener( + "connectionstatechange", + () => resolve(object.connectionState), + { once: true } + ) + ); + } else { + throw "First parameter is neither an RTCIceTransport, an RTCDtlsTransport, nor an RTCPeerConnection"; + } +} + +function nextIceConnectionState(pc) { + if (pc instanceof RTCPeerConnection) { + return new Promise(resolve => + pc.addEventListener( + "iceconnectionstatechange", + () => resolve(pc.iceConnectionState), + { once: true } + ) + ); + } else { + throw "First parameter is not an RTCPeerConnection"; + } +} diff --git a/dom/media/webrtc/tests/mochitests/pc.js b/dom/media/webrtc/tests/mochitests/pc.js index 73e1b2c2f0..5778c61392 100644 --- a/dom/media/webrtc/tests/mochitests/pc.js +++ b/dom/media/webrtc/tests/mochitests/pc.js @@ -12,8 +12,8 @@ const iceStateTransitions = { checking: ["new", "connected", "failed", "closed"], //Note: do we need to // allow 'completed' in // here as well? - connected: ["new", "completed", "disconnected", "closed"], - completed: ["new", "disconnected", "closed"], + connected: ["new", "checking", "completed", "disconnected", "closed"], + completed: ["new", "checking", "disconnected", "closed"], disconnected: ["new", "connected", "completed", "failed", "closed"], failed: ["new", "disconnected", "closed"], closed: [], @@ -367,9 +367,7 @@ PeerConnectionTest.prototype.createDataChannel = function (options) { PeerConnectionTest.prototype.createAnswer = function (peer) { return peer.createAnswer().then(answer => { // make a copy so this does not get updated with ICE candidates - this.originalAnswer = new RTCSessionDescription( - JSON.parse(JSON.stringify(answer)) - ); + this.originalAnswer = JSON.parse(JSON.stringify(answer)); return answer; }); }; @@ -384,9 +382,7 @@ PeerConnectionTest.prototype.createAnswer = function (peer) { PeerConnectionTest.prototype.createOffer = function (peer) { return peer.createOffer().then(offer => { // make a copy so this does not get updated with ICE candidates - this.originalOffer = new RTCSessionDescription( - JSON.parse(JSON.stringify(offer)) - ); + this.originalOffer = JSON.parse(JSON.stringify(offer)); return offer; }); }; @@ -1399,10 +1395,6 @@ PeerConnectionWrapper.prototype = { }); }, - isTrackOnPC(track) { - return !!this.getStreamForRecvTrack(track); - }, - allExpectedTracksAreObserved(expected, observed) { return Object.keys(expected).every(trackId => observed[trackId]); }, @@ -1459,7 +1451,10 @@ PeerConnectionWrapper.prototype = { setupTrackEventHandler() { this._pc.addEventListener("track", ({ track, streams }) => { info(`${this}: 'ontrack' event fired for ${track.id}`); - ok(this.isTrackOnPC(track), `Found track ${track.id}`); + ok( + this._pc.getReceivers().some(r => r.track == track), + `Found track ${track.id}` + ); let gratuitousEvent = true; let streamsContainingTrack = this.remoteStreamsByTrackId.get(track.id); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_RTCIceTransport.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_RTCIceTransport.html new file mode 100644 index 0000000000..893d1aad5b --- /dev/null +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_RTCIceTransport.html @@ -0,0 +1,197 @@ + + + + + + + +
+
+
+ + diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelay.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelay.html index 180abc075a..7f3f9f57ad 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelay.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelay.html @@ -24,6 +24,8 @@ if (!("mediaDevices" in navigator)) { ['media.peerconnection.nat_simulator.filtering_type', 'PORT_DEPENDENT'], ['media.peerconnection.nat_simulator.mapping_type', 'PORT_DEPENDENT'], ['media.peerconnection.nat_simulator.block_tcp', true], + // The above triggers warning about 5 ICE servers + ['media.peerconnection.treat_warnings_as_errors', false], ['media.getusermedia.insecure.enabled', true]); options.expectedLocalCandidateType = "srflx"; options.expectedRemoteCandidateType = "relay"; diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelayTCP.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelayTCP.html index 7bb51764bd..387341151f 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelayTCP.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelayTCP.html @@ -26,6 +26,8 @@ if (!("mediaDevices" in navigator)) { ['media.peerconnection.nat_simulator.block_tcp', false], ['media.peerconnection.nat_simulator.block_tls', true], ['media.peerconnection.ice.loopback', true], + // The above sets up 5+ ICE servers which triggers a warning + ['media.peerconnection.treat_warnings_as_errors', false], ['media.getusermedia.insecure.enabled', true]); options.expectedLocalCandidateType = "relay-tcp"; options.expectedRemoteCandidateType = "relay-tcp"; diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelayTLS.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelayTLS.html index 7446401f87..2ac886c156 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelayTLS.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATRelayTLS.html @@ -25,6 +25,8 @@ if (!("mediaDevices" in navigator)) { ['media.peerconnection.nat_simulator.block_udp', true], ['media.peerconnection.nat_simulator.block_tcp', true], ['media.peerconnection.ice.loopback', true], + // The above triggers warning about 5 ICE servers + ['media.peerconnection.treat_warnings_as_errors', false], ['media.getusermedia.insecure.enabled', true]); options.expectedLocalCandidateType = "relay-tls"; options.expectedRemoteCandidateType = "relay-tls"; diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATSrflx.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATSrflx.html index 78fa8bcb2c..69fc2af80a 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATSrflx.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNATSrflx.html @@ -24,6 +24,8 @@ if (!("mediaDevices" in navigator)) { ['media.peerconnection.nat_simulator.filtering_type', 'ENDPOINT_INDEPENDENT'], ['media.peerconnection.nat_simulator.mapping_type', 'ENDPOINT_INDEPENDENT'], ['media.peerconnection.nat_simulator.block_tcp', true], + // The above triggers warning about 5 ICE servers + ['media.peerconnection.treat_warnings_as_errors', false], ['media.getusermedia.insecure.enabled', true]); options.expectedLocalCandidateType = "srflx"; options.expectedRemoteCandidateType = "srflx"; diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNoisyUDPBlock.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNoisyUDPBlock.html index 297121cd94..4175419d44 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNoisyUDPBlock.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioNoisyUDPBlock.html @@ -25,6 +25,8 @@ if (!("mediaDevices" in navigator)) { ['media.peerconnection.nat_simulator.block_udp', true], ['media.peerconnection.nat_simulator.error_code_for_drop', 3 /*R_INTERNAL*/], ['media.peerconnection.nat_simulator.block_tls', true], + // The above triggers warning about 5 ICE servers + ['media.peerconnection.treat_warnings_as_errors', false], ['media.getusermedia.insecure.enabled', true]); options.expectedLocalCandidateType = "relay-tcp"; options.expectedRemoteCandidateType = "relay-tcp"; diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioRelayPolicy.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioRelayPolicy.html index ced57ff8a3..313d76441b 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioRelayPolicy.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_basicAudioRelayPolicy.html @@ -14,7 +14,10 @@ createHTML({ runNetworkTest(async () => { await pushPrefs( // Enable mDNS, since there are some checks we want to run with that - ['media.peerconnection.ice.obfuscate_host_addresses', true]); + ['media.peerconnection.ice.obfuscate_host_addresses', true], + // The above triggers warning about 5 ICE servers + ['media.peerconnection.treat_warnings_as_errors', false], + ); const offerer = new RTCPeerConnection({iceServers: iceServersArray, iceTransportPolicy: 'relay'}); const answerer = new RTCPeerConnection({iceServers: iceServersArray}); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_bug825703.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_bug825703.html index 5cd168af8a..5efb4a17d2 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_bug825703.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_bug825703.html @@ -25,7 +25,7 @@ var makePC = (config, expected_error) => { exception = e; } is((exception? exception.name : "success"), expected_error || "success", - "RTCPeerConnection(" + JSON.stringify(config) + ")"); + "RTCPeerConnection(" + JSON.stringify(config) + ") " + exception?.message); }; // The order of properties in objects is not guaranteed in JavaScript, so this @@ -39,8 +39,8 @@ var toComparable = o => }, {}); // This is a test of the iceServers parsing code + readable errors -runNetworkTest(() => { - var exception = null; +runNetworkTest(async () => { + let exception = null; try { new RTCPeerConnection().close(); @@ -56,26 +56,37 @@ runNetworkTest(() => { { urls:"stun:127.0.0.1" }, { urls:"stun:localhost", foo:"" }, { urls: ["stun:127.0.0.1", "stun:localhost"] }, - { urls:"stuns:localhost", foo:"" }, + ]}); + makePC({ iceServers: [ { urls:"turn:[::1]:3478", username:"p", credential:"p" }, { urls:"turn:[::1]:3478", username:"", credential:"" }, { urls:"turns:[::1]:3478", username:"", credential:"" }, + ]}); + makePC({ iceServers: [ { urls:"turn:localhost:3478?transport=udp", username:"p", credential:"p" }, { urls: ["turn:[::1]:3478", "turn:localhost"], username:"p", credential:"p" }, { urls:"turns:localhost:3478?transport=udp", username:"p", credential:"p" }, - { url:"stun:localhost", foo:"" }, - { url:"turn:localhost", username:"p", credential:"p" } ]}); - makePC({ iceServers: [{ urls:"http:0.0.0.0" }] }, "SyntaxError"); try { - new RTCPeerConnection({ iceServers: [{ url:"http:0.0.0.0" }] }).close(); + new RTCPeerConnection({ iceServers: [{ urls:"http:0.0.0.0" }] }).close(); } catch (e) { ok(e.message.indexOf("http") > 0, "RTCPeerConnection() constructor has readable exceptions"); } + const push = prefs => SpecialPowers.pushPrefEnv(prefs); + + // Remaining tests trigger warnings + await push({ set: [['media.peerconnection.treat_warnings_as_errors', false]] }); + + makePC({ iceServers: [ + { urls:"stuns:localhost", foo:"" }, + { url:"stun:localhost", foo:"" }, + { url:"turn:localhost", username:"p", credential:"p" } + ]}); + // Test getConfiguration const config = { bundlePolicy: "max-bundle", @@ -98,41 +109,35 @@ runNetworkTest(() => { JSON.stringify(toComparable(config)), "getConfiguration"); pc.close(); - var push = prefs => SpecialPowers.pushPrefEnv(prefs); - - return Promise.resolve() // This set of tests are setting the about:config User preferences for default // ice servers and checking the outputs when RTCPeerConnection() is // invoked. See Bug 1167922 for more information. - .then(() => push({ set: [['media.peerconnection.default_iceservers', ""]] }) - .then(() => makePC()) - .then(() => push({ set: [['media.peerconnection.default_iceservers', "k"]] })) - .then(() => makePC()) - .then(() => push({ set: [['media.peerconnection.default_iceservers', "[{\"urls\": [\"stun:stun.services.mozilla.com\"]}]"]] })) - .then(() => makePC())) + await push({ set: [['media.peerconnection.default_iceservers', ""]] }); + makePC(); + await push({ set: [['media.peerconnection.default_iceservers', "k"]] }); + makePC(); + await push({ set: [['media.peerconnection.default_iceservers', + "[{\"urls\": [\"stun:stun.services.mozilla.com\"]}]"]]}); + makePC(); // This set of tests check that warnings work. See Bug 1254839 for more. - .then(() => { - let promise = new Promise(resolve => { - SpecialPowers.registerConsoleListener(msg => { - if (msg.message.includes("onaddstream")) { - SpecialPowers.postConsoleSentinel(); - resolve(msg.message); - } - }); + const warning = await new Promise(resolve => { + SpecialPowers.registerConsoleListener(msg => { + if (msg.message.includes("onaddstream")) { + SpecialPowers.postConsoleSentinel(); + resolve(msg.message); + } }); lineNumberAndFunction.func(); - return promise; - }).then(warning => { - is(warning.split('"')[1], - "WebRTC: onaddstream is deprecated! Use peerConnection.ontrack instead.", - "warning logged"); - var remainder = warning.split('"').slice(2).join('"'); - info(remainder); - ok(remainder.includes('file: "' + window.location + '"'), - "warning has this file"); - ok(remainder.includes('line: ' + lineNumberAndFunction.line), - "warning has correct line number"); - }); + }); + is(warning.split('"')[1], + "WebRTC: onaddstream is deprecated! Use peerConnection.ontrack instead.", + "warning logged"); + const remainder = warning.split('"').slice(2).join('"'); + info(remainder); + ok(remainder.includes('file: "' + window.location + '"'), + "warning has this file"); + ok(remainder.includes('line: ' + lineNumberAndFunction.line), + "warning has correct line number"); }); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_callbacks.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_callbacks.html index 4c890e4400..ffd7a1b0c1 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_callbacks.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_callbacks.html @@ -1,4 +1,4 @@ - + @@ -50,22 +50,27 @@ pc2.onicecandidate = e => { .catch(generateErrorCallback()); }; -var v1, v2; -var delivered = new Promise(resolve => { - pc2.onaddstream = e => { - v2.srcObject = e.stream; - resolve(e.stream); - }; -}); +runNetworkTest(async function() { + // Tests trigger warnings + await SpecialPowers.pushPrefEnv({ + set: [['media.peerconnection.treat_warnings_as_errors', false]] + }); + + const v1 = createMediaElement('video', 'v1'); + const v2 = createMediaElement('video', 'v2'); + + const delivered = new Promise(resolve => { + pc2.onaddstream = e => { + v2.srcObject = e.stream; + resolve(e.stream); + }; + }); -runNetworkTest(function() { - v1 = createMediaElement('video', 'v1'); - v2 = createMediaElement('video', 'v2'); var canPlayThrough = new Promise(resolve => v2.canplaythrough = resolve); is(v2.currentTime, 0, "v2.currentTime is zero at outset"); // not testing legacy gUM here - return navigator.mediaDevices.getUserMedia({ video: true, audio: true }) + await navigator.mediaDevices.getUserMedia({ video: true, audio: true }) .then(stream => pc1.addStream(v1.srcObject = stream)) .then(() => pcall(pc1, pc1.createOffer)) .then(offer => pcall(pc1, pc1.setLocalDescription, offer)) diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_localRollback.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_localRollback.html index 5bdc8cc029..8c9470e7f2 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_localRollback.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_localRollback.html @@ -32,7 +32,7 @@ // Rolling back should shut down gathering function PC_REMOTE_WAIT_FOR_END_OF_TRICKLE(test) { - return test.pcRemote.endOfTrickleIce; + is(test.pcRemote._pc.iceGatheringState, "new"); }, function PC_REMOTE_SETUP_ICE_HANDLER(test) { diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_portRestrictions.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_portRestrictions.html index 7cd695ff54..589d8ed6ad 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_portRestrictions.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_portRestrictions.html @@ -46,10 +46,14 @@ runNetworkTest(() => { { urls:"turn:[::1]:5349", username:"p", credential:"p" }, { urls:"turn:[::1]:3478", username:"p", credential:"p" }, { urls:"turn:[::1]", username:"p", credential:"p" }, + ]}); + makePC({ iceServers: [ { urls:"turn:localhost:53?transport=udp", username:"p", credential:"p" }, { urls:"turn:localhost:3478?transport=udp", username:"p", credential:"p" }, { urls:"turn:localhost:53?transport=tcp", username:"p", credential:"p" }, { urls:"turn:localhost:3478?transport=tcp", username:"p", credential:"p" }, + ]}); + makePC({ iceServers: [ { urls:"turns:localhost:3478?transport=udp", username:"p", credential:"p" }, { urls:"stun:localhost", foo:"" } ]}); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIce.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIce.html index d94bb084b7..add3882a4d 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIce.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIce.html @@ -21,12 +21,14 @@ function PC_LOCAL_SET_OFFER_OPTION(test) { test.setOfferOptions({ iceRestart: true }); }, - function PC_LOCAL_EXPECT_ICE_CHECKING(test) { - test.pcLocal.expectIceChecking(); + // Make sure we don't get the end of gathering racing against the + // setting of the new offer + function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) { + return test.pcLocal.endOfTrickleIce; + }, + function PC_LOCAL_SETUP_ICE_HANDLER(test) { + test.pcLocal.setupIceCandidateHandler(test); }, - function PC_REMOTE_EXPECT_ICE_CHECKING(test) { - test.pcRemote.expectIceChecking(); - } ] ); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceLocalAndRemoteRollback.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceLocalAndRemoteRollback.html index 6bbf9440fc..3307258ef9 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceLocalAndRemoteRollback.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceLocalAndRemoteRollback.html @@ -31,10 +31,6 @@ test.chain.replaceAfter('PC_REMOTE_CREATE_ANSWER', [ - function PC_LOCAL_EXPECT_ICE_CONNECTED(test) { - test.pcLocal.iceCheckingIceRollbackExpected = true; - }, - function PC_REMOTE_ROLLBACK(test) { return test.setRemoteDescription(test.pcRemote, { type: "rollback" }, STABLE); @@ -58,12 +54,6 @@ return test.pcLocal.endOfTrickleIce; }, - function PC_LOCAL_EXPECT_ICE_CHECKING(test) { - test.pcLocal.expectIceChecking(); - }, - function PC_REMOTE_EXPECT_ICE_CHECKING(test) { - test.pcRemote.expectIceChecking(); - } ], 1 // Replaces after second PC_REMOTE_CREATE_ANSWER ); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceLocalRollback.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceLocalRollback.html index f5f9a1f220..a7adb7c3e9 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceLocalRollback.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceLocalRollback.html @@ -33,9 +33,6 @@ HAVE_LOCAL_OFFER); }); }, - function PC_LOCAL_EXPECT_ICE_CONNECTED(test) { - test.pcLocal.iceCheckingIceRollbackExpected = true; - }, function PC_LOCAL_WAIT_FOR_GATHERING(test) { return new Promise(r => { test.pcLocal._pc.addEventListener("icegatheringstatechange", () => { @@ -54,12 +51,6 @@ function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) { return test.pcLocal.endOfTrickleIce; }, - function PC_LOCAL_EXPECT_ICE_CHECKING(test) { - test.pcLocal.expectIceChecking(); - }, - function PC_REMOTE_EXPECT_ICE_CHECKING(test) { - test.pcRemote.expectIceChecking(); - } ] ); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoBundle.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoBundle.html index 134fa97cc0..ef40d4039c 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoBundle.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoBundle.html @@ -23,12 +23,14 @@ function PC_LOCAL_SET_OFFER_OPTION(test) { test.setOfferOptions({ iceRestart: true }); }, - function PC_LOCAL_EXPECT_ICE_CHECKING(test) { - test.pcLocal.expectIceChecking(); + // Make sure we don't get the end of gathering racing against the + // setting of the new offer + function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) { + return test.pcLocal.endOfTrickleIce; + }, + function PC_LOCAL_SETUP_ICE_HANDLER(test) { + test.pcLocal.setupIceCandidateHandler(test); }, - function PC_REMOTE_EXPECT_ICE_CHECKING(test) { - test.pcRemote.expectIceChecking(); - } ] ); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoBundleNoRtcpMux.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoBundleNoRtcpMux.html index 06a3a3c980..85a3fcc45d 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoBundleNoRtcpMux.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoBundleNoRtcpMux.html @@ -24,12 +24,14 @@ function PC_LOCAL_SET_OFFER_OPTION(test) { test.setOfferOptions({ iceRestart: true }); }, - function PC_LOCAL_EXPECT_ICE_CHECKING(test) { - test.pcLocal.expectIceChecking(); + // Make sure we don't get the end of gathering racing against the + // setting of the new offer + function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) { + return test.pcLocal.endOfTrickleIce; + }, + function PC_LOCAL_SETUP_ICE_HANDLER(test) { + test.pcLocal.setupIceCandidateHandler(test); }, - function PC_REMOTE_EXPECT_ICE_CHECKING(test) { - test.pcRemote.expectIceChecking(); - } ] ); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoRtcpMux.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoRtcpMux.html index 5d4780211a..80fd1090bc 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoRtcpMux.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_restartIceNoRtcpMux.html @@ -23,12 +23,15 @@ function PC_LOCAL_SET_OFFER_OPTION(test) { test.setOfferOptions({ iceRestart: true }); }, - function PC_LOCAL_EXPECT_ICE_CHECKING(test) { - test.pcLocal.expectIceChecking(); + // Make sure we don't get the end of gathering racing against the + // setting of the new offer + function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) { + return test.pcLocal.endOfTrickleIce; + }, + // Expect gathering to start again + function PC_LOCAL_SETUP_ICE_HANDLER(test) { + test.pcLocal.setupIceCandidateHandler(test); }, - function PC_REMOTE_EXPECT_ICE_CHECKING(test) { - test.pcRemote.expectIceChecking(); - } ] ); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_sillyCodecPriorities.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_sillyCodecPriorities.html index c8354356b0..aa0644ebbb 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_sillyCodecPriorities.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_sillyCodecPriorities.html @@ -15,12 +15,12 @@ visible: true }); - function makeCodecTopPriority(sdp, codec) { + function makeCodecTopPriority({type, sdp}, codec) { const ptToMove = sdputils.findCodecId(sdp, codec); - return sdp.replace( + return {type, sdp: sdp.replace( // m=video port type pts ptToMove more-pts? new RegExp(`(m=video [^ ]+ [^ ]+)(.*)( ${ptToMove})( [^ ]+)?`, "g"), - '$1$3$2$4'); + '$1$3$2$4')}; } function isCodecFirst(sdp, codec) { @@ -34,8 +34,7 @@ const stream = await navigator.mediaDevices.getUserMedia({ video: true }); const sender = pc1.addTrack(stream.getTracks()[0]); await pc1.setLocalDescription(); - let mungedOffer = pc1.localDescription; - mungedOffer.sdp = makeCodecTopPriority(mungedOffer.sdp, codec); + const mungedOffer = makeCodecTopPriority(pc1.localDescription, codec); await pc2.setRemoteDescription(mungedOffer); await pc2.setLocalDescription(); await pc1.setRemoteDescription(pc2.localDescription); @@ -51,8 +50,7 @@ await pc1.setLocalDescription(); await pc2.setRemoteDescription(pc1.localDescription); await pc2.setLocalDescription(); - let mungedAnswer = pc2.localDescription; - mungedAnswer.sdp = makeCodecTopPriority(mungedAnswer.sdp, codec); + const mungedAnswer = makeCodecTopPriority(pc2.localDescription, codec); await pc1.setRemoteDescription(mungedAnswer); is(isCodecFirst((await pc1.createOffer()).sdp, codec), !isPseudoCodec, "Top-priority codecs should come first in reoffers, unless they are pseudo codecs (eg; ulpfec)"); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_stats_relayProtocol.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_stats_relayProtocol.html index cdc328fd2b..5146d423a6 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_stats_relayProtocol.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_stats_relayProtocol.html @@ -23,6 +23,8 @@ if (!("mediaDevices" in navigator)) { await pushPrefs( ['media.peerconnection.nat_simulator.filtering_type', 'ENDPOINT_INDEPENDENT'], ['media.peerconnection.nat_simulator.mapping_type', 'ENDPOINT_INDEPENDENT'], + // The above triggers warning about 5 ICE servers + ['media.peerconnection.treat_warnings_as_errors', false], ['media.getusermedia.insecure.enabled', true]); const test = new PeerConnectionTest(options); makeOffererNonTrickle(test.chain); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_threeUnbundledConnections.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_threeUnbundledConnections.html index 75f0d12463..8627590b65 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_threeUnbundledConnections.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_threeUnbundledConnections.html @@ -98,10 +98,10 @@ runNetworkTest(async () => { //info("Original OFFER: " + JSON.stringify(offer)); offer.sdp = sdputils.removeBundle(offer.sdp); //info("OFFER w/o BUNDLE: " + JSON.stringify(offer)); - const offerAudio = new RTCSessionDescription(JSON.parse(JSON.stringify(offer))); + const offerAudio = JSON.parse(JSON.stringify(offer)); offerAudio.sdp = offerAudio.sdp.replace('m=video 9', 'm=video 0'); //info("offerAudio: " + JSON.stringify(offerAudio)); - const offerVideo = new RTCSessionDescription(JSON.parse(JSON.stringify(offer))); + const offerVideo = JSON.parse(JSON.stringify(offer)); offerVideo.sdp = offerVideo.sdp.replace('m=audio 9', 'm=audio 0'); //info("offerVideo: " + JSON.stringify(offerVideo)); diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_throwInCallbacks.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_throwInCallbacks.html index c476c60161..342b72a5e1 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_throwInCallbacks.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_throwInCallbacks.html @@ -11,7 +11,12 @@ title: "Throw in PeerConnection callbacks" }); -runNetworkTest(function () { +runNetworkTest(async function () { + // Tests trigger warnings + await SpecialPowers.pushPrefEnv({ + set: [['media.peerconnection.treat_warnings_as_errors', false]] + }); + let finish; const onfinished = new Promise(r => finish = async () => { window.onerror = oldOnError; diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_verifyDescriptions.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_verifyDescriptions.html index f685f7c99a..8c12a50942 100644 --- a/dom/media/webrtc/tests/mochitests/test_peerConnection_verifyDescriptions.html +++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_verifyDescriptions.html @@ -8,49 +8,70 @@ diff --git a/dom/media/webrtc/third_party_build/default_config_env b/dom/media/webrtc/third_party_build/default_config_env index af8b77bba6..7013520a30 100644 --- a/dom/media/webrtc/third_party_build/default_config_env +++ b/dom/media/webrtc/third_party_build/default_config_env @@ -5,41 +5,41 @@ export MOZ_LIBWEBRTC_SRC=$STATE_DIR/moz-libwebrtc # The previous fast-forward bug number is used for some error messaging. -export MOZ_PRIOR_FASTFORWARD_BUG="1867099" +export MOZ_PRIOR_FASTFORWARD_BUG="1871981" # Fast-forwarding each Chromium version of libwebrtc should be done # under a separate bugzilla bug. This bug number is used when crafting # the commit summary as each upstream commit is vendored into the # mercurial repository. The bug used for the v106 fast-forward was # 1800920. -export MOZ_FASTFORWARD_BUG="1871981" +export MOZ_FASTFORWARD_BUG="1876843" # MOZ_NEXT_LIBWEBRTC_MILESTONE and MOZ_NEXT_FIREFOX_REL_TARGET are # not used during fast-forward processing, but facilitate generating this # default config. To generate an default config for the next update, run # bash dom/media/webrtc/third_party_build/update_default_config_env.sh -export MOZ_NEXT_LIBWEBRTC_MILESTONE=120 -export MOZ_NEXT_FIREFOX_REL_TARGET=124 +export MOZ_NEXT_LIBWEBRTC_MILESTONE=121 +export MOZ_NEXT_FIREFOX_REL_TARGET=125 # For Chromium release branches, see: # https://chromiumdash.appspot.com/branches -# Chromium's v119 release branch was 6045. This is used to pre-stack +# Chromium's v120 release branch was 6099. This is used to pre-stack # the previous release branch's commits onto the appropriate base commit # (the first common commit between trunk and the release branch). -export MOZ_PRIOR_UPSTREAM_BRANCH_HEAD_NUM="6045" +export MOZ_PRIOR_UPSTREAM_BRANCH_HEAD_NUM="6099" -# New target release branch for v120 is branch-heads/6099. This is used +# New target release branch for v121 is branch-heads/6167. This is used # to calculate the next upstream commit. -export MOZ_TARGET_UPSTREAM_BRANCH_HEAD="branch-heads/6099" +export MOZ_TARGET_UPSTREAM_BRANCH_HEAD="branch-heads/6167" # For local development 'mozpatches' is fine for a branch name, but when # pushing the patch stack to github, it should be named something like -# 'moz-mods-chr120-for-rel124'. +# 'moz-mods-chr121-for-rel125'. export MOZ_LIBWEBRTC_BRANCH="mozpatches" # After elm has been merged to mozilla-central, the patch stack in # moz-libwebrtc should be pushed to github. The script # push_official_branch.sh uses this branch name when pushing to the # public repo. -export MOZ_LIBWEBRTC_OFFICIAL_BRANCH="moz-mods-chr120-for-rel124" +export MOZ_LIBWEBRTC_OFFICIAL_BRANCH="moz-mods-chr121-for-rel125" diff --git a/dom/media/webrtc/third_party_build/filter_git_changes.py b/dom/media/webrtc/third_party_build/filter_git_changes.py index ee6cdbcbd9..86358b0b01 100644 --- a/dom/media/webrtc/third_party_build/filter_git_changes.py +++ b/dom/media/webrtc/third_party_build/filter_git_changes.py @@ -48,7 +48,7 @@ def filter_git_changes(github_path, commit_sha, diff_filter): # out the excluded directory paths (note the lack of trailing '$' # in the regex). regex_excludes = "|".join( - ["^(M|A|D|R\d\d\d)\t{}".format(i) for i in exclude_dir_list] + ["^(M|A|D|R\\d\\d\\d)\t{}".format(i) for i in exclude_dir_list] ) files_not_excluded = [ path for path in changed_files if not re.findall(regex_excludes, path) diff --git a/dom/media/webrtc/third_party_build/prep_repo.sh b/dom/media/webrtc/third_party_build/prep_repo.sh index b601ebc808..8cd9ff6816 100644 --- a/dom/media/webrtc/third_party_build/prep_repo.sh +++ b/dom/media/webrtc/third_party_build/prep_repo.sh @@ -69,6 +69,12 @@ rm -f *.patch CHERRY_PICK_BASE=`git merge-base branch-heads/$MOZ_PRIOR_UPSTREAM_BRANCH_HEAD_NUM master` echo "common commit: $CHERRY_PICK_BASE" +# find the last upstream commit used by the previous update, so we don't +# accidentally grab release branch commits that were added after we started +# the previous update. +LAST_UPSTREAM_COMMIT_SHA=`tail -1 $CURRENT_DIR/third_party/libwebrtc/README.moz-ff-commit` +echo "previous update's last commit: $LAST_UPSTREAM_COMMIT_SHA" + # create a new branch at the common commit and checkout the new branch ERROR_HELP=$" Unable to create branch '$MOZ_LIBWEBRTC_BRANCH'. This probably means @@ -89,7 +95,7 @@ git checkout $MOZ_LIBWEBRTC_BRANCH rm -f $TMP_DIR/*.patch $TMP_DIR/*.patch.bak # grab the patches for all the commits in chrome's release branch for libwebrtc -git format-patch -o $TMP_DIR -k $CHERRY_PICK_BASE..branch-heads/$MOZ_PRIOR_UPSTREAM_BRANCH_HEAD_NUM +git format-patch -o $TMP_DIR -k $CHERRY_PICK_BASE..$LAST_UPSTREAM_COMMIT_SHA # tweak the release branch commit summaries to show they were cherry picked sed -i.bak -e "/^Subject: / s/^Subject: /Subject: (cherry-pick-branch-heads\/$MOZ_PRIOR_UPSTREAM_BRANCH_HEAD_NUM) /" $TMP_DIR/*.patch git am $TMP_DIR/*.patch # applies to branch mozpatches diff --git a/dom/media/webrtc/third_party_build/save_patch_stack.py b/dom/media/webrtc/third_party_build/save_patch_stack.py index 9b080ac0ff..907b90f593 100644 --- a/dom/media/webrtc/third_party_build/save_patch_stack.py +++ b/dom/media/webrtc/third_party_build/save_patch_stack.py @@ -52,7 +52,7 @@ def save_patch_stack( # remove the commit summary from the file name patches_to_rename = os.listdir(patch_directory) for file in patches_to_rename: - shortened_name = re.sub("^(\d\d\d\d)-.*\.patch", "\\1.patch", file) + shortened_name = re.sub(r"^(\d\d\d\d)-.*\.patch", "\\1.patch", file) os.rename( os.path.join(patch_directory, file), os.path.join(patch_directory, shortened_name), diff --git a/dom/media/webrtc/third_party_build/verify_vendoring.sh b/dom/media/webrtc/third_party_build/verify_vendoring.sh index 869a2c8c67..008ab0d5db 100644 --- a/dom/media/webrtc/third_party_build/verify_vendoring.sh +++ b/dom/media/webrtc/third_party_build/verify_vendoring.sh @@ -17,12 +17,12 @@ echo "MOZ_LIBWEBRTC_SRC: $MOZ_LIBWEBRTC_SRC" echo "MOZ_LIBWEBRTC_BRANCH: $MOZ_LIBWEBRTC_BRANCH" echo "MOZ_FASTFORWARD_BUG: $MOZ_FASTFORWARD_BUG" -TIP_SHA=`hg id -r tip | awk '{ print $1; }'` -echo "TIP_SHA: $TIP_SHA" +CURRENT_SHA=`hg id -r . | awk '{ print $1; }'` +echo "CURRENT_SHA: $CURRENT_SHA" # we grab the entire firstline description for convenient logging -LAST_PATCHSTACK_UPDATE_COMMIT=`hg log --template "{node|short} {desc|firstline}\n" \ - --include "third_party/libwebrtc/moz-patch-stack/*.patch" | head -1` +LAST_PATCHSTACK_UPDATE_COMMIT=`hg log -r ::. --template "{node|short} {desc|firstline}\n" \ + --include "third_party/libwebrtc/moz-patch-stack/*.patch" | tail -1` echo "LAST_PATCHSTACK_UPDATE_COMMIT: $LAST_PATCHSTACK_UPDATE_COMMIT" LAST_PATCHSTACK_UPDATE_COMMIT_SHA=`echo $LAST_PATCHSTACK_UPDATE_COMMIT \ @@ -31,7 +31,7 @@ echo "LAST_PATCHSTACK_UPDATE_COMMIT_SHA: $LAST_PATCHSTACK_UPDATE_COMMIT_SHA" # grab the oldest, non "Vendor from libwebrtc" line OLDEST_CANDIDATE_COMMIT=`hg log --template "{node|short} {desc|firstline}\n" \ - -r $LAST_PATCHSTACK_UPDATE_COMMIT_SHA::tip \ + -r $LAST_PATCHSTACK_UPDATE_COMMIT_SHA::. \ | grep -v "Vendor libwebrtc from" | head -1` echo "OLDEST_CANDIDATE_COMMIT: $OLDEST_CANDIDATE_COMMIT" @@ -39,9 +39,9 @@ OLDEST_CANDIDATE_SHA=`echo $OLDEST_CANDIDATE_COMMIT \ | awk '{ print $1; }'` echo "OLDEST_CANDIDATE_SHA: $OLDEST_CANDIDATE_SHA" -EXTRACT_COMMIT_RANGE="{start-commit-sha}::tip" -if [ "x$TIP_SHA" != "x$OLDEST_CANDIDATE_SHA" ]; then - EXTRACT_COMMIT_RANGE="$OLDEST_CANDIDATE_SHA::tip" +EXTRACT_COMMIT_RANGE="{start-commit-sha}::." +if [ "x$CURRENT_SHA" != "x$OLDEST_CANDIDATE_SHA" ]; then + EXTRACT_COMMIT_RANGE="$OLDEST_CANDIDATE_SHA::." echo "EXTRACT_COMMIT_RANGE: $EXTRACT_COMMIT_RANGE" fi diff --git a/dom/media/webrtc/transport/nricectx.cpp b/dom/media/webrtc/transport/nricectx.cpp index f30c2734c2..7c71c0ab06 100644 --- a/dom/media/webrtc/transport/nricectx.cpp +++ b/dom/media/webrtc/transport/nricectx.cpp @@ -257,14 +257,14 @@ nsresult NrIceTurnServer::ToNicerTurnStruct(nr_ice_turn_server* server) const { } NrIceCtx::NrIceCtx(const std::string& name) - : connection_state_(ICE_CTX_INIT), - gathering_state_(ICE_CTX_GATHER_INIT), - name_(name), + : name_(name), ice_controlling_set_(false), ctx_(nullptr), peer_(nullptr), ice_handler_vtbl_(nullptr), ice_handler_(nullptr), + ice_gather_handler_vtbl_(nullptr), + ice_gather_handler_(nullptr), trickle_(true), config_(), nat_(nullptr), @@ -343,13 +343,10 @@ void NrIceCtx::DestroyStream(const std::string& id) { auto it = streams_.find(id); if (it != streams_.end()) { auto preexisting_stream = it->second; + SignalConnectionStateChange(preexisting_stream, ICE_CTX_CLOSED); streams_.erase(it); preexisting_stream->Close(); } - - if (streams_.empty()) { - SetGatheringState(ICE_CTX_GATHER_INIT); - } } // Handler callbacks @@ -377,6 +374,7 @@ int NrIceCtx::stream_ready(void* obj, nr_ice_media_stream* stream) { MOZ_ASSERT(s); s->Ready(stream); + ctx->SignalConnectionStateChange(s, ICE_CTX_CONNECTED); return 0; } @@ -393,44 +391,101 @@ int NrIceCtx::stream_failed(void* obj, nr_ice_media_stream* stream) { // Streams which do not exist should never fail. MOZ_ASSERT(s); - ctx->SetConnectionState(ICE_CTX_FAILED); + if (!ctx->dumped_rlog_) { + // Do this at most once per ctx + ctx->dumped_rlog_ = true; + MOZ_MTLOG(ML_INFO, + "NrIceCtx(" << ctx->name_ << "): dumping r_log ringbuffer... "); + std::deque logs; + RLogConnector::GetInstance()->GetAny(0, &logs); + for (auto& log : logs) { + MOZ_MTLOG(ML_INFO, log); + } + } + s->Failed(); + ctx->SignalConnectionStateChange(s, ICE_CTX_FAILED); return 0; } -int NrIceCtx::ice_checking(void* obj, nr_ice_peer_ctx* pctx) { - MOZ_MTLOG(ML_DEBUG, "ice_checking called"); +int NrIceCtx::stream_checking(void* obj, nr_ice_media_stream* stream) { + MOZ_MTLOG(ML_DEBUG, "stream_checking called"); + MOZ_ASSERT(!stream->local_stream); + MOZ_ASSERT(!stream->obsolete); // Get the ICE ctx NrIceCtx* ctx = static_cast(obj); + RefPtr s = ctx->FindStream(stream); - ctx->SetConnectionState(ICE_CTX_CHECKING); + MOZ_ASSERT(s); + if (!s->AnyGenerationIsConnected()) { + // the checking state only applies if we aren't connected + ctx->SignalConnectionStateChange(s, ICE_CTX_CHECKING); + } return 0; } -int NrIceCtx::ice_connected(void* obj, nr_ice_peer_ctx* pctx) { - MOZ_MTLOG(ML_DEBUG, "ice_connected called"); +int NrIceCtx::stream_disconnected(void* obj, nr_ice_media_stream* stream) { + MOZ_MTLOG(ML_DEBUG, "stream_disconnected called"); + MOZ_ASSERT(!stream->local_stream); + MOZ_ASSERT(!stream->obsolete); // Get the ICE ctx NrIceCtx* ctx = static_cast(obj); + RefPtr s = ctx->FindStream(stream); - // This is called even on failed contexts. - if (ctx->connection_state() != ICE_CTX_FAILED) { - ctx->SetConnectionState(ICE_CTX_CONNECTED); - } + MOZ_ASSERT(s); + ctx->SignalConnectionStateChange(s, ICE_CTX_DISCONNECTED); return 0; } -int NrIceCtx::ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) { - MOZ_MTLOG(ML_DEBUG, "ice_disconnected called"); +int NrIceCtx::stream_gathering(void* obj, nr_ice_media_stream* stream) { + MOZ_MTLOG(ML_DEBUG, "stream_gathering called"); + MOZ_ASSERT(!stream->local_stream); + MOZ_ASSERT(!stream->obsolete); + + // Get the ICE ctx + NrIceCtx* ctx = static_cast(obj); + RefPtr s = ctx->FindStream(stream); + + MOZ_ASSERT(s); + + s->OnGatheringStarted(stream); + return 0; +} + +int NrIceCtx::stream_gathered(void* obj, nr_ice_media_stream* stream) { + MOZ_MTLOG(ML_DEBUG, "stream_gathered called"); + MOZ_ASSERT(!stream->local_stream); // Get the ICE ctx NrIceCtx* ctx = static_cast(obj); + RefPtr s = ctx->FindStream(stream); - ctx->SetConnectionState(ICE_CTX_DISCONNECTED); + // We get this callback for destroyed streams in some cases + if (s) { + s->OnGatheringComplete(stream); + } + return 0; +} + +int NrIceCtx::ice_checking(void* obj, nr_ice_peer_ctx* pctx) { + MOZ_MTLOG(ML_DEBUG, "ice_checking called"); + // We don't use this; we react to the stream-specific callbacks instead + return 0; +} + +int NrIceCtx::ice_connected(void* obj, nr_ice_peer_ctx* pctx) { + MOZ_MTLOG(ML_DEBUG, "ice_connected called"); + // We don't use this; we react to the stream-specific callbacks instead + return 0; +} +int NrIceCtx::ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) { + MOZ_MTLOG(ML_DEBUG, "ice_disconnected called"); + // We don't use this; we react to the stream-specific callbacks instead return 0; } @@ -466,7 +521,6 @@ void NrIceCtx::trickle_cb(void* arg, nr_ice_ctx* ice_ctx, } if (!candidate) { - s->SignalCandidate(s, "", stream->ufrag, "", ""); return; } @@ -587,11 +641,20 @@ void NrIceCtx::SetStunAddrs(const nsTArray& addrs) { } bool NrIceCtx::Initialize() { + // Create the gather handler objects + ice_gather_handler_vtbl_ = new nr_ice_gather_handler_vtbl(); + ice_gather_handler_vtbl_->stream_gathering = &NrIceCtx::stream_gathering; + ice_gather_handler_vtbl_->stream_gathered = &NrIceCtx::stream_gathered; + ice_gather_handler_ = new nr_ice_gather_handler(); + ice_gather_handler_->vtbl = ice_gather_handler_vtbl_; + ice_gather_handler_->obj = this; + // Create the ICE context int r; UINT4 flags = NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION; - r = nr_ice_ctx_create(const_cast(name_.c_str()), flags, &ctx_); + r = nr_ice_ctx_create(const_cast(name_.c_str()), flags, + ice_gather_handler_, &ctx_); if (r) { MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name_ << "'"); @@ -634,6 +697,16 @@ bool NrIceCtx::Initialize() { ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair; ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready; ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed; + ice_handler_vtbl_->stream_checking = &NrIceCtx::stream_checking; + ice_handler_vtbl_->stream_disconnected = &NrIceCtx::stream_disconnected; + // stream_gathering and stream_gathered do not go here, since those are tied + // to the _local_ nr_ice_media_stream in nICEr. nICEr allows a local + // nr_ice_media_stream (which has a single set of candidates, and therefore a + // single gathering state) to be associated with multiple remote + // nr_ice_media_streams (which each have their own ICE connection state) + // because it allows forking. We never encounter forking, so these will be + // one-to-one in practice, but the architecture in nICEr means we have to set + // up these callbacks on the nr_ice_ctx, not the nr_ice_peer_ctx. ice_handler_vtbl_->ice_connected = &NrIceCtx::ice_connected; ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd; ice_handler_vtbl_->ice_checking = &NrIceCtx::ice_checking; @@ -720,8 +793,13 @@ NrIceStats NrIceCtx::Destroy() { delete ice_handler_vtbl_; delete ice_handler_; + delete ice_gather_handler_vtbl_; + delete ice_gather_handler_; + ice_handler_vtbl_ = nullptr; ice_handler_ = nullptr; + ice_gather_handler_vtbl_ = nullptr; + ice_gather_handler_ = nullptr; proxy_config_ = nullptr; streams_.clear(); @@ -854,15 +932,10 @@ nsresult NrIceCtx::StartGathering(bool default_route_only, // finished. int r = nr_ice_gather(ctx_, &NrIceCtx::gather_cb, this); - if (!r) { - SetGatheringState(ICE_CTX_GATHER_COMPLETE); - } else if (r == R_WOULDBLOCK) { - SetGatheringState(ICE_CTX_GATHER_STARTED); - } else { - SetGatheringState(ICE_CTX_GATHER_COMPLETE); + if (r && r != R_WOULDBLOCK) { MOZ_MTLOG(ML_ERROR, "ICE FAILED: Couldn't gather ICE candidates for '" << name_ << "', error=" << r); - SetConnectionState(ICE_CTX_FAILED); + SignalAllStreamsFailed(); return NS_ERROR_FAILURE; } @@ -940,7 +1013,7 @@ nsresult NrIceCtx::StartChecks() { r = nr_ice_peer_ctx_pair_candidates(peer_); if (r) { MOZ_MTLOG(ML_ERROR, "ICE FAILED: Couldn't pair candidates on " << name_); - SetConnectionState(ICE_CTX_FAILED); + SignalAllStreamsFailed(); return NS_ERROR_FAILURE; } @@ -952,7 +1025,7 @@ nsresult NrIceCtx::StartChecks() { } else { MOZ_MTLOG(ML_ERROR, "ICE FAILED: Couldn't start peer checks on " << name_); - SetConnectionState(ICE_CTX_FAILED); + SignalAllStreamsFailed(); return NS_ERROR_FAILURE; } } @@ -961,18 +1034,21 @@ nsresult NrIceCtx::StartChecks() { } void NrIceCtx::gather_cb(NR_SOCKET s, int h, void* arg) { - NrIceCtx* ctx = static_cast(arg); + MOZ_MTLOG(ML_DEBUG, "gather_cb called"); + // We don't use this; we react to the stream-specific callbacks instead +} - ctx->SetGatheringState(ICE_CTX_GATHER_COMPLETE); +void NrIceCtx::SignalAllStreamsFailed() { + for (auto& [id, stream] : streams_) { + Unused << id; + stream->Failed(); + SignalConnectionStateChange(stream, ICE_CTX_FAILED); + } } void NrIceCtx::UpdateNetworkState(bool online) { MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): updating network state to " << (online ? "online" : "offline")); - if (connection_state_ == ICE_CTX_CLOSED) { - return; - } - if (online) { nr_ice_peer_ctx_refresh_consent_all_streams(peer_); } else { @@ -980,36 +1056,6 @@ void NrIceCtx::UpdateNetworkState(bool online) { } } -void NrIceCtx::SetConnectionState(ConnectionState state) { - if (state == connection_state_) return; - - MOZ_MTLOG(ML_INFO, "NrIceCtx(" << name_ << "): state " << connection_state_ - << "->" << state); - connection_state_ = state; - - if (connection_state_ == ICE_CTX_FAILED) { - MOZ_MTLOG(ML_INFO, - "NrIceCtx(" << name_ << "): dumping r_log ringbuffer... "); - std::deque logs; - RLogConnector::GetInstance()->GetAny(0, &logs); - for (auto& log : logs) { - MOZ_MTLOG(ML_INFO, log); - } - } - - SignalConnectionStateChange(this, state); -} - -void NrIceCtx::SetGatheringState(GatheringState state) { - if (state == gathering_state_) return; - - MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << name_ << "): gathering state " - << gathering_state_ << "->" << state); - gathering_state_ = state; - - SignalGatheringStateChange(this, state); -} - void NrIceCtx::GenerateObfuscatedAddress(nr_ice_candidate* candidate, std::string* mdns_address, std::string* actual_address) { diff --git a/dom/media/webrtc/transport/nricectx.h b/dom/media/webrtc/transport/nricectx.h index a0a0b5b772..01ad6b5dbd 100644 --- a/dom/media/webrtc/transport/nricectx.h +++ b/dom/media/webrtc/transport/nricectx.h @@ -74,6 +74,8 @@ typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx; typedef struct nr_ice_media_stream_ nr_ice_media_stream; typedef struct nr_ice_handler_ nr_ice_handler; typedef struct nr_ice_handler_vtbl_ nr_ice_handler_vtbl; +typedef struct nr_ice_gather_handler_ nr_ice_gather_handler; +typedef struct nr_ice_gather_handler_vtbl_ nr_ice_gather_handler_vtbl; typedef struct nr_ice_candidate_ nr_ice_candidate; typedef struct nr_ice_cand_pair_ nr_ice_cand_pair; typedef struct nr_ice_stun_server_ nr_ice_stun_server; @@ -200,12 +202,6 @@ class NrIceCtx { ICE_CTX_CLOSED }; - enum GatheringState { - ICE_CTX_GATHER_INIT, - ICE_CTX_GATHER_STARTED, - ICE_CTX_GATHER_COMPLETE - }; - enum Controlling { ICE_CONTROLLING, ICE_CONTROLLED }; enum Policy { ICE_POLICY_RELAY, ICE_POLICY_NO_HOST, ICE_POLICY_ALL }; @@ -294,12 +290,6 @@ class NrIceCtx { // The name of the ctx const std::string& name() const { return name_; } - // Current state - ConnectionState connection_state() const { return connection_state_; } - - // Current state - GatheringState gathering_state() const { return gathering_state_; } - // Get the global attributes std::vector GetGlobalAttributes(); @@ -351,9 +341,7 @@ class NrIceCtx { // Signals to indicate events. API users can (and should) // register for these. - sigslot::signal2 - SignalGatheringStateChange; - sigslot::signal2 + sigslot::signal2 SignalConnectionStateChange; // The thread to direct method calls to @@ -375,7 +363,11 @@ class NrIceCtx { static int select_pair(void* obj, nr_ice_media_stream* stream, int component_id, nr_ice_cand_pair** potentials, int potential_ct); + static int stream_gathering(void* obj, nr_ice_media_stream* stream); + static int stream_gathered(void* obj, nr_ice_media_stream* stream); + static int stream_checking(void* obj, nr_ice_media_stream* stream); static int stream_ready(void* obj, nr_ice_media_stream* stream); + static int stream_disconnected(void* obj, nr_ice_media_stream* stream); static int stream_failed(void* obj, nr_ice_media_stream* stream); static int ice_checking(void* obj, nr_ice_peer_ctx* pctx); static int ice_connected(void* obj, nr_ice_peer_ctx* pctx); @@ -387,28 +379,25 @@ class NrIceCtx { nr_ice_media_stream* stream, int component_id, nr_ice_candidate* candidate); + void SignalAllStreamsFailed(); + // Find a media stream by stream ptr. Gross RefPtr FindStream(nr_ice_media_stream* stream); - // Set the state - void SetConnectionState(ConnectionState state); - - // Set the state - void SetGatheringState(GatheringState state); - void GenerateObfuscatedAddress(nr_ice_candidate* candidate, std::string* mdns_address, std::string* actual_address); - ConnectionState connection_state_; - GatheringState gathering_state_; + bool dumped_rlog_ = false; const std::string name_; bool ice_controlling_set_; std::map> streams_; nr_ice_ctx* ctx_; nr_ice_peer_ctx* peer_; - nr_ice_handler_vtbl* ice_handler_vtbl_; // Must be pointer - nr_ice_handler* ice_handler_; // Must be pointer + nr_ice_handler_vtbl* ice_handler_vtbl_; // Must be pointer + nr_ice_handler* ice_handler_; // Must be pointer + nr_ice_gather_handler_vtbl* ice_gather_handler_vtbl_; // Must be pointer + nr_ice_gather_handler* ice_gather_handler_; // Must be pointer bool trickle_; nsCOMPtr sts_target_; // The thread to run on Config config_; diff --git a/dom/media/webrtc/transport/nricemediastream.cpp b/dom/media/webrtc/transport/nricemediastream.cpp index 426aee230e..a3a0c147c9 100644 --- a/dom/media/webrtc/transport/nricemediastream.cpp +++ b/dom/media/webrtc/transport/nricemediastream.cpp @@ -204,11 +204,17 @@ nsresult NrIceMediaStream::ConnectToPeer( MOZ_ASSERT(stream_); if (Matches(old_stream_, ufrag, pwd)) { + bool wasGathering = !AllGenerationsDoneGathering(); // (We swap before we close so we never have stream_ == nullptr) MOZ_MTLOG(ML_DEBUG, "Rolling back to old stream ufrag=" << ufrag << " " << name_); std::swap(stream_, old_stream_); CloseStream(&old_stream_); + if (wasGathering && AllGenerationsDoneGathering()) { + // Special case; we do not need to send another empty candidate, but we + // do need to handle the transition from gathering to complete. + SignalGatheringStateChange(GetId(), ICE_STREAM_GATHER_COMPLETE); + } } else if (old_stream_) { // Right now we wait for ICE to complete before closing the old stream. // It might be worth it to close it sooner, but we don't want to close it @@ -273,6 +279,10 @@ nsresult NrIceMediaStream::SetIceCredentials(const std::string& ufrag, } state_ = ICE_CONNECTING; + + MOZ_MTLOG(ML_WARNING, + "SetIceCredentials new=" << stream_ << " old=" << old_stream_); + return NS_OK; } @@ -661,6 +671,21 @@ void NrIceMediaStream::Failed() { } } +void NrIceMediaStream::OnGatheringStarted(nr_ice_media_stream* stream) { + MOZ_MTLOG(ML_WARNING, "OnGatheringStarted called for " << stream); + SignalGatheringStateChange(GetId(), ICE_STREAM_GATHER_STARTED); +} + +void NrIceMediaStream::OnGatheringComplete(nr_ice_media_stream* stream) { + MOZ_MTLOG(ML_WARNING, "OnGatheringComplete called for " << stream); + // Spec says to queue two separate tasks; one for the empty candidate, and + // the next for the state change. + SignalCandidate(this, "", stream->ufrag, "", ""); + if (AllGenerationsDoneGathering()) { + SignalGatheringStateChange(GetId(), ICE_STREAM_GATHER_COMPLETE); + } +} + void NrIceMediaStream::Close() { MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'"); state_ = ICE_CLOSED; @@ -709,4 +734,34 @@ nr_ice_media_stream* NrIceMediaStream::GetStreamForRemoteUfrag( return nullptr; } +bool NrIceMediaStream::AllGenerationsDoneGathering() const { + if (stream_ && !nr_ice_media_stream_is_done_gathering(stream_)) { + return false; + } + if (old_stream_ && !nr_ice_media_stream_is_done_gathering(old_stream_)) { + return false; + } + return true; +} + +bool NrIceMediaStream::AnyGenerationIsConnected() const { + nr_ice_media_stream* peer_stream = nullptr; + + if (stream_ && + !nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream)) { + if (peer_stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED && + !peer_stream->disconnected) { + return true; + } + } + + if (old_stream_ && + !nr_ice_peer_ctx_find_pstream(ctx_->peer(), old_stream_, &peer_stream)) { + if (peer_stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED && + !peer_stream->disconnected) { + return true; + } + } + return false; +} } // namespace mozilla diff --git a/dom/media/webrtc/transport/nricemediastream.h b/dom/media/webrtc/transport/nricemediastream.h index 406373573c..d434dd34dd 100644 --- a/dom/media/webrtc/transport/nricemediastream.h +++ b/dom/media/webrtc/transport/nricemediastream.h @@ -126,6 +126,12 @@ struct NrIceCandidatePair { class NrIceMediaStream { public: + enum GatheringState { + ICE_STREAM_GATHER_INIT, + ICE_STREAM_GATHER_STARTED, + ICE_STREAM_GATHER_COMPLETE + }; + NrIceMediaStream(NrIceCtx* ctx, const std::string& id, const std::string& name, size_t components); @@ -182,6 +188,9 @@ class NrIceMediaStream { void Ready(nr_ice_media_stream* stream); void Failed(); + void OnGatheringStarted(nr_ice_media_stream* stream); + void OnGatheringComplete(nr_ice_media_stream* stream); + // Close the stream. Called by the NrIceCtx. // Different from the destructor because other people // might be holding RefPtrs but we want those writes to fail once @@ -192,9 +201,14 @@ class NrIceMediaStream { // the candidate belongs to. const std::string& GetId() const { return id_; } + bool AllGenerationsDoneGathering() const; + bool AnyGenerationIsConnected() const; + sigslot::signal5 SignalCandidate; // A new ICE candidate: + sigslot::signal2 + SignalGatheringStateChange; sigslot::signal1 SignalReady; // Candidate pair ready. sigslot::signal1 SignalFailed; // Candidate pair failed. diff --git a/dom/media/webrtc/transport/test/ice_unittest.cpp b/dom/media/webrtc/transport/test/ice_unittest.cpp index 4d097fafa3..50febb3cdd 100644 --- a/dom/media/webrtc/transport/test/ice_unittest.cpp +++ b/dom/media/webrtc/transport/test/ice_unittest.cpp @@ -304,6 +304,8 @@ class SchedulableTrickleCandidate { } void Schedule(unsigned int ms) { + std::cerr << "Scheduling " << Candidate() << " in " << ms << "ms" + << std::endl; test_utils_->SyncDispatchToSTS( WrapRunnable(this, &SchedulableTrickleCandidate::Schedule_s, ms)); } @@ -355,10 +357,7 @@ class IceTestPeer : public sigslot::has_slots<> { offerer_(offerer), stream_counter_(0), shutting_down_(false), - gathering_complete_(false), ready_ct_(0), - ice_connected_(false), - ice_failed_(false), ice_reached_checking_(false), received_(0), sent_(0), @@ -372,8 +371,6 @@ class IceTestPeer : public sigslot::has_slots<> { simulate_ice_lite_(false), nat_(new TestNat), test_utils_(utils) { - ice_ctx_->SignalGatheringStateChange.connect( - this, &IceTestPeer::GatheringStateChange); ice_ctx_->SignalConnectionStateChange.connect( this, &IceTestPeer::ConnectionStateChange); @@ -426,6 +423,10 @@ class IceTestPeer : public sigslot::has_slots<> { stream->SignalReady.connect(this, &IceTestPeer::StreamReady); stream->SignalFailed.connect(this, &IceTestPeer::StreamFailed); stream->SignalPacketReceived.connect(this, &IceTestPeer::PacketReceived); + stream->SignalGatheringStateChange.connect( + this, &IceTestPeer::GatheringStateChange); + mConnectionStates[id] = NrIceCtx::ICE_CTX_INIT; + mGatheringStates[id] = NrIceMediaStream::ICE_STREAM_GATHER_INIT; } void AddStream(int components) { @@ -434,7 +435,10 @@ class IceTestPeer : public sigslot::has_slots<> { } void RemoveStream_s(size_t index) { - ice_ctx_->DestroyStream(MakeTransportId(index)); + const std::string id = MakeTransportId(index); + ice_ctx_->DestroyStream(id); + mConnectionStates.erase(id); + mGatheringStates.erase(id); } void RemoveStream(size_t index) { @@ -650,7 +654,15 @@ class IceTestPeer : public sigslot::has_slots<> { return host_net; } - bool gathering_complete() { return gathering_complete_; } + bool gathering_complete() { + for (const auto& [id, state] : mGatheringStates) { + Unused << id; + if (state != NrIceMediaStream::ICE_STREAM_GATHER_COMPLETE) { + return false; + } + } + return true; + } int ready_ct() { return ready_ct_; } bool is_ready_s(size_t index) { auto media_stream = GetStream_s(index); @@ -666,8 +678,33 @@ class IceTestPeer : public sigslot::has_slots<> { WrapRunnableRet(&result, this, &IceTestPeer::is_ready_s, stream)); return result; } - bool ice_connected() { return ice_connected_; } - bool ice_failed() { return ice_failed_; } + bool ice_connected() { + for (const auto& [id, state] : mConnectionStates) { + if (state != NrIceCtx::ICE_CTX_CONNECTED) { + return false; + } + } + return true; + } + bool ice_failed() { + for (const auto& [id, state] : mConnectionStates) { + if (state == NrIceCtx::ICE_CTX_FAILED) { + return true; + } + } + return false; + } + bool ice_checking() { + if (ice_failed() || ice_connected()) { + return false; + } + for (const auto& [id, state] : mConnectionStates) { + if (state == NrIceCtx::ICE_CTX_CHECKING) { + return true; + } + } + return false; + } bool ice_reached_checking() { return ice_reached_checking_; } size_t received() { return received_; } size_t sent() { return sent_; } @@ -680,13 +717,16 @@ class IceTestPeer : public sigslot::has_slots<> { void RestartIce_s() { for (auto& stream : ice_ctx_->GetStreams()) { SetIceCredentials_s(*stream); + mConnectionStates[stream->GetId()] = NrIceCtx::ICE_CTX_INIT; + mGatheringStates[stream->GetId()] = + NrIceMediaStream::ICE_STREAM_GATHER_INIT; } // take care of some local bookkeeping ready_ct_ = 0; - gathering_complete_ = false; - ice_connected_ = false; - ice_failed_ = false; - ice_reached_checking_ = false; + // We do not unset ice_reached_checking_ here, since we do not expect + // ICE to return to checking in an ICE restart, because the ICE stack + // continues using the old streams (which are probably connected) until the + // new ones are connected. remote_ = nullptr; } @@ -709,9 +749,6 @@ class IceTestPeer : public sigslot::has_slots<> { remote_ = remote; trickle_mode_ = trickle_mode; - ice_connected_ = false; - ice_failed_ = false; - ice_reached_checking_ = false; res = ice_ctx_->ParseGlobalAttributes(remote->GetGlobalAttributes()); ASSERT_FALSE(remote->simulate_ice_lite_ && (ice_ctx_->GetControlling() == NrIceCtx::ICE_CONTROLLED)); @@ -793,8 +830,11 @@ class IceTestPeer : public sigslot::has_slots<> { auto stream = GetStream_s(index); if (!stream) { // stream might have gone away before the trickle timer popped + std::cerr << "Trickle candidate has no stream: " << index << std::endl; return NS_OK; } + std::cerr << "Trickle candidate for " << index << " (" << stream->GetId() + << "):" << candidate << std::endl; return stream->ParseTrickleCandidate(candidate, ufrag, ""); } @@ -940,16 +980,18 @@ class IceTestPeer : public sigslot::has_slots<> { } // Handle events - void GatheringStateChange(NrIceCtx* ctx, NrIceCtx::GatheringState state) { + void GatheringStateChange(const std::string& aTransportId, + NrIceMediaStream::GatheringState state) { if (shutting_down_) { return; } - if (state != NrIceCtx::ICE_CTX_GATHER_COMPLETE) { + mGatheringStates[aTransportId] = state; + + if (!gathering_complete()) { return; } std::cerr << name_ << " Gathering complete" << std::endl; - gathering_complete_ = true; std::cerr << name_ << " ATTRIBUTES:" << std::endl; for (const auto& stream : ice_ctx_->GetStreams()) { @@ -973,9 +1015,9 @@ class IceTestPeer : public sigslot::has_slots<> { if (candidate.empty()) { return; } - std::cerr << "Candidate for stream " << stream->name() + std::cerr << "Candidate for stream " << stream->GetId() << " initialized: " << candidate << std::endl; - candidates_[stream->name()].push_back(candidate); + candidates_[stream->GetId()].push_back(candidate); // If we are connected, then try to trickle to the other side. if (remote_ && remote_->remote_ && (trickle_mode_ != TRICKLE_SIMULATE)) { @@ -990,7 +1032,7 @@ class IceTestPeer : public sigslot::has_slots<> { return; } } - ADD_FAILURE() << "No matching stream found for " << stream; + ADD_FAILURE() << "No matching stream found for " << stream->GetId(); } } @@ -1133,32 +1175,45 @@ class IceTestPeer : public sigslot::has_slots<> { DumpCandidatePairs_s(stream); } - void ConnectionStateChange(NrIceCtx* ctx, NrIceCtx::ConnectionState state) { - (void)ctx; + void ConnectionStateChange(NrIceMediaStream* stream, + NrIceCtx::ConnectionState state) { + mConnectionStates[stream->GetId()] = state; + if (ice_checking()) { + ice_reached_checking_ = true; + } + switch (state) { case NrIceCtx::ICE_CTX_INIT: break; case NrIceCtx::ICE_CTX_CHECKING: - std::cerr << name_ << " ICE reached checking" << std::endl; - ice_reached_checking_ = true; + std::cerr << name_ << " ICE reached checking (" << stream->GetId() + << ")" << std::endl; + MOZ_ASSERT(ice_reached_checking_); break; case NrIceCtx::ICE_CTX_CONNECTED: - std::cerr << name_ << " ICE connected" << std::endl; - ice_connected_ = true; + std::cerr << name_ << " ICE reached connected (" << stream->GetId() + << ")" << std::endl; + MOZ_ASSERT(ice_reached_checking_); break; case NrIceCtx::ICE_CTX_COMPLETED: - std::cerr << name_ << " ICE completed" << std::endl; + std::cerr << name_ << " ICE reached completed (" << stream->GetId() + << ")" << std::endl; + MOZ_ASSERT(ice_reached_checking_); break; case NrIceCtx::ICE_CTX_FAILED: - std::cerr << name_ << " ICE failed" << std::endl; - ice_failed_ = true; + std::cerr << name_ << " ICE reached failed (" << stream->GetId() << ")" + << std::endl; + MOZ_ASSERT(ice_reached_checking_); break; case NrIceCtx::ICE_CTX_DISCONNECTED: - std::cerr << name_ << " ICE disconnected" << std::endl; - ice_connected_ = false; + std::cerr << name_ << " ICE reached disconnected (" << stream->GetId() + << ")" << std::endl; + MOZ_ASSERT(ice_reached_checking_); + break; + case NrIceCtx::ICE_CTX_CLOSED: + std::cerr << name_ << " ICE reached closed (" << stream->GetId() << ")" + << std::endl; break; - default: - MOZ_CRASH(); } } @@ -1326,10 +1381,9 @@ class IceTestPeer : public sigslot::has_slots<> { std::map> mOldIceCredentials; size_t stream_counter_; bool shutting_down_; - bool gathering_complete_; + std::map mConnectionStates; + std::map mGatheringStates; int ready_ct_; - bool ice_connected_; - bool ice_failed_; bool ice_reached_checking_; size_t received_; size_t sent_; @@ -1686,10 +1740,8 @@ class WebRtcIceConnectTest : public StunTest { TrickleMode mode = TRICKLE_NONE) { ASSERT_TRUE(caller->ready_ct() == 0); ASSERT_TRUE(caller->ice_connected() == 0); - ASSERT_TRUE(caller->ice_reached_checking() == 0); ASSERT_TRUE(callee->ready_ct() == 0); ASSERT_TRUE(callee->ice_connected() == 0); - ASSERT_TRUE(callee->ice_reached_checking() == 0); // IceTestPeer::Connect grabs attributes from the first arg, and // gives them to |this|, meaning that callee->Connect(caller, ...) @@ -3361,6 +3413,8 @@ TEST_F(WebRtcIceConnectTest, TestConnectTrickleAddStreamDuringICE) { RealisticTrickleDelay(p1_->ControlTrickle(0)); RealisticTrickleDelay(p2_->ControlTrickle(0)); AddStream(1); + ASSERT_TRUE(Gather()); + ConnectTrickle(); RealisticTrickleDelay(p1_->ControlTrickle(1)); RealisticTrickleDelay(p2_->ControlTrickle(1)); WaitForConnected(1000); diff --git a/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp b/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp index b55b05f10c..20754f033b 100644 --- a/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp +++ b/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp @@ -73,7 +73,8 @@ class IcePeer { peer_ctx_(nullptr), nat_(nat), test_utils_(test_utils) { - nr_ice_ctx_create(const_cast(name_.c_str()), flags, &ice_ctx_); + nr_ice_ctx_create(const_cast(name_.c_str()), flags, nullptr, + &ice_ctx_); if (nat_) { nr_socket_factory* factory; diff --git a/dom/media/webrtc/transport/test/transport_unittests.cpp b/dom/media/webrtc/transport/test/transport_unittests.cpp index 7729151ade..0e9702ced8 100644 --- a/dom/media/webrtc/transport/test/transport_unittests.cpp +++ b/dom/media/webrtc/transport/test/transport_unittests.cpp @@ -586,16 +586,15 @@ class TransportTestPeer : public sigslot::has_slots<> { void InitIce() { nsresult res; - // Attach our slots - ice_ctx_->SignalGatheringStateChange.connect( - this, &TransportTestPeer::GatheringStateChange); - char name[100]; snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(), (int)streams_.size()); // Create the media stream RefPtr stream = ice_ctx_->CreateStream(name, name, 1); + // Attach our slots + stream->SignalGatheringStateChange.connect( + this, &TransportTestPeer::GatheringStateChange); ASSERT_TRUE(stream != nullptr); stream->SetIceCredentials("ufrag", "pass"); @@ -639,9 +638,12 @@ class TransportTestPeer : public sigslot::has_slots<> { << std::endl; } - void GatheringStateChange(NrIceCtx* ctx, NrIceCtx::GatheringState state) { - (void)ctx; - if (state == NrIceCtx::ICE_CTX_GATHER_COMPLETE) { + void GatheringStateChange(const std::string& aTransportId, + NrIceMediaStream::GatheringState state) { + // We only use one stream, no need to check whether all streams are done + // gathering. + Unused << aTransportId; + if (state == NrIceMediaStream::ICE_STREAM_GATHER_COMPLETE) { GatheringComplete(); } } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c index 0d498845a4..b428264e5a 100644 --- a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c @@ -325,8 +325,9 @@ int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out) #endif /* USE_TURN */ #define MAXADDRS 100 /* Ridiculously high */ -int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp) - { + int nr_ice_ctx_create(char* label, UINT4 flags, + nr_ice_gather_handler* gather_handler, + nr_ice_ctx** ctxp) { nr_ice_ctx *ctx=0; int r,_status; @@ -341,6 +342,8 @@ int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp) if(!(ctx->label=r_strdup(label))) ABORT(R_NO_MEMORY); + ctx->gather_handler = gather_handler; + /* Get the STUN servers */ if(r=NR_reg_get_child_count(NR_ICE_REG_STUN_SRV_PRFX, (unsigned int *)&ctx->stun_server_ct_cfg)||ctx->stun_server_ct_cfg==0) { @@ -442,7 +445,7 @@ int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp) int i; nr_ice_stun_id *id1,*id2; - ctx->done_cb = 0; + ctx->gather_done_cb = 0; ctx->trickle_cb = 0; STAILQ_FOREACH_SAFE(s1, &ctx->streams, entry, s2){ @@ -452,6 +455,8 @@ int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp) RFREE(ctx->label); + ctx->gather_handler = 0; + RFREE(ctx->stun_servers_cfg); RFREE(ctx->local_addrs); @@ -539,20 +544,26 @@ void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg) } } - if (nr_ice_media_stream_is_done_gathering(stream) && - ctx->trickle_cb) { - ctx->trickle_cb(ctx->trickle_cb_arg, ctx, stream, component_id, NULL); + if (nr_ice_media_stream_is_done_gathering(stream)) { + if (ctx->gather_handler && ctx->gather_handler->vtbl->stream_gathered) { + ctx->gather_handler->vtbl->stream_gathered(ctx->gather_handler->obj, + stream); + } + if (ctx->trickle_cb) { + ctx->trickle_cb(ctx->trickle_cb_arg, ctx, stream, component_id, NULL); + } } if(ctx->uninitialized_candidates==0){ + assert(nr_ice_media_stream_is_done_gathering(stream)); r_log(LOG_ICE, LOG_INFO, "ICE(%s): All candidates initialized", ctx->label); - if (ctx->done_cb) { - ctx->done_cb(0,0,ctx->cb_arg); - } - else { + if (ctx->gather_done_cb) { + ctx->gather_done_cb(0, 0, ctx->cb_arg); + } else { r_log(LOG_ICE, LOG_INFO, - "ICE(%s): No done_cb. We were probably destroyed.", ctx->label); + "ICE(%s): No gather_done_cb. We were probably destroyed.", + ctx->label); } } else { @@ -850,8 +861,7 @@ int nr_ice_set_target_for_default_local_address_lookup(nr_ice_ctx *ctx, const ch return(_status); } -int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg) - { + int nr_ice_gather(nr_ice_ctx* ctx, NR_async_cb gather_done_cb, void* cb_arg) { int r,_status; nr_ice_media_stream *stream; nr_local_addr stun_addrs[MAXADDRS]; @@ -872,7 +882,7 @@ int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg) } r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Initializing candidates",ctx->label); - ctx->done_cb=done_cb; + ctx->gather_done_cb = gather_done_cb; ctx->cb_arg=cb_arg; /* Initialize all the media stream/component pairs */ diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h index 8b3081f567..4039c741ec 100644 --- a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h @@ -97,9 +97,23 @@ typedef struct nr_ice_stats_ { UINT2 turn_438s; } nr_ice_stats; +typedef struct nr_ice_gather_handler_vtbl_ { + /* This media stream is gathering */ + int (*stream_gathering)(void* obj, nr_ice_media_stream* stream); + + /* This media stream has finished gathering */ + int (*stream_gathered)(void* obj, nr_ice_media_stream* stream); +} nr_ice_gather_handler_vtbl; + +typedef struct nr_ice_gather_handler_ { + void* obj; + nr_ice_gather_handler_vtbl* vtbl; +} nr_ice_gather_handler; + struct nr_ice_ctx_ { UINT4 flags; char *label; + nr_ice_gather_handler* gather_handler; UINT4 Ta; @@ -129,7 +143,7 @@ struct nr_ice_ctx_ { nr_ice_peer_ctx_head peers; nr_ice_stun_id_head ids; - NR_async_cb done_cb; + NR_async_cb gather_done_cb; void *cb_arg; nr_ice_trickle_candidate_cb trickle_cb; @@ -141,7 +155,8 @@ struct nr_ice_ctx_ { nr_transport_addr *target_for_default_local_address_lookup; }; -int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp); +int nr_ice_ctx_create(char* label, UINT4 flags, + nr_ice_gather_handler* gather_handler, nr_ice_ctx** ctxp); int nr_ice_ctx_create_with_credentials(char *label, UINT4 flags, char* ufrag, char* pwd, nr_ice_ctx **ctxp); #define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION (1) #define NR_ICE_CTX_FLAGS_LITE (1<<1) @@ -156,7 +171,7 @@ void nr_ice_ctx_remove_flags(nr_ice_ctx *ctx, UINT4 flags); void nr_ice_ctx_destroy(nr_ice_ctx** ctxp); int nr_ice_set_local_addresses(nr_ice_ctx *ctx, nr_local_addr* stun_addrs, int stun_addr_ct); int nr_ice_set_target_for_default_local_address_lookup(nr_ice_ctx *ctx, const char *target_ip, UINT2 target_port); -int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg); +int nr_ice_gather(nr_ice_ctx* ctx, NR_async_cb gather_done_cb, void* cb_arg); int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand); void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg); int nr_ice_add_media_stream(nr_ice_ctx *ctx,const char *label,const char *ufrag,const char *pwd,int components, nr_ice_media_stream **streamp); diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_handler.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_handler.h index 5a0690adad..ab3e41ef2d 100644 --- a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_handler.h +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_handler.h @@ -56,9 +56,15 @@ int component_id, nr_ice_cand_pair **potentials,int potential_ct); */ int (*stream_ready)(void *obj, nr_ice_media_stream *stream); + /* This media stream is checking */ + int (*stream_checking)(void* obj, nr_ice_media_stream* stream); + /* This media stream has failed */ int (*stream_failed)(void *obj, nr_ice_media_stream *stream); + /* This media stream has disconnected */ + int (*stream_disconnected)(void* obj, nr_ice_media_stream* stream); + /* ICE is connected for this peer ctx */ int (*ice_connected)(void *obj, nr_ice_peer_ctx *pctx); diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c index 62bfbad629..90e278bedb 100644 --- a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c @@ -80,6 +80,7 @@ int nr_ice_media_stream_create(nr_ice_ctx *ctx,const char *label,const char *ufr stream->component_ct=components; stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED; stream->obsolete = 0; + stream->actually_started_checking = 0; stream->r2l_user = 0; stream->l2r_user = 0; stream->flags = ctx->flags; @@ -177,8 +178,20 @@ int nr_ice_media_stream_initialize(nr_ice_ctx *ctx, nr_ice_media_stream *stream) comp=STAILQ_NEXT(comp,entry); } + if (!nr_ice_media_stream_is_done_gathering(stream) && ctx->gather_handler && + ctx->gather_handler->vtbl->stream_gathering) { + ctx->gather_handler->vtbl->stream_gathering(ctx->gather_handler->obj, + stream); + } + _status=0; abort: + if (_status) { + if (ctx->gather_handler && ctx->gather_handler->vtbl->stream_gathered) { + ctx->gather_handler->vtbl->stream_gathered(ctx->gather_handler->obj, + stream); + } + } return(_status); } @@ -413,6 +426,19 @@ static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg) if(pair){ nr_ice_candidate_pair_start(pair->pctx,pair); /* Ignore failures */ + + /* stream->ice_state goes to checking when we decide that it is ok to + * start checking, which can happen before we get remote candidates. We + * want to fire this event when we _actually_ start sending checks. */ + if (!stream->actually_started_checking) { + stream->actually_started_checking = 1; + if (stream->pctx->handler && + stream->pctx->handler->vtbl->stream_checking) { + stream->pctx->handler->vtbl->stream_checking( + stream->pctx->handler->obj, stream->local_stream); + } + } + NR_ASYNC_TIMER_SET(timer_val,nr_ice_media_stream_check_timer_cb,cb_arg,&stream->timer); } else { @@ -729,9 +755,21 @@ void nr_ice_media_stream_set_disconnected(nr_ice_media_stream *stream, int disco if (disconnected == NR_ICE_MEDIA_STREAM_DISCONNECTED) { if (!stream->local_stream->obsolete) { + if (stream->pctx->handler && + stream->pctx->handler->vtbl->stream_disconnected) { + stream->pctx->handler->vtbl->stream_disconnected( + stream->pctx->handler->obj, stream->local_stream); + } nr_ice_peer_ctx_disconnected(stream->pctx); } } else { + if (!stream->local_stream->obsolete) { + if (stream->pctx->handler && + stream->pctx->handler->vtbl->stream_ready) { + stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj, + stream->local_stream); + } + } nr_ice_peer_ctx_check_if_connected(stream->pctx); } } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h index 99f906c100..3da20e3b5e 100644 --- a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h @@ -72,6 +72,7 @@ struct nr_ice_media_stream_ { * processing. If this stream is connected already, traffic can continue to * flow for a limited time while the new stream gets ready. */ int obsolete; + int actually_started_checking; #define NR_ICE_MEDIA_STREAM_UNPAIRED 1 #define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 2 diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c index 0bf97eb984..bfa1fcf44b 100644 --- a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c +++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c @@ -671,6 +671,12 @@ void nr_ice_peer_ctx_refresh_consent_all_streams(nr_ice_peer_ctx *pctx) void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx) { + if (pctx->connected_cb_timer) { + /* Whoops, never mind */ + NR_async_timer_cancel(pctx->connected_cb_timer); + pctx->connected_cb_timer = 0; + } + if (pctx->reported_connected && pctx->handler && pctx->handler->vtbl->ice_disconnected) { diff --git a/dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp b/dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp index fc1a51dd0d..275e1bf45a 100644 --- a/dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp +++ b/dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp @@ -85,23 +85,27 @@ bool MediaPipelineFilter::Filter(const webrtc::RTPHeader& header) { // Remote SSRC based filtering // - if (remote_ssrc_set_.count(header.ssrc)) { + if (!remote_ssrc_set_.empty()) { + if (remote_ssrc_set_.count(header.ssrc)) { + DEBUG_LOG( + ("MediaPipelineFilter SSRC: %u matched remote SSRC set." + " passing packet", + header.ssrc)); + return true; + } DEBUG_LOG( - ("MediaPipelineFilter SSRC: %u matched remote SSRC set." - " passing packet", - header.ssrc)); - return true; + ("MediaPipelineFilter SSRC: %u did not match any of %zu" + " remote SSRCS.", + header.ssrc, remote_ssrc_set_.size())); + return false; } - DEBUG_LOG( - ("MediaPipelineFilter SSRC: %u did not match any of %zu" - " remote SSRCS.", - header.ssrc, remote_ssrc_set_.size())); // - // PT, payload type, last ditch effort filtering + // PT, payload type, last ditch effort filtering. We only try this if we do + // not have any ssrcs configured (either by learning them, or negotiation). // - if (payload_type_set_.count(header.payloadType)) { + if (receive_payload_type_set_.count(header.payloadType)) { DEBUG_LOG( ("MediaPipelineFilter payload-type: %u matched %zu" " unique payload type. learning ssrc. passing packet", @@ -114,7 +118,7 @@ bool MediaPipelineFilter::Filter(const webrtc::RTPHeader& header) { DEBUG_LOG( ("MediaPipelineFilter payload-type: %u did not match any of %zu" " unique payload-types.", - header.payloadType, payload_type_set_.size())); + header.payloadType, receive_payload_type_set_.size())); DEBUG_LOG( ("MediaPipelineFilter packet failed to match any criteria." " ignoring packet")); @@ -125,8 +129,8 @@ void MediaPipelineFilter::AddRemoteSSRC(uint32_t ssrc) { remote_ssrc_set_.insert(ssrc); } -void MediaPipelineFilter::AddUniquePT(uint8_t payload_type) { - payload_type_set_.insert(payload_type); +void MediaPipelineFilter::AddUniqueReceivePT(uint8_t payload_type) { + receive_payload_type_set_.insert(payload_type); } void MediaPipelineFilter::Update(const MediaPipelineFilter& filter_update) { @@ -143,7 +147,7 @@ void MediaPipelineFilter::Update(const MediaPipelineFilter& filter_update) { mRemoteMid = filter_update.mRemoteMid; mRemoteMidBindings = filter_update.mRemoteMidBindings; } - payload_type_set_ = filter_update.payload_type_set_; + receive_payload_type_set_ = filter_update.receive_payload_type_set_; // Use extmapping from new filter mExtMap = filter_update.mExtMap; diff --git a/dom/media/webrtc/transportbridge/MediaPipelineFilter.h b/dom/media/webrtc/transportbridge/MediaPipelineFilter.h index d6bb7abd43..62d4b63ab8 100644 --- a/dom/media/webrtc/transportbridge/MediaPipelineFilter.h +++ b/dom/media/webrtc/transportbridge/MediaPipelineFilter.h @@ -66,7 +66,7 @@ class MediaPipelineFilter { void SetRemoteMediaStreamId(const Maybe& aMid); // When a payload type id is unique to our media section, add it here. - void AddUniquePT(uint8_t payload_type); + void AddUniqueReceivePT(uint8_t payload_type); void Update(const MediaPipelineFilter& filter_update); @@ -76,7 +76,7 @@ class MediaPipelineFilter { // The number of filters we manage here is quite small, so I am optimizing // for readability. std::set remote_ssrc_set_; - std::set payload_type_set_; + std::set receive_payload_type_set_; Maybe mRemoteMid; std::set mRemoteMidBindings; // RID extension can be set by tests and is sticky, the rest of -- cgit v1.2.3