author | Byron Campen [:bwc] <docfaraday@gmail.com> |
Fri, 26 Oct 2018 00:39:07 +0000 | |
changeset 499516 | 11819394462eb9695db2fb3021c1dec220e52c8f |
parent 499515 | f3d23a7bcbb6e30a5d7368882fdf1b04498d9bdf |
child 499517 | 81837314ba09b55b0dfe6423b8308e5808bafd32 |
push id | 10290 |
push user | ffxbld-merge |
push date | Mon, 03 Dec 2018 16:23:23 +0000 |
treeherder | mozilla-beta@700bed2445e6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mjf |
bugs | 1494301 |
milestone | 65.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/media/mtransport/mediapacket.cpp +++ b/media/mtransport/mediapacket.cpp @@ -17,10 +17,65 @@ MediaPacket::Copy(const uint8_t* data, s capacity = len; } data_.reset(new uint8_t[capacity]); len_ = len; capacity_ = capacity; memcpy(data_.get(), data, len); } +static bool IsRtp(const uint8_t* data, size_t len) +{ + if (len < 2) + return false; + + // Check if this is a RTCP packet. Logic based on the types listed in + // media/webrtc/trunk/src/modules/rtp_rtcp/source/rtp_utility.cc + + // Anything outside this range is RTP. + if ((data[1] < 192) || (data[1] > 207)) + return true; + + if (data[1] == 192) // FIR + return false; + + if (data[1] == 193) // NACK, but could also be RTP. This makes us sad + return true; // but it's how webrtc.org behaves. + + if (data[1] == 194) + return true; + + if (data[1] == 195) // IJ. + return false; + + if ((data[1] > 195) && (data[1] < 200)) // the > 195 is redundant + return true; + + if ((data[1] >= 200) && (data[1] <= 207)) // SR, RR, SDES, BYE, + return false; // APP, RTPFB, PSFB, XR + + MOZ_ASSERT(false); // Not reached, belt and suspenders. + return true; +} + +void +MediaPacket::Categorize() +{ + SetType(MediaPacket::UNCLASSIFIED); + + if (!data_ || len_ < 4) { + return; + } + + if (data_[0] >= 20 && data_[0] <= 63) { + // DTLS per RFC 7983 + SetType(MediaPacket::DTLS); + } else if (data_[0] > 127 && data_[0] < 192) { + // RTP/RTCP per RFC 7983 + if (IsRtp(data_.get(), len_)) { + SetType(MediaPacket::SRTP); + } else { + SetType(MediaPacket::SRTCP); + } + } +} } // namespace mozilla
--- a/media/mtransport/mediapacket.h +++ b/media/mtransport/mediapacket.h @@ -81,21 +81,26 @@ class MediaPacket { size_t encrypted_len() const { return encrypted_len_; } enum Type { UNCLASSIFIED, + SRTP, + SRTCP, + DTLS, RTP, RTCP, SCTP }; + void Categorize(); + void SetType(Type type) { type_ = type; } Type type() const { return type_;
--- a/media/mtransport/nricectx.cpp +++ b/media/mtransport/nricectx.cpp @@ -781,17 +781,16 @@ NrIceStats NrIceCtx::Destroy() { ice_handler_vtbl_ = nullptr; ice_handler_ = nullptr; streams_.clear(); return stats; } NrIceCtx::~NrIceCtx() { - Destroy(); } void NrIceCtx::destroy_peer_ctx() { nr_ice_peer_ctx_destroy(&peer_); } nsresult NrIceCtx::SetControlling(Controlling controlling) { if (!ice_controlling_set_) {
--- a/media/mtransport/nricemediastream.cpp +++ b/media/mtransport/nricemediastream.cpp @@ -472,16 +472,20 @@ nsresult NrIceMediaStream::GetCandidateP return NS_OK; } nsresult NrIceMediaStream::GetDefaultCandidate( int component, NrIceCandidate* candidate) const { + if (!stream_) { + return NS_ERROR_NOT_AVAILABLE; + } + nr_ice_candidate *cand; int r = nr_ice_media_stream_get_default_candidate(stream_, component, &cand); if (r) { if (r == R_NOT_FOUND) { MOZ_MTLOG(ML_INFO, "Couldn't get default ICE candidate for '" << name_ << "', no candidates."); } else {
--- a/media/mtransport/test/ice_unittest.cpp +++ b/media/mtransport/test/ice_unittest.cpp @@ -974,16 +974,17 @@ class IceTestPeer : public sigslot::has_ std::cerr << name_ << " Shutdown" << std::endl; shutting_down_ = true; for (auto& controlled_trickle_candidate : controlled_trickle_candidates_) { for (auto& cand : controlled_trickle_candidate.second) { delete cand; } } + ice_ctx_->Destroy(); ice_ctx_ = nullptr; if (remote_) { remote_->UnsetRemote(); remote_ = nullptr; } }
--- a/media/mtransport/test/transport_unittests.cpp +++ b/media/mtransport/test/transport_unittests.cpp @@ -477,16 +477,17 @@ class TransportTestPeer : public sigslot void DestroyFlow() { disconnect_all(); if (flow_) { loopback_->Disconnect(); flow_ = nullptr; } + ice_ctx_->Destroy(); ice_ctx_ = nullptr; streams_.clear(); } void DisconnectDestroyFlow() { loopback_->Disconnect(); disconnect_all(); // Disconnect from the signals; flow_ = nullptr;
--- a/media/mtransport/transportflow.cpp +++ b/media/mtransport/transportflow.cpp @@ -7,36 +7,38 @@ // Original author: ekr@rtfm.com #include <deque> #include "logging.h" #include "runnable_utils.h" #include "transportflow.h" #include "transportlayer.h" +#include "mozilla/DebugOnly.h" + namespace mozilla { NS_IMPL_ISUPPORTS0(TransportFlow) // There are some hacks here to allow destruction off of // the main thread. TransportFlow::~TransportFlow() { // Push the destruction onto the STS thread. Note that there // is still some possibility that someone is accessing this // object simultaneously, but as long as smart pointer discipline // is maintained, it shouldn't be possible to access and // destroy it simultaneously. The conversion to an nsAutoPtr // ensures automatic destruction of the queue at exit of // DestroyFinal. - if (target_) { - nsAutoPtr<std::deque<TransportLayer*>> layers_tmp(layers_.release()); - RUN_ON_THREAD(target_, - WrapRunnableNM(&TransportFlow::DestroyFinal, layers_tmp), - NS_DISPATCH_NORMAL); - } + MOZ_RELEASE_ASSERT(target_); + nsAutoPtr<std::deque<TransportLayer*>> layers_tmp(layers_.release()); + DebugOnly<nsresult> rv = target_->Dispatch( + WrapRunnableNM(&TransportFlow::DestroyFinal, layers_tmp), + NS_DISPATCH_NORMAL); + MOZ_ASSERT(NS_SUCCEEDED(rv)); } void TransportFlow::DestroyFinal(nsAutoPtr<std::deque<TransportLayer *> > layers) { ClearLayers(layers.get()); } void TransportFlow::ClearLayers(std::deque<TransportLayer *>* layers) { while (!layers->empty()) {
--- a/media/mtransport/transportlayerdtls.cpp +++ b/media/mtransport/transportlayerdtls.cpp @@ -95,16 +95,17 @@ int32_t TransportLayerNSPRAdapter::Write if (!enabled_) { MOZ_MTLOG(ML_WARNING, "Writing to disabled transport layer"); return -1; } MediaPacket packet; // Copies. Oh well. packet.Copy(static_cast<const uint8_t*>(buf), static_cast<size_t>(length)); + packet.SetType(MediaPacket::DTLS); TransportResult r = output_->SendPacket(packet); if (r >= 0) { return r; } if (r == TE_WOULDBLOCK) { PR_SetError(PR_WOULD_BLOCK_ERROR, 0); @@ -1012,18 +1013,17 @@ void TransportLayerDtls::PacketReceived( return; } if (!packet.data()) { // Something ate this, probably the SRTP layer return; } - // not DTLS per RFC 7983 - if (packet.data()[0] < 20 || packet.data()[0] > 63) { + if (packet.type() != MediaPacket::DTLS) { return; } nspr_io_adapter_->PacketReceived(packet); GetDecryptedPackets(); } void @@ -1043,16 +1043,17 @@ TransportLayerDtls::GetDecryptedPackets( // Can we peek to get a better idea of the actual size? static const size_t kBufferSize = 9216; auto buffer = MakeUnique<uint8_t[]>(kBufferSize); rv = PR_Recv(ssl_fd_.get(), buffer.get(), kBufferSize, 0, PR_INTERVAL_NO_WAIT); if (rv > 0) { // We have data MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS"); MediaPacket packet; + packet.SetType(MediaPacket::SCTP); packet.Take(std::move(buffer), static_cast<size_t>(rv)); SignalPacketReceived(this, packet); } else if (rv == 0) { TL_SET_STATE(TS_CLOSED); } else { int32_t err = PR_GetError(); if (err == PR_WOULD_BLOCK_ERROR) {
--- a/media/mtransport/transportlayerice.cpp +++ b/media/mtransport/transportlayerice.cpp @@ -120,16 +120,17 @@ void TransportLayerIce::PostSetup() { &TransportLayerIce::IcePacketReceived); if (stream_->state() == NrIceMediaStream::ICE_OPEN) { TL_SET_STATE(TS_OPEN); } } TransportResult TransportLayerIce::SendPacket(MediaPacket& packet) { CheckThread(); + SignalPacketSending(this, packet); nsresult res = stream_->SendPacket(component_, packet.data(), packet.len()); if (!NS_SUCCEEDED(res)) { return (res == NS_BASE_STREAM_WOULD_BLOCK) ? TE_WOULDBLOCK : TE_ERROR; } @@ -177,12 +178,14 @@ void TransportLayerIce::IcePacketReceive MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << stream->name() << "," << component << "," << len << ")"); // Might be useful to allow MediaPacket to borrow a buffer (ie; not take // ownership, but copy it if the MediaPacket is moved). This could be a // footgun though with MediaPackets that end up on the heap. MediaPacket packet; packet.Copy(data, len); + packet.Categorize(); + SignalPacketReceived(this, packet); } } // close namespace
--- a/media/mtransport/transportlayerice.h +++ b/media/mtransport/transportlayerice.h @@ -45,16 +45,19 @@ class TransportLayerIce : public Transpo // Slots for ICE void IceCandidate(NrIceMediaStream *stream, const std::string&); void IceReady(NrIceMediaStream *stream); void IceFailed(NrIceMediaStream *stream); void IcePacketReceived(NrIceMediaStream *stream, int component, const unsigned char *data, int len); + // Useful for capturing encrypted packets + sigslot::signal2<TransportLayer*, MediaPacket&> SignalPacketSending; + TRANSPORT_LAYER_ID("ice") private: DISALLOW_COPY_ASSIGN(TransportLayerIce); void PostSetup(); RefPtr<NrIceMediaStream> stream_; int component_;
--- a/media/mtransport/transportlayersrtp.cpp +++ b/media/mtransport/transportlayersrtp.cpp @@ -51,68 +51,36 @@ TransportLayerSrtp::Setup() } // downward_ is the TransportLayerIce downward_->SignalPacketReceived.connect(this, &TransportLayerSrtp::PacketReceived); return true; } -static bool IsRtp(const unsigned char* data, size_t len) -{ - if (len < 2) - return false; - - // Check if this is a RTCP packet. Logic based on the types listed in - // media/webrtc/trunk/src/modules/rtp_rtcp/source/rtp_utility.cc - - // Anything outside this range is RTP. - if ((data[1] < 192) || (data[1] > 207)) - return true; - - if (data[1] == 192) // FIR - return false; - - if (data[1] == 193) // NACK, but could also be RTP. This makes us sad - return true; // but it's how webrtc.org behaves. - - if (data[1] == 194) - return true; - - if (data[1] == 195) // IJ. - return false; - - if ((data[1] > 195) && (data[1] < 200)) // the > 195 is redundant - return true; - - if ((data[1] >= 200) && (data[1] <= 207)) // SR, RR, SDES, BYE, - return false; // APP, RTPFB, PSFB, XR - - MOZ_ASSERT(false); // Not reached, belt and suspenders. - return true; -} - TransportResult TransportLayerSrtp::SendPacket(MediaPacket& packet) { if (packet.len() < 4) { MOZ_ASSERT(false); return TE_ERROR; } MOZ_ASSERT(packet.capacity() - packet.len() >= SRTP_MAX_EXPANSION); int out_len; nsresult res; switch (packet.type()) { case MediaPacket::RTP: res = mSendSrtp->ProtectRtp(packet.data(), packet.len(), packet.capacity(), &out_len); + packet.SetType(MediaPacket::SRTP); break; case MediaPacket::RTCP: res = mSendSrtp->ProtectRtcp(packet.data(), packet.len(), packet.capacity(), &out_len); + packet.SetType(MediaPacket::SRTCP); break; default: MOZ_CRASH("SRTP layer asked to send packet that is neither RTP or RTCP"); } if (NS_FAILED(res)) { MOZ_MTLOG(ML_ERROR, "Error protecting " @@ -222,31 +190,27 @@ TransportLayerSrtp::PacketReceived(Trans return; } if (!packet.data()) { // Something ate this, probably the DTLS layer return; } - if (packet.len() < 4) { - return; - } - - // not RTP/RTCP per RFC 7983 - if (packet.data()[0] <= 127 || packet.data()[0] >= 192) { + if (packet.type() != MediaPacket::SRTP && + packet.type() != MediaPacket::SRTCP) { return; } // We want to keep the encrypted packet around for packet dumping packet.CopyDataToEncrypted(); int outLen; nsresult res; - if (IsRtp(packet.data(), packet.len())) { + if (packet.type() == MediaPacket::SRTP) { packet.SetType(MediaPacket::RTP); res = mRecvSrtp->UnprotectRtp(packet.data(), packet.len(), packet.len(), &outLen); } else { packet.SetType(MediaPacket::RTCP); res = mRecvSrtp->UnprotectRtcp(packet.data(), packet.len(), packet.len(), &outLen); } if (NS_SUCCEEDED(res)) {
--- a/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp +++ b/media/webrtc/signaling/gtest/mediapipeline_unittest.cpp @@ -21,16 +21,17 @@ #include "MediaStreamTrack.h" #include "transportflow.h" #include "transportlayerloopback.h" #include "transportlayerdtls.h" #include "transportlayersrtp.h" #include "mozilla/SyncRunnable.h" #include "mtransport_test_utils.h" #include "SharedBuffer.h" +#include "MediaTransportHandler.h" #define GTEST_HAS_RTTI 0 #include "gtest/gtest.h" using namespace mozilla; MOZ_MTLOG_MODULE("mediapipeline") static MtransportTestUtils *test_utils; @@ -159,112 +160,127 @@ public: PRINCIPAL_HANDLE_NONE); for (auto& listener: mst->mListeners) { listener->NotifyQueuedChanges(nullptr, 0, segment); } } }; -class TransportInfo { +class LoopbackTransport : public MediaTransportBase { public: - TransportInfo() : - flow_(nullptr), - loopback_(nullptr) {} + LoopbackTransport() + { + SetState("mux", TransportLayer::TS_INIT, false); + SetState("mux", TransportLayer::TS_INIT, true); + SetState("non-mux", TransportLayer::TS_INIT, false); + SetState("non-mux", TransportLayer::TS_INIT, true); + } - static void InitAndConnect(TransportInfo &client, TransportInfo &server) { - client.Init(true); - server.Init(false); + static void InitAndConnect(LoopbackTransport &client, LoopbackTransport &server) { client.Connect(&server); server.Connect(&client); } - void Init(bool client) { - UniquePtr<TransportLayerLoopback> loopback(new TransportLayerLoopback); - UniquePtr<TransportLayerDtls> dtls(new TransportLayerDtls); - UniquePtr<TransportLayerSrtp> srtp(new TransportLayerSrtp(*dtls)); - - std::vector<uint16_t> ciphers; - ciphers.push_back(kDtlsSrtpAeadAes256Gcm); - dtls->SetSrtpCiphers(ciphers); - dtls->SetIdentity(DtlsIdentity::Generate()); - dtls->SetRole(client ? TransportLayerDtls::CLIENT : - TransportLayerDtls::SERVER); - dtls->SetVerificationAllowAll(); - - ASSERT_EQ(NS_OK, loopback->Init()); - ASSERT_EQ(NS_OK, dtls->Init()); - ASSERT_EQ(NS_OK, srtp->Init()); - - dtls->Chain(loopback.get()); - srtp->Chain(loopback.get()); - - flow_ = new TransportFlow(); - loopback_ = loopback.release(); - flow_->PushLayer(loopback_); - flow_->PushLayer(dtls.release()); - flow_->PushLayer(srtp.release()); - } - - void Connect(TransportInfo* peer) { - MOZ_ASSERT(loopback_); - MOZ_ASSERT(peer->loopback_); - - loopback_->Connect(peer->loopback_); + void Connect(LoopbackTransport* peer) { + peer_ = peer; } void Shutdown() { - if (loopback_) { - loopback_->Disconnect(); - } - loopback_ = nullptr; - flow_ = nullptr; + peer_ = nullptr; + } + + nsresult SendPacket(const std::string& aTransportId, + MediaPacket& aPacket) override + { + peer_->SignalPacketReceived(aTransportId, aPacket); + return NS_OK; } - RefPtr<TransportFlow> flow_; - TransportLayerLoopback *loopback_; + TransportLayer::State GetState(const std::string& aTransportId, + bool aRtcp) const override + { + if (aRtcp) { + auto it = mRtcpStates.find(aTransportId); + if (it != mRtcpStates.end()) { + return it->second; + } + } else { + auto it = mRtpStates.find(aTransportId); + if (it != mRtpStates.end()) { + return it->second; + } + } + + return TransportLayer::TS_NONE; + } + + void SetState(const std::string& aTransportId, + TransportLayer::State aState, + bool aRtcp) + { + if (aRtcp) { + mRtcpStates[aTransportId] = aState; + SignalRtcpStateChange(aTransportId, aState); + } else { + mRtpStates[aTransportId] = aState; + SignalStateChange(aTransportId, aState); + } + } + +private: + RefPtr<MediaTransportBase> peer_; + std::map<std::string, TransportLayer::State> mRtpStates; + std::map<std::string, TransportLayer::State> mRtcpStates; }; class TestAgent { public: TestAgent() : audio_config_(109, "opus", 48000, 960, 2, 64000, false), audio_conduit_(mozilla::AudioSessionConduit::Create()), audio_pipeline_(), - use_bundle_(false) { + transport_(new LoopbackTransport) { } - static void ConnectRtp(TestAgent *client, TestAgent *server) { - TransportInfo::InitAndConnect(client->audio_rtp_transport_, - server->audio_rtp_transport_); + static void Connect(TestAgent *client, TestAgent *server) { + LoopbackTransport::InitAndConnect(*client->transport_, + *server->transport_); } - static void ConnectRtcp(TestAgent *client, TestAgent *server) { - TransportInfo::InitAndConnect(client->audio_rtcp_transport_, - server->audio_rtcp_transport_); + virtual void CreatePipeline(const std::string& aTransportId) = 0; + + void SetState(const std::string& aTransportId, + TransportLayer::State aState, + bool aRtcp) + { + mozilla::SyncRunnable::DispatchToThread( + test_utils->sts_target(), + WrapRunnable(transport_, + &LoopbackTransport::SetState, aTransportId, aState, aRtcp)); } - static void ConnectBundle(TestAgent *client, TestAgent *server) { - TransportInfo::InitAndConnect(client->bundle_transport_, - server->bundle_transport_); + void UpdateTransport(const std::string& aTransportId, + nsAutoPtr<MediaPipelineFilter> aFilter) + { + mozilla::SyncRunnable::DispatchToThread( + test_utils->sts_target(), + WrapRunnable(audio_pipeline_, + &MediaPipeline::UpdateTransport_s, aTransportId, aFilter)); } - virtual void CreatePipeline(bool aIsRtcpMux) = 0; - void Stop() { MOZ_MTLOG(ML_DEBUG, "Stopping"); if (audio_pipeline_) audio_pipeline_->Stop(); } void Shutdown_s() { - audio_rtp_transport_.Shutdown(); - audio_rtcp_transport_.Shutdown(); - bundle_transport_.Shutdown(); + transport_->Shutdown(); } void Shutdown() { if (audio_pipeline_) audio_pipeline_->Shutdown_m(); if (audio_stream_track_) audio_stream_track_->Stop(); @@ -296,77 +312,58 @@ class TestAgent { int GetAudioRtcpCountSent() { return audio_pipeline_->RtcpPacketsSent(); } int GetAudioRtcpCountReceived() { return audio_pipeline_->RtcpPacketsReceived(); } - - void SetUsingBundle(bool use_bundle) { - use_bundle_ = use_bundle; - } - protected: mozilla::AudioCodecConfig audio_config_; RefPtr<mozilla::MediaSessionConduit> audio_conduit_; RefPtr<FakeAudioStreamTrack> audio_stream_track_; // TODO(bcampen@mozilla.com): Right now this does not let us test RTCP in // both directions; only the sender's RTCP is sent, but the receiver should // be sending it too. RefPtr<mozilla::MediaPipeline> audio_pipeline_; - TransportInfo audio_rtp_transport_; - TransportInfo audio_rtcp_transport_; - TransportInfo bundle_transport_; - bool use_bundle_; + RefPtr<LoopbackTransport> transport_; }; class TestAgentSend : public TestAgent { public: TestAgentSend() { mozilla::MediaConduitErrorCode err = static_cast<mozilla::AudioSessionConduit *>(audio_conduit_.get())-> ConfigureSendMediaCodec(&audio_config_); EXPECT_EQ(mozilla::kMediaConduitNoError, err); audio_stream_track_ = new FakeAudioStreamTrack(); } - virtual void CreatePipeline(bool aIsRtcpMux) { + virtual void CreatePipeline(const std::string& aTransportId) { std::string test_pc; - if (aIsRtcpMux) { - ASSERT_FALSE(audio_rtcp_transport_.flow_); - } - RefPtr<MediaPipelineTransmit> audio_pipeline = new mozilla::MediaPipelineTransmit( test_pc, + transport_, nullptr, test_utils->sts_target(), false, audio_conduit_); audio_pipeline->SetTrack(audio_stream_track_.get()); audio_pipeline->Start(); audio_pipeline_ = audio_pipeline; - RefPtr<TransportFlow> rtp(audio_rtp_transport_.flow_); - RefPtr<TransportFlow> rtcp(audio_rtcp_transport_.flow_); - - if (use_bundle_) { - rtp = bundle_transport_.flow_; - rtcp = nullptr; - } - - audio_pipeline_->UpdateTransport_m( - rtp, rtcp, nsAutoPtr<MediaPipelineFilter>(nullptr)); + audio_pipeline_->UpdateTransport_m(aTransportId, + nsAutoPtr<MediaPipelineFilter>(nullptr)); } }; class TestAgentReceive : public TestAgent { public: TestAgentReceive() { @@ -374,52 +371,39 @@ class TestAgentReceive : public TestAgen codecs.push_back(&audio_config_); mozilla::MediaConduitErrorCode err = static_cast<mozilla::AudioSessionConduit *>(audio_conduit_.get())-> ConfigureRecvMediaCodecs(codecs); EXPECT_EQ(mozilla::kMediaConduitNoError, err); } - virtual void CreatePipeline(bool aIsRtcpMux) { + virtual void CreatePipeline(const std::string& aTransportId) { std::string test_pc; - if (aIsRtcpMux) { - ASSERT_FALSE(audio_rtcp_transport_.flow_); - } - audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio( test_pc, + transport_, nullptr, test_utils->sts_target(), static_cast<mozilla::AudioSessionConduit *>(audio_conduit_.get()), nullptr); audio_pipeline_->Start(); - RefPtr<TransportFlow> rtp(audio_rtp_transport_.flow_); - RefPtr<TransportFlow> rtcp(audio_rtcp_transport_.flow_); - - if (use_bundle_) { - rtp = bundle_transport_.flow_; - rtcp = nullptr; - } - - audio_pipeline_->UpdateTransport_m(rtp, rtcp, bundle_filter_); + audio_pipeline_->UpdateTransport_m(aTransportId, bundle_filter_); } void SetBundleFilter(nsAutoPtr<MediaPipelineFilter> filter) { bundle_filter_ = filter; } - void UpdateFilter_s( + void UpdateTransport_s(const std::string& aTransportId, nsAutoPtr<MediaPipelineFilter> filter) { - audio_pipeline_->UpdateTransport_s(audio_rtp_transport_.flow_, - audio_rtcp_transport_.flow_, - filter); + audio_pipeline_->UpdateTransport_s(aTransportId, filter); } private: nsAutoPtr<MediaPipelineFilter> bundle_filter_; }; class MediaPipelineTest : public ::testing::Test { @@ -431,34 +415,20 @@ class MediaPipelineTest : public ::testi static void SetUpTestCase() { test_utils = new MtransportTestUtils(); NSS_NoDB_Init(nullptr); NSS_SetDomesticPolicy(); } // Setup transport. - void InitTransports(bool aIsRtcpMux) { - // RTP, p1_ is server, p2_ is client + void InitTransports() { mozilla::SyncRunnable::DispatchToThread( test_utils->sts_target(), - WrapRunnableNM(&TestAgent::ConnectRtp, &p2_, &p1_)); - - // Create RTCP flows separately if we are not muxing them. - if(!aIsRtcpMux) { - // RTCP, p1_ is server, p2_ is client - mozilla::SyncRunnable::DispatchToThread( - test_utils->sts_target(), - WrapRunnableNM(&TestAgent::ConnectRtcp, &p2_, &p1_)); - } - - // BUNDLE, p1_ is server, p2_ is client - mozilla::SyncRunnable::DispatchToThread( - test_utils->sts_target(), - WrapRunnableNM(&TestAgent::ConnectBundle, &p2_, &p1_)); + WrapRunnableNM(&TestAgent::Connect, &p2_, &p1_)); } // Verify RTP and RTCP void TestAudioSend(bool aIsRtcpMux, nsAutoPtr<MediaPipelineFilter> initialFilter = nsAutoPtr<MediaPipelineFilter>(nullptr), nsAutoPtr<MediaPipelineFilter> refinedFilter = nsAutoPtr<MediaPipelineFilter>(nullptr), @@ -468,37 +438,51 @@ class MediaPipelineTest : public ::testi bool bundle = !!(initialFilter); // We do not support testing bundle without rtcp mux, since that doesn't // make any sense. ASSERT_FALSE(!aIsRtcpMux && bundle); p2_.SetBundleFilter(initialFilter); // Setup transport flows - InitTransports(aIsRtcpMux); + InitTransports(); + + std::string transportId = aIsRtcpMux ? "mux" : "non-mux"; + p1_.CreatePipeline(transportId); + p2_.CreatePipeline(transportId); - p1_.CreatePipeline(aIsRtcpMux); - p2_.CreatePipeline(aIsRtcpMux); + // Set state of transports to CONNECTING. MediaPipeline doesn't really care + // about this transition, but we're trying to simluate what happens in a + // real case. + p1_.SetState(transportId, TransportLayer::TS_CONNECTING, false); + p1_.SetState(transportId, TransportLayer::TS_CONNECTING, true); + p2_.SetState(transportId, TransportLayer::TS_CONNECTING, false); + p2_.SetState(transportId, TransportLayer::TS_CONNECTING, true); + + PR_Sleep(10); + + // Set state of transports to OPEN (ie; connected). This should result in + // media flowing. + p1_.SetState(transportId, TransportLayer::TS_OPEN, false); + p1_.SetState(transportId, TransportLayer::TS_OPEN, true); + p2_.SetState(transportId, TransportLayer::TS_OPEN, false); + p2_.SetState(transportId, TransportLayer::TS_OPEN, true); if (bundle) { PR_Sleep(ms_until_filter_update); // Leaving refinedFilter not set implies we want to just update with // the other side's SSRC if (!refinedFilter) { refinedFilter = new MediaPipelineFilter; // Might not be safe, strictly speaking. refinedFilter->AddRemoteSSRC(p1_.GetLocalSSRC()); } - mozilla::SyncRunnable::DispatchToThread( - test_utils->sts_target(), - WrapRunnable(&p2_, - &TestAgentReceive::UpdateFilter_s, - refinedFilter)); + p2_.UpdateTransport(transportId, refinedFilter); } // wait for some RTP/RTCP tx and rx to happen PR_Sleep(ms_of_traffic_after_answer); p1_.Stop(); p2_.Stop();
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -39,23 +39,18 @@ #include "mozilla/UniquePtrExtensions.h" #include "mozilla/dom/ImageBitmapBinding.h" #include "mozilla/dom/ImageUtils.h" #include "mozilla/dom/RTCStatsReportBinding.h" #include "mozilla/gfx/Point.h" #include "mozilla/gfx/Types.h" #include "nsError.h" #include "nsThreadUtils.h" -#include "nspr.h" #include "runnable_utils.h" -#include "srtp.h" -#include "transportflow.h" -#include "transportlayer.h" -#include "transportlayerdtls.h" -#include "transportlayerice.h" +#include "signaling/src/peerconnection/MediaTransportHandler.h" #include "Tracing.h" #include "webrtc/base/bind.h" #include "webrtc/base/keep_ref_until_done.h" #include "webrtc/common_video/include/i420_buffer_pool.h" #include "webrtc/common_video/include/video_frame_buffer.h" #include "webrtc/video_frame.h" @@ -597,25 +592,25 @@ protected: // A buffer to hold a single packet of audio. UniquePtr<int16_t[]> mPacket; nsTArray<int16_t> mInterleavedAudio; AlignedShortBuffer mOutputAudio; UniquePtr<AudioConverter> mAudioConverter; }; MediaPipeline::MediaPipeline(const std::string& aPc, + MediaTransportBase* aTransportHandler, DirectionType aDirection, nsCOMPtr<nsIEventTarget> aMainThread, nsCOMPtr<nsIEventTarget> aStsThread, RefPtr<MediaSessionConduit> aConduit) : mDirection(aDirection) , mLevel(0) + , mTransportHandler(aTransportHandler) , mConduit(aConduit) - , mRtp(nullptr, RTP) - , mRtcp(nullptr, RTCP) , mMainThread(aMainThread) , mStsThread(aStsThread) , mTransport(new PipelineTransport(aStsThread)) , mRtpPacketsSent(0) , mRtcpPacketsSent(0) , mRtpPacketsReceived(0) , mRtcpPacketsReceived(0) , mRtpBytesSent(0) @@ -655,84 +650,59 @@ void MediaPipeline::DetachTransport_s() { ASSERT_ON_THREAD(mStsThread); MOZ_LOG(gMediaPipelineLog, LogLevel::Info, ("%s in %s", mDescription.c_str(), __FUNCTION__)); disconnect_all(); + mRtpState = TransportLayer::TS_NONE; + mRtcpState = TransportLayer::TS_NONE; + mTransportId.clear(); mTransport->Detach(); - mRtp.Detach(); - mRtcp.Detach(); // Make sure any cycles are broken mPacketDumper = nullptr; } -nsresult -MediaPipeline::AttachTransport_s() -{ - ASSERT_ON_THREAD(mStsThread); - nsresult res; - MOZ_ASSERT(mRtp.mTransport); - MOZ_ASSERT(mRtcp.mTransport); - res = ConnectTransport_s(mRtp); - if (NS_FAILED(res)) { - return res; - } - - if (mRtcp.mTransport != mRtp.mTransport) { - res = ConnectTransport_s(mRtcp); - if (NS_FAILED(res)) { - return res; - } - } - - mTransport->Attach(this); - - return NS_OK; -} - void -MediaPipeline::UpdateTransport_m(RefPtr<TransportFlow> aRtpTransport, - RefPtr<TransportFlow> aRtcpTransport, +MediaPipeline::UpdateTransport_m(const std::string& aTransportId, nsAutoPtr<MediaPipelineFilter> aFilter) { RUN_ON_THREAD(mStsThread, WrapRunnable(RefPtr<MediaPipeline>(this), &MediaPipeline::UpdateTransport_s, - aRtpTransport, - aRtcpTransport, + aTransportId, aFilter), NS_DISPATCH_NORMAL); } void -MediaPipeline::UpdateTransport_s(RefPtr<TransportFlow> aRtpTransport, - RefPtr<TransportFlow> aRtcpTransport, +MediaPipeline::UpdateTransport_s(const std::string& aTransportId, nsAutoPtr<MediaPipelineFilter> aFilter) { - bool rtcp_mux = false; - if (!aRtcpTransport) { - aRtcpTransport = aRtpTransport; - rtcp_mux = true; + ASSERT_ON_THREAD(mStsThread); + if (!mSignalsConnected) { + mTransportHandler->SignalStateChange.connect( + this, &MediaPipeline::RtpStateChange); + mTransportHandler->SignalRtcpStateChange.connect( + this, &MediaPipeline::RtcpStateChange); + mTransportHandler->SignalEncryptedSending.connect( + this, &MediaPipeline::EncryptedPacketSending); + mTransportHandler->SignalPacketReceived.connect( + this, &MediaPipeline::PacketReceived); + mSignalsConnected = true; } - if ((aRtpTransport != mRtp.mTransport) || - (aRtcpTransport != mRtcp.mTransport)) { - disconnect_all(); - mTransport->Detach(); - mRtp.Detach(); - mRtcp.Detach(); - if (aRtpTransport && aRtcpTransport) { - mRtp = TransportInfo(aRtpTransport, rtcp_mux ? MUX : RTP); - mRtcp = TransportInfo(aRtcpTransport, rtcp_mux ? MUX : RTCP); - AttachTransport_s(); - } + if (aTransportId != mTransportId) { + mTransportId = aTransportId; + mRtpState = mTransportHandler->GetState(mTransportId, false); + mRtcpState = mTransportHandler->GetState(mTransportId, true); + CheckTransportStates(); } if (mFilter && aFilter) { // Use the new filter, but don't forget any remote SSRCs that we've learned // by receiving traffic. mFilter->Update(*aFilter); } else { mFilter = aFilter; @@ -785,204 +755,159 @@ MediaPipeline::GetContributingSourceStat RTCRTPContributingSourceStats stats; info.second.GetWebidlInstance(stats, aInboundRtpStreamId); aArr.AppendElement(stats, fallible); } } } void -MediaPipeline::StateChange(TransportLayer* aLayer, TransportLayer::State aState) -{ - TransportInfo* info = GetTransportInfo_s(aLayer); - MOZ_ASSERT(info); - - if (aState == TransportLayer::TS_OPEN) { - MOZ_LOG(gMediaPipelineLog, LogLevel::Info, ("Flow is ready")); - TransportReady_s(*info); - } else if (aState == TransportLayer::TS_CLOSED || - aState == TransportLayer::TS_ERROR) { - TransportFailed_s(*info); - } -} - -static bool -MakeRtpTypeToStringArray(const char** aArray) +MediaPipeline::RtpStateChange(const std::string& aTransportId, + TransportLayer::State aState) { - static const char* RTP_str = "RTP"; - static const char* RTCP_str = "RTCP"; - static const char* MUX_str = "RTP/RTCP mux"; - aArray[MediaPipeline::RTP] = RTP_str; - aArray[MediaPipeline::RTCP] = RTCP_str; - aArray[MediaPipeline::MUX] = MUX_str; - return true; -} - -static const char* -ToString(MediaPipeline::RtpType type) -{ - static const char* array[(int)MediaPipeline::MAX_RTP_TYPE] = { nullptr }; - // Dummy variable to cause init to happen only on first call - static bool dummy = MakeRtpTypeToStringArray(array); - (void)dummy; - return array[type]; + if (mTransportId != aTransportId) { + return; + } + mRtpState = aState; + CheckTransportStates(); } -nsresult -MediaPipeline::TransportReady_s(TransportInfo& aInfo) +void +MediaPipeline::RtcpStateChange(const std::string& aTransportId, + TransportLayer::State aState) { - // TODO(ekr@rtfm.com): implement some kind of notification on - // failure. bug 852665. - if (aInfo.mState != StateType::MP_CONNECTING) { - MOZ_LOG(gMediaPipelineLog, LogLevel::Error, - ("Transport ready for flow in wrong state:%s :%s", - mDescription.c_str(), - ToString(aInfo.mType))); - return NS_ERROR_FAILURE; + if (mTransportId != aTransportId) { + return; } - - MOZ_LOG(gMediaPipelineLog, LogLevel::Info, - ("Transport ready for pipeline %p flow %s: %s", - this, - mDescription.c_str(), - ToString(aInfo.mType))); - - if (mDirection == DirectionType::RECEIVE) { - MOZ_LOG(gMediaPipelineLog, LogLevel::Info, - ("Listening for %s packets received on %p", - ToString(aInfo.mType), - aInfo.mSrtp)); - - aInfo.mSrtp->SignalPacketReceived.connect( - this, &MediaPipeline::PacketReceived); - } - - aInfo.mState = StateType::MP_OPEN; - UpdateRtcpMuxState(aInfo); - return NS_OK; + mRtcpState = aState; + CheckTransportStates(); } -nsresult -MediaPipeline::TransportFailed_s(TransportInfo& aInfo) +void +MediaPipeline::CheckTransportStates() { ASSERT_ON_THREAD(mStsThread); - aInfo.mState = StateType::MP_CLOSED; - UpdateRtcpMuxState(aInfo); - - MOZ_LOG(gMediaPipelineLog, LogLevel::Info, - ("Transport closed for flow %s", ToString(aInfo.mType))); - - NS_WARNING( - "MediaPipeline Transport failed. This is not properly cleaned up yet"); + if (mRtpState == TransportLayer::TS_CLOSED || + mRtpState == TransportLayer::TS_ERROR || + mRtcpState == TransportLayer::TS_CLOSED || + mRtcpState == TransportLayer::TS_ERROR) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Warning, + ("RTP Transport failed for pipeline %p flow %s", + this, + mDescription.c_str())); - // TODO(ekr@rtfm.com): SECURITY: Figure out how to clean up if the - // connection was good and now it is bad. - // TODO(ekr@rtfm.com): Report up so that the PC knows we - // have experienced an error. - - return NS_OK; -} + NS_WARNING( + "MediaPipeline Transport failed. This is not properly cleaned up yet"); + // TODO(ekr@rtfm.com): SECURITY: Figure out how to clean up if the + // connection was good and now it is bad. + // TODO(ekr@rtfm.com): Report up so that the PC knows we + // have experienced an error. + mTransport->Detach(); + return; + } -void -MediaPipeline::UpdateRtcpMuxState(TransportInfo& aInfo) -{ - if (aInfo.mType == MUX) { - if (aInfo.mTransport == mRtcp.mTransport) { - mRtcp.mState = aInfo.mState; - } + if (mRtpState == TransportLayer::TS_OPEN) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("RTP Transport ready for pipeline %p flow %s", + this, + mDescription.c_str())); + } + + if (mRtcpState == TransportLayer::TS_OPEN) { + MOZ_LOG(gMediaPipelineLog, LogLevel::Info, + ("RTCP Transport ready for pipeline %p flow %s", + this, + mDescription.c_str())); + } + + if (mRtpState == TransportLayer::TS_OPEN && mRtcpState == mRtpState) { + mTransport->Attach(this); + TransportReady_s(); } } nsresult -MediaPipeline::SendPacket(TransportLayer* aLayer, MediaPacket& packet) +MediaPipeline::SendPacket(MediaPacket& packet) { ASSERT_ON_THREAD(mStsThread); - - int len = packet.len(); - TransportResult res = aLayer->SendPacket(packet); + MOZ_ASSERT(mRtpState == TransportLayer::TS_OPEN); + MOZ_ASSERT(!mTransportId.empty()); + nsresult rv = mTransportHandler->SendPacket(mTransportId, packet); - if (res != len) { - // Ignore blocking indications - if (res == TE_WOULDBLOCK) - return NS_OK; - + if (NS_FAILED(rv)) { MOZ_LOG(gMediaPipelineLog, LogLevel::Error, ("Failed write on stream %s", mDescription.c_str())); return NS_BASE_STREAM_CLOSED; } return NS_OK; } void MediaPipeline::IncrementRtpPacketsSent(int32_t aBytes) { ++mRtpPacketsSent; mRtpBytesSent += aBytes; if (!(mRtpPacketsSent % 100)) { MOZ_LOG(gMediaPipelineLog, LogLevel::Info, - ("RTP sent packet count for %s Pipeline %p Flow: %p: %u (%" PRId64 + ("RTP sent packet count for %s Pipeline %p: %u (%" PRId64 " bytes)", mDescription.c_str(), this, - static_cast<void*>(mRtp.mTransport), mRtpPacketsSent, mRtpBytesSent)); } } void MediaPipeline::IncrementRtcpPacketsSent() { ++mRtcpPacketsSent; if (!(mRtcpPacketsSent % 100)) { MOZ_LOG(gMediaPipelineLog, LogLevel::Info, - ("RTCP sent packet count for %s Pipeline %p Flow: %p: %u", + ("RTCP sent packet count for %s Pipeline %p: %u", mDescription.c_str(), this, - static_cast<void*>(mRtp.mTransport), mRtcpPacketsSent)); } } void MediaPipeline::IncrementRtpPacketsReceived(int32_t aBytes) { ++mRtpPacketsReceived; mRtpBytesReceived += aBytes; if (!(mRtpPacketsReceived % 100)) { MOZ_LOG(gMediaPipelineLog, LogLevel::Info, - ("RTP received packet count for %s Pipeline %p Flow: %p: %u (%" + ("RTP received packet count for %s Pipeline %p: %u (%" PRId64 " bytes)", mDescription.c_str(), this, - static_cast<void*>(mRtp.mTransport), mRtpPacketsReceived, mRtpBytesReceived)); } } void MediaPipeline::IncrementRtcpPacketsReceived() { ++mRtcpPacketsReceived; if (!(mRtcpPacketsReceived % 100)) { MOZ_LOG(gMediaPipelineLog, LogLevel::Info, - ("RTCP received packet count for %s Pipeline %p Flow: %p: %u", + ("RTCP received packet count for %s Pipeline %p: %u", mDescription.c_str(), this, - static_cast<void*>(mRtp.mTransport), mRtcpPacketsReceived)); } } void -MediaPipeline::RtpPacketReceived(TransportLayer* aLayer, MediaPacket& packet) +MediaPipeline::RtpPacketReceived(MediaPacket& packet) { if (mDirection == DirectionType::TRANSMIT) { return; } if (!mTransport->Pipeline()) { MOZ_LOG(gMediaPipelineLog, LogLevel::Error, ("Discarding incoming packet; transport disconnected")); @@ -990,28 +915,16 @@ MediaPipeline::RtpPacketReceived(Transpo } if (!mConduit) { MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, ("Discarding incoming packet; media disconnected")); return; } - if (mRtp.mState != StateType::MP_OPEN) { - MOZ_LOG(gMediaPipelineLog, LogLevel::Error, - ("Discarding incoming packet; pipeline not open")); - return; - } - - if (mRtp.mSrtp->state() != TransportLayer::TS_OPEN) { - MOZ_LOG(gMediaPipelineLog, LogLevel::Error, - ("Discarding incoming packet; transport not open")); - return; - } - if (!packet.len()) { return; } webrtc::RTPHeader header; if (!mRtpParser->Parse(packet.data(), packet.len(), &header, true)) { return; } @@ -1073,42 +986,30 @@ MediaPipeline::RtpPacketReceived(Transpo mPacketDumper->Dump( mLevel, dom::mozPacketDumpType::Rtp, false, packet.data(), packet.len()); (void)mConduit->ReceivedRTPPacket( packet.data(), packet.len(), header.ssrc); // Ignore error codes } void -MediaPipeline::RtcpPacketReceived(TransportLayer* aLayer, MediaPacket& packet) +MediaPipeline::RtcpPacketReceived(MediaPacket& packet) { if (!mTransport->Pipeline()) { MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, ("Discarding incoming packet; transport disconnected")); return; } if (!mConduit) { MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, ("Discarding incoming packet; media disconnected")); return; } - if (mRtcp.mState != StateType::MP_OPEN) { - MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, - ("Discarding incoming packet; pipeline not open")); - return; - } - - if (mRtcp.mSrtp->state() != TransportLayer::TS_OPEN) { - MOZ_LOG(gMediaPipelineLog, LogLevel::Error, - ("Discarding incoming packet; transport not open")); - return; - } - if (!packet.len()) { return; } // We do not filter RTCP. This is because a compound RTCP packet can contain // any collection of RTCP packets, and webrtc.org already knows how to filter // out what it is interested in, and what it is not. Maybe someday we should // have a TransportLayer that breaks up compound RTCP so we can filter them @@ -1126,33 +1027,59 @@ MediaPipeline::RtcpPacketReceived(Transp mLevel, dom::mozPacketDumpType::Srtcp, false, packet.encrypted_data(), packet.encrypted_len()); mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Rtcp, false, packet.data(), packet.len()); (void)mConduit->ReceivedRTCPPacket(packet.data(), packet.len()); // Ignore error codes } void -MediaPipeline::PacketReceived(TransportLayer* aLayer, MediaPacket& packet) +MediaPipeline::PacketReceived(const std::string& aTransportId, + MediaPacket& packet) { + if (mTransportId != aTransportId) { + return; + } + if (!mTransport->Pipeline()) { MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, ("Discarding incoming packet; transport disconnected")); return; } switch (packet.type()) { case MediaPacket::RTP: - RtpPacketReceived(aLayer, packet); + RtpPacketReceived(packet); break; case MediaPacket::RTCP: - RtcpPacketReceived(aLayer, packet); + RtcpPacketReceived(packet); break; default: - MOZ_CRASH("TransportLayerSrtp let something other than RTP/RTCP through"); + ; + } +} + +void +MediaPipeline::EncryptedPacketSending(const std::string& aTransportId, + MediaPacket& aPacket) +{ + if (mTransportId == aTransportId) { + dom::mozPacketDumpType type; + if (aPacket.type() == MediaPacket::SRTP) { + type = dom::mozPacketDumpType::Srtp; + } else if (aPacket.type() == MediaPacket::SRTCP) { + type = dom::mozPacketDumpType::Srtcp; + } else if (aPacket.type() == MediaPacket::DTLS) { + // TODO(bug 1497936): Implement packet dump for DTLS + return; + } else { + MOZ_ASSERT(false); + return; + } + mPacketDumper->Dump(Level(), type, true, aPacket.data(), aPacket.len()); } } class MediaPipelineTransmit::PipelineListener : public MediaStreamVideoSink { friend class MediaPipelineTransmit; public: @@ -1268,21 +1195,23 @@ protected: virtual ~VideoFrameFeeder() { MOZ_COUNT_DTOR(VideoFrameFeeder); } Mutex mMutex; // Protects the member below. RefPtr<PipelineListener> mListener; }; MediaPipelineTransmit::MediaPipelineTransmit( const std::string& aPc, + MediaTransportBase* aTransportHandler, nsCOMPtr<nsIEventTarget> aMainThread, nsCOMPtr<nsIEventTarget> aStsThread, bool aIsVideo, RefPtr<MediaSessionConduit> aConduit) : MediaPipeline(aPc, + aTransportHandler, DirectionType::TRANSMIT, aMainThread, aStsThread, aConduit) , mIsVideo(aIsVideo) , mListener(new PipelineListener(aConduit)) , mFeeder(aIsVideo ? MakeAndAddRef<VideoFrameFeeder>(mListener) : nullptr) // For video we send frames to an @@ -1463,29 +1392,23 @@ MediaPipelineTransmit::UpdateSinkIdentit void MediaPipelineTransmit::DetachMedia() { ASSERT_ON_THREAD(mMainThread); mDomTrack = nullptr; // Let the listener be destroyed with the pipeline (or later). } -nsresult -MediaPipelineTransmit::TransportReady_s(TransportInfo& aInfo) +void +MediaPipelineTransmit::TransportReady_s() { ASSERT_ON_THREAD(mStsThread); // Call base ready function. - MediaPipeline::TransportReady_s(aInfo); - - // Should not be set for a transmitter - if (&aInfo == &mRtp) { - mListener->SetActive(true); - } - - return NS_OK; + MediaPipeline::TransportReady_s(); + mListener->SetActive(true); } nsresult MediaPipelineTransmit::SetTrack(MediaStreamTrack* aDomTrack) { // MainThread, checked in calls we make if (aDomTrack) { nsString nsTrackId; @@ -1506,61 +1429,16 @@ MediaPipelineTransmit::SetTrack(MediaStr if (wasTransmitting) { Start(); } return NS_OK; } nsresult -MediaPipeline::ConnectTransport_s(TransportInfo& aInfo) -{ - MOZ_ASSERT(aInfo.mTransport); - MOZ_ASSERT(aInfo.mSrtp); - ASSERT_ON_THREAD(mStsThread); - - // Look to see if the transport is ready - if (aInfo.mSrtp->state() == TransportLayer::TS_OPEN) { - nsresult res = TransportReady_s(aInfo); - if (NS_FAILED(res)) { - MOZ_LOG(gMediaPipelineLog, LogLevel::Error, - ("Error calling TransportReady(); res=%u in %s", - static_cast<uint32_t>(res), - __FUNCTION__)); - return res; - } - } else if (aInfo.mSrtp->state() == TransportLayer::TS_ERROR) { - MOZ_LOG(gMediaPipelineLog, LogLevel::Error, - ("%s transport is already in error state", - ToString(aInfo.mType))); - TransportFailed_s(aInfo); - return NS_ERROR_FAILURE; - } - - aInfo.mSrtp->SignalStateChange.connect(this, &MediaPipeline::StateChange); - - return NS_OK; -} - -MediaPipeline::TransportInfo* -MediaPipeline::GetTransportInfo_s(TransportLayer* aLayer) -{ - ASSERT_ON_THREAD(mStsThread); - if (aLayer == mRtp.mSrtp) { - return &mRtp; - } - - if (aLayer == mRtcp.mSrtp) { - return &mRtcp; - } - - return nullptr; -} - -nsresult MediaPipeline::PipelineTransport::SendRtpPacket(const uint8_t* aData, size_t aLen) { nsAutoPtr<MediaPacket> packet(new MediaPacket); packet->Copy(aData, aLen, aLen + SRTP_MAX_EXPANSION); packet->SetType(MediaPacket::RTP); RUN_ON_THREAD( mStsThread, @@ -1578,25 +1456,23 @@ MediaPipeline::PipelineTransport::SendRt { bool isRtp = aPacket->type() == MediaPacket::RTP; ASSERT_ON_THREAD(mStsThread); if (!mPipeline) { return NS_OK; // Detached } - TransportInfo& transport = isRtp ? mPipeline->mRtp : mPipeline->mRtcp; - - if (transport.mSrtp->state() != TransportLayer::TS_OPEN) { - // SRTP not ready yet. + if (isRtp && mPipeline->mRtpState != TransportLayer::TS_OPEN) { return NS_OK; } - MOZ_ASSERT(transport.mTransport); - NS_ENSURE_TRUE(transport.mTransport, NS_ERROR_NULL_POINTER); + if (!isRtp && mPipeline->mRtcpState != TransportLayer::TS_OPEN) { + return NS_OK; + } MediaPacket packet(std::move(*aPacket)); packet.sdp_level() = Some(mPipeline->Level()); if (RtpLogger::IsPacketLoggingOn()) { RtpLogger::LogPacket(packet, false, mPipeline->mDescription); } @@ -1616,17 +1492,17 @@ MediaPipeline::PipelineTransport::SendRt mPipeline->IncrementRtcpPacketsSent(); } MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, ("%s sending %s packet", mPipeline->mDescription.c_str(), (isRtp ? "RTP" : "RTCP"))); - return mPipeline->SendPacket(transport.mSrtp, packet); + return mPipeline->SendPacket(packet); } nsresult MediaPipeline::PipelineTransport::SendRtcpPacket(const uint8_t* aData, size_t aLen) { nsAutoPtr<MediaPacket> packet(new MediaPacket); packet->Copy(aData, aLen, aLen + SRTP_MAX_EXPANSION); @@ -1893,21 +1769,24 @@ protected: const TrackID mTrackId; const RefPtr<SourceMediaStream> mSource; TrackTicks mPlayedTicks; PrincipalHandle mPrincipalHandle; bool mListening; Atomic<bool> mMaybeTrackNeedsUnmute; }; -MediaPipelineReceive::MediaPipelineReceive(const std::string& aPc, - nsCOMPtr<nsIEventTarget> aMainThread, - nsCOMPtr<nsIEventTarget> aStsThread, - RefPtr<MediaSessionConduit> aConduit) +MediaPipelineReceive::MediaPipelineReceive( + const std::string& aPc, + MediaTransportBase* aTransportHandler, + nsCOMPtr<nsIEventTarget> aMainThread, + nsCOMPtr<nsIEventTarget> aStsThread, + RefPtr<MediaSessionConduit> aConduit) : MediaPipeline(aPc, + aTransportHandler, DirectionType::RECEIVE, aMainThread, aStsThread, aConduit) { } MediaPipelineReceive::~MediaPipelineReceive() {} @@ -2043,21 +1922,26 @@ private: // 48kHz, mRate is capped to 48kHz. If mRate does not match the graph rate, // audio is resampled to the graph rate. const TrackRate mRate; const RefPtr<TaskQueue> mTaskQueue; }; MediaPipelineReceiveAudio::MediaPipelineReceiveAudio( const std::string& aPc, + MediaTransportBase* aTransportHandler, nsCOMPtr<nsIEventTarget> aMainThread, nsCOMPtr<nsIEventTarget> aStsThread, RefPtr<AudioSessionConduit> aConduit, dom::MediaStreamTrack* aTrack) - : MediaPipelineReceive(aPc, aMainThread, aStsThread, aConduit) + : MediaPipelineReceive(aPc, + aTransportHandler, + aMainThread, + aStsThread, + aConduit) , mListener(aTrack ? new PipelineListener(aTrack, mConduit) : nullptr) { mDescription = mPc + "| Receive audio"; } void MediaPipelineReceiveAudio::DetachMedia() { @@ -2231,21 +2115,26 @@ public: } private: MediaPipelineReceiveVideo* mPipeline; // Raw pointer to avoid cycles }; MediaPipelineReceiveVideo::MediaPipelineReceiveVideo( const std::string& aPc, + MediaTransportBase* aTransportHandler, nsCOMPtr<nsIEventTarget> aMainThread, nsCOMPtr<nsIEventTarget> aStsThread, RefPtr<VideoSessionConduit> aConduit, dom::MediaStreamTrack* aTrack) - : MediaPipelineReceive(aPc, aMainThread, aStsThread, aConduit) + : MediaPipelineReceive(aPc, + aTransportHandler, + aMainThread, + aStsThread, + aConduit) , mRenderer(new PipelineRenderer(this)) , mListener(aTrack ? new PipelineListener(aTrack) : nullptr) { mDescription = mPc + "| Receive video"; aConduit->AttachRenderer(mRenderer); } void
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h @@ -6,38 +6,39 @@ // Original author: ekr@rtfm.com #ifndef mediapipeline_h__ #define mediapipeline_h__ #include <map> #include "sigslot.h" +#include "transportlayer.h" // For TransportLayer::State #include "signaling/src/media-conduit/MediaConduitInterface.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/Atomics.h" -#include "SrtpFlow.h" +#include "SrtpFlow.h" // For SRTP_MAX_EXPANSION #include "mediapacket.h" #include "mtransport/runnable_utils.h" -#include "mtransport/transportflow.h" #include "AudioPacketizer.h" #include "StreamTracks.h" #include "signaling/src/peerconnection/PacketDumper.h" #include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h" // Should come from MediaEngine.h, but that's a pain to include here // because of the MOZILLA_EXTERNAL_LINKAGE stuff. #define WEBRTC_MAX_SAMPLE_RATE 48000 class nsIPrincipal; namespace mozilla { class MediaPipelineFilter; +class MediaTransportBase; class PeerIdentity; class AudioProxyThread; class VideoFrameConverter; namespace dom { class MediaStreamTrack; struct RTCRTPContributingSourceStats; } // namespace dom @@ -78,60 +79,51 @@ class SourceMediaStream; class MediaPipeline : public sigslot::has_slots<> { public: enum class DirectionType { TRANSMIT, RECEIVE }; - enum class StateType - { - MP_CONNECTING, - MP_OPEN, - MP_CLOSED - }; MediaPipeline(const std::string& aPc, + MediaTransportBase* aTransportHandler, DirectionType aDirection, nsCOMPtr<nsIEventTarget> aMainThread, nsCOMPtr<nsIEventTarget> aStsThread, RefPtr<MediaSessionConduit> aConduit); virtual void Start() = 0; virtual void Stop() = 0; virtual void DetachMedia() {} void SetLevel(size_t aLevel) { mLevel = aLevel; } // Must be called on the main thread. void Shutdown_m(); - void UpdateTransport_m(RefPtr<TransportFlow> aRtpTransport, - RefPtr<TransportFlow> aRtcpTransport, + void UpdateTransport_m(const std::string& aTransportId, nsAutoPtr<MediaPipelineFilter> aFilter); - void UpdateTransport_s(RefPtr<TransportFlow> aRtpTransport, - RefPtr<TransportFlow> aRtcpTransport, + void UpdateTransport_s(const std::string& aTransportId, nsAutoPtr<MediaPipelineFilter> aFilter); // Used only for testing; adds RTP header extension for RTP Stream Id with // the given id. void AddRIDExtension_m(size_t aExtensionId); void AddRIDExtension_s(size_t aExtensionId); // Used only for testing; installs a MediaPipelineFilter that filters // everything but the given RID void AddRIDFilter_m(const std::string& aRid); void AddRIDFilter_s(const std::string& aRid); virtual DirectionType Direction() const { return mDirection; } int Level() const { return mLevel; } virtual bool IsVideo() const = 0; - bool IsDoingRtcpMux() const { return mRtp.mType == MUX; } - class RtpCSRCStats { public: // Gets an expiration time for CRC info given a reference time, // this reference time would normally be the time of calling. // This value can then be used to check if a RtpCSRCStats // has expired via Expired(...) static DOMHighResTimeStamp GetExpiryFromTime( @@ -173,18 +165,16 @@ public: int64_t RtpBytesReceived() const { return mRtpBytesReceived; } int32_t RtcpPacketsReceived() const { return mRtcpPacketsReceived; } MediaSessionConduit* Conduit() const { return mConduit; } // Thread counting NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPipeline) - typedef enum { RTP, RTCP, MUX, MAX_RTP_TYPE } RtpType; - // Separate class to allow ref counting class PipelineTransport : public TransportInterface { public: // Implement the TransportInterface functions explicit PipelineTransport(nsIEventTarget* aStsThread) : mPipeline(nullptr) , mStsThread(aStsThread) @@ -203,87 +193,63 @@ public: // Creates a cycle, which we break with Detach RefPtr<MediaPipeline> mPipeline; const nsCOMPtr<nsIEventTarget> mStsThread; }; protected: virtual ~MediaPipeline(); - nsresult AttachTransport_s(); friend class PipelineTransport; - struct TransportInfo - { - TransportInfo(RefPtr<TransportFlow> aFlow, RtpType aType) - : mTransport(aFlow) - , mSrtp(mTransport ? mTransport->GetLayer("srtp") : nullptr) - , mState(StateType::MP_CONNECTING) - , mType(aType) - { - } - - void Detach() - { - mTransport = nullptr; - mSrtp = nullptr; - } - - RefPtr<TransportFlow> mTransport; - TransportLayer* mSrtp; - StateType mState; - RtpType mType; - }; - - // The transport is down - virtual nsresult TransportFailed_s(TransportInfo& aInfo); // The transport is ready - virtual nsresult TransportReady_s(TransportInfo& aInfo); - void UpdateRtcpMuxState(TransportInfo& aInfo); - - nsresult ConnectTransport_s(TransportInfo& aInfo); - - TransportInfo* GetTransportInfo_s(TransportLayer* aLayer); + virtual void TransportReady_s() {} void IncrementRtpPacketsSent(int aBytes); void IncrementRtcpPacketsSent(); void IncrementRtpPacketsReceived(int aBytes); virtual void OnRtpPacketReceived() {}; void IncrementRtcpPacketsReceived(); - virtual nsresult SendPacket(TransportLayer* aLayer, - MediaPacket& packet); + virtual nsresult SendPacket(MediaPacket& packet); // Process slots on transports - void StateChange(TransportLayer* aLayer, TransportLayer::State); - void RtpPacketReceived(TransportLayer* aLayer, MediaPacket& packet); - void RtcpPacketReceived(TransportLayer* aLayer, MediaPacket& packet); - void PacketReceived(TransportLayer* aLayer, MediaPacket& packet); + void RtpStateChange(const std::string& aTransportId, TransportLayer::State); + void RtcpStateChange(const std::string& aTransportId, TransportLayer::State); + virtual void CheckTransportStates(); + void PacketReceived(const std::string& aTransportId, MediaPacket& packet); + + void RtpPacketReceived(MediaPacket& packet); + void RtcpPacketReceived(MediaPacket& packet); + + void EncryptedPacketSending(const std::string& aTransportId, + MediaPacket& aPacket); void SetDescription_s(const std::string& description); const DirectionType mDirection; size_t mLevel; + std::string mTransportId; + RefPtr<MediaTransportBase> mTransportHandler; RefPtr<MediaSessionConduit> mConduit; // Our conduit. Written on the main // thread. Read on STS thread. - // The transport objects. Read/written on STS thread. - TransportInfo mRtp; - TransportInfo mRtcp; + TransportLayer::State mRtpState = TransportLayer::TS_NONE; + TransportLayer::State mRtcpState = TransportLayer::TS_NONE; + bool mSignalsConnected = false; // Pointers to the threads we need. Initialized at creation // and used all over the place. const nsCOMPtr<nsIEventTarget> mMainThread; const nsCOMPtr<nsIEventTarget> mStsThread; // Created in c'tor. Referenced by the conduit. RefPtr<PipelineTransport> mTransport; // Only safe to access from STS thread. - // Build into TransportInfo? int32_t mRtpPacketsSent; int32_t mRtcpPacketsSent; int32_t mRtpPacketsReceived; int32_t mRtcpPacketsReceived; int64_t mRtpBytesSent; int64_t mRtpBytesReceived; // Only safe to access from STS thread. @@ -310,16 +276,17 @@ private: // A specialization of pipeline for reading from an input device // and transmitting to the network. class MediaPipelineTransmit : public MediaPipeline { public: // Set aRtcpTransport to nullptr to use rtcp-mux MediaPipelineTransmit(const std::string& aPc, + MediaTransportBase* aTransportHandler, nsCOMPtr<nsIEventTarget> aMainThread, nsCOMPtr<nsIEventTarget> aStsThread, bool aIsVideo, RefPtr<MediaSessionConduit> aConduit); bool Transmitting() const; void Start() override; @@ -333,18 +300,18 @@ public: // `track` has to be null or equal `mDomTrack` for us to apply the update. virtual void UpdateSinkIdentity_m(const dom::MediaStreamTrack* aTrack, nsIPrincipal* aPrincipal, const PeerIdentity* aSinkIdentity); // Called on the main thread. void DetachMedia() override; - // Override MediaPipeline::TransportReady. - nsresult TransportReady_s(TransportInfo& aInfo) override; + // Override MediaPipeline::TransportReady_s. + void TransportReady_s() override; // Replace a track with a different one // In non-compliance with the likely final spec, allow the new // track to be part of a different stream (since we don't support // multiple tracks of a type in a stream yet). bug 1056650 virtual nsresult SetTrack(dom::MediaStreamTrack* aDomTrack); // Separate classes to allow ref counting @@ -368,16 +335,17 @@ private: // A specialization of pipeline for reading from the network and // rendering media. class MediaPipelineReceive : public MediaPipeline { public: // Set aRtcpTransport to nullptr to use rtcp-mux MediaPipelineReceive(const std::string& aPc, + MediaTransportBase* aTransportHandler, nsCOMPtr<nsIEventTarget> aMainThread, nsCOMPtr<nsIEventTarget> aStsThread, RefPtr<MediaSessionConduit> aConduit); // Sets the PrincipalHandle we set on the media chunks produced by this // pipeline. Must be called on the main thread. virtual void SetPrincipalHandle_m( const PrincipalHandle& aPrincipalHandle) = 0; @@ -387,16 +355,17 @@ protected: }; // A specialization of pipeline for reading from the network and // rendering audio. class MediaPipelineReceiveAudio : public MediaPipelineReceive { public: MediaPipelineReceiveAudio(const std::string& aPc, + MediaTransportBase* aTransportHandler, nsCOMPtr<nsIEventTarget> aMainThread, nsCOMPtr<nsIEventTarget> aStsThread, RefPtr<AudioSessionConduit> aConduit, dom::MediaStreamTrack* aTrack); void DetachMedia() override; bool IsVideo() const override { return false; } @@ -416,16 +385,17 @@ private: }; // A specialization of pipeline for reading from the network and // rendering video. class MediaPipelineReceiveVideo : public MediaPipelineReceive { public: MediaPipelineReceiveVideo(const std::string& aPc, + MediaTransportBase* aTransportHandler, nsCOMPtr<nsIEventTarget> aMainThread, nsCOMPtr<nsIEventTarget> aStsThread, RefPtr<VideoSessionConduit> aConduit, dom::MediaStreamTrack* aTrack); // Called on the main thread. void DetachMedia() override;
deleted file mode 100644 --- a/media/webrtc/signaling/src/mediapipeline/TransportLayerPacketDumper.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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/. */ - -// Original author: ekr@rtfm.com - -#include "TransportLayerPacketDumper.h" - -#include "logging.h" -#include "nsError.h" -#include "mozilla/Assertions.h" - -namespace mozilla { - -MOZ_MTLOG_MODULE("mtransport") - -TransportLayerPacketDumper::TransportLayerPacketDumper( - nsAutoPtr<PacketDumper>&& aPacketDumper, dom::mozPacketDumpType aType) : - mPacketDumper(std::move(aPacketDumper)), - mType(aType) -{} - -void -TransportLayerPacketDumper::WasInserted() -{ - CheckThread(); - if (!downward_) { - MOZ_MTLOG(ML_ERROR, "Packet dumper with nothing below. This is useless"); - TL_SET_STATE(TS_ERROR); - } - - downward_->SignalStateChange.connect(this, - &TransportLayerPacketDumper::StateChange); - downward_->SignalPacketReceived.connect(this, - &TransportLayerPacketDumper::PacketReceived); -} - -TransportResult -TransportLayerPacketDumper::SendPacket(MediaPacket& packet) -{ - if (packet.sdp_level().isSome()) { - dom::mozPacketDumpType dumpType = mType; - if (mType == dom::mozPacketDumpType::Srtp && - packet.type() == MediaPacket::RTCP) { - dumpType = dom::mozPacketDumpType::Srtcp; - } - - mPacketDumper->Dump(*packet.sdp_level(), - dumpType, - true, - packet.data(), - packet.len()); - } - return downward_->SendPacket(packet); -} - -void -TransportLayerPacketDumper::StateChange(TransportLayer* aLayer, State aState) -{ - TL_SET_STATE(aState); -} - -void -TransportLayerPacketDumper::PacketReceived(TransportLayer* aLayer, - MediaPacket& packet) -{ - // There's no way to know the level yet, so we can't use the packet dumper - // yet. We rely on the SRTP layer saving the encrypted packet in - // MediaPacket::encrypted_, to allow MediaPipeline to dump it later. - SignalPacketReceived(this, packet); -} - -} // namespace mozilla - -
deleted file mode 100644 --- a/media/webrtc/signaling/src/mediapipeline/TransportLayerPacketDumper.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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 transportlayerpacketdumper_h__ -#define transportlayerpacketdumper_h__ - -#include "transportlayer.h" -#include "signaling/src/peerconnection/PacketDumper.h" -#include "mozilla/dom/RTCPeerConnectionBinding.h" - -namespace mozilla { - -class TransportLayerPacketDumper final : public TransportLayer { - public: - explicit TransportLayerPacketDumper(nsAutoPtr<PacketDumper>&& aPacketDumper, - dom::mozPacketDumpType aType); - virtual ~TransportLayerPacketDumper() {}; - - // Transport layer overrides. - void WasInserted() override; - TransportResult SendPacket(MediaPacket& packet) override; - - // Signals - void StateChange(TransportLayer *aLayer, State state); - void PacketReceived(TransportLayer* aLayer, MediaPacket& packet); - - TRANSPORT_LAYER_ID("packet-dumper") - - private: - DISALLOW_COPY_ASSIGN(TransportLayerPacketDumper); - nsAutoPtr<PacketDumper> mPacketDumper; - dom::mozPacketDumpType mType; -}; - - -} // close namespace -#endif
--- a/media/webrtc/signaling/src/mediapipeline/moz.build +++ b/media/webrtc/signaling/src/mediapipeline/moz.build @@ -17,14 +17,13 @@ LOCAL_INCLUDES += [ '/netwerk/srtp/src/crypto/include', '/netwerk/srtp/src/include', ] UNIFIED_SOURCES += [ 'MediaPipeline.cpp', 'MediaPipelineFilter.cpp', 'RtpLogger.cpp', - 'TransportLayerPacketDumper.cpp', ] DEFINES['TRACING'] = True FINAL_LIBRARY = 'xul'
new file mode 100644 --- /dev/null +++ b/media/webrtc/signaling/src/peerconnection/MediaTransportHandler.cpp @@ -0,0 +1,906 @@ +/* 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 "MediaTransportHandler.h" +#include "nricemediastream.h" +#include "nriceresolver.h" +#include "transportflow.h" +#include "transportlayerice.h" +#include "transportlayerdtls.h" +#include "transportlayersrtp.h" + +// Config stuff +#include "nsIPrefService.h" +#include "mozilla/dom/RTCConfigurationBinding.h" + +// Parsing STUN/TURN URIs +#include "nsIURI.h" +#include "nsNetUtil.h" +#include "nsURLHelper.h" +#include "nsIURLParser.h" + +// Logging stuff +#include "CSFLog.h" + +// DTLS +#include "signaling/src/sdp/SdpAttribute.h" + +#include "mozilla/Telemetry.h" + +#include "mozilla/dom/RTCStatsReportBinding.h" + +#include <string> +#include <vector> + +namespace mozilla { + +static const char* mthLogTag = "MediaTransportHandler"; +#ifdef LOGTAG +#undef LOGTAG +#endif +#define LOGTAG mthLogTag + +MediaTransportHandler::MediaTransportHandler() +{} + +MediaTransportHandler::~MediaTransportHandler() +{} + +static NrIceCtx::Policy toNrIcePolicy(dom::RTCIceTransportPolicy aPolicy) +{ + switch (aPolicy) { + case dom::RTCIceTransportPolicy::Relay: + return NrIceCtx::ICE_POLICY_RELAY; + case dom::RTCIceTransportPolicy::All: + if (Preferences::GetBool("media.peerconnection.ice.no_host", false)) { + return NrIceCtx::ICE_POLICY_NO_HOST; + } else { + return NrIceCtx::ICE_POLICY_ALL; + } + default: + MOZ_CRASH(); + } + return NrIceCtx::ICE_POLICY_ALL; +} + +static nsresult addNrIceServer(const nsString& aIceUrl, + const dom::RTCIceServer& aIceServer, + std::vector<NrIceStunServer>* aStunServersOut, + std::vector<NrIceTurnServer>* aTurnServersOut) +{ + // Without STUN/TURN handlers, NS_NewURI returns nsSimpleURI rather than + // nsStandardURL. To parse STUN/TURN URI's to spec + // http://tools.ietf.org/html/draft-nandakumar-rtcweb-stun-uri-02#section-3 + // http://tools.ietf.org/html/draft-petithuguenin-behave-turn-uri-03#section-3 + // we parse out the query-string, and use ParseAuthority() on the rest + RefPtr<nsIURI> url; + nsresult rv = NS_NewURI(getter_AddRefs(url), aIceUrl); + NS_ENSURE_SUCCESS(rv, rv); + bool isStun = false, isStuns = false, isTurn = false, isTurns = false; + url->SchemeIs("stun", &isStun); + url->SchemeIs("stuns", &isStuns); + url->SchemeIs("turn", &isTurn); + url->SchemeIs("turns", &isTurns); + if (!(isStun || isStuns || isTurn || isTurns)) { + return NS_ERROR_FAILURE; + } + if (isStuns) { + return NS_OK; // TODO: Support STUNS (Bug 1056934) + } + + nsAutoCString spec; + rv = url->GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + + // TODO(jib@mozilla.com): Revisit once nsURI supports STUN/TURN (Bug 833509) + int32_t port; + nsAutoCString host; + nsAutoCString transport; + { + uint32_t hostPos; + int32_t hostLen; + nsAutoCString path; + rv = url->GetPathQueryRef(path); + NS_ENSURE_SUCCESS(rv, rv); + + // Tolerate query-string + parse 'transport=[udp|tcp]' by hand. + int32_t questionmark = path.FindChar('?'); + if (questionmark >= 0) { + const nsCString match = NS_LITERAL_CSTRING("transport="); + + for (int32_t i = questionmark, endPos; i >= 0; i = endPos) { + endPos = path.FindCharInSet("&", i + 1); + const nsDependentCSubstring fieldvaluepair = Substring(path, i + 1, + endPos); + if (StringBeginsWith(fieldvaluepair, match)) { + transport = Substring(fieldvaluepair, match.Length()); + ToLowerCase(transport); + } + } + path.SetLength(questionmark); + } + + rv = net_GetAuthURLParser()->ParseAuthority(path.get(), path.Length(), + nullptr, nullptr, + nullptr, nullptr, + &hostPos, &hostLen, &port); + NS_ENSURE_SUCCESS(rv, rv); + if (!hostLen) { + return NS_ERROR_FAILURE; + } + if (hostPos > 1) /* The username was removed */ + return NS_ERROR_FAILURE; + path.Mid(host, hostPos, hostLen); + } + if (port == -1) + port = (isStuns || isTurns)? 5349 : 3478; + + if (isStuns || isTurns) { + // Should we barf if transport is set to udp or something? + transport = kNrIceTransportTls; + } + + if (transport.IsEmpty()) { + transport = kNrIceTransportUdp; + } + + if (isTurn || isTurns) { + std::string pwd(NS_ConvertUTF16toUTF8(aIceServer.mCredential.Value()).get()); + std::string username(NS_ConvertUTF16toUTF8(aIceServer.mUsername.Value()).get()); + + std::vector<unsigned char> password(pwd.begin(), pwd.end()); + + UniquePtr<NrIceTurnServer> server(NrIceTurnServer::Create(host.get(), port, username, password, transport.get())); + if (!server) { + return NS_ERROR_FAILURE; + } + aTurnServersOut->emplace_back(std::move(*server)); + } else { + UniquePtr<NrIceStunServer> server(NrIceStunServer::Create(host.get(), port, transport.get())); + if (!server) { + return NS_ERROR_FAILURE; + } + aStunServersOut->emplace_back(std::move(*server)); + } + return NS_OK; +} + +nsresult +MediaTransportHandler::Init(const std::string& aName, + const dom::RTCConfiguration& aConfiguration) +{ + bool allowIceLoopback = Preferences::GetBool( + "media.peerconnection.ice.loopback", false); + bool iceTcp = Preferences::GetBool("media.peerconnection.ice.tcp", false); + bool allowIceLinkLocal = Preferences::GetBool( + "media.peerconnection.ice.link_local", false); + + NrIceCtx::InitializeGlobals(allowIceLoopback, iceTcp, allowIceLinkLocal); + + bool allowLoopback = Preferences::GetBool( + "media.peerconnection.ice.loopback", false); + bool tcpEnabled = Preferences::GetBool( + "media.peerconnection.ice.tcp", false); + bool allowLinkLocal = Preferences::GetBool( + "media.peerconnection.ice.link_local", false); + + mIceCtx = NrIceCtx::Create(aName, allowLoopback, tcpEnabled, allowLinkLocal, + toNrIcePolicy(aConfiguration.mIceTransportPolicy)); + if (!mIceCtx) { + return NS_ERROR_FAILURE; + } + + mProxyOnly = Preferences::GetBool( + "media.peerconnection.ice.proxy_only", false); + + mIceCtx->SignalGatheringStateChange.connect( + this, &MediaTransportHandler::OnGatheringStateChange); + mIceCtx->SignalConnectionStateChange.connect( + this, &MediaTransportHandler::OnConnectionStateChange); + + std::vector<NrIceStunServer> stunServers; + std::vector<NrIceTurnServer> turnServers; + + nsresult rv; + if (aConfiguration.mIceServers.WasPassed()) { + for (const auto& iceServer : aConfiguration.mIceServers.Value()) { + NS_ENSURE_STATE(iceServer.mUrls.WasPassed()); + NS_ENSURE_STATE(iceServer.mUrls.Value().IsStringSequence()); + for (const auto& iceUrl : iceServer.mUrls.Value().GetAsStringSequence()) { + rv = addNrIceServer(iceUrl, iceServer, &stunServers, &turnServers); + if (NS_FAILED(rv)) { + CSFLogError(LOGTAG, "%s: invalid STUN/TURN server: %s", + __FUNCTION__, NS_ConvertUTF16toUTF8(iceUrl).get()); + return rv; + } + } + } + } + + if (NS_FAILED(rv = mIceCtx->SetStunServers(stunServers))) { + CSFLogError(LOGTAG, "%s: Failed to set stun servers", __FUNCTION__); + return rv; + } + // Give us a way to globally turn off TURN support + bool disabled = Preferences::GetBool("media.peerconnection.turn.disable", false); + if (!disabled) { + if (NS_FAILED(rv = mIceCtx->SetTurnServers(turnServers))) { + CSFLogError(LOGTAG, "%s: Failed to set turn servers", __FUNCTION__); + return rv; + } + } else if (!turnServers.empty()) { + CSFLogError(LOGTAG, "%s: Setting turn servers disabled", __FUNCTION__); + } + + mDNSResolver = new NrIceResolver; + if (NS_FAILED(rv = mDNSResolver->Init())) { + CSFLogError(LOGTAG, "%s: Failed to initialize dns resolver", __FUNCTION__); + return rv; + } + if (NS_FAILED(rv = mIceCtx->SetResolver(mDNSResolver->AllocateResolver()))) { + CSFLogError(LOGTAG, "%s: Failed to get dns resolver", __FUNCTION__); + return rv; + } + + return NS_OK; +} + +void +MediaTransportHandler::Destroy() +{ + disconnect_all(); + NrIceStats stats = mIceCtx->Destroy(); + CSFLogDebug(LOGTAG, "Ice Telemetry: stun (retransmits: %d)" + " turn (401s: %d 403s: %d 438s: %d)", + stats.stun_retransmits, stats.turn_401s, stats.turn_403s, + stats.turn_438s); + + Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_STUN_RETRANSMITS, + stats.stun_retransmits); + Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_401S, + stats.turn_401s); + Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_403S, + stats.turn_403s); + Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_438S, + stats.turn_438s); +} + +nsresult +MediaTransportHandler::SetProxyServer(const std::string& aProxyHost, + uint16_t aProxyPort, + const std::string& aAlpnProtocols) +{ + NrIceProxyServer proxyServer(aProxyHost, aProxyPort, aAlpnProtocols); + return mIceCtx->SetProxyServer(proxyServer); +} + +void +MediaTransportHandler::EnsureProvisionalTransport( + const std::string& aTransportId, + const std::string& aUfrag, + const std::string& aPwd, + size_t aComponentCount) +{ + RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId)); + if (!stream) { + CSFLogDebug(LOGTAG, "%s: Creating ICE media stream=%s components=%u", + mIceCtx->name().c_str(), + aTransportId.c_str(), + static_cast<unsigned>(aComponentCount)); + + std::ostringstream os; + os << mIceCtx->name() << " transport-id=" << aTransportId; + stream = mIceCtx->CreateStream(aTransportId, + os.str(), + aComponentCount); + + if (!stream) { + CSFLogError(LOGTAG, "Failed to create ICE stream."); + return; + } + + stream->SignalCandidate.connect(this, + &MediaTransportHandler::OnCandidateFound); + } + + // Begins an ICE restart if this stream has a different ufrag/pwd + stream->SetIceCredentials(aUfrag, aPwd); + + // Make sure there's an entry in mTransports + mTransports[aTransportId]; +} + +nsresult +MediaTransportHandler::ActivateTransport( + const std::string& aTransportId, + const std::string& aLocalUfrag, + const std::string& aLocalPwd, + size_t aComponentCount, + const std::string& aUfrag, + const std::string& aPassword, + const std::vector<std::string>& aCandidateList, + RefPtr<DtlsIdentity> aDtlsIdentity, + bool aDtlsClient, + const SdpFingerprintAttributeList& aFingerprints, + bool aPrivacyRequested) +{ + MOZ_ASSERT(aComponentCount); + MOZ_ASSERT(aDtlsIdentity); + + RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId)); + if (!stream) { + MOZ_ASSERT(false); + return NS_ERROR_FAILURE; + } + + CSFLogDebug(LOGTAG, "%s: Activating ICE media stream=%s components=%u", + mIceCtx->name().c_str(), + aTransportId.c_str(), + static_cast<unsigned>(aComponentCount)); + + std::vector<std::string> attrs; + attrs.reserve(aCandidateList.size() + 2 /* ufrag + pwd */); + for (const auto& candidate : aCandidateList) { + attrs.push_back("candidate:" + candidate); + } + attrs.push_back("ice-ufrag:" + aUfrag); + attrs.push_back("ice-pwd:" + aPassword); + + // If we started an ICE restart in EnsureProvisionalTransport, this is where + // we decide whether to commit or rollback. + nsresult rv = stream->ConnectToPeer(aLocalUfrag, aLocalPwd, attrs); + if (NS_FAILED(rv)) { + CSFLogError(LOGTAG, "Couldn't parse ICE attributes, rv=%u", + static_cast<unsigned>(rv)); + MOZ_ASSERT(false); + return rv; + } + + Transport transport = mTransports[aTransportId]; + if (!transport.mFlow) { + transport.mFlow = CreateTransportFlow(aTransportId, false, aDtlsIdentity, + aDtlsClient, aFingerprints, aPrivacyRequested); + if (!transport.mFlow) { + return NS_ERROR_FAILURE; + } + TransportLayer* dtls = transport.mFlow->GetLayer(TransportLayerDtls::ID()); + dtls->SignalStateChange.connect( + this, &MediaTransportHandler::OnStateChange); + if (aComponentCount < 2) { + dtls->SignalStateChange.connect( + this, &MediaTransportHandler::OnRtcpStateChange); + } + } + + if (aComponentCount == 2) { + if (!transport.mRtcpFlow) { + transport.mRtcpFlow = CreateTransportFlow(aTransportId, true, + aDtlsIdentity, aDtlsClient, aFingerprints, aPrivacyRequested); + if (!transport.mRtcpFlow) { + return NS_ERROR_FAILURE; + } + TransportLayer* dtls = transport.mRtcpFlow->GetLayer( + TransportLayerDtls::ID()); + dtls->SignalStateChange.connect( + this, &MediaTransportHandler::OnRtcpStateChange); + } + } else { + transport.mRtcpFlow = nullptr; + // components are 1-indexed + stream->DisableComponent(2); + } + + mTransports[aTransportId] = transport; + return NS_OK; +} + +void +MediaTransportHandler::StartIceGathering( + bool aDefaultRouteOnly, + const nsTArray<NrIceStunAddr>& aStunAddrs) +{ + // Belt and suspenders - in e10s mode, the call below to SetStunAddrs + // needs to have the proper flags set on ice ctx. For non-e10s, + // setting those flags happens in StartGathering. We could probably + // just set them here, and only do it here. + mIceCtx->SetCtxFlags(aDefaultRouteOnly, mProxyOnly); + + if (aStunAddrs.Length()) { + mIceCtx->SetStunAddrs(aStunAddrs); + } + + // Start gathering, but only if there are streams + if (!mIceCtx->GetStreams().empty()) { + mIceCtx->StartGathering(aDefaultRouteOnly, mProxyOnly); + return; + } + + CSFLogWarn(LOGTAG, + "%s: No streams to start gathering on. Can happen with rollback", + __FUNCTION__); + + // If there are no streams, we're probably in a situation where we've rolled + // back while still waiting for our proxy configuration to come back. Make + // sure content knows that the rollback has stuck wrt gathering. + SignalGatheringStateChange(dom::PCImplIceGatheringState::Complete); +} + +nsresult +MediaTransportHandler::StartIceChecks( + bool aIsControlling, + bool aIsOfferer, + const std::vector<std::string>& aIceOptions) +{ + nsresult rv = mIceCtx->ParseGlobalAttributes(aIceOptions); + if (NS_FAILED(rv)) { + CSFLogError(LOGTAG, "%s: couldn't parse global parameters", __FUNCTION__ ); + return rv; + } + + rv = mIceCtx->SetControlling(aIsControlling ? NrIceCtx::ICE_CONTROLLING : + NrIceCtx::ICE_CONTROLLED); + if (NS_FAILED(rv)) { + CSFLogError(LOGTAG, "%s: couldn't set controlling to %d", + __FUNCTION__, aIsControlling); + return rv; + } + + rv = mIceCtx->StartChecks(aIsOfferer); + if (NS_FAILED(rv)) { + CSFLogError(LOGTAG, "%s: couldn't start checks", __FUNCTION__); + return rv; + } + + return NS_OK; +} + +nsresult +MediaTransportHandler::AddIceCandidate(const std::string& aTransportId, + const std::string& aCandidate) +{ + RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId)); + if (!stream) { + CSFLogError(LOGTAG, "No ICE stream for candidate with transport id %s: %s", + aTransportId.c_str(), aCandidate.c_str()); + return NS_ERROR_NOT_AVAILABLE; + } + + nsresult rv = stream->ParseTrickleCandidate(aCandidate); + if (NS_FAILED(rv)) { + CSFLogError(LOGTAG, "Couldn't process ICE candidate with transport id %s: " + "%s", + aTransportId.c_str(), aCandidate.c_str()); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +void +MediaTransportHandler::UpdateNetworkState(bool aOnline) +{ + mIceCtx->UpdateNetworkState(aOnline); +} + +void +MediaTransportHandler::RemoveTransportsExcept( + const std::set<std::string>& aTransportIds) +{ + for (auto it = mTransports.begin(); it != mTransports.end();) { + if (!aTransportIds.count(it->first)) { + if (it->second.mFlow) { + SignalStateChange(it->first, TransportLayer::TS_NONE); + SignalRtcpStateChange(it->first, TransportLayer::TS_NONE); + } + mIceCtx->DestroyStream(it->first); + it = mTransports.erase(it); + } else { + MOZ_ASSERT(it->second.mFlow); + ++it; + } + } +} + +nsresult +MediaTransportHandler::SendPacket(const std::string& aTransportId, + MediaPacket& aPacket) +{ + MOZ_ASSERT(aPacket.type() != MediaPacket::UNCLASSIFIED); + RefPtr<TransportFlow> flow = GetTransportFlow( + aTransportId, aPacket.type() == MediaPacket::RTCP); + + if (!flow) { + CSFLogError(LOGTAG, "%s: No such transport flow (%s) for outgoing packet", + mIceCtx->name().c_str(), aTransportId.c_str()); + MOZ_ASSERT(false); + return NS_ERROR_NOT_AVAILABLE; + } + + TransportLayer* layer = nullptr; + switch (aPacket.type()) { + case MediaPacket::SCTP: + layer = flow->GetLayer(TransportLayerDtls::ID()); + break; + case MediaPacket::RTP: + case MediaPacket::RTCP: + layer = flow->GetLayer(TransportLayerSrtp::ID()); + break; + default: + // Maybe it would be useful to allow the injection of other packet types + // for testing? + MOZ_ASSERT(false); + return NS_ERROR_FAILURE; + } + + MOZ_ASSERT(layer); + + if (layer->SendPacket(aPacket) < 0) { + CSFLogError(LOGTAG, "%s: Transport flow (%s) failed to send packet", + mIceCtx->name().c_str(), aTransportId.c_str()); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +TransportLayer::State +MediaTransportHandler::GetState(const std::string& aTransportId, + bool aRtcp) const +{ + RefPtr<TransportFlow> flow = GetTransportFlow(aTransportId, aRtcp); + if (flow) { + return flow->GetLayer(TransportLayerDtls::ID())->state(); + } + return TransportLayer::TS_NONE; +} + +void +MediaTransportHandler::GetAllIceStats(bool aInternalStats, + DOMHighResTimeStamp aNow, + dom::RTCStatsReportInternal* aReport) +{ + for (const auto& stream : mIceCtx->GetStreams()) { + GetIceStats(*stream, aInternalStats, aNow, aReport); + } +} + +void +MediaTransportHandler::GetIceStats(const std::string& aTransportId, + bool aInternalStats, + DOMHighResTimeStamp aNow, + dom::RTCStatsReportInternal* aReport) +{ + auto stream = mIceCtx->GetStream(aTransportId); + if (stream) { + GetIceStats(*stream, aInternalStats, aNow, aReport); + } +} + +static void ToRTCIceCandidateStats( + const std::vector<NrIceCandidate>& candidates, + dom::RTCStatsType candidateType, + const nsString& componentId, + DOMHighResTimeStamp now, + dom::RTCStatsReportInternal* report) { + + MOZ_ASSERT(report); + for (const auto& candidate : candidates) { + dom::RTCIceCandidateStats cand; + cand.mType.Construct(candidateType); + NS_ConvertASCIItoUTF16 codeword(candidate.codeword.c_str()); + cand.mComponentId.Construct(componentId); + cand.mId.Construct(codeword); + cand.mTimestamp.Construct(now); + cand.mCandidateType.Construct( + dom::RTCStatsIceCandidateType(candidate.type)); + cand.mIpAddress.Construct( + NS_ConvertASCIItoUTF16(candidate.cand_addr.host.c_str())); + cand.mPortNumber.Construct(candidate.cand_addr.port); + cand.mTransport.Construct( + NS_ConvertASCIItoUTF16(candidate.cand_addr.transport.c_str())); + if (candidateType == dom::RTCStatsType::Local_candidate) { + cand.mMozLocalTransport.Construct( + NS_ConvertASCIItoUTF16(candidate.local_addr.transport.c_str())); + if (dom::RTCStatsIceCandidateType(candidate.type) == + dom::RTCStatsIceCandidateType::Relayed) { + cand.mRelayProtocol.Construct( + NS_ConvertASCIItoUTF16(candidate.local_addr.transport.c_str())); + } + } + report->mIceCandidateStats.Value().AppendElement(cand, fallible); + if (candidate.trickled) { + report->mTrickledIceCandidateStats.Value().AppendElement(cand, fallible); + } + } +} + +void +MediaTransportHandler::GetIceStats( + const NrIceMediaStream& aStream, + bool aInternalStats, + DOMHighResTimeStamp aNow, + dom::RTCStatsReportInternal* aReport) const +{ + NS_ConvertASCIItoUTF16 transportId(aStream.GetId().c_str()); + + std::vector<NrIceCandidatePair> candPairs; + nsresult res = aStream.GetCandidatePairs(&candPairs); + if (NS_FAILED(res)) { + CSFLogError(LOGTAG, + "%s: Error getting candidate pairs for transport id \"%s\"", + __FUNCTION__, aStream.GetId().c_str()); + return; + } + + for (auto& candPair : candPairs) { + NS_ConvertASCIItoUTF16 codeword(candPair.codeword.c_str()); + NS_ConvertASCIItoUTF16 localCodeword(candPair.local.codeword.c_str()); + NS_ConvertASCIItoUTF16 remoteCodeword(candPair.remote.codeword.c_str()); + // Only expose candidate-pair statistics to chrome, until we've thought + // through the implications of exposing it to content. + + dom::RTCIceCandidatePairStats s; + s.mId.Construct(codeword); + s.mTransportId.Construct(transportId); + s.mTimestamp.Construct(aNow); + s.mType.Construct(dom::RTCStatsType::Candidate_pair); + s.mLocalCandidateId.Construct(localCodeword); + s.mRemoteCandidateId.Construct(remoteCodeword); + s.mNominated.Construct(candPair.nominated); + s.mWritable.Construct(candPair.writable); + s.mReadable.Construct(candPair.readable); + s.mPriority.Construct(candPair.priority); + s.mSelected.Construct(candPair.selected); + s.mBytesSent.Construct(candPair.bytes_sent); + s.mBytesReceived.Construct(candPair.bytes_recvd); + s.mLastPacketSentTimestamp.Construct(candPair.ms_since_last_send); + s.mLastPacketReceivedTimestamp.Construct(candPair.ms_since_last_recv); + s.mState.Construct(dom::RTCStatsIceCandidatePairState(candPair.state)); + s.mComponentId.Construct(candPair.component_id); + aReport->mIceCandidatePairStats.Value().AppendElement(s, fallible); + } + + std::vector<NrIceCandidate> candidates; + if (NS_SUCCEEDED(aStream.GetLocalCandidates(&candidates))) { + ToRTCIceCandidateStats(candidates, + dom::RTCStatsType::Local_candidate, + transportId, + aNow, + aReport); + // add the local candidates unparsed string to a sequence + for (const auto& candidate : candidates) { + aReport->mRawLocalCandidates.Value().AppendElement( + NS_ConvertASCIItoUTF16(candidate.label.c_str()), fallible); + } + } + candidates.clear(); + + if (NS_SUCCEEDED(aStream.GetRemoteCandidates(&candidates))) { + ToRTCIceCandidateStats(candidates, + dom::RTCStatsType::Remote_candidate, + transportId, + aNow, + aReport); + // add the remote candidates unparsed string to a sequence + for (const auto& candidate : candidates) { + aReport->mRawRemoteCandidates.Value().AppendElement( + NS_ConvertASCIItoUTF16(candidate.label.c_str()), fallible); + } + } +} + +RefPtr<TransportFlow> +MediaTransportHandler::GetTransportFlow(const std::string& aTransportId, + bool aIsRtcp) const +{ + auto it = mTransports.find(aTransportId); + if (it == mTransports.end()) { + return nullptr; + } + + if (aIsRtcp) { + return it->second.mRtcpFlow ? it->second.mRtcpFlow : it->second.mFlow;; + } + + return it->second.mFlow; +} + +RefPtr<TransportFlow> +MediaTransportHandler::CreateTransportFlow( + const std::string& aTransportId, + bool aIsRtcp, + RefPtr<DtlsIdentity> aDtlsIdentity, + bool aDtlsClient, + const SdpFingerprintAttributeList& aFingerprints, + bool aPrivacyRequested) +{ + nsresult rv; + RefPtr<TransportFlow> flow = new TransportFlow(aTransportId); + + // The media streams are made on STS so we need to defer setup. + auto ice = MakeUnique<TransportLayerIce>(); + auto dtls = MakeUnique<TransportLayerDtls>(); + auto srtp = MakeUnique<TransportLayerSrtp>(*dtls); + dtls->SetRole(aDtlsClient + ? TransportLayerDtls::CLIENT + : TransportLayerDtls::SERVER); + + dtls->SetIdentity(aDtlsIdentity); + + for (const auto& fingerprint : aFingerprints.mFingerprints) { + std::ostringstream ss; + ss << fingerprint.hashFunc; + rv = dtls->SetVerificationDigest(ss.str(), &fingerprint.fingerprint[0], + fingerprint.fingerprint.size()); + if (NS_FAILED(rv)) { + CSFLogError(LOGTAG, "Could not set fingerprint"); + return nullptr; + } + } + + std::vector<uint16_t> srtpCiphers = TransportLayerDtls::GetDefaultSrtpCiphers(); + + rv = dtls->SetSrtpCiphers(srtpCiphers); + if (NS_FAILED(rv)) { + CSFLogError(LOGTAG, "Couldn't set SRTP ciphers"); + return nullptr; + } + + // Always permits negotiation of the confidential mode. + // Only allow non-confidential (which is an allowed default), + // if we aren't confidential. + std::set<std::string> alpn; + std::string alpnDefault = ""; + alpn.insert("c-webrtc"); + if (!aPrivacyRequested) { + alpnDefault = "webrtc"; + alpn.insert(alpnDefault); + } + rv = dtls->SetAlpn(alpn, alpnDefault); + if (NS_FAILED(rv)) { + CSFLogError(LOGTAG, "Couldn't set ALPN"); + return nullptr; + } + + ice->SetParameters(mIceCtx->GetStream(aTransportId), aIsRtcp ? 2 : 1); + NS_ENSURE_SUCCESS(ice->Init(), nullptr); + NS_ENSURE_SUCCESS(dtls->Init(), nullptr); + NS_ENSURE_SUCCESS(srtp->Init(), nullptr); + dtls->Chain(ice.get()); + srtp->Chain(ice.get()); + + dtls->SignalPacketReceived.connect( + this, &MediaTransportHandler::PacketReceived); + srtp->SignalPacketReceived.connect( + this, &MediaTransportHandler::PacketReceived); + ice->SignalPacketSending.connect( + this, &MediaTransportHandler::EncryptedPacketSending); + flow->PushLayer(ice.release()); + flow->PushLayer(dtls.release()); + flow->PushLayer(srtp.release()); + return flow; +} + +static mozilla::dom::PCImplIceGatheringState +toDomIceGatheringState(NrIceCtx::GatheringState aState) +{ + switch (aState) { + case NrIceCtx::ICE_CTX_GATHER_INIT: + return dom::PCImplIceGatheringState::New; + case NrIceCtx::ICE_CTX_GATHER_STARTED: + return dom::PCImplIceGatheringState::Gathering; + case NrIceCtx::ICE_CTX_GATHER_COMPLETE: + return dom::PCImplIceGatheringState::Complete; + } + MOZ_CRASH(); +} + +void +MediaTransportHandler::OnGatheringStateChange(NrIceCtx* aIceCtx, + NrIceCtx::GatheringState aState) +{ + if (aState == NrIceCtx::ICE_CTX_GATHER_COMPLETE) { + for (const auto& stream : mIceCtx->GetStreams()) { + OnCandidateFound(stream, ""); + } + } + SignalGatheringStateChange(toDomIceGatheringState(aState)); +} + +static mozilla::dom::PCImplIceConnectionState +toDomIceConnectionState(NrIceCtx::ConnectionState aState) +{ + switch (aState) { + case NrIceCtx::ICE_CTX_INIT: + return dom::PCImplIceConnectionState::New; + case NrIceCtx::ICE_CTX_CHECKING: + return dom::PCImplIceConnectionState::Checking; + case NrIceCtx::ICE_CTX_CONNECTED: + return dom::PCImplIceConnectionState::Connected; + case NrIceCtx::ICE_CTX_COMPLETED: + return dom::PCImplIceConnectionState::Completed; + case NrIceCtx::ICE_CTX_FAILED: + return dom::PCImplIceConnectionState::Failed; + case NrIceCtx::ICE_CTX_DISCONNECTED: + return dom::PCImplIceConnectionState::Disconnected; + case NrIceCtx::ICE_CTX_CLOSED: + return dom::PCImplIceConnectionState::Closed; + } + MOZ_CRASH(); +} + +void +MediaTransportHandler::OnConnectionStateChange(NrIceCtx* aIceCtx, + NrIceCtx::ConnectionState aState) +{ + SignalConnectionStateChange(toDomIceConnectionState(aState)); +} + +// The stuff below here will eventually go into the MediaTransportChild class +void +MediaTransportHandler::OnCandidateFound(NrIceMediaStream* aStream, + const std::string& aCandidate) +{ + CandidateInfo info; + info.mCandidate = aCandidate; + NrIceCandidate defaultRtpCandidate; + NrIceCandidate defaultRtcpCandidate; + nsresult rv = aStream->GetDefaultCandidate(1, &defaultRtpCandidate); + if (NS_SUCCEEDED(rv)) { + info.mDefaultHostRtp = defaultRtpCandidate.cand_addr.host; + info.mDefaultPortRtp = defaultRtpCandidate.cand_addr.port; + } else { + CSFLogError(LOGTAG, "%s: GetDefaultCandidates failed for transport id %s, " + "res=%u", + __FUNCTION__, + aStream->GetId().c_str(), + static_cast<unsigned>(rv)); + } + + // Optional; component won't exist if doing rtcp-mux + if (NS_SUCCEEDED(aStream->GetDefaultCandidate(2, &defaultRtcpCandidate))) { + info.mDefaultHostRtcp = defaultRtcpCandidate.cand_addr.host; + info.mDefaultPortRtcp = defaultRtcpCandidate.cand_addr.port; + } + + SignalCandidate(aStream->GetId(), info); +} + +void +MediaTransportHandler::OnStateChange(TransportLayer* aLayer, + TransportLayer::State aState) +{ + if (aState == TransportLayer::TS_OPEN) { + MOZ_ASSERT(aLayer->id() == TransportLayerDtls::ID()); + TransportLayerDtls* dtlsLayer = static_cast<TransportLayerDtls*>(aLayer); + SignalAlpnNegotiated(dtlsLayer->GetNegotiatedAlpn()); + } + + // DTLS state indicates the readiness of the transport as a whole, because + // SRTP uses the keys from the DTLS handshake. + SignalStateChange(aLayer->flow_id(), aState); +} + +void +MediaTransportHandler::OnRtcpStateChange(TransportLayer* aLayer, + TransportLayer::State aState) +{ + SignalRtcpStateChange(aLayer->flow_id(), aState); +} + +void +MediaTransportHandler::PacketReceived(TransportLayer* aLayer, + MediaPacket& aPacket) +{ + SignalPacketReceived(aLayer->flow_id(), aPacket); +} + +void +MediaTransportHandler::EncryptedPacketSending(TransportLayer* aLayer, + MediaPacket& aPacket) +{ + SignalEncryptedSending(aLayer->flow_id(), aPacket); +} +} // namespace mozilla +
new file mode 100644 --- /dev/null +++ b/media/webrtc/signaling/src/peerconnection/MediaTransportHandler.h @@ -0,0 +1,183 @@ +/* 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 _MTRANSPORTHANDLER_H__ +#define _MTRANSPORTHANDLER_H__ + +#include "mozilla/RefPtr.h" +#include "nsISupportsImpl.h" +#include "sigslot.h" +#include "transportlayer.h" // Need the State enum +#include "mozilla/dom/PeerConnectionImplEnumsBinding.h" +#include "nricectx.h" // Need some enums +#include "nsDOMNavigationTiming.h" // DOMHighResTimeStamp + +#include <map> +#include <string> +#include <set> +#include <vector> + +namespace mozilla { +class DtlsIdentity; // TODO(bug 1494311) Use IPC type +class NrIceCtx; +class NrIceMediaStream; +class NrIceResolver; +class SdpFingerprintAttributeList; // TODO(bug 1494311) Use IPC type +class TransportFlow; + +namespace dom { +struct RTCConfiguration; +struct RTCStatsReportInternal; +} + +// Base-class, makes some testing easier +class MediaTransportBase { + public: + virtual nsresult SendPacket(const std::string& aTransportId, + MediaPacket& aPacket) = 0; + + virtual TransportLayer::State GetState(const std::string& aTransportId, + bool aRtcp) const = 0; + sigslot::signal2<const std::string&, MediaPacket&> SignalPacketReceived; + sigslot::signal2<const std::string&, MediaPacket&> SignalEncryptedSending; + sigslot::signal2<const std::string&, TransportLayer::State> + SignalStateChange; + sigslot::signal2<const std::string&, TransportLayer::State> + SignalRtcpStateChange; + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaTransportBase) + + protected: + virtual ~MediaTransportBase() {} +}; + +class MediaTransportHandler : public MediaTransportBase, + public sigslot::has_slots<> { + public: + MediaTransportHandler(); + nsresult Init(const std::string& aName, + const dom::RTCConfiguration& aConfiguration); + void Destroy(); + + // We will probably be able to move the proxy lookup stuff into + // this class once we move mtransport to its own process. + nsresult SetProxyServer(const std::string& aProxyHost, + uint16_t aProxyPort, + const std::string& aAlpnProtocols); + + void EnsureProvisionalTransport(const std::string& aTransportId, + const std::string& aLocalUfrag, + const std::string& aLocalPwd, + size_t aComponentCount); + + // We set default-route-only as late as possible because it depends on what + // capture permissions have been granted on the window, which could easily + // change between Init (ie; when the PC is created) and StartIceGathering + // (ie; when we set the local description). + void StartIceGathering(bool aDefaultRouteOnly, + // This will go away once mtransport moves to its + // own process, because we won't need to get this + // via IPC anymore + const nsTArray<NrIceStunAddr>& aStunAddrs); + + + nsresult ActivateTransport(const std::string& aTransportId, + const std::string& aLocalUfrag, + const std::string& aLocalPwd, + size_t aComponentCount, + const std::string& aUfrag, + const std::string& aPassword, + const std::vector<std::string>& aCandidateList, + // TODO(bug 1494311): Use an IPC type. + RefPtr<DtlsIdentity> aDtlsIdentity, + bool aDtlsClient, + // TODO(bug 1494311): Use IPC type + const SdpFingerprintAttributeList& aFingerprints, + bool aPrivacyRequested); + + void RemoveTransportsExcept(const std::set<std::string>& aTransportIds); + + nsresult StartIceChecks(bool aIsControlling, + bool aIsOfferer, + const std::vector<std::string>& aIceOptions); + + nsresult AddIceCandidate(const std::string& aTransportId, + const std::string& aCandidate); + + void UpdateNetworkState(bool aOnline); + + nsresult SendPacket(const std::string& aTransportId, + MediaPacket& aPacket) override; + + // TODO(bug 1494312): Figure out how this fits with an async API. Maybe we + // cache on the content process. + TransportLayer::State GetState(const std::string& aTransportId, + bool aRtcp) const override; + + // TODO(bug 1494312): Stats stuff needs to be async. + void GetAllIceStats(bool internalStats, + DOMHighResTimeStamp now, + dom::RTCStatsReportInternal* report); + + // TODO(bug 1494312): Stats stuff needs to be async. + void GetIceStats(const std::string& aTransportId, + bool internalStats, + DOMHighResTimeStamp now, + dom::RTCStatsReportInternal* report); + + // TODO(bug 1494311) Use IPC type + struct CandidateInfo { + std::string mCandidate; + std::string mDefaultHostRtp; + uint16_t mDefaultPortRtp = 0; + std::string mDefaultHostRtcp; + uint16_t mDefaultPortRtcp = 0; + }; + + sigslot::signal2<const std::string&, const CandidateInfo&> SignalCandidate; + sigslot::signal1<const std::string&> SignalAlpnNegotiated; + sigslot::signal1<dom::PCImplIceGatheringState> SignalGatheringStateChange; + sigslot::signal1<dom::PCImplIceConnectionState> SignalConnectionStateChange; + + private: + RefPtr<TransportFlow> CreateTransportFlow( + const std::string& aTransportId, + bool aIsRtcp, + RefPtr<DtlsIdentity> aDtlsIdentity, + bool aDtlsClient, + // TODO(bug 1494312) Use IPC type + const SdpFingerprintAttributeList& aFingerprints, + bool aPrivacyRequested); + + struct Transport { + RefPtr<TransportFlow> mFlow; + RefPtr<TransportFlow> mRtcpFlow; + }; + + void OnGatheringStateChange(NrIceCtx* aIceCtx, + NrIceCtx::GatheringState aState); + void OnConnectionStateChange(NrIceCtx* aIceCtx, + NrIceCtx::ConnectionState aState); + void OnCandidateFound(NrIceMediaStream* aStream, + const std::string& aCandidate); + void OnStateChange(TransportLayer* aLayer, TransportLayer::State); + void OnRtcpStateChange(TransportLayer* aLayer, TransportLayer::State); + void PacketReceived(TransportLayer* aLayer, MediaPacket& aPacket); + void EncryptedPacketSending(TransportLayer* aLayer, MediaPacket& aPacket); + RefPtr<TransportFlow> GetTransportFlow(const std::string& aId, + bool aIsRtcp) const; + void GetIceStats(const NrIceMediaStream& aStream, + bool aInternalStats, + DOMHighResTimeStamp aNow, + dom::RTCStatsReportInternal* aReport) const; + + + ~MediaTransportHandler() override; + RefPtr<NrIceCtx> mIceCtx; + RefPtr<NrIceResolver> mDNSResolver; + std::map<std::string, Transport> mTransports; + bool mProxyOnly = false; +}; +} + +#endif //_MTRANSPORTHANDLER_H__
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -314,23 +314,19 @@ bool IsPrivateBrowsing(nsPIDOMWindowInne } PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal) : mTimeCard(MOZ_LOG_TEST(logModuleInfo,LogLevel::Error) ? create_timecard() : nullptr) , mSignalingState(PCImplSignalingState::SignalingStable) , mIceConnectionState(PCImplIceConnectionState::New) , mIceGatheringState(PCImplIceGatheringState::New) - , mDtlsConnected(false) , mWindow(nullptr) , mCertificate(nullptr) - , mPrivacyRequested(false) , mSTSThread(nullptr) - , mAllowIceLoopback(false) - , mAllowIceLinkLocal(false) , mForceIceTcp(false) , mMedia(nullptr) , mUuidGen(MakeUnique<PCUuidGenerator>()) , mIceRestartCount(0) , mIceRollbackCount(0) , mHaveConfiguredCodecs(false) , mAddCandidateErrorCount(0) , mTrickle(true) // TODO(ekr@rtfm.com): Use pref @@ -351,20 +347,16 @@ PeerConnectionImpl::PeerConnectionImpl(c log->EnterPrivateMode(); } mWindow->AddPeerConnection(); mActiveOnWindow = true; } CSFLogInfo(LOGTAG, "%s: PeerConnectionImpl constructor for %s", __FUNCTION__, mHandle.c_str()); STAMP_TIMECARD(mTimeCard, "Constructor Completed"); - mAllowIceLoopback = Preferences::GetBool( - "media.peerconnection.ice.loopback", false); - mAllowIceLinkLocal = Preferences::GetBool( - "media.peerconnection.ice.link_local", false); mForceIceTcp = Preferences::GetBool( "media.peerconnection.ice.force_ice_tcp", false); memset(mMaxReceiving, 0, sizeof(mMaxReceiving)); memset(mMaxSending, 0, sizeof(mMaxSending)); } PeerConnectionImpl::~PeerConnectionImpl() { @@ -502,19 +494,16 @@ PeerConnectionImpl::Initialize(PeerConne // Connect ICE slots. mMedia->SignalIceGatheringStateChange.connect( this, &PeerConnectionImpl::IceGatheringStateChange); mMedia->SignalUpdateDefaultCandidate.connect( this, &PeerConnectionImpl::UpdateDefaultCandidate); - mMedia->SignalEndOfLocalCandidates.connect( - this, - &PeerConnectionImpl::EndOfLocalCandidates); mMedia->SignalIceConnectionStateChange.connect( this, &PeerConnectionImpl::IceConnectionStateChange); mMedia->SignalCandidate.connect(this, &PeerConnectionImpl::CandidateReady); // Initialize the media object. res = mMedia->Init(aConfiguration); @@ -577,17 +566,17 @@ PeerConnectionImpl::Initialize(PeerConne nsresult res = Initialize(aObserver, &aWindow, aConfiguration, aThread); if (NS_FAILED(res)) { rv.Throw(res); return; } if (!aConfiguration.mPeerIdentity.IsEmpty()) { mPeerIdentity = new PeerIdentity(aConfiguration.mPeerIdentity); - mPrivacyRequested = true; + mPrivacyRequested = Some(true); } } void PeerConnectionImpl::SetCertificate(mozilla::dom::RTCCertificate& aCertificate) { PC_AUTO_ENTER_API_CALL_NO_CHECK(); MOZ_ASSERT(!mCertificate, "This can only be called once"); @@ -917,34 +906,36 @@ PeerConnectionImpl::EnsureDataConnection CSFLogDebug(LOGTAG,"%s DataConnection already connected",__FUNCTION__); mDataConnection->SetMaxMessageSize(aMMSSet, aMaxMessageSize); return NS_OK; } nsCOMPtr<nsIEventTarget> target = mWindow ? mWindow->EventTargetFor(TaskCategory::Other) : nullptr; - mDataConnection = new DataChannelConnection(this, target); + mDataConnection = new DataChannelConnection( + this, target, mMedia->mTransportHandler); if (!mDataConnection->Init(aLocalPort, aNumstreams, aMMSSet, aMaxMessageSize)) { CSFLogError(LOGTAG,"%s DataConnection Init Failed",__FUNCTION__); return NS_ERROR_FAILURE; } CSFLogDebug(LOGTAG,"%s DataChannelConnection %p attached to %s", __FUNCTION__, (void*) mDataConnection.get(), mHandle.c_str()); return NS_OK; } nsresult PeerConnectionImpl::GetDatachannelParameters( uint32_t* channels, uint16_t* localport, uint16_t* remoteport, uint32_t* remotemaxmessagesize, bool* mmsset, - std::string* transportId) const { + std::string* transportId, + bool* client) const { for (const auto& transceiver : mJsepSession->GetTransceivers()) { bool dataChannel = transceiver->GetMediaType() == SdpMediaSection::kApplication; if (dataChannel && transceiver->mSendTrack.GetNegotiatedDetails()) { // This will release assert if there is no such index, and that's ok const JsepTrackEncoding& encoding = @@ -985,16 +976,19 @@ PeerConnectionImpl::GetDatachannelParame *remoteport = static_cast<const JsepApplicationCodecDescription*>(codec)->mRemotePort; *remotemaxmessagesize = static_cast<const JsepApplicationCodecDescription*> (codec)->mRemoteMaxMessageSize; *mmsset = static_cast<const JsepApplicationCodecDescription*> (codec)->mRemoteMMSSet; MOZ_ASSERT(!transceiver->mTransport.mTransportId.empty()); *transportId = transceiver->mTransport.mTransportId; + *client = + transceiver->mTransport.mDtls->GetRole() == + JsepDtlsTransport::kJsepDtlsClient; return NS_OK; } } } *channels = 0; *localport = 0; *remoteport = 0; @@ -1110,40 +1104,34 @@ PeerConnectionImpl::InitializeDataChanne CSFLogDebug(LOGTAG, "%s", __FUNCTION__); uint32_t channels = 0; uint16_t localport = 0; uint16_t remoteport = 0; uint32_t remotemaxmessagesize = 0; bool mmsset = false; std::string transportId; + bool client = false; nsresult rv = GetDatachannelParameters(&channels, &localport, &remoteport, - &remotemaxmessagesize, &mmsset, &transportId); + &remotemaxmessagesize, &mmsset, &transportId, &client); if (NS_FAILED(rv)) { CSFLogDebug(LOGTAG, "%s: We did not negotiate datachannel", __FUNCTION__); return NS_OK; } if (channels > MAX_NUM_STREAMS) { channels = MAX_NUM_STREAMS; } rv = EnsureDataConnection(localport, channels, remotemaxmessagesize, mmsset); if (NS_SUCCEEDED(rv)) { - // use the specified TransportFlow - RefPtr<TransportFlow> flow = mMedia->GetTransportFlow(transportId, false).get(); - CSFLogDebug(LOGTAG, "Transportflow[%s] = %p", - transportId.c_str(), flow.get()); - if (flow) { - if (mDataConnection->ConnectViaTransportFlow(flow, - localport, - remoteport)) { - return NS_OK; - } + if (mDataConnection->ConnectToTransport( + transportId, client, localport, remoteport)) { + return NS_OK; } // If we inited the DataConnection, call Destroy() before releasing it mDataConnection->Destroy(); } mDataConnection = nullptr; return NS_ERROR_FAILURE; } @@ -1438,18 +1426,19 @@ PeerConnectionImpl::SetLocalDescription( JSErrorResult rv; RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); if (!pco) { return NS_OK; } STAMP_TIMECARD(mTimeCard, "Set Local Description"); - bool isolated = mMedia->AnyLocalTrackHasPeerIdentity(); - mPrivacyRequested = mPrivacyRequested || isolated; + if (mMedia->AnyLocalTrackHasPeerIdentity()) { + mPrivacyRequested = Some(true); + } mLocalRequestedSDP = aSDP; bool wasRestartingIce = mJsepSession->IsIceRestarting(); JsepSdpType sdpType; switch (aAction) { case IPeerConnection::kActionOffer: sdpType = mozilla::kJsepSdpOffer; @@ -1815,35 +1804,39 @@ PeerConnectionImpl::SetPeerIdentity(cons } MediaStreamTrack* allTracks = nullptr; mMedia->UpdateSinkIdentity_m(allTracks, doc->NodePrincipal(), mPeerIdentity); } return NS_OK; } nsresult -PeerConnectionImpl::SetDtlsConnected(bool aPrivacyRequested) +PeerConnectionImpl::OnAlpnNegotiated(const std::string& aAlpn) { PC_AUTO_ENTER_API_CALL(false); + if (mPrivacyRequested.isSome()) { + return NS_OK; + } + + mPrivacyRequested = Some(aAlpn == "c-webrtc"); // For this, as with mPrivacyRequested, once we've connected to a peer, we // fixate on that peer. Dealing with multiple peers or connections is more // than this run-down wreck of an object can handle. // Besides, this is only used to say if we have been connected ever. - if (!mPrivacyRequested && !aPrivacyRequested && !mDtlsConnected) { - // now we know that privacy isn't needed for sure + if (!*mPrivacyRequested) { + // Neither side wants privacy nsIDocument* doc = GetWindow()->GetExtantDoc(); if (!doc) { CSFLogInfo(LOGTAG, "Can't update principal on streams; document gone"); return NS_ERROR_FAILURE; } mMedia->UpdateRemoteStreamPrincipals_m(doc->NodePrincipal()); } - mDtlsConnected = true; - mPrivacyRequested = mPrivacyRequested || aPrivacyRequested; + return NS_OK; } void PeerConnectionImpl::PrincipalChanged(MediaStreamTrack* aTrack) { nsIDocument* doc = GetWindow()->GetExtantDoc(); if (doc) { mMedia->UpdateSinkIdentity_m(aTrack, doc->NodePrincipal(), mPeerIdentity); @@ -2064,17 +2057,17 @@ PeerConnectionImpl::CreateReceiveTrack(S CSFLogDebug(LOGTAG, "Created media stream %p, inner: %p", stream.get(), stream->GetInputStream()); // Set the principal used for creating the tracks. This makes the stream // data (audio/video samples) accessible to the receiving page. We're // only certain that privacy hasn't been requested if we're connected. nsCOMPtr<nsIPrincipal> principal; nsIDocument* doc = GetWindow()->GetExtantDoc(); MOZ_ASSERT(doc); - if (mDtlsConnected && !PrivacyRequested()) { + if (mPrivacyRequested.isSome() && !*mPrivacyRequested) { principal = doc->NodePrincipal(); } else { // we're either certain that we need isolation for the streams, OR // we're not sure and we can fix the stream in SetDtlsConnected principal = NullPrincipal::CreateWithInheritedAttributes(doc->NodePrincipal()); } RefPtr<MediaStreamTrack> track; @@ -2664,16 +2657,21 @@ PeerConnectionImpl::GetName() return mName; } void PeerConnectionImpl::CandidateReady(const std::string& candidate, const std::string& transportId) { PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); + if (candidate.empty()) { + mJsepSession->EndOfLocalCandidates(transportId); + return; + } + if (mForceIceTcp && std::string::npos != candidate.find(" UDP ")) { CSFLogWarn(LOGTAG, "Blocking local UDP candidate: %s", candidate.c_str()); return; } // One of the very few places we still use level; required by the JSEP API uint16_t level = 0; std::string mid; @@ -2881,22 +2879,16 @@ PeerConnectionImpl::UpdateDefaultCandida CSFLogDebug(LOGTAG, "%s", __FUNCTION__); mJsepSession->UpdateDefaultCandidate(defaultAddr, defaultPort, defaultRtcpAddr, defaultRtcpPort, transportId); } -void -PeerConnectionImpl::EndOfLocalCandidates(const std::string& transportId) { - CSFLogDebug(LOGTAG, "%s", __FUNCTION__); - mJsepSession->EndOfLocalCandidates(transportId); -} - nsresult PeerConnectionImpl::BuildStatsQuery_m( mozilla::dom::MediaStreamTrack *aSelector, RTCStatsQuery *query) { if (!HasMedia()) { return NS_ERROR_UNEXPECTED; } @@ -2907,20 +2899,16 @@ PeerConnectionImpl::BuildStatsQuery_m( } nsresult rv = GetTimeSinceEpoch(&(query->now)); if (NS_FAILED(rv)) { CSFLogError(LOGTAG, "Could not build stats query, could not get timestamp"); return rv; } - // Note: mMedia->ice_ctx() is deleted on STS thread; so make sure we grab and hold - // a ref instead of making multiple calls. NrIceCtx uses threadsafe refcounting. - // NOTE: Do this after all other failure tests, to ensure we don't - // accidentally release the Ctx on Mainthread. query->media = mMedia; if (!query->media) { CSFLogError(LOGTAG, "Could not build stats query, no ice_ctx"); return NS_ERROR_UNEXPECTED; } // We do not use the pcHandle here, since that's risky to expose to content. query->report = new RTCStatsReportInternalConstruct( @@ -3194,25 +3182,27 @@ PeerConnectionImpl::ExecuteStatsQuery_s( // Fill in Contributing Source statistics mp.GetContributingSourceStats(localId, query->report->mRtpContributingSourceStats.Value()); break; } } } - if (query->grabAllLevels) { - query->media->GetAllIceStats_s(query->internalStats, - query->now, - query->report); - } else { - query->media->GetIceStats_s(query->transportId, - query->internalStats, - query->now, - query->report); + if (query->media->mTransportHandler) { + if (query->grabAllLevels) { + query->media->mTransportHandler->GetAllIceStats(query->internalStats, + query->now, + query->report); + } else { + query->media->mTransportHandler->GetIceStats(query->transportId, + query->internalStats, + query->now, + query->report); + } } return NS_OK; } void PeerConnectionImpl::GetStatsForPCObserver_s( const std::string& pcHandle, // The Runnable holds the memory nsAutoPtr<RTCStatsQuery> query) {
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -206,39 +206,30 @@ public: ; // Get the media object const RefPtr<PeerConnectionMedia>& media() const { PC_AUTO_ENTER_API_CALL_NO_CHECK(); return mMedia; } - // Configure the ability to use localhost. - void SetAllowIceLoopback(bool val) { mAllowIceLoopback = val; } - bool GetAllowIceLoopback() const { return mAllowIceLoopback; } - - // Configure the ability to use IPV6 link-local addresses. - void SetAllowIceLinkLocal(bool val) { mAllowIceLinkLocal = val; } - bool GetAllowIceLinkLocal() const { return mAllowIceLinkLocal; } - // Handle system to allow weak references to be passed through C code virtual const std::string& GetHandle(); // Name suitable for exposing to content virtual const std::string& GetName(); // ICE events void IceConnectionStateChange(dom::PCImplIceConnectionState state); void IceGatheringStateChange(dom::PCImplIceGatheringState state); void UpdateDefaultCandidate(const std::string& defaultAddr, uint16_t defaultPort, const std::string& defaultRtcpAddr, uint16_t defaultRtcpPort, const std::string& transportId); - void EndOfLocalCandidates(const std::string& transportId); static void ListenThread(void *aData); static void ConnectThread(void *aData); // Get the main thread nsCOMPtr<nsIThread> GetMainThread() { PC_AUTO_ENTER_API_CALL_NO_CHECK(); return mThread; @@ -442,17 +433,20 @@ public: } void SetId(const nsAString& id) { mName = NS_ConvertUTF16toUTF8(id).get(); } // this method checks to see if we've made a promise to protect media. - bool PrivacyRequested() const { return mPrivacyRequested; } + bool PrivacyRequested() const + { + return mPrivacyRequested.isSome() && *mPrivacyRequested; + } NS_IMETHODIMP GetFingerprint(char** fingerprint); void GetFingerprint(nsAString& fingerprint) { char *tmp; GetFingerprint(&tmp); fingerprint.AssignASCII(tmp); delete[] tmp; @@ -536,17 +530,17 @@ public: void SetSignalingState_m(mozilla::dom::PCImplSignalingState aSignalingState, bool rollback = false); // Updates the RTC signaling state based on the JsepSession state void UpdateSignalingState(bool rollback = false); bool IsClosed() const; // called when DTLS connects; we only need this once - nsresult SetDtlsConnected(bool aPrivacyRequested); + nsresult OnAlpnNegotiated(const std::string& aAlpn); bool HasMedia() const; // initialize telemetry for when calls start void startCallTelem(); nsresult BuildStatsQuery_m( mozilla::dom::MediaStreamTrack *aSelector, @@ -606,17 +600,18 @@ private: const std::string& candidate); nsresult GetDatachannelParameters( uint32_t* channels, uint16_t* localport, uint16_t* remoteport, uint32_t* maxmessagesize, bool* mmsset, - std::string* transportId) const; + std::string* transportId, + bool* client) const; nsresult AddRtpTransceiverToJsepSession(RefPtr<JsepTransceiver>& transceiver); already_AddRefed<TransceiverImpl> CreateTransceiverImpl( JsepTransceiver* aJsepTransceiver, dom::MediaStreamTrack* aSendTrack, ErrorResult& aRv); static void GetStatsForPCObserver_s( @@ -644,20 +639,16 @@ private: Timecard *mTimeCard; mozilla::dom::PCImplSignalingState mSignalingState; // ICE State mozilla::dom::PCImplIceConnectionState mIceConnectionState; mozilla::dom::PCImplIceGatheringState mIceGatheringState; - // DTLS - // this is true if we have been connected ever, see SetDtlsConnected - bool mDtlsConnected; - nsCOMPtr<nsIThread> mThread; // TODO: Remove if we ever properly wire PeerConnection for cycle-collection. nsWeakPtr mPCObserver; nsCOMPtr<nsPIDOMWindowInner> mWindow; // The SDP sent in from JS std::string mLocalRequestedSDP; @@ -674,32 +665,30 @@ private: // The certificate we are using. RefPtr<mozilla::dom::RTCCertificate> mCertificate; // Whether an app should be prevented from accessing media produced by the PC // If this is true, then media will not be sent until mPeerIdentity matches // local streams PeerIdentity; and remote streams are protected from content // // This can be false if mPeerIdentity is set, in the case where identity is // provided, but the media is not protected from the app on either side - bool mPrivacyRequested; + Maybe<bool> mPrivacyRequested; // A handle to refer to this PC with std::string mHandle; // A name for this PC that we are willing to expose to content. std::string mName; // The target to run stuff on nsCOMPtr<nsIEventTarget> mSTSThread; // DataConnection that's used to get all the DataChannels RefPtr<mozilla::DataChannelConnection> mDataConnection; - bool mAllowIceLoopback; - bool mAllowIceLinkLocal; bool mForceIceTcp; RefPtr<PeerConnectionMedia> mMedia; // The JSEP negotiation session. mozilla::UniquePtr<PCUuidGenerator> mUuidGen; mozilla::UniquePtr<mozilla::JsepSession> mJsepSession; unsigned long mIceRestartCount; unsigned long mIceRollbackCount;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -1,51 +1,33 @@ /* 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 <ostream> -#include <string> -#include <vector> - #include "CSFLog.h" -#include "nspr.h" - -#include "nricectx.h" -#include "nricemediastream.h" #include "MediaPipelineFilter.h" #include "MediaPipeline.h" #include "PeerConnectionImpl.h" #include "PeerConnectionMedia.h" #include "runnable_utils.h" -#include "transportlayerice.h" -#include "transportlayerdtls.h" -#include "transportlayersrtp.h" #include "signaling/src/jsep/JsepSession.h" #include "signaling/src/jsep/JsepTransport.h" -#include "signaling/src/mediapipeline/TransportLayerPacketDumper.h" -#include "signaling/src/peerconnection/PacketDumper.h" #include "nsContentUtils.h" -#include "nsNetCID.h" -#include "nsNetUtil.h" #include "nsIURI.h" #include "nsIScriptSecurityManager.h" #include "nsICancelable.h" #include "nsILoadInfo.h" #include "nsIContentPolicy.h" #include "nsIProxyInfo.h" #include "nsIProtocolProxyService.h" -#include "nsProxyRelease.h" - #include "nsIScriptGlobalObject.h" #include "mozilla/Preferences.h" -#include "mozilla/Telemetry.h" #include "MediaManager.h" #include "WebrtcGmpVideoCodec.h" namespace mozilla { using namespace dom; static const char* pcmLogTag = "PeerConnectionMedia"; #ifdef LOGTAG @@ -93,31 +75,19 @@ PeerConnectionMedia::ProtocolProxyQueryH } rv = proxyinfo.GetPort(&httpsProxyPort); if (NS_FAILED(rv)) { CSFLogError(LOGTAG, "%s: Failed to get proxy server port", __FUNCTION__); return; } - if (pcm_->mIceCtx.get()) { - assert(httpsProxyPort >= 0 && httpsProxyPort < (1 << 16)); - // Note that this could check if PrivacyRequested() is set on the PC and - // remove "webrtc" from the ALPN list. But that would only work if the PC - // was constructed with a peerIdentity constraint, not when isolated - // streams are added. If we ever need to signal to the proxy that the - // media is isolated, then we would need to restructure this code. - pcm_->mProxyServer.reset( - new NrIceProxyServer(httpsProxyHost.get(), - static_cast<uint16_t>(httpsProxyPort), - "webrtc,c-webrtc")); - } else { - CSFLogError(LOGTAG, "%s: Failed to set proxy server (ICE ctx unavailable)", - __FUNCTION__); - } + assert(httpsProxyPort >= 0 && httpsProxyPort < (1 << 16)); + pcm_->mProxyHost = httpsProxyHost.get(); + pcm_->mProxyPort = static_cast<uint16_t>(httpsProxyPort); } NS_IMPL_ISUPPORTS(PeerConnectionMedia::ProtocolProxyQueryHandler, nsIProtocolProxyCallback) void PeerConnectionMedia::StunAddrsHandler::OnStunAddrsAvailable( const mozilla::net::NrIceStunAddrArray& addrs) { @@ -134,22 +104,20 @@ PeerConnectionMedia::StunAddrsHandler::O pcm_->SignalIceConnectionStateChange(dom::PCImplIceConnectionState::Failed); } pcm_ = nullptr; } } PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent) - : mParent(parent), + : mTransportHandler(nullptr), + mParent(parent), mParentHandle(parent->GetHandle()), mParentName(parent->GetName()), - mIceCtx(nullptr), - mDNSResolver(new NrIceResolver()), - mUuidGen(MakeUnique<PCUuidGenerator>()), mMainThread(mParent->GetMainThread()), mSTSThread(mParent->GetSTSThread()), mProxyResolveCompleted(false), mLocalAddrsCompleted(false) { } PeerConnectionMedia::~PeerConnectionMedia() { @@ -234,293 +202,85 @@ PeerConnectionMedia::InitProxy() if (NS_FAILED(rv)) { CSFLogError(LOGTAG, "%s: Failed to resolve protocol proxy: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } return NS_OK; } -static nsresult addNrIceServer(const nsString& aIceUrl, - const dom::RTCIceServer& aIceServer, - std::vector<NrIceStunServer>* aStunServersOut, - std::vector<NrIceTurnServer>* aTurnServersOut) -{ - // Without STUN/TURN handlers, NS_NewURI returns nsSimpleURI rather than - // nsStandardURL. To parse STUN/TURN URI's to spec - // http://tools.ietf.org/html/draft-nandakumar-rtcweb-stun-uri-02#section-3 - // http://tools.ietf.org/html/draft-petithuguenin-behave-turn-uri-03#section-3 - // we parse out the query-string, and use ParseAuthority() on the rest - RefPtr<nsIURI> url; - nsresult rv = NS_NewURI(getter_AddRefs(url), aIceUrl); - NS_ENSURE_SUCCESS(rv, rv); - bool isStun = false, isStuns = false, isTurn = false, isTurns = false; - url->SchemeIs("stun", &isStun); - url->SchemeIs("stuns", &isStuns); - url->SchemeIs("turn", &isTurn); - url->SchemeIs("turns", &isTurns); - if (!(isStun || isStuns || isTurn || isTurns)) { - return NS_ERROR_FAILURE; - } - if (isStuns) { - return NS_OK; // TODO: Support STUNS (Bug 1056934) - } - - nsAutoCString spec; - rv = url->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - - // TODO(jib@mozilla.com): Revisit once nsURI supports STUN/TURN (Bug 833509) - int32_t port; - nsAutoCString host; - nsAutoCString transport; - { - uint32_t hostPos; - int32_t hostLen; - nsAutoCString path; - rv = url->GetPathQueryRef(path); - NS_ENSURE_SUCCESS(rv, rv); - - // Tolerate query-string + parse 'transport=[udp|tcp]' by hand. - int32_t questionmark = path.FindChar('?'); - if (questionmark >= 0) { - const nsCString match = NS_LITERAL_CSTRING("transport="); - - for (int32_t i = questionmark, endPos; i >= 0; i = endPos) { - endPos = path.FindCharInSet("&", i + 1); - const nsDependentCSubstring fieldvaluepair = Substring(path, i + 1, - endPos); - if (StringBeginsWith(fieldvaluepair, match)) { - transport = Substring(fieldvaluepair, match.Length()); - ToLowerCase(transport); - } - } - path.SetLength(questionmark); - } - - rv = net_GetAuthURLParser()->ParseAuthority(path.get(), path.Length(), - nullptr, nullptr, - nullptr, nullptr, - &hostPos, &hostLen, &port); - NS_ENSURE_SUCCESS(rv, rv); - if (!hostLen) { - return NS_ERROR_FAILURE; - } - if (hostPos > 1) { - /* The username was removed */ - return NS_ERROR_FAILURE; - } - path.Mid(host, hostPos, hostLen); - } - if (port == -1) { - port = (isStuns || isTurns)? 5349 : 3478; - } - - if (isStuns || isTurns) { - // Should we barf if transport is set to udp or something? - transport = kNrIceTransportTls; - } - - if (transport.IsEmpty()) { - transport = kNrIceTransportUdp; - } - - if (isTurn || isTurns) { - std::string pwd(NS_ConvertUTF16toUTF8(aIceServer.mCredential.Value()).get()); - std::string username(NS_ConvertUTF16toUTF8(aIceServer.mUsername.Value()).get()); - - std::vector<unsigned char> password(pwd.begin(), pwd.end()); - - UniquePtr<NrIceTurnServer> server(NrIceTurnServer::Create(host.get(), port, username, password, transport.get())); - if (!server) { - return NS_ERROR_FAILURE; - } - aTurnServersOut->emplace_back(std::move(*server)); - } else { - UniquePtr<NrIceStunServer> server(NrIceStunServer::Create(host.get(), port, transport.get())); - if (!server) { - return NS_ERROR_FAILURE; - } - aStunServersOut->emplace_back(std::move(*server)); - } - return NS_OK; -} - -static NrIceCtx::Policy toNrIcePolicy(dom::RTCIceTransportPolicy aPolicy) -{ - switch (aPolicy) { - case dom::RTCIceTransportPolicy::Relay: - return NrIceCtx::ICE_POLICY_RELAY; - case dom::RTCIceTransportPolicy::All: - if (Preferences::GetBool("media.peerconnection.ice.no_host", false)) { - return NrIceCtx::ICE_POLICY_NO_HOST; - } else { - return NrIceCtx::ICE_POLICY_ALL; - } - default: - MOZ_CRASH(); - } - return NrIceCtx::ICE_POLICY_ALL; -} - nsresult PeerConnectionMedia::Init(const dom::RTCConfiguration& aConfiguration) { nsresult rv = InitProxy(); NS_ENSURE_SUCCESS(rv, rv); - bool ice_tcp = Preferences::GetBool("media.peerconnection.ice.tcp", false); - // setup the stun local addresses IPC async call InitLocalAddrs(); - NrIceCtx::InitializeGlobals(mParent->GetAllowIceLoopback(), - ice_tcp, - mParent->GetAllowIceLinkLocal()); - - // TODO(ekr@rtfm.com): need some way to set not offerer later - // Looks like a bug in the NrIceCtx API. - mIceCtx = NrIceCtx::Create("PC:" + mParentName, - toNrIcePolicy(aConfiguration.mIceTransportPolicy)); - if(!mIceCtx) { - CSFLogError(LOGTAG, "%s: Failed to create Ice Context", __FUNCTION__); + mTransportHandler = new MediaTransportHandler; + rv = mTransportHandler->Init("PC:" + mParentName, aConfiguration); + if(NS_FAILED(rv)) { + CSFLogError(LOGTAG, "%s: Failed to init mtransport", __FUNCTION__); return NS_ERROR_FAILURE; } - std::vector<NrIceStunServer> stunServers; - std::vector<NrIceTurnServer> turnServers; - - if (aConfiguration.mIceServers.WasPassed()) { - for (const auto& iceServer : aConfiguration.mIceServers.Value()) { - NS_ENSURE_STATE(iceServer.mUrls.WasPassed()); - NS_ENSURE_STATE(iceServer.mUrls.Value().IsStringSequence()); - for (const auto& iceUrl : iceServer.mUrls.Value().GetAsStringSequence()) { - rv = addNrIceServer(iceUrl, iceServer, &stunServers, &turnServers); - if (NS_FAILED(rv)) { - CSFLogError(LOGTAG, "%s: invalid STUN/TURN server: %s", - __FUNCTION__, NS_ConvertUTF16toUTF8(iceUrl).get()); - return rv; - } - } - } - } - - if (NS_FAILED(rv = mIceCtx->SetStunServers(stunServers))) { - CSFLogError(LOGTAG, "%s: Failed to set stun servers", __FUNCTION__); - return rv; - } - // Give us a way to globally turn off TURN support - bool disabled = Preferences::GetBool("media.peerconnection.turn.disable", false); - if (!disabled) { - if (NS_FAILED(rv = mIceCtx->SetTurnServers(turnServers))) { - CSFLogError(LOGTAG, "%s: Failed to set turn servers", __FUNCTION__); - return rv; - } - } else if (!turnServers.empty()) { - CSFLogError(LOGTAG, "%s: Setting turn servers disabled", __FUNCTION__); - } - if (NS_FAILED(rv = mDNSResolver->Init())) { - CSFLogError(LOGTAG, "%s: Failed to initialize dns resolver", __FUNCTION__); - return rv; - } - if (NS_FAILED(rv = - mIceCtx->SetResolver(mDNSResolver->AllocateResolver()))) { - CSFLogError(LOGTAG, "%s: Failed to get dns resolver", __FUNCTION__); - return rv; - } - ConnectSignals(mIceCtx.get()); + ConnectSignals(); return NS_OK; } void PeerConnectionMedia::EnsureTransports(const JsepSession& aSession) { for (const auto& transceiver : aSession.GetTransceivers()) { if (transceiver->HasOwnTransport()) { RUN_ON_THREAD( GetSTSThread(), - WrapRunnable(RefPtr<PeerConnectionMedia>(this), - &PeerConnectionMedia::EnsureTransport_s, + WrapRunnable(mTransportHandler, + &MediaTransportHandler::EnsureProvisionalTransport, transceiver->mTransport.mTransportId, transceiver->mTransport.mLocalUfrag, transceiver->mTransport.mLocalPwd, transceiver->mTransport.mComponents), NS_DISPATCH_NORMAL); } } GatherIfReady(); } -void -PeerConnectionMedia::EnsureTransport_s(const std::string& aTransportId, - const std::string& aUfrag, - const std::string& aPwd, - size_t aComponentCount) -{ - RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId)); - if (!stream) { - CSFLogDebug(LOGTAG, "%s: Creating ICE media stream=%s components=%u", - mParentHandle.c_str(), - aTransportId.c_str(), - static_cast<unsigned>(aComponentCount)); - - std::ostringstream os; - os << mParentName << " transport-id=" << aTransportId; - stream = mIceCtx->CreateStream(aTransportId, - os.str(), - aComponentCount); - - if (!stream) { - CSFLogError(LOGTAG, "Failed to create ICE stream."); - return; - } - - stream->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady_s); - stream->SignalCandidate.connect(this, - &PeerConnectionMedia::OnCandidateFound_s); - } - // This might begin an ICE restart - stream->SetIceCredentials(aUfrag, aPwd); -} - nsresult PeerConnectionMedia::UpdateTransports(const JsepSession& aSession, const bool forceIceTcp) { std::set<std::string> finalTransports; for (const auto& transceiver : aSession.GetTransceivers()) { if (transceiver->HasOwnTransport()) { finalTransports.insert(transceiver->mTransport.mTransportId); UpdateTransport(*transceiver, forceIceTcp); } } RUN_ON_THREAD( GetSTSThread(), - WrapRunnable(RefPtr<PeerConnectionMedia>(this), - &PeerConnectionMedia::RemoveTransportsExcept_s, + WrapRunnable(mTransportHandler, + &MediaTransportHandler::RemoveTransportsExcept, finalTransports), NS_DISPATCH_NORMAL); for (const auto& transceiverImpl : mTransceivers) { - transceiverImpl->UpdateTransport(*this); + transceiverImpl->UpdateTransport(); } return NS_OK; } -nsresult +void PeerConnectionMedia::UpdateTransport(const JsepTransceiver& aTransceiver, bool aForceIceTcp) { - nsresult rv = UpdateTransportFlows(aTransceiver); - if (NS_FAILED(rv)) { - return rv; - } - std::string ufrag; std::string pwd; std::vector<std::string> candidates; size_t components = 0; const JsepTransport& transport = aTransceiver.mTransport; unsigned level = aTransceiver.GetLevel(); @@ -538,82 +298,31 @@ PeerConnectionMedia::UpdateTransport(con [](const std::string & s) { return s.find(" UDP ") != std::string::npos || s.find(" udp ") != std::string::npos; }), candidates.end()); } RUN_ON_THREAD( GetSTSThread(), - WrapRunnable(RefPtr<PeerConnectionMedia>(this), - &PeerConnectionMedia::ActivateTransport_s, + WrapRunnable(mTransportHandler, + &MediaTransportHandler::ActivateTransport, transport.mTransportId, transport.mLocalUfrag, transport.mLocalPwd, components, ufrag, pwd, - candidates), + candidates, + mParent->Identity(), + transport.mDtls->GetRole() == + JsepDtlsTransport::kJsepDtlsClient, + transport.mDtls->GetFingerprints(), + mParent->PrivacyRequested()), NS_DISPATCH_NORMAL); - - return NS_OK; -} - -void -PeerConnectionMedia::ActivateTransport_s( - const std::string& aTransportId, - const std::string& aLocalUfrag, - const std::string& aLocalPwd, - size_t aComponentCount, - const std::string& aUfrag, - const std::string& aPassword, - const std::vector<std::string>& aCandidateList) { - - MOZ_ASSERT(aComponentCount); - - RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId)); - if (!stream) { - MOZ_ASSERT(false); - return; - } - - CSFLogDebug(LOGTAG, "%s: Activating ICE media stream=%s components=%u", - mParentHandle.c_str(), - aTransportId.c_str(), - static_cast<unsigned>(aComponentCount)); - - std::vector<std::string> attrs; - attrs.reserve(aCandidateList.size() + 2 /* ufrag + pwd */); - for (const auto& candidate : aCandidateList) { - attrs.push_back("candidate:" + candidate); - } - attrs.push_back("ice-ufrag:" + aUfrag); - attrs.push_back("ice-pwd:" + aPassword); - - nsresult rv = stream->ConnectToPeer(aLocalUfrag, aLocalPwd, attrs); - if (NS_FAILED(rv)) { - CSFLogError(LOGTAG, "Couldn't parse ICE attributes, rv=%u", - static_cast<unsigned>(rv)); - } - - for (size_t c = aComponentCount; c < stream->components(); ++c) { - // components are 1-indexed - stream->DisableComponent(c + 1); - } -} - -void -PeerConnectionMedia::RemoveTransportsExcept_s( - const std::set<std::string>& aIds) -{ - for (const auto& stream : mIceCtx->GetStreams()) { - if (!aIds.count(stream->GetId())) { - mIceCtx->DestroyStream(stream->GetId()); - } - } } nsresult PeerConnectionMedia::UpdateMediaPipelines() { // The GMP code is all the way on the other side of webrtc.org, and it is not // feasible to plumb error information all the way back. So, we set up a // handle to the PC (for the duration of this call) in a global variable. @@ -634,156 +343,16 @@ PeerConnectionMedia::UpdateMediaPipeline // TODO: If there is no audio, we should probably de-sync. However, this // has never been done before, and it is unclear whether it is safe... } } return NS_OK; } -nsresult -PeerConnectionMedia::UpdateTransportFlows(const JsepTransceiver& aTransceiver) -{ - nsresult rv = UpdateTransportFlow(false, aTransceiver.mTransport); - if (NS_FAILED(rv)) { - return rv; - } - - return UpdateTransportFlow(true, aTransceiver.mTransport); -} - -// Accessing the PCMedia should be safe here because we shouldn't -// have enqueued this function unless it was still active and -// the ICE data is destroyed on the STS. -static void -FinalizeTransportFlow_s(const RefPtr<NrIceCtx>& aIceCtx, - nsAutoPtr<PacketDumper> aPacketDumper, - const RefPtr<TransportFlow>& aFlow, - const std::string& aId, - bool aIsRtcp, - TransportLayerIce* aIceLayer, - TransportLayerDtls* aDtlsLayer, - TransportLayerSrtp* aSrtpLayer) -{ - TransportLayerPacketDumper* srtpDumper(new TransportLayerPacketDumper( - std::move(aPacketDumper), dom::mozPacketDumpType::Srtp)); - - aIceLayer->SetParameters(aIceCtx->GetStream(aId), aIsRtcp ? 2 : 1); - // TODO(bug 854518): Process errors. - (void)aIceLayer->Init(); - (void)aDtlsLayer->Init(); - (void)srtpDumper->Init(); - (void)aSrtpLayer->Init(); - aDtlsLayer->Chain(aIceLayer); - srtpDumper->Chain(aIceLayer); - aSrtpLayer->Chain(srtpDumper); - aFlow->PushLayer(aIceLayer); - aFlow->PushLayer(aDtlsLayer); - aFlow->PushLayer(srtpDumper); - aFlow->PushLayer(aSrtpLayer); -} - -nsresult -PeerConnectionMedia::UpdateTransportFlow(bool aIsRtcp, - const JsepTransport& aTransport) -{ - if (aIsRtcp && aTransport.mComponents < 2) { - RemoveTransportFlow(aTransport.mTransportId, aIsRtcp); - return NS_OK; - } - - if (!aIsRtcp && !aTransport.mComponents) { - RemoveTransportFlow(aTransport.mTransportId, aIsRtcp); - return NS_OK; - } - - MOZ_ASSERT(!aTransport.mTransportId.empty()); - - nsresult rv; - - RefPtr<TransportFlow> flow = GetTransportFlow(aTransport.mTransportId, aIsRtcp); - if (flow) { - return NS_OK; - } - - std::ostringstream osId; - osId << mParentHandle << ":" << aTransport.mTransportId << "," - << (aIsRtcp ? "rtcp" : "rtp"); - flow = new TransportFlow(osId.str()); - - // The media streams are made on STS so we need to defer setup. - auto ice = MakeUnique<TransportLayerIce>(); - auto dtls = MakeUnique<TransportLayerDtls>(); - auto srtp = MakeUnique<TransportLayerSrtp>(*dtls); - dtls->SetRole(aTransport.mDtls->GetRole() == - JsepDtlsTransport::kJsepDtlsClient - ? TransportLayerDtls::CLIENT - : TransportLayerDtls::SERVER); - - RefPtr<DtlsIdentity> pcid = mParent->Identity(); - if (!pcid) { - CSFLogError(LOGTAG, "Failed to get DTLS identity."); - return NS_ERROR_FAILURE; - } - dtls->SetIdentity(pcid); - - const SdpFingerprintAttributeList& fingerprints = - aTransport.mDtls->GetFingerprints(); - for (const auto& fingerprint : fingerprints.mFingerprints) { - std::ostringstream ss; - ss << fingerprint.hashFunc; - rv = dtls->SetVerificationDigest(ss.str(), &fingerprint.fingerprint[0], - fingerprint.fingerprint.size()); - if (NS_FAILED(rv)) { - CSFLogError(LOGTAG, "Could not set fingerprint"); - return rv; - } - } - - std::vector<uint16_t> srtpCiphers = TransportLayerDtls::GetDefaultSrtpCiphers(); - - rv = dtls->SetSrtpCiphers(srtpCiphers); - if (NS_FAILED(rv)) { - CSFLogError(LOGTAG, "Couldn't set SRTP ciphers"); - return rv; - } - - // Always permits negotiation of the confidential mode. - // Only allow non-confidential (which is an allowed default), - // if we aren't confidential. - std::set<std::string> alpn; - std::string alpnDefault = ""; - alpn.insert("c-webrtc"); - if (!mParent->PrivacyRequested()) { - alpnDefault = "webrtc"; - alpn.insert(alpnDefault); - } - rv = dtls->SetAlpn(alpn, alpnDefault); - if (NS_FAILED(rv)) { - CSFLogError(LOGTAG, "Couldn't set ALPN"); - return rv; - } - - nsAutoPtr<PacketDumper> packetDumper(new PacketDumper(mParent)); - - rv = GetSTSThread()->Dispatch( - WrapRunnableNM(FinalizeTransportFlow_s, mIceCtx, packetDumper, flow, - aTransport.mTransportId, aIsRtcp, - ice.release(), dtls.release(), srtp.release()), - NS_DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - CSFLogError(LOGTAG, "Failed to dispatch FinalizeTransportFlow_s"); - return rv; - } - - AddTransportFlow(aTransport.mTransportId, aIsRtcp, flow); - - return NS_OK; -} - void PeerConnectionMedia::StartIceChecks(const JsepSession& aSession) { nsCOMPtr<nsIRunnable> runnable( WrapRunnable( RefPtr<PeerConnectionMedia>(this), &PeerConnectionMedia::StartIceChecks_s, aSession.IsIceControlling(), @@ -811,129 +380,80 @@ PeerConnectionMedia::StartIceChecks_s( if (!aIceOptionsList.empty()) { attributes.push_back("ice-options:"); for (const auto& option : aIceOptionsList) { attributes.back() += option + ' '; } } - nsresult rv = mIceCtx->ParseGlobalAttributes(attributes); + nsresult rv = mTransportHandler->StartIceChecks(aIsControlling, + aIsOfferer, + attributes); if (NS_FAILED(rv)) { - CSFLogError(LOGTAG, "%s: couldn't parse global parameters", __FUNCTION__ ); + CSFLogError(LOGTAG, "%s: couldn't start ICE", __FUNCTION__ ); } - - mIceCtx->SetControlling(aIsControlling ? - NrIceCtx::ICE_CONTROLLING : - NrIceCtx::ICE_CONTROLLED); - - mIceCtx->StartChecks(aIsOfferer); } bool PeerConnectionMedia::GetPrefDefaultAddressOnly() const { ASSERT_ON_THREAD(mMainThread); // will crash on STS thread uint64_t winId = mParent->GetWindow()->WindowID(); bool default_address_only = Preferences::GetBool( "media.peerconnection.ice.default_address_only", false); default_address_only |= !MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId); return default_address_only; } -bool -PeerConnectionMedia::GetPrefProxyOnly() const +void +PeerConnectionMedia::ConnectSignals() { - ASSERT_ON_THREAD(mMainThread); // will crash on STS thread - - return Preferences::GetBool("media.peerconnection.ice.proxy_only", false); + mTransportHandler->SignalGatheringStateChange.connect( + this, + &PeerConnectionMedia::IceGatheringStateChange_s); + mTransportHandler->SignalConnectionStateChange.connect( + this, + &PeerConnectionMedia::IceConnectionStateChange_s); + mTransportHandler->SignalCandidate.connect( + this, + &PeerConnectionMedia::OnCandidateFound_s); + mTransportHandler->SignalAlpnNegotiated.connect( + this, + &PeerConnectionMedia::AlpnNegotiated_s); } void -PeerConnectionMedia::ConnectSignals(NrIceCtx *aCtx, NrIceCtx *aOldCtx) +PeerConnectionMedia::AddIceCandidate(const std::string& aCandidate, + const std::string& aTransportId) { - aCtx->SignalGatheringStateChange.connect( - this, - &PeerConnectionMedia::IceGatheringStateChange_s); - aCtx->SignalConnectionStateChange.connect( - this, - &PeerConnectionMedia::IceConnectionStateChange_s); - - if (aOldCtx) { - MOZ_ASSERT(aCtx != aOldCtx); - aOldCtx->SignalGatheringStateChange.disconnect(this); - aOldCtx->SignalConnectionStateChange.disconnect(this); - - // if the old and new connection state and/or gathering state is - // different fire the state update. Note: we don't fire the update - // if the state is *INIT since updates for the INIT state aren't - // sent during the normal flow. (mjf) - if (aOldCtx->connection_state() != aCtx->connection_state() && - aCtx->connection_state() != NrIceCtx::ICE_CTX_INIT) { - aCtx->SignalConnectionStateChange(aCtx, aCtx->connection_state()); - } - - if (aOldCtx->gathering_state() != aCtx->gathering_state() && - aCtx->gathering_state() != NrIceCtx::ICE_CTX_GATHER_INIT) { - aCtx->SignalGatheringStateChange(aCtx, aCtx->gathering_state()); - } - } -} - -void -PeerConnectionMedia::AddIceCandidate(const std::string& candidate, - const std::string& aTransportId) { MOZ_ASSERT(!aTransportId.empty()); RUN_ON_THREAD(GetSTSThread(), WrapRunnable( - RefPtr<PeerConnectionMedia>(this), - &PeerConnectionMedia::AddIceCandidate_s, - std::string(candidate), // Make copies. - std::string(aTransportId)), + mTransportHandler, + &MediaTransportHandler::AddIceCandidate, + aTransportId, + aCandidate), NS_DISPATCH_NORMAL); } void -PeerConnectionMedia::AddIceCandidate_s(const std::string& aCandidate, - const std::string& aTransportId) { - RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId)); - if (!stream) { - CSFLogError(LOGTAG, "No ICE stream for candidate with transport id %s: %s", - aTransportId.c_str(), aCandidate.c_str()); - return; - } - - nsresult rv = stream->ParseTrickleCandidate(aCandidate); - if (NS_FAILED(rv)) { - CSFLogError(LOGTAG, "Couldn't process ICE candidate with transport id %s: " - "%s", - aTransportId.c_str(), aCandidate.c_str()); - return; - } -} - -void PeerConnectionMedia::UpdateNetworkState(bool online) { RUN_ON_THREAD(GetSTSThread(), WrapRunnable( - RefPtr<PeerConnectionMedia>(this), - &PeerConnectionMedia::UpdateNetworkState_s, + mTransportHandler, + &MediaTransportHandler::UpdateNetworkState, online), NS_DISPATCH_NORMAL); } void -PeerConnectionMedia::UpdateNetworkState_s(bool online) { - mIceCtx->UpdateNetworkState(online); -} - -void PeerConnectionMedia::FlushIceCtxOperationQueueIfReady() { ASSERT_ON_THREAD(mMainThread); if (IsIceCtxReady()) { for (auto& mQueuedIceCtxOperation : mQueuedIceCtxOperations) { GetSTSThread()->Dispatch(mQueuedIceCtxOperation, NS_DISPATCH_NORMAL); } @@ -952,72 +472,51 @@ PeerConnectionMedia::PerformOrEnqueueIce mQueuedIceCtxOperations.push_back(runnable); } } void PeerConnectionMedia::GatherIfReady() { ASSERT_ON_THREAD(mMainThread); + // If we had previously queued gathering or ICE start, unqueue them + mQueuedIceCtxOperations.clear(); nsCOMPtr<nsIRunnable> runnable(WrapRunnable( RefPtr<PeerConnectionMedia>(this), &PeerConnectionMedia::EnsureIceGathering_s, - GetPrefDefaultAddressOnly(), - GetPrefProxyOnly())); + GetPrefDefaultAddressOnly())); PerformOrEnqueueIceCtxOperation(runnable); } void -PeerConnectionMedia::EnsureIceGathering_s(bool aDefaultRouteOnly, - bool aProxyOnly) { - if (mProxyServer) { - mIceCtx->SetProxyServer(*mProxyServer); - } else if (aProxyOnly) { - IceGatheringStateChange_s(mIceCtx.get(), - NrIceCtx::ICE_CTX_GATHER_COMPLETE); - return; +PeerConnectionMedia::EnsureIceGathering_s(bool aDefaultRouteOnly) { + if (!mProxyHost.empty()) { + // Note that this could check if PrivacyRequested() is set on the PC and + // remove "webrtc" from the ALPN list. But that would only work if the PC + // was constructed with a peerIdentity constraint, not when isolated + // streams are added. If we ever need to signal to the proxy that the + // media is isolated, then we would need to restructure this code. + mTransportHandler->SetProxyServer( + mProxyHost, mProxyPort, "webrtc,c-webrtc"); } - // Make sure we don't call NrIceCtx::StartGathering if we're in e10s mode + // Make sure we don't call StartIceGathering if we're in e10s mode // and we received no STUN addresses from the parent process. In the - // absence of previously provided STUN addresses, StartGathering will + // absence of previously provided STUN addresses, StartIceGathering will // attempt to gather them (as in non-e10s mode), and this will cause a // sandboxing exception in e10s mode. if (!mStunAddrs.Length() && XRE_IsContentProcess()) { CSFLogInfo(LOGTAG, "%s: No STUN addresses returned from parent process", __FUNCTION__); return; } - // Belt and suspenders - in e10s mode, the call below to SetStunAddrs - // needs to have the proper flags set on ice ctx. For non-e10s, - // setting those flags happens in StartGathering. We could probably - // just set them here, and only do it here. - mIceCtx->SetCtxFlags(aDefaultRouteOnly, aProxyOnly); - - if (mStunAddrs.Length()) { - mIceCtx->SetStunAddrs(mStunAddrs); - } - - // Start gathering, but only if there are streams - if (!mIceCtx->GetStreams().empty()) { - mIceCtx->StartGathering(aDefaultRouteOnly, aProxyOnly); - return; - } - - CSFLogWarn(LOGTAG, - "%s: No streams to start gathering on. Can happen with rollback", - __FUNCTION__); - // If there are no streams, we're probably in a situation where we've rolled - // back while still waiting for our proxy configuration to come back. Make - // sure content knows that the rollback has stuck wrt gathering. - IceGatheringStateChange_s(mIceCtx.get(), - NrIceCtx::ICE_CTX_GATHER_COMPLETE); + mTransportHandler->StartIceGathering(aDefaultRouteOnly, mStunAddrs); } void PeerConnectionMedia::SelfDestruct() { ASSERT_ON_THREAD(mMainThread); CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__); @@ -1066,197 +565,39 @@ PeerConnectionMedia::SelfDestruct_m() void PeerConnectionMedia::ShutdownMediaTransport_s() { ASSERT_ON_THREAD(mSTSThread); CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__); disconnect_all(); - mTransportFlows.clear(); -#if !defined(MOZILLA_EXTERNAL_LINKAGE) - NrIceStats stats = mIceCtx->Destroy(); - - CSFLogDebug(LOGTAG, "Ice Telemetry: stun (retransmits: %d)" - " turn (401s: %d 403s: %d 438s: %d)", - stats.stun_retransmits, stats.turn_401s, stats.turn_403s, - stats.turn_438s); - - Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_STUN_RETRANSMITS, - stats.stun_retransmits); - Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_401S, - stats.turn_401s); - Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_403S, - stats.turn_403s); - Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_438S, - stats.turn_438s); -#endif - - mIceCtx = nullptr; + mTransportHandler->Destroy(); + mTransportHandler = nullptr; // we're holding a ref to 'this' that's released by SelfDestruct_m mMainThread->Dispatch(WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m), NS_DISPATCH_NORMAL); } - -void -PeerConnectionMedia::GetIceStats_s( - const std::string& aTransportId, - bool internalStats, - DOMHighResTimeStamp now, - RTCStatsReportInternal* report) const -{ - if (mIceCtx) { - auto stream = mIceCtx->GetStream(aTransportId); - if (stream) { - GetIceStats_s(*stream, internalStats, now, report); - } - } -} - -void -PeerConnectionMedia::GetAllIceStats_s( - bool internalStats, - DOMHighResTimeStamp now, - RTCStatsReportInternal* report) const -{ - if (mIceCtx) { - for (const auto& stream : mIceCtx->GetStreams()) { - GetIceStats_s(*stream, internalStats, now, report); - } - } -} - -static void ToRTCIceCandidateStats( - const std::vector<NrIceCandidate>& candidates, - RTCStatsType candidateType, - const nsString& componentId, - DOMHighResTimeStamp now, - RTCStatsReportInternal* report) { - - MOZ_ASSERT(report); - for (const auto& candidate : candidates) { - RTCIceCandidateStats cand; - cand.mType.Construct(candidateType); - NS_ConvertASCIItoUTF16 codeword(candidate.codeword.c_str()); - cand.mComponentId.Construct(componentId); - cand.mId.Construct(codeword); - cand.mTimestamp.Construct(now); - cand.mCandidateType.Construct( - RTCStatsIceCandidateType(candidate.type)); - cand.mIpAddress.Construct( - NS_ConvertASCIItoUTF16(candidate.cand_addr.host.c_str())); - cand.mPortNumber.Construct(candidate.cand_addr.port); - cand.mTransport.Construct( - NS_ConvertASCIItoUTF16(candidate.cand_addr.transport.c_str())); - if (candidateType == RTCStatsType::Local_candidate) { - cand.mMozLocalTransport.Construct( - NS_ConvertASCIItoUTF16(candidate.local_addr.transport.c_str())); - if (RTCStatsIceCandidateType(candidate.type) == RTCStatsIceCandidateType::Relayed) { - cand.mRelayProtocol.Construct( - NS_ConvertASCIItoUTF16(candidate.local_addr.transport.c_str())); - } - } - report->mIceCandidateStats.Value().AppendElement(cand, fallible); - if (candidate.trickled) { - report->mTrickledIceCandidateStats.Value().AppendElement(cand, fallible); - } - } -} - -void -PeerConnectionMedia::GetIceStats_s( - const NrIceMediaStream& aStream, - bool internalStats, - DOMHighResTimeStamp now, - RTCStatsReportInternal* report) const -{ - NS_ConvertASCIItoUTF16 transportId(aStream.GetId().c_str()); - - std::vector<NrIceCandidatePair> candPairs; - nsresult res = aStream.GetCandidatePairs(&candPairs); - if (NS_FAILED(res)) { - CSFLogError(LOGTAG, - "%s: Error getting candidate pairs for transport id \"%s\"", - __FUNCTION__, aStream.GetId().c_str()); - return; - } - - for (auto& candPair : candPairs) { - NS_ConvertASCIItoUTF16 codeword(candPair.codeword.c_str()); - NS_ConvertASCIItoUTF16 localCodeword(candPair.local.codeword.c_str()); - NS_ConvertASCIItoUTF16 remoteCodeword(candPair.remote.codeword.c_str()); - // Only expose candidate-pair statistics to chrome, until we've thought - // through the implications of exposing it to content. - - RTCIceCandidatePairStats s; - s.mId.Construct(codeword); - s.mTransportId.Construct(transportId); - s.mTimestamp.Construct(now); - s.mType.Construct(RTCStatsType::Candidate_pair); - s.mLocalCandidateId.Construct(localCodeword); - s.mRemoteCandidateId.Construct(remoteCodeword); - s.mNominated.Construct(candPair.nominated); - s.mWritable.Construct(candPair.writable); - s.mReadable.Construct(candPair.readable); - s.mPriority.Construct(candPair.priority); - s.mSelected.Construct(candPair.selected); - s.mBytesSent.Construct(candPair.bytes_sent); - s.mBytesReceived.Construct(candPair.bytes_recvd); - s.mLastPacketSentTimestamp.Construct(candPair.ms_since_last_send); - s.mLastPacketReceivedTimestamp.Construct(candPair.ms_since_last_recv); - s.mState.Construct(RTCStatsIceCandidatePairState(candPair.state)); - s.mComponentId.Construct(candPair.component_id); - report->mIceCandidatePairStats.Value().AppendElement(s, fallible); - } - - std::vector<NrIceCandidate> candidates; - if (NS_SUCCEEDED(aStream.GetLocalCandidates(&candidates))) { - ToRTCIceCandidateStats(candidates, - RTCStatsType::Local_candidate, - transportId, - now, - report); - // add the local candidates unparsed string to a sequence - for (const auto& candidate : candidates) { - report->mRawLocalCandidates.Value().AppendElement( - NS_ConvertASCIItoUTF16(candidate.label.c_str()), fallible); - } - } - candidates.clear(); - - if (NS_SUCCEEDED(aStream.GetRemoteCandidates(&candidates))) { - ToRTCIceCandidateStats(candidates, - RTCStatsType::Remote_candidate, - transportId, - now, - report); - // add the remote candidates unparsed string to a sequence - for (const auto& candidate : candidates) { - report->mRawRemoteCandidates.Value().AppendElement( - NS_ConvertASCIItoUTF16(candidate.label.c_str()), fallible); - } - } -} - nsresult PeerConnectionMedia::AddTransceiver( JsepTransceiver* aJsepTransceiver, dom::MediaStreamTrack& aReceiveTrack, dom::MediaStreamTrack* aSendTrack, RefPtr<TransceiverImpl>* aTransceiverImpl) { if (!mCall) { mCall = WebRtcCallWrapper::Create(); } RefPtr<TransceiverImpl> transceiver = new TransceiverImpl( mParent->GetHandle(), + mTransportHandler, aJsepTransceiver, mMainThread.get(), mSTSThread.get(), &aReceiveTrack, aSendTrack, mCall.get()); if (!transceiver->IsValid()) { @@ -1345,291 +686,118 @@ PeerConnectionMedia::AddRIDFilter(MediaS trackFound = true; } } MOZ_ASSERT(trackFound); return NS_OK; } void -PeerConnectionMedia::IceGatheringStateChange_s(NrIceCtx* ctx, - NrIceCtx::GatheringState state) +PeerConnectionMedia::IceGatheringStateChange_s( + dom::PCImplIceGatheringState aState) { ASSERT_ON_THREAD(mSTSThread); - if (state == NrIceCtx::ICE_CTX_GATHER_COMPLETE) { - // Fire off EndOfLocalCandidates for each stream - for (auto& stream : ctx->GetStreams()) { - NrIceCandidate candidate; - NrIceCandidate rtcpCandidate; - GetDefaultCandidates(*stream, &candidate, &rtcpCandidate); - EndOfLocalCandidates(candidate.cand_addr.host, - candidate.cand_addr.port, - rtcpCandidate.cand_addr.host, - rtcpCandidate.cand_addr.port, - stream->GetId()); - } - } - // ShutdownMediaTransport_s has not run yet because it unhooks this function // from its signal, which means that SelfDestruct_m has not been dispatched // yet either, so this PCMedia will still be around when this dispatch reaches // main. GetMainThread()->Dispatch( WrapRunnable(this, &PeerConnectionMedia::IceGatheringStateChange_m, - ctx, - state), + aState), NS_DISPATCH_NORMAL); } void -PeerConnectionMedia::IceConnectionStateChange_s(NrIceCtx* ctx, - NrIceCtx::ConnectionState state) +PeerConnectionMedia::IceConnectionStateChange_s( + dom::PCImplIceConnectionState aState) { ASSERT_ON_THREAD(mSTSThread); // ShutdownMediaTransport_s has not run yet because it unhooks this function // from its signal, which means that SelfDestruct_m has not been dispatched // yet either, so this PCMedia will still be around when this dispatch reaches // main. GetMainThread()->Dispatch( WrapRunnable(this, - &PeerConnectionMedia::IceConnectionStateChange_m, - ctx, - state), + &PeerConnectionMedia::IceConnectionStateChange_m, aState), NS_DISPATCH_NORMAL); } void -PeerConnectionMedia::OnCandidateFound_s(NrIceMediaStream *aStream, - const std::string &aCandidateLine) +PeerConnectionMedia::OnCandidateFound_s( + const std::string& aTransportId, + const MediaTransportHandler::CandidateInfo& aCandidateInfo) { ASSERT_ON_THREAD(mSTSThread); - MOZ_ASSERT(aStream); - MOZ_ASSERT(!aStream->GetId().empty()); - MOZ_RELEASE_ASSERT(mIceCtx); + MOZ_RELEASE_ASSERT(mTransportHandler); - CSFLogDebug(LOGTAG, "%s: %s", __FUNCTION__, aStream->name().c_str()); - - NrIceCandidate candidate; - NrIceCandidate rtcpCandidate; - GetDefaultCandidates(*aStream, &candidate, &rtcpCandidate); + CSFLogDebug(LOGTAG, "%s: %s", __FUNCTION__, aTransportId.c_str()); // ShutdownMediaTransport_s has not run yet because it unhooks this function // from its signal, which means that SelfDestruct_m has not been dispatched // yet either, so this PCMedia will still be around when this dispatch reaches // main. GetMainThread()->Dispatch( WrapRunnable(this, &PeerConnectionMedia::OnCandidateFound_m, - aCandidateLine, - candidate.cand_addr.host, - candidate.cand_addr.port, - rtcpCandidate.cand_addr.host, - rtcpCandidate.cand_addr.port, - aStream->GetId()), - NS_DISPATCH_NORMAL); -} - -void -PeerConnectionMedia::EndOfLocalCandidates(const std::string& aDefaultAddr, - uint16_t aDefaultPort, - const std::string& aDefaultRtcpAddr, - uint16_t aDefaultRtcpPort, - const std::string& aTransportId) -{ - GetMainThread()->Dispatch( - WrapRunnable(this, - &PeerConnectionMedia::EndOfLocalCandidates_m, - aDefaultAddr, - aDefaultPort, - aDefaultRtcpAddr, - aDefaultRtcpPort, - aTransportId), + aTransportId, + aCandidateInfo), NS_DISPATCH_NORMAL); } void -PeerConnectionMedia::GetDefaultCandidates(const NrIceMediaStream& aStream, - NrIceCandidate* aCandidate, - NrIceCandidate* aRtcpCandidate) +PeerConnectionMedia::IceGatheringStateChange_m( + dom::PCImplIceGatheringState aState) { - nsresult res = aStream.GetDefaultCandidate(1, aCandidate); - // Optional; component won't exist if doing rtcp-mux - if (NS_FAILED(aStream.GetDefaultCandidate(2, aRtcpCandidate))) { - aRtcpCandidate->cand_addr.host.clear(); - aRtcpCandidate->cand_addr.port = 0; - } - if (NS_FAILED(res)) { - aCandidate->cand_addr.host.clear(); - aCandidate->cand_addr.port = 0; - CSFLogError(LOGTAG, "%s: GetDefaultCandidates failed for transport id %s, " - "res=%u", - __FUNCTION__, - aStream.GetId().c_str(), - static_cast<unsigned>(res)); - } -} - -static mozilla::dom::PCImplIceConnectionState -toDomIceConnectionState(NrIceCtx::ConnectionState state) { - switch (state) { - case NrIceCtx::ICE_CTX_INIT: - return PCImplIceConnectionState::New; - case NrIceCtx::ICE_CTX_CHECKING: - return PCImplIceConnectionState::Checking; - case NrIceCtx::ICE_CTX_CONNECTED: - return PCImplIceConnectionState::Connected; - case NrIceCtx::ICE_CTX_COMPLETED: - return PCImplIceConnectionState::Completed; - case NrIceCtx::ICE_CTX_FAILED: - return PCImplIceConnectionState::Failed; - case NrIceCtx::ICE_CTX_DISCONNECTED: - return PCImplIceConnectionState::Disconnected; - case NrIceCtx::ICE_CTX_CLOSED: - return PCImplIceConnectionState::Closed; - } - MOZ_CRASH(); -} - -static mozilla::dom::PCImplIceGatheringState -toDomIceGatheringState(NrIceCtx::GatheringState state) { - switch (state) { - case NrIceCtx::ICE_CTX_GATHER_INIT: - return PCImplIceGatheringState::New; - case NrIceCtx::ICE_CTX_GATHER_STARTED: - return PCImplIceGatheringState::Gathering; - case NrIceCtx::ICE_CTX_GATHER_COMPLETE: - return PCImplIceGatheringState::Complete; - } - MOZ_CRASH(); + ASSERT_ON_THREAD(mMainThread); + SignalIceGatheringStateChange(aState); } void -PeerConnectionMedia::IceGatheringStateChange_m(NrIceCtx* ctx, - NrIceCtx::GatheringState state) +PeerConnectionMedia::IceConnectionStateChange_m( + dom::PCImplIceConnectionState aState) { ASSERT_ON_THREAD(mMainThread); - SignalIceGatheringStateChange(toDomIceGatheringState(state)); + SignalIceConnectionStateChange(aState); } void -PeerConnectionMedia::IceConnectionStateChange_m(NrIceCtx* ctx, - NrIceCtx::ConnectionState state) +PeerConnectionMedia::OnCandidateFound_m( + const std::string& aTransportId, + const MediaTransportHandler::CandidateInfo& aCandidateInfo) { ASSERT_ON_THREAD(mMainThread); - SignalIceConnectionStateChange(toDomIceConnectionState(state)); -} - -void -PeerConnectionMedia::IceStreamReady_s(NrIceMediaStream *aStream) -{ - MOZ_ASSERT(aStream); - - CSFLogDebug(LOGTAG, "%s: %s", __FUNCTION__, aStream->name().c_str()); + if (!aCandidateInfo.mDefaultHostRtp.empty()) { + SignalUpdateDefaultCandidate(aCandidateInfo.mDefaultHostRtp, + aCandidateInfo.mDefaultPortRtp, + aCandidateInfo.mDefaultHostRtcp, + aCandidateInfo.mDefaultPortRtcp, + aTransportId); + } + SignalCandidate(aCandidateInfo.mCandidate, aTransportId); } void -PeerConnectionMedia::OnCandidateFound_m(const std::string& aCandidateLine, - const std::string& aDefaultAddr, - uint16_t aDefaultPort, - const std::string& aDefaultRtcpAddr, - uint16_t aDefaultRtcpPort, - const std::string& aTransportId) +PeerConnectionMedia::AlpnNegotiated_s(const std::string& aAlpn) { - ASSERT_ON_THREAD(mMainThread); - if (!aDefaultAddr.empty()) { - SignalUpdateDefaultCandidate(aDefaultAddr, - aDefaultPort, - aDefaultRtcpAddr, - aDefaultRtcpPort, - aTransportId); - } - SignalCandidate(aCandidateLine, aTransportId); -} - -void -PeerConnectionMedia::EndOfLocalCandidates_m(const std::string& aDefaultAddr, - uint16_t aDefaultPort, - const std::string& aDefaultRtcpAddr, - uint16_t aDefaultRtcpPort, - const std::string& aTransportId) { - ASSERT_ON_THREAD(mMainThread); - if (!aDefaultAddr.empty()) { - SignalUpdateDefaultCandidate(aDefaultAddr, - aDefaultPort, - aDefaultRtcpAddr, - aDefaultRtcpPort, - aTransportId); - } - SignalEndOfLocalCandidates(aTransportId); -} - -void -PeerConnectionMedia::DtlsConnected_s(TransportLayer *layer, - TransportLayer::State state) -{ - MOZ_ASSERT(layer->id() == "dtls"); - TransportLayerDtls* dtlsLayer = static_cast<TransportLayerDtls*>(layer); - dtlsLayer->SignalStateChange.disconnect(this); - - bool privacyRequested = (dtlsLayer->GetNegotiatedAlpn() == "c-webrtc"); GetMainThread()->Dispatch( - WrapRunnableNM(&PeerConnectionMedia::DtlsConnected_m, - mParentHandle, privacyRequested), + WrapRunnableNM(&PeerConnectionMedia::AlpnNegotiated_m, + mParentHandle, aAlpn), NS_DISPATCH_NORMAL); } void -PeerConnectionMedia::DtlsConnected_m(const std::string& aParentHandle, - bool aPrivacyRequested) +PeerConnectionMedia::AlpnNegotiated_m(const std::string& aParentHandle, + const std::string& aAlpn) { PeerConnectionWrapper pcWrapper(aParentHandle); PeerConnectionImpl* pc = pcWrapper.impl(); if (pc) { - pc->SetDtlsConnected(aPrivacyRequested); - } -} - -void -PeerConnectionMedia::AddTransportFlow(const std::string& aId, bool aRtcp, - const RefPtr<TransportFlow> &aFlow) -{ - auto& flows = aRtcp ? mRtcpTransportFlows : mTransportFlows; - - if (flows.count(aId)) { - MOZ_ASSERT(false); - return; - } - flows[aId] = aFlow; - - GetSTSThread()->Dispatch( - WrapRunnable(this, &PeerConnectionMedia::ConnectDtlsListener_s, aFlow), - NS_DISPATCH_NORMAL); -} - -void -PeerConnectionMedia::RemoveTransportFlow(const std::string& aId, bool aRtcp) -{ - auto& flows = aRtcp ? mRtcpTransportFlows : mTransportFlows; - auto it = flows.find(aId); - if (it != flows.end()) { - NS_ProxyRelease( - "PeerConnectionMedia::mTransportFlows[aId] or mRtcpTransportFlows[aId]", - GetSTSThread(), it->second.forget()); - flows.erase(it); - } -} - -void -PeerConnectionMedia::ConnectDtlsListener_s(const RefPtr<TransportFlow>& aFlow) -{ - TransportLayer* dtls = aFlow->GetLayer(TransportLayerDtls::ID()); - if (dtls) { - dtls->SignalStateChange.connect(this, &PeerConnectionMedia::DtlsConnected_s); + pc->OnAlpnNegotiated(aAlpn); } } /** * Tells you if any local track is isolated to a specific peer identity. * Obviously, we want all the tracks to be isolated equally so that they can * all be sent or not. We check once when we are setting a local description * and that determines if we flip the "privacy requested" bit on. Once the bit
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h @@ -8,27 +8,26 @@ #include <string> #include <vector> #include <map> #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" #include "mozilla/net/StunAddrsRequestChild.h" #include "nsIProtocolProxyCallback.h" +#include "MediaTransportHandler.h" #include "TransceiverImpl.h" class nsIPrincipal; namespace mozilla { class DataChannel; class PeerIdentity; namespace dom { -struct RTCInboundRTPStreamStats; -struct RTCOutboundRTPStreamStats; class MediaStreamTrack; } } #include "nriceresolver.h" #include "nricemediastream.h" namespace mozilla { @@ -44,29 +43,20 @@ class JsepSession; // will be a class that handles just the transport stuff, and we can rename it // to something more explanatory (say, PeerConnectionTransportManager). class PeerConnectionMedia : public sigslot::has_slots<> { ~PeerConnectionMedia(); public: explicit PeerConnectionMedia(PeerConnectionImpl *parent); - PeerConnectionImpl* GetPC() { return mParent; } nsresult Init(const dom::RTCConfiguration& aConfiguration); // WARNING: This destroys the object! void SelfDestruct(); - void GetIceStats_s(const std::string& aTransportId, - bool internalStats, - DOMHighResTimeStamp now, - RTCStatsReportInternal* report) const; - void GetAllIceStats_s(bool internalStats, - DOMHighResTimeStamp now, - RTCStatsReportInternal* report) const; - // Ensure ICE transports exist that we might need when offer/answer concludes void EnsureTransports(const JsepSession& aSession); // Activate ICE transports at the conclusion of offer/answer, // or when rollback occurs. nsresult UpdateTransports(const JsepSession& aSession, const bool forceIceTcp); @@ -124,63 +114,46 @@ class PeerConnectionMedia : public sigsl // on streams void UpdateRemoteStreamPrincipals_m(nsIPrincipal* aPrincipal); bool AnyCodecHasPluginID(uint64_t aPluginID); const nsCOMPtr<nsIThread>& GetMainThread() const { return mMainThread; } const nsCOMPtr<nsIEventTarget>& GetSTSThread() const { return mSTSThread; } - // Get a transport flow either RTP/RTCP for a particular stream - // A stream can be of audio/video/datachannel/budled(?) types - RefPtr<TransportFlow> GetTransportFlow(const std::string& aId, - bool aIsRtcp) { - auto& flows = aIsRtcp ? mRtcpTransportFlows : mTransportFlows; - auto it = flows.find(aId); - if (it == flows.end()) { - return nullptr; - } - - return it->second; - } - // Used by PCImpl in a couple of places. Might be good to move that code in // here. std::vector<RefPtr<TransceiverImpl>>& GetTransceivers() { return mTransceivers; } - // Add a transport flow - void AddTransportFlow(const std::string& aId, bool aRtcp, - const RefPtr<TransportFlow> &aFlow); - void RemoveTransportFlow(const std::string& aId, bool aRtcp); - void ConnectDtlsListener_s(const RefPtr<TransportFlow>& aFlow); - void DtlsConnected_s(TransportLayer* aFlow, - TransportLayer::State state); - static void DtlsConnected_m(const std::string& aParentHandle, - bool aPrivacyRequested); + void AlpnNegotiated_s(const std::string& aAlpn); + static void AlpnNegotiated_m(const std::string& aParentHandle, + const std::string& aAlpn); // ICE state signals sigslot::signal1<mozilla::dom::PCImplIceGatheringState> SignalIceGatheringStateChange; sigslot::signal1<mozilla::dom::PCImplIceConnectionState> SignalIceConnectionStateChange; // This passes a candidate:... attribute and transport id + // end-of-candidates is signaled with the empty string sigslot::signal2<const std::string&, const std::string&> SignalCandidate; // This passes address, port, transport id of the default candidate. sigslot::signal5<const std::string&, uint16_t, const std::string&, uint16_t, const std::string&> SignalUpdateDefaultCandidate; - sigslot::signal1<const std::string&> - SignalEndOfLocalCandidates; // TODO: Move to PeerConnectionImpl RefPtr<WebRtcCallWrapper> mCall; + // mtransport objects + RefPtr<MediaTransportHandler> mTransportHandler; + private: void InitLocalAddrs(); // for stun local address IPC request nsresult InitProxy(); class ProtocolProxyQueryHandler : public nsIProtocolProxyCallback { public: explicit ProtocolProxyQueryHandler(PeerConnectionMedia *pcm) : pcm_(pcm) {} @@ -210,119 +183,57 @@ class PeerConnectionMedia : public sigsl // Shutdown media transport. Must be called on STS thread. void ShutdownMediaTransport_s(); // Final destruction of the media stream. Must be called on the main // thread. void SelfDestruct_m(); // Manage ICE transports. - nsresult UpdateTransport(const JsepTransceiver& aTransceiver, - bool aForceIceTcp); - - void EnsureTransport_s(const std::string& aTransportId, - const std::string& aUfrag, - const std::string& aPwd, - size_t aComponentCount); - void ActivateTransport_s(const std::string& aTransportId, - const std::string& aLocalUfrag, - const std::string& aLocalPwd, - size_t aComponentCount, - const std::string& aUfrag, - const std::string& aPassword, - const std::vector<std::string>& aCandidateList); - void RemoveTransportsExcept_s(const std::set<std::string>& aTransportIds); - nsresult UpdateTransportFlows(const JsepTransceiver& transceiver); - nsresult UpdateTransportFlow(bool aIsRtcp, - const JsepTransport& aTransport); + void UpdateTransport(const JsepTransceiver& aTransceiver, + bool aForceIceTcp); void GatherIfReady(); void FlushIceCtxOperationQueueIfReady(); void PerformOrEnqueueIceCtxOperation(nsIRunnable* runnable); - void EnsureIceGathering_s(bool aDefaultRouteOnly, bool aProxyOnly); + void EnsureIceGathering_s(bool aDefaultRouteOnly); void StartIceChecks_s(bool aIsControlling, bool aIsOfferer, bool aIsIceLite, const std::vector<std::string>& aIceOptionsList); bool GetPrefDefaultAddressOnly() const; - bool GetPrefProxyOnly() const; - void ConnectSignals(NrIceCtx *aCtx, NrIceCtx *aOldCtx=nullptr); - - // Process a trickle ICE candidate. - void AddIceCandidate_s(const std::string& aCandidate, - const std::string& aTransportId); - - void UpdateNetworkState_s(bool online); + void ConnectSignals(); // ICE events - void IceGatheringStateChange_s(NrIceCtx* ctx, - NrIceCtx::GatheringState state); - void IceConnectionStateChange_s(NrIceCtx* ctx, - NrIceCtx::ConnectionState state); - void IceStreamReady_s(NrIceMediaStream *aStream); - void OnCandidateFound_s(NrIceMediaStream *aStream, - const std::string& aCandidate); - void EndOfLocalCandidates(const std::string& aDefaultAddr, - uint16_t aDefaultPort, - const std::string& aDefaultRtcpAddr, - uint16_t aDefaultRtcpPort, - const std::string& aTransportId); - void GetDefaultCandidates(const NrIceMediaStream& aStream, - NrIceCandidate* aCandidate, - NrIceCandidate* aRtcpCandidate); + void IceGatheringStateChange_s(dom::PCImplIceGatheringState aState); + void IceConnectionStateChange_s(dom::PCImplIceConnectionState aState); + void OnCandidateFound_s( + const std::string& aTransportId, + const MediaTransportHandler::CandidateInfo& aCandidateInfo); - void IceGatheringStateChange_m(NrIceCtx* ctx, - NrIceCtx::GatheringState state); - void IceConnectionStateChange_m(NrIceCtx* ctx, - NrIceCtx::ConnectionState state); - void OnCandidateFound_m(const std::string& aCandidateLine, - const std::string& aDefaultAddr, - uint16_t aDefaultPort, - const std::string& aDefaultRtcpAddr, - uint16_t aDefaultRtcpPort, - const std::string& aTransportId); - void EndOfLocalCandidates_m(const std::string& aDefaultAddr, - uint16_t aDefaultPort, - const std::string& aDefaultRtcpAddr, - uint16_t aDefaultRtcpPort, - const std::string& aTransportId); + void IceGatheringStateChange_m(dom::PCImplIceGatheringState aState); + void IceConnectionStateChange_m(dom::PCImplIceConnectionState aState); + void OnCandidateFound_m( + const std::string& aTransportId, + const MediaTransportHandler::CandidateInfo& aCandidateInfo); + bool IsIceCtxReady() const { return mProxyResolveCompleted && mLocalAddrsCompleted; } - void GetIceStats_s(const NrIceMediaStream& aStream, - bool internalStats, - DOMHighResTimeStamp now, - RTCStatsReportInternal* report) const; - // The parent PC PeerConnectionImpl *mParent; // and a loose handle on it for event driven stuff std::string mParentHandle; std::string mParentName; std::vector<RefPtr<TransceiverImpl>> mTransceivers; - // ICE objects - RefPtr<NrIceCtx> mIceCtx; - - // DNS - RefPtr<NrIceResolver> mDNSResolver; - - // Transport flows for RTP and RTP/RTCP mux - std::map<std::string, RefPtr<TransportFlow> > mTransportFlows; - - // Transport flows for standalone RTCP (rarely used) - std::map<std::string, RefPtr<TransportFlow> > mRtcpTransportFlows; - - // UUID Generator - UniquePtr<PCUuidGenerator> mUuidGen; - // The main thread. nsCOMPtr<nsIThread> mMainThread; // The STS thread. nsCOMPtr<nsIEventTarget> mSTSThread; // 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 @@ -332,17 +243,18 @@ class PeerConnectionMedia : public sigsl // Used to cancel any ongoing proxy request. nsCOMPtr<nsICancelable> mProxyRequest; // Used to track the state of the request. bool mProxyResolveCompleted; // Used to store the result of the request. - UniquePtr<NrIceProxyServer> mProxyServer; + std::string mProxyHost; + uint16_t mProxyPort; // Used to cancel incoming stun addrs response RefPtr<net::StunAddrsRequestChild> mStunAddrsRequest; // Used to track the state of the stun addr IPC request bool mLocalAddrsCompleted; // Used to store the result of the stun addr IPC request
--- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp @@ -1,53 +1,52 @@ /* 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 "TransceiverImpl.h" -#include "mtransport/runnable_utils.h" #include "mozilla/UniquePtr.h" -#include <sstream> #include <string> #include <vector> -#include <queue> #include "AudioConduit.h" #include "VideoConduit.h" #include "MediaStreamGraph.h" #include "MediaPipeline.h" #include "MediaPipelineFilter.h" #include "signaling/src/jsep/JsepTrack.h" #include "MediaStreamGraphImpl.h" #include "logging.h" #include "MediaEngine.h" #include "nsIPrincipal.h" #include "MediaSegment.h" #include "RemoteTrackSource.h" #include "MediaConduitInterface.h" -#include "PeerConnectionMedia.h" +#include "MediaTransportHandler.h" #include "mozilla/dom/RTCRtpReceiverBinding.h" #include "mozilla/dom/RTCRtpSenderBinding.h" #include "mozilla/dom/RTCRtpTransceiverBinding.h" #include "mozilla/dom/TransceiverImplBinding.h" namespace mozilla { MOZ_MTLOG_MODULE("transceiverimpl") using LocalDirection = MediaSessionConduitLocalDirection; TransceiverImpl::TransceiverImpl( const std::string& aPCHandle, + MediaTransportHandler* aTransportHandler, JsepTransceiver* aJsepTransceiver, nsIEventTarget* aMainThread, nsIEventTarget* aStsThread, dom::MediaStreamTrack* aReceiveTrack, dom::MediaStreamTrack* aSendTrack, WebRtcCallWrapper* aCallWrapper) : mPCHandle(aPCHandle), + mTransportHandler(aTransportHandler), mJsepTransceiver(aJsepTransceiver), mHaveStartedReceiving(false), mHaveSetupTransport(false), mMainThread(aMainThread), mStsThread(aStsThread), mReceiveTrack(aReceiveTrack), mSendTrack(aSendTrack), mCallWrapper(aCallWrapper) @@ -61,16 +60,17 @@ TransceiverImpl::TransceiverImpl( if (!IsValid()) { return; } mConduit->SetPCHandle(mPCHandle); mTransmitPipeline = new MediaPipelineTransmit( mPCHandle, + mTransportHandler, mMainThread.get(), mStsThread.get(), IsVideo(), mConduit); mTransmitPipeline->SetTrack(mSendTrack); } @@ -88,16 +88,17 @@ TransceiverImpl::InitAudio() ": Failed to create AudioSessionConduit"); // TODO(bug 1422897): We need a way to record this when it happens in the // wild. return; } mReceivePipeline = new MediaPipelineReceiveAudio( mPCHandle, + mTransportHandler, mMainThread.get(), mStsThread.get(), static_cast<AudioSessionConduit*>(mConduit.get()), mReceiveTrack); } void TransceiverImpl::InitVideo() @@ -109,16 +110,17 @@ TransceiverImpl::InitVideo() ": Failed to create VideoSessionConduit"); // TODO(bug 1422897): We need a way to record this when it happens in the // wild. return; } mReceivePipeline = new MediaPipelineReceiveVideo( mPCHandle, + mTransportHandler, mMainThread.get(), mStsThread.get(), static_cast<VideoSessionConduit*>(mConduit.get()), mReceiveTrack); } nsresult TransceiverImpl::UpdateSinkIdentity(const dom::MediaStreamTrack* aTrack, @@ -135,57 +137,53 @@ TransceiverImpl::UpdateSinkIdentity(cons void TransceiverImpl::Shutdown_m() { mReceivePipeline->Shutdown_m(); mTransmitPipeline->Shutdown_m(); mReceivePipeline = nullptr; mTransmitPipeline = nullptr; + mTransportHandler = nullptr; mSendTrack = nullptr; if (mConduit) { mConduit->DeleteStreams(); } mConduit = nullptr; - RUN_ON_THREAD(mStsThread, WrapRelease(mRtpFlow.forget()), NS_DISPATCH_NORMAL); - RUN_ON_THREAD(mStsThread, WrapRelease(mRtcpFlow.forget()), NS_DISPATCH_NORMAL); } nsresult TransceiverImpl::UpdateSendTrack(dom::MediaStreamTrack* aSendTrack) { if (mJsepTransceiver->IsStopped()) { return NS_ERROR_UNEXPECTED; } MOZ_MTLOG(ML_DEBUG, mPCHandle << "[" << mMid << "]: " << __FUNCTION__ << "(" << aSendTrack << ")"); mSendTrack = aSendTrack; return mTransmitPipeline->SetTrack(mSendTrack); } nsresult -TransceiverImpl::UpdateTransport(PeerConnectionMedia& aTransportManager) +TransceiverImpl::UpdateTransport() { if (!mJsepTransceiver->HasLevel()) { return NS_OK; } if (!mHaveSetupTransport) { mReceivePipeline->SetLevel(mJsepTransceiver->GetLevel()); mTransmitPipeline->SetLevel(mJsepTransceiver->GetLevel()); mHaveSetupTransport = true; } ASSERT_ON_THREAD(mMainThread); nsAutoPtr<MediaPipelineFilter> filter; - mRtpFlow = aTransportManager.GetTransportFlow(GetTransportId(), false); - mRtcpFlow = aTransportManager.GetTransportFlow(GetTransportId(), true); - if (mJsepTransceiver->HasBundleLevel() && mJsepTransceiver->mRecvTrack.GetNegotiatedDetails()) { filter = new MediaPipelineFilter; // Add remote SSRCs so we can distinguish which RTP packets actually // belong to this pipeline (also RTCP sender reports). for (unsigned int ssrc : mJsepTransceiver->mRecvTrack.GetSsrcs()) { filter->AddRemoteSSRC(ssrc); @@ -196,18 +194,20 @@ TransceiverImpl::UpdateTransport(PeerCon // Add unique payload types as a last-ditch fallback auto uniquePts = mJsepTransceiver->mRecvTrack.GetNegotiatedDetails()->GetUniquePayloadTypes(); for (unsigned char& uniquePt : uniquePts) { filter->AddUniquePT(uniquePt); } } - mReceivePipeline->UpdateTransport_m(mRtpFlow, mRtcpFlow, filter); - mTransmitPipeline->UpdateTransport_m(mRtpFlow, mRtcpFlow, nsAutoPtr<MediaPipelineFilter>()); + mReceivePipeline->UpdateTransport_m( + mJsepTransceiver->mTransport.mTransportId, filter); + mTransmitPipeline->UpdateTransport_m( + mJsepTransceiver->mTransport.mTransportId, filter); return NS_OK; } nsresult TransceiverImpl::UpdateConduit() { if (mJsepTransceiver->IsStopped()) { return NS_OK;
--- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.h +++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.h @@ -4,60 +4,59 @@ #ifndef _TRANSCEIVERIMPL_H_ #define _TRANSCEIVERIMPL_H_ #include <string> #include "mozilla/RefPtr.h" #include "nsCOMPtr.h" #include "nsIEventTarget.h" #include "nsTArray.h" -#include "mozilla/OwningNonNull.h" #include "mozilla/dom/MediaStreamTrack.h" #include "ErrorList.h" -#include "mtransport/transportflow.h" #include "signaling/src/jsep/JsepTransceiver.h" class nsIPrincipal; namespace mozilla { class PeerIdentity; -class PeerConnectionMedia; class JsepTransceiver; enum class MediaSessionConduitLocalDirection : int; class MediaSessionConduit; class VideoSessionConduit; class AudioSessionConduit; class MediaPipelineReceive; class MediaPipelineTransmit; class MediaPipeline; class MediaPipelineFilter; +class MediaTransportHandler; class WebRtcCallWrapper; class JsepTrackNegotiatedDetails; namespace dom { class RTCRtpTransceiver; struct RTCRtpSourceEntry; } /** * This is what ties all the various pieces that make up a transceiver * together. This includes: * MediaStreamTrack for rendering and capture - * TransportFlow for RTP transmission/reception + * MediaTransportHandler for RTP transmission/reception * Audio/VideoConduit for feeding RTP/RTCP into webrtc.org for decoding, and * feeding audio/video frames into webrtc.org for encoding into RTP/RTCP. */ class TransceiverImpl : public nsISupports { public: /** * |aReceiveStream| is always set; this holds even if the remote end has not * negotiated one for this transceiver. |aSendTrack| might or might not be * set. */ TransceiverImpl(const std::string& aPCHandle, + MediaTransportHandler* aTransportHandler, JsepTransceiver* aJsepTransceiver, nsIEventTarget* aMainThread, nsIEventTarget* aStsThread, dom::MediaStreamTrack* aReceiveTrack, dom::MediaStreamTrack* aSendTrack, WebRtcCallWrapper* aCallWrapper); bool IsValid() const @@ -66,17 +65,17 @@ public: } nsresult UpdateSendTrack(dom::MediaStreamTrack* aSendTrack); nsresult UpdateSinkIdentity(const dom::MediaStreamTrack* aTrack, nsIPrincipal* aPrincipal, const PeerIdentity* aSinkIdentity); - nsresult UpdateTransport(PeerConnectionMedia& aTransportManager); + nsresult UpdateTransport(); nsresult UpdateConduit(); nsresult UpdatePrincipal(nsIPrincipal* aPrincipal); // TODO: We probably need to de-Sync when transceivers are stopped. nsresult SyncWithMatchingVideoConduits( std::vector<RefPtr<TransceiverImpl>>& transceivers); @@ -139,28 +138,27 @@ private: nsresult UpdateAudioConduit(); nsresult UpdateVideoConduit(); nsresult ConfigureVideoCodecMode(VideoSessionConduit& aConduit); void UpdateConduitRtpExtmap(const JsepTrackNegotiatedDetails& aDetails, const MediaSessionConduitLocalDirection aDir); void Stop(); const std::string mPCHandle; + RefPtr<MediaTransportHandler> mTransportHandler; RefPtr<JsepTransceiver> mJsepTransceiver; std::string mMid; bool mHaveStartedReceiving; bool mHaveSetupTransport; nsCOMPtr<nsIEventTarget> mMainThread; nsCOMPtr<nsIEventTarget> mStsThread; RefPtr<dom::MediaStreamTrack> mReceiveTrack; RefPtr<dom::MediaStreamTrack> mSendTrack; // state for webrtc.org that is shared between all transceivers RefPtr<WebRtcCallWrapper> mCallWrapper; - RefPtr<TransportFlow> mRtpFlow; - RefPtr<TransportFlow> mRtcpFlow; RefPtr<MediaSessionConduit> mConduit; RefPtr<MediaPipelineReceive> mReceivePipeline; RefPtr<MediaPipelineTransmit> mTransmitPipeline; }; } // namespace mozilla #endif // _TRANSCEIVERIMPL_H_
--- a/media/webrtc/signaling/src/peerconnection/moz.build +++ b/media/webrtc/signaling/src/peerconnection/moz.build @@ -17,16 +17,17 @@ LOCAL_INCLUDES += [ '/media/webrtc/signaling/src/common/time_profiling', '/media/webrtc/signaling/src/media-conduit', '/media/webrtc/signaling/src/mediapipeline', '/media/webrtc/trunk', '/netwerk/srtp/src/include', ] UNIFIED_SOURCES += [ + 'MediaTransportHandler.cpp', 'PacketDumper.cpp', 'PeerConnectionCtx.cpp', 'PeerConnectionImpl.cpp', 'PeerConnectionMedia.cpp', 'TransceiverImpl.cpp', 'WebrtcGlobalInformation.cpp', ]
--- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -45,16 +45,18 @@ #include "nsAutoPtr.h" #include "nsNetUtil.h" #include "nsNetCID.h" #include "mozilla/StaticPtr.h" #include "mozilla/StaticMutex.h" #include "mozilla/Unused.h" #ifdef MOZ_PEERCONNECTION #include "mtransport/runnable_utils.h" +#include "signaling/src/peerconnection/MediaTransportHandler.h" +#include "mediapacket.h" #endif #define DATACHANNEL_LOG(args) LOG(args) #include "DataChannel.h" #include "DataChannelProtocol.h" // Let us turn on and off important assertions in non-debug builds #ifdef DEBUG @@ -299,31 +301,32 @@ debug_printf(const char *format, ...) #endif SCTP_LOG(("%s", buffer)); } va_end(ap); } } DataChannelConnection::DataChannelConnection(DataConnectionListener *listener, - nsIEventTarget *aTarget) + nsIEventTarget *aTarget, + MediaTransportHandler* aHandler) : NeckoTargetHolder(aTarget) , mLock("netwerk::sctp::DataChannelConnection") , mSendInterleaved(false) , mPpidFragmentation(false) , mMaxMessageSizeSet(false) , mMaxMessageSize(0) , mAllocateEven(false) + , mTransportHandler(aHandler) { mCurrentStream = 0; mState = CLOSED; mSocket = nullptr; mMasterSocket = nullptr; mListener = listener; - mDtls = nullptr; mLocalPort = 0; mRemotePort = 0; mPendingType = PENDING_NONE; LOG(("Constructor DataChannelConnection=%p, listener=%p", this, mListener.get())); mInternalIOThread = nullptr; #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED mShutdown = false; #endif @@ -331,20 +334,17 @@ DataChannelConnection::DataChannelConnec DataChannelConnection::~DataChannelConnection() { LOG(("Deleting DataChannelConnection %p", (void *) this)); // This may die on the MainThread, or on the STS thread ASSERT_WEBRTC(mState == CLOSED); MOZ_ASSERT(!mMasterSocket); MOZ_ASSERT(mPending.GetSize() == 0); - MOZ_ASSERT(!mDtls); - - // Already disconnected from sigslot/mTransportFlow - // TransportFlows must be released from the STS thread + if (!IsSTSThread()) { ASSERT_WEBRTC(NS_IsMainThread()); if (mInternalIOThread) { // Avoid spinning the event thread from here (which if we're mainthread // is in the event loop already) nsCOMPtr<nsIRunnable> r = WrapRunnable(nsCOMPtr<nsIThread>(mInternalIOThread), &nsIThread::Shutdown); @@ -416,18 +416,17 @@ void DataChannelConnection::DestroyOnSTS // DON'T use RUN_ON_THREAD, it queue-jumps! mSTS->Dispatch(WrapRunnable(RefPtr<DataChannelConnection>(this), &DataChannelConnection::DestroyOnSTSFinal), NS_DISPATCH_NORMAL); } void DataChannelConnection::DestroyOnSTSFinal() { - mTransportFlow = nullptr; - mDtls = nullptr; + mTransportHandler = nullptr; sDataChannelShutdown->CreateConnectionShutdown(this); } bool DataChannelConnection::Init(unsigned short aPort, uint16_t aNumStreams, bool aMaxMessageSizeSet, uint64_t aMaxMessageSize) { struct sctp_initmsg initmsg; @@ -681,70 +680,81 @@ DataChannelConnection::SetMaxMessageSize uint64_t DataChannelConnection::GetMaxMessageSize() { return mMaxMessageSize; } #ifdef MOZ_PEERCONNECTION -void -DataChannelConnection::SetEvenOdd() -{ - ASSERT_WEBRTC(IsSTSThread()); - - MOZ_ASSERT(mDtls); // DTLS is mandatory - mAllocateEven = (mDtls->role() == TransportLayerDtls::CLIENT); -} bool -DataChannelConnection::ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport) +DataChannelConnection::ConnectToTransport( + const std::string& aTransportId, + bool aClient, + uint16_t localport, uint16_t remoteport) { LOG(("Connect DTLS local %u, remote %u", localport, remoteport)); - MOZ_ASSERT(mMasterSocket, "SCTP wasn't initialized before ConnectViaTransportFlow!"); - if (NS_WARN_IF(!aFlow)) { + MOZ_ASSERT(mMasterSocket, "SCTP wasn't initialized before ConnectToTransport!"); + if (NS_WARN_IF(aTransportId.empty())) { return false; } - mTransportFlow = aFlow; mLocalPort = localport; mRemotePort = remoteport; mState = CONNECTING; RUN_ON_THREAD(mSTS, WrapRunnable(RefPtr<DataChannelConnection>(this), - &DataChannelConnection::SetSignals), + &DataChannelConnection::SetSignals, + aTransportId, + aClient), NS_DISPATCH_NORMAL); return true; } void -DataChannelConnection::SetSignals() +DataChannelConnection::SetSignals(const std::string& aTransportId, + bool aClient) { ASSERT_WEBRTC(IsSTSThread()); - mDtls = static_cast<TransportLayerDtls*>(mTransportFlow->GetLayer("dtls")); - ASSERT_WEBRTC(mDtls); - LOG(("Setting transport signals, state: %d", mDtls->state())); - mDtls->SignalPacketReceived.connect(this, &DataChannelConnection::SctpDtlsInput); + mTransportId = aTransportId; + mAllocateEven = aClient; + mTransportHandler->SignalPacketReceived.connect( + this, &DataChannelConnection::SctpDtlsInput); // SignalStateChange() doesn't call you with the initial state - mDtls->SignalStateChange.connect(this, &DataChannelConnection::CompleteConnect); - CompleteConnect(mDtls, mDtls->state()); + if (mTransportHandler->GetState(mTransportId, false) + == TransportLayer::TS_OPEN) { + LOG(("Setting transport signals, dtls already open")); + CompleteConnect(); + } else { + LOG(("Setting transport signals, dtls not open yet")); + mTransportHandler->SignalStateChange.connect( + this, &DataChannelConnection::TransportStateChange); + } } void -DataChannelConnection::CompleteConnect(TransportLayer *layer, TransportLayer::State state) +DataChannelConnection::TransportStateChange(const std::string& aTransportId, + TransportLayer::State aState) { - LOG(("Data transport state: %d", state)); + if (aState == TransportLayer::TS_OPEN) { + CompleteConnect(); + } +} + +void +DataChannelConnection::CompleteConnect() +{ + LOG(("dtls open")); MutexAutoLock lock(mLock); ASSERT_WEBRTC(IsSTSThread()); - // We should abort connection on TS_ERROR. - // Note however that the association will also fail (perhaps with a delay) and - // notify us in that way - if (state != TransportLayer::TS_OPEN || !mMasterSocket) + if (!mMasterSocket) { return; + } struct sockaddr_conn addr; memset(&addr, 0, sizeof(addr)); addr.sconn_family = AF_CONN; #if defined(__Userspace_os_Darwin) addr.sconn_len = sizeof(addr); #endif addr.sconn_port = htons(mLocalPort); @@ -790,18 +800,17 @@ DataChannelConnection::CompleteConnect(T if (r < 0) { if (errno == EINPROGRESS) { // non-blocking return; } LOG(("usrsctp_connect failed: %d", errno)); mState = CLOSED; } else { - // We set Even/Odd and fire ON_CONNECTION via SCTP_COMM_UP when we get that - // This also avoids issues with calling TransportFlow stuff on Mainthread + // We fire ON_CONNECTION via SCTP_COMM_UP when we get that return; } } // Note: currently this doesn't actually notify the application Dispatch(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CONNECTION, this))); } @@ -833,18 +842,23 @@ DataChannelConnection::ProcessQueuedOpen } else { NS_ASSERTION(false, "How did a DataChannel get queued without the FINISH_OPEN flag?"); } } } void -DataChannelConnection::SctpDtlsInput(TransportLayer *layer, MediaPacket& packet) +DataChannelConnection::SctpDtlsInput(const std::string& aTransportId, + MediaPacket& packet) { + if ((packet.type() != MediaPacket::SCTP) || (mTransportId != aTransportId)) { + return; + } + if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)) { char *buf; if ((buf = usrsctp_dumppacket((void *)packet.data(), packet.len(), SCTP_DUMP_INBOUND)) != nullptr) { SCTP_LOG(("%s", buf)); usrsctp_freedumpbuffer(buf); @@ -854,18 +868,19 @@ DataChannelConnection::SctpDtlsInput(Tra MutexAutoLock lock(mLock); usrsctp_conninput(static_cast<void *>(this), packet.data(), packet.len(), 0); } int DataChannelConnection::SendPacket(nsAutoPtr<MediaPacket> packet) { //LOG(("%p: SCTP/DTLS sent %ld bytes", this, len)); - if (mDtls) { - return mDtls->SendPacket(*packet) < 0 ? 1 : 0; + if (!mTransportId.empty()) { + nsresult rv = mTransportHandler->SendPacket(mTransportId, *packet); + return NS_FAILED(rv) ? 1 : 0; } return 0; } /* static */ int DataChannelConnection::SctpDtlsOutput(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df) @@ -883,16 +898,17 @@ DataChannelConnection::SctpDtlsOutput(vo } // We're async proxying even if on the STSThread because this is called // with internal SCTP locks held in some cases (such as in usrsctp_connect()). // SCTP has an option for Apple, on IP connections only, to release at least // one of the locks before calling a packet output routine; with changes to // the underlying SCTP stack this might remove the need to use an async proxy. nsAutoPtr<MediaPacket> packet(new MediaPacket); + packet->SetType(MediaPacket::SCTP); packet->Copy(static_cast<const uint8_t*>(buffer), length); // XXX It might be worthwhile to add an assertion against the thread // somehow getting into the DataChannel/SCTP code again, as // DISPATCH_SYNC is not fully blocking. This may be tricky, as it // needs to be a per-thread check, not a global. peer->mSTS->Dispatch(WrapRunnable( RefPtr<DataChannelConnection>(peer), @@ -901,18 +917,16 @@ DataChannelConnection::SctpDtlsOutput(vo return 0; // cheat! Packets can always be dropped later anyways } #endif #ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT // listen for incoming associations // Blocks! - Don't call this from main thread! -#error This code will not work as-is since SetEvenOdd() runs on Mainthread - bool DataChannelConnection::Listen(unsigned short port) { struct sockaddr_in addr; socklen_t addr_len; NS_WARNING_ASSERTION(!NS_IsMainThread(), "Blocks, do not call from main thread!!!"); @@ -947,18 +961,16 @@ DataChannelConnection::Listen(unsigned s struct linger l; l.l_onoff = 1; l.l_linger = 0; if (usrsctp_setsockopt(mSocket, SOL_SOCKET, SO_LINGER, (const void *)&l, (socklen_t)sizeof(struct linger)) < 0) { LOG(("Couldn't set SO_LINGER on SCTP socket")); } - SetEvenOdd(); - // Notify Connection open // XXX We need to make sure connection sticks around until the message is delivered LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this)); Dispatch(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CONNECTION, this, (DataChannel *) nullptr))); return true; } @@ -1026,18 +1038,16 @@ DataChannelConnection::Connect(const cha } #endif mSocket = mMasterSocket; LOG(("connect() succeeded! Entering connected mode")); mState = OPEN; - SetEvenOdd(); - // Notify Connection open // XXX We need to make sure connection sticks around until the message is delivered LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this)); Dispatch(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CONNECTION, this, (DataChannel *) nullptr))); return true; } @@ -1805,18 +1815,16 @@ DataChannelConnection::HandleAssociation // Check for older Firefox by looking at the amount of incoming streams LOG(("Negotiated number of incoming streams: %" PRIu16, sac->sac_inbound_streams)); if (!mMaxMessageSizeSet && sac->sac_inbound_streams == WEBRTC_DATACHANNEL_STREAMS_OLDER_FIREFOX) { LOG(("Older Firefox detected, using PPID-based fragmentation")); mPpidFragmentation = true; } - SetEvenOdd(); - Dispatch(do_AddRef(new DataChannelOnMessageAvailable( DataChannelOnMessageAvailable::ON_CONNECTION, this))); LOG(("DTLS connect() succeeded! Entering connected mode")); // Open any streams pending... ProcessQueuedOpens();
--- a/netwerk/sctp/datachannel/DataChannel.h +++ b/netwerk/sctp/datachannel/DataChannel.h @@ -22,19 +22,17 @@ #include "nsDeque.h" #include "nsIInputStream.h" #include "mozilla/Mutex.h" #include "DataChannelProtocol.h" #include "DataChannelListener.h" #include "mozilla/net/NeckoTargetHolder.h" #ifdef SCTP_DTLS_SUPPORTED #include "mtransport/sigslot.h" -#include "mtransport/transportflow.h" -#include "mtransport/transportlayer.h" -#include "mtransport/transportlayerdtls.h" +#include "mtransport/transportlayer.h" // For TransportLayer::State #endif #ifndef DATACHANNEL_LOG #define DATACHANNEL_LOG(args) #endif #ifndef EALREADY #define EALREADY WSAEALREADY @@ -45,16 +43,18 @@ extern "C" { struct sctp_rcvinfo; } namespace mozilla { class DataChannelConnection; class DataChannel; class DataChannelOnMessageAvailable; +class MediaPacket; +class MediaTransportHandler; // For sending outgoing messages. // This class only holds a reference to the data and the info structure but does // not copy it. class OutgoingMsg { public: OutgoingMsg(struct sctp_sendv_spa &info, const uint8_t *data, @@ -140,17 +140,18 @@ public: MOZ_DECLARE_WEAKREFERENCE_TYPENAME(DataChannelConnection::DataConnectionListener) virtual ~DataConnectionListener() = default; // Called when a new DataChannel has been opened by the other side. virtual void NotifyDataChannel(already_AddRefed<DataChannel> channel) = 0; }; DataChannelConnection(DataConnectionListener *listener, - nsIEventTarget *aTarget); + nsIEventTarget *aTarget, + MediaTransportHandler* aTransportHandler); bool Init(unsigned short aPort, uint16_t aNumStreams, bool aMaxMessageSizeSet, uint64_t aMaxMessageSize); void Destroy(); // So we can spawn refs tied to runnables in shutdown // Finish Destroy on STS to avoid SCTP race condition with ABORT from far end void DestroyOnSTS(struct socket *aMasterSocket, struct socket *aSocket); @@ -163,21 +164,21 @@ public: // These block; they require something to decide on listener/connector // (though you can do simultaneous Connect()). Do not call these from // the main thread! bool Listen(unsigned short port); bool Connect(const char *addr, unsigned short port); #endif #ifdef SCTP_DTLS_SUPPORTED - // Connect using a TransportFlow (DTLS) channel - void SetEvenOdd(); - bool ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport); - void CompleteConnect(TransportLayer *layer, TransportLayer::State state); - void SetSignals(); + bool ConnectToTransport(const std::string& aTransportId, bool aClient, uint16_t localport, uint16_t remoteport); + void TransportStateChange(const std::string& aTransportId, + TransportLayer::State aState); + void CompleteConnect(); + void SetSignals(const std::string& aTransportId, bool aClient); #endif typedef enum { RELIABLE=0, PARTIAL_RELIABLE_REXMIT = 1, PARTIAL_RELIABLE_TIMED = 2 } Type; @@ -242,17 +243,17 @@ protected: WeakPtr<DataConnectionListener> mListener; private: friend class DataChannelConnectRunnable; #ifdef SCTP_DTLS_SUPPORTED static void DTLSConnectThread(void *data); int SendPacket(nsAutoPtr<MediaPacket> packet); - void SctpDtlsInput(TransportLayer *layer, MediaPacket& packet); + void SctpDtlsInput(const std::string& aTransportId, MediaPacket& packet); static int SctpDtlsOutput(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df); #endif DataChannel* FindChannelByStream(uint16_t stream); uint16_t FindFreeStream(); bool RequestMoreStreams(int32_t aNeeded = 16); uint32_t UpdateCurrentStreamIndex(); uint32_t GetCurrentStreamIndex(); int SendControlMessage(const uint8_t *data, uint32_t len, uint16_t stream); @@ -305,19 +306,16 @@ private: bool on = false; if (mSTS) { mSTS->IsOnCurrentThread(&on); } return on; } #endif - // Exists solely for proxying release of the TransportFlow to the STS thread - static void ReleaseTransportFlow(const RefPtr<TransportFlow>& aFlow) {} - bool mSendInterleaved; bool mPpidFragmentation; bool mMaxMessageSizeSet; uint64_t mMaxMessageSize; // Data: // NOTE: while this array will auto-expand, increases in the number of // channels available from the stack must be negotiated! @@ -333,18 +331,18 @@ private: // Streams pending reset AutoTArray<uint16_t,4> mStreamsResetting; struct socket *mMasterSocket; // accessed from STS thread struct socket *mSocket; // cloned from mMasterSocket on successful Connect on STS thread uint16_t mState; // Protected with mLock #ifdef SCTP_DTLS_SUPPORTED - RefPtr<TransportFlow> mTransportFlow; - TransportLayerDtls* mDtls; + std::string mTransportId; + RefPtr<MediaTransportHandler> mTransportHandler; nsCOMPtr<nsIEventTarget> mSTS; #endif uint16_t mLocalPort; // Accessed from connect thread uint16_t mRemotePort; nsCOMPtr<nsIThread> mInternalIOThread; uint8_t mPendingType; nsCString mRecvBuffer;
--- a/netwerk/sctp/datachannel/moz.build +++ b/netwerk/sctp/datachannel/moz.build @@ -15,16 +15,17 @@ SOURCES += [ ] include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ '/media/mtransport', + '/media/webrtc', '/netwerk/sctp/src', ] DEFINES['SCTP_DEBUG'] = 1 if CONFIG['OS_TARGET'] == 'WINNT': DEFINES['__Userspace_os_Windows'] = 1 else: