From 67b3bbe639645ab719972682359acda303d94454 Mon Sep 17 00:00:00 2001 From: deadbeef Date: Wed, 4 Jan 2017 18:38:02 -0800 Subject: [PATCH] Separating SCTP code from BaseChannel/MediaChannel. The BaseChannel code is geared around RTP; the presence of media engines, send and receive streams, SRTP, SDP directional attribute negotiation, etc. It doesn't make sense to use it for SCTP as well. This separation should make future work both on BaseChannel and the SCTP code paths easier. SctpDataEngine now becomes SctpTransport, and is used by WebRtcSession directly. cricket::DataChannel is also renamed, to RtpDataChannel, so it doesn't get confused with webrtc::DataChannel any more. Beyond just moving code around, some consequences of this CL: - We'll now stop using the worker thread for SCTP. Packets will be processed right on the network thread instead. - The SDP directional attribute is ignored, as it's supposed to be. BUG=None Review-Url: https://codereview.webrtc.org/2564333002 Cr-Commit-Position: refs/heads/master@{#15906} --- webrtc/api/datachannel.cc | 29 +- webrtc/api/datachannel.h | 7 +- webrtc/api/datachannel_unittest.cc | 22 +- webrtc/api/peerconnection.cc | 17 +- .../api/peerconnectioninterface_unittest.cc | 2 +- webrtc/api/rtcstatscollector.cc | 12 +- webrtc/api/test/mock_webrtcsession.h | 4 +- webrtc/api/webrtcsdp.cc | 2 +- webrtc/api/webrtcsession.cc | 485 +++++--- webrtc/api/webrtcsession.h | 104 +- webrtc/api/webrtcsession_unittest.cc | 167 ++- webrtc/api/webrtcsessiondescriptionfactory.cc | 2 +- webrtc/media/BUILD.gn | 7 +- webrtc/media/base/fakemediaengine.h | 9 +- webrtc/media/base/hybriddataengine.h | 60 - webrtc/media/base/mediachannel.h | 40 +- webrtc/media/base/mediaconstants.h | 6 +- webrtc/media/base/mediaengine.h | 3 +- webrtc/media/base/rtpdataengine.cc | 4 - webrtc/media/base/rtpdataengine.h | 3 +- webrtc/media/base/rtpdataengine_unittest.cc | 3 +- webrtc/media/sctp/sctpdataengine.cc | 1066 ---------------- webrtc/media/sctp/sctpdataengine.h | 250 ---- webrtc/media/sctp/sctpdataengine_unittest.cc | 523 -------- webrtc/media/sctp/sctptransport.cc | 1090 +++++++++++++++++ webrtc/media/sctp/sctptransport.h | 193 +++ webrtc/media/sctp/sctptransport_unittest.cc | 563 +++++++++ webrtc/media/sctp/sctptransportinternal.h | 137 +++ webrtc/pc/channel.cc | 191 +-- webrtc/pc/channel.h | 61 +- webrtc/pc/channel_unittest.cc | 152 +-- webrtc/pc/channelmanager.cc | 59 +- webrtc/pc/channelmanager.h | 18 +- webrtc/pc/channelmanager_unittest.cc | 16 +- 34 files changed, 2826 insertions(+), 2481 deletions(-) delete mode 100644 webrtc/media/base/hybriddataengine.h delete mode 100644 webrtc/media/sctp/sctpdataengine.cc delete mode 100644 webrtc/media/sctp/sctpdataengine.h delete mode 100644 webrtc/media/sctp/sctpdataengine_unittest.cc create mode 100644 webrtc/media/sctp/sctptransport.cc create mode 100644 webrtc/media/sctp/sctptransport.h create mode 100644 webrtc/media/sctp/sctptransport_unittest.cc create mode 100644 webrtc/media/sctp/sctptransportinternal.h diff --git a/webrtc/api/datachannel.cc b/webrtc/api/datachannel.cc index 9812e9bb6a..ad7cb57199 100644 --- a/webrtc/api/datachannel.cc +++ b/webrtc/api/datachannel.cc @@ -16,7 +16,7 @@ #include "webrtc/api/sctputils.h" #include "webrtc/base/logging.h" #include "webrtc/base/refcount.h" -#include "webrtc/media/sctp/sctpdataengine.h" +#include "webrtc/media/sctp/sctptransportinternal.h" namespace webrtc { @@ -328,12 +328,12 @@ void DataChannel::OnMessage(rtc::Message* msg) { } } -void DataChannel::OnDataReceived(cricket::DataChannel* channel, - const cricket::ReceiveDataParams& params, +void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& payload) { - uint32_t expected_ssrc = - (data_channel_type_ == cricket::DCT_RTP) ? receive_ssrc_ : config_.id; - if (params.ssrc != expected_ssrc) { + if (data_channel_type_ == cricket::DCT_RTP && params.ssrc != receive_ssrc_) { + return; + } + if (data_channel_type_ == cricket::DCT_SCTP && params.sid != config_.id) { return; } @@ -342,17 +342,17 @@ void DataChannel::OnDataReceived(cricket::DataChannel* channel, if (handshake_state_ != kHandshakeWaitingForAck) { // Ignore it if we are not expecting an ACK message. LOG(LS_WARNING) << "DataChannel received unexpected CONTROL message, " - << "sid = " << params.ssrc; + << "sid = " << params.sid; return; } if (ParseDataChannelOpenAckMessage(payload)) { // We can send unordered as soon as we receive the ACK message. handshake_state_ = kHandshakeReady; LOG(LS_INFO) << "DataChannel received OPEN_ACK message, sid = " - << params.ssrc; + << params.sid; } else { LOG(LS_WARNING) << "DataChannel failed to parse OPEN_ACK message, sid = " - << params.ssrc; + << params.sid; } return; } @@ -360,7 +360,7 @@ void DataChannel::OnDataReceived(cricket::DataChannel* channel, ASSERT(params.type == cricket::DMT_BINARY || params.type == cricket::DMT_TEXT); - LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.ssrc; + LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.sid; // We can send unordered as soon as we receive any DATA message since the // remote side must have received the OPEN (and old clients do not send // OPEN_ACK). @@ -390,9 +390,8 @@ void DataChannel::OnDataReceived(cricket::DataChannel* channel, } } -void DataChannel::OnStreamClosedRemotely(uint32_t sid) { - if (data_channel_type_ == cricket::DCT_SCTP && - sid == static_cast(config_.id)) { +void DataChannel::OnStreamClosedRemotely(int sid) { + if (data_channel_type_ == cricket::DCT_SCTP && sid == config_.id) { Close(); } } @@ -551,7 +550,7 @@ bool DataChannel::SendDataMessage(const DataBuffer& buffer, send_params.max_rtx_count = config_.maxRetransmits; send_params.max_rtx_ms = config_.maxRetransmitTime; - send_params.ssrc = config_.id; + send_params.sid = config_.id; } else { send_params.ssrc = send_ssrc_; } @@ -623,7 +622,7 @@ bool DataChannel::SendControlMessage(const rtc::CopyOnWriteBuffer& buffer) { (!is_open_message || !config_.negotiated)); cricket::SendDataParams send_params; - send_params.ssrc = config_.id; + send_params.sid = config_.id; // Send data as ordered before we receive any message from the remote peer to // make sure the remote peer will not receive any data before it receives the // OPEN message. diff --git a/webrtc/api/datachannel.h b/webrtc/api/datachannel.h index 9208ada5ca..19f95df444 100644 --- a/webrtc/api/datachannel.h +++ b/webrtc/api/datachannel.h @@ -144,11 +144,10 @@ class DataChannel : public DataChannelInterface, // stream on an existing DataMediaChannel, and we've finished negotiation. void OnChannelReady(bool writable); - // Sigslots from cricket::DataChannel - void OnDataReceived(cricket::DataChannel* channel, - const cricket::ReceiveDataParams& params, + // Slots for provider to connect signals to. + void OnDataReceived(const cricket::ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& payload); - void OnStreamClosedRemotely(uint32_t sid); + void OnStreamClosedRemotely(int sid); // The remote peer request that this channel should be closed. void RemotePeerRequestClose(); diff --git a/webrtc/api/datachannel_unittest.cc b/webrtc/api/datachannel_unittest.cc index a8c8361cb1..4ce1be5736 100644 --- a/webrtc/api/datachannel_unittest.cc +++ b/webrtc/api/datachannel_unittest.cc @@ -329,7 +329,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceivesOpenAck) { params.type = cricket::DMT_CONTROL; rtc::CopyOnWriteBuffer payload; webrtc::WriteDataChannelOpenAckMessage(&payload); - dc->OnDataReceived(NULL, params, payload); + dc->OnDataReceived(params, payload); // Sends another message and verifies it's unordered. ASSERT_TRUE(dc->Send(buffer)); @@ -353,7 +353,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceiveData) { params.ssrc = init.id; params.type = cricket::DMT_TEXT; webrtc::DataBuffer buffer("data"); - dc->OnDataReceived(NULL, params, buffer.data); + dc->OnDataReceived(params, buffer.data); // Sends a message and verifies it's unordered. ASSERT_TRUE(dc->Send(buffer)); @@ -414,7 +414,7 @@ TEST_F(SctpDataChannelTest, ReceiveDataWithInvalidSsrc) { cricket::ReceiveDataParams params; params.ssrc = 0; webrtc::DataBuffer buffer("abcd"); - webrtc_data_channel_->OnDataReceived(NULL, params, buffer.data); + webrtc_data_channel_->OnDataReceived(params, buffer.data); EXPECT_EQ(0U, observer_->messages_received()); } @@ -430,7 +430,7 @@ TEST_F(SctpDataChannelTest, ReceiveDataWithValidSsrc) { params.ssrc = 1; webrtc::DataBuffer buffer("abcd"); - webrtc_data_channel_->OnDataReceived(NULL, params, buffer.data); + webrtc_data_channel_->OnDataReceived(params, buffer.data); EXPECT_EQ(1U, observer_->messages_received()); } @@ -472,9 +472,9 @@ TEST_F(SctpDataChannelTest, VerifyMessagesAndBytesReceived) { EXPECT_EQ(0U, webrtc_data_channel_->bytes_received()); // Receive three buffers while data channel isn't open. - webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[0].data); - webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[1].data); - webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[2].data); + webrtc_data_channel_->OnDataReceived(params, buffers[0].data); + webrtc_data_channel_->OnDataReceived(params, buffers[1].data); + webrtc_data_channel_->OnDataReceived(params, buffers[2].data); EXPECT_EQ(0U, observer_->messages_received()); EXPECT_EQ(0U, webrtc_data_channel_->messages_received()); EXPECT_EQ(0U, webrtc_data_channel_->bytes_received()); @@ -488,9 +488,9 @@ TEST_F(SctpDataChannelTest, VerifyMessagesAndBytesReceived) { EXPECT_EQ(bytes_received, webrtc_data_channel_->bytes_received()); // Receive three buffers while open. - webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[3].data); - webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[4].data); - webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[5].data); + webrtc_data_channel_->OnDataReceived(params, buffers[3].data); + webrtc_data_channel_->OnDataReceived(params, buffers[4].data); + webrtc_data_channel_->OnDataReceived(params, buffers[5].data); bytes_received += buffers[3].size() + buffers[4].size() + buffers[5].size(); EXPECT_EQ(6U, observer_->messages_received()); EXPECT_EQ(6U, webrtc_data_channel_->messages_received()); @@ -593,7 +593,7 @@ TEST_F(SctpDataChannelTest, ClosedWhenReceivedBufferFull) { // Receiving data without having an observer will overflow the buffer. for (size_t i = 0; i < 16 * 1024 + 1; ++i) { - webrtc_data_channel_->OnDataReceived(NULL, params, buffer); + webrtc_data_channel_->OnDataReceived(params, buffer); } EXPECT_EQ(webrtc::DataChannelInterface::kClosed, webrtc_data_channel_->state()); diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc index cdf5a97b68..f8bea1bd37 100644 --- a/webrtc/api/peerconnection.cc +++ b/webrtc/api/peerconnection.cc @@ -38,7 +38,7 @@ #include "webrtc/base/trace_event.h" #include "webrtc/call/call.h" #include "webrtc/logging/rtc_event_log/rtc_event_log.h" -#include "webrtc/media/sctp/sctpdataengine.h" +#include "webrtc/media/sctp/sctptransport.h" #include "webrtc/pc/channelmanager.h" #include "webrtc/system_wrappers/include/field_trial.h" @@ -652,7 +652,14 @@ bool PeerConnection::Initialize( std::unique_ptr( factory_->CreateTransportController( port_allocator_.get(), - configuration.redetermine_role_on_ice_restart)))); + configuration.redetermine_role_on_ice_restart)), +#ifdef HAVE_SCTP + std::unique_ptr( + new cricket::SctpTransportFactory(factory_->network_thread())) +#else + nullptr +#endif + )); stats_.reset(new StatsCollector(this)); stats_collector_ = RTCStatsCollector::Create(this); @@ -1119,7 +1126,7 @@ void PeerConnection::SetLocalDescription( // SCTP sids. rtc::SSLRole role; if (session_->data_channel_type() == cricket::DCT_SCTP && - session_->GetSslRole(session_->data_channel(), &role)) { + session_->GetSctpSslRole(&role)) { AllocateSctpSids(role); } @@ -1201,7 +1208,7 @@ void PeerConnection::SetRemoteDescription( // SCTP sids. rtc::SSLRole role; if (session_->data_channel_type() == cricket::DCT_SCTP && - session_->GetSslRole(session_->data_channel(), &role)) { + session_->GetSctpSslRole(&role)) { AllocateSctpSids(role); } @@ -2143,7 +2150,7 @@ rtc::scoped_refptr PeerConnection::InternalCreateDataChannel( if (session_->data_channel_type() == cricket::DCT_SCTP) { if (new_config.id < 0) { rtc::SSLRole role; - if ((session_->GetSslRole(session_->data_channel(), &role)) && + if ((session_->GetSctpSslRole(&role)) && !sid_allocator_.AllocateSid(role, &new_config.id)) { LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel."; return nullptr; diff --git a/webrtc/api/peerconnectioninterface_unittest.cc b/webrtc/api/peerconnectioninterface_unittest.cc index c52df41f2b..d6da24ee19 100644 --- a/webrtc/api/peerconnectioninterface_unittest.cc +++ b/webrtc/api/peerconnectioninterface_unittest.cc @@ -35,7 +35,7 @@ #include "webrtc/base/stringutils.h" #include "webrtc/base/thread.h" #include "webrtc/media/base/fakevideocapturer.h" -#include "webrtc/media/sctp/sctpdataengine.h" +#include "webrtc/media/sctp/sctptransportinternal.h" #include "webrtc/p2p/base/fakeportallocator.h" #include "webrtc/p2p/base/faketransportcontroller.h" #include "webrtc/pc/mediasession.h" diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc index 3ab7ac94e1..dc14970854 100644 --- a/webrtc/api/rtcstatscollector.cc +++ b/webrtc/api/rtcstatscollector.cc @@ -463,10 +463,16 @@ void RTCStatsCollector::GetStatsReport( ChannelNamePair(pc_->session()->video_channel()->content_name(), pc_->session()->video_channel()->transport_name())); } - if (pc_->session()->data_channel()) { + if (pc_->session()->rtp_data_channel()) { + channel_name_pairs_->data = + rtc::Optional(ChannelNamePair( + pc_->session()->rtp_data_channel()->content_name(), + pc_->session()->rtp_data_channel()->transport_name())); + } + if (pc_->session()->sctp_content_name()) { channel_name_pairs_->data = rtc::Optional( - ChannelNamePair(pc_->session()->data_channel()->content_name(), - pc_->session()->data_channel()->transport_name())); + ChannelNamePair(*pc_->session()->sctp_content_name(), + *pc_->session()->sctp_transport_name())); } media_info_.reset(PrepareMediaInfo_s().release()); diff --git a/webrtc/api/test/mock_webrtcsession.h b/webrtc/api/test/mock_webrtcsession.h index 7fefad857d..ae75035608 100644 --- a/webrtc/api/test/mock_webrtcsession.h +++ b/webrtc/api/test/mock_webrtcsession.h @@ -15,6 +15,7 @@ #include #include "webrtc/api/webrtcsession.h" +#include "webrtc/media/sctp/sctptransportinternal.h" #include "webrtc/test/gmock.h" namespace webrtc { @@ -35,7 +36,8 @@ class MockWebRtcSession : public webrtc::WebRtcSession { std::unique_ptr( new cricket::TransportController(rtc::Thread::Current(), rtc::Thread::Current(), - nullptr))) {} + nullptr)), + std::unique_ptr()) {} MOCK_METHOD0(voice_channel, cricket::VoiceChannel*()); MOCK_METHOD0(video_channel, cricket::VideoChannel*()); // Libjingle uses "local" for a outgoing track, and "remote" for a incoming diff --git a/webrtc/api/webrtcsdp.cc b/webrtc/api/webrtcsdp.cc index e5ee4167ee..2c1ba640f8 100644 --- a/webrtc/api/webrtcsdp.cc +++ b/webrtc/api/webrtcsdp.cc @@ -33,7 +33,7 @@ #include "webrtc/media/base/cryptoparams.h" #include "webrtc/media/base/mediaconstants.h" #include "webrtc/media/base/rtputils.h" -#include "webrtc/media/sctp/sctpdataengine.h" +#include "webrtc/media/sctp/sctptransportinternal.h" #include "webrtc/p2p/base/candidate.h" #include "webrtc/p2p/base/p2pconstants.h" #include "webrtc/p2p/base/port.h" diff --git a/webrtc/api/webrtcsession.cc b/webrtc/api/webrtcsession.cc index 5d414baac2..dad485c9bd 100644 --- a/webrtc/api/webrtcsession.cc +++ b/webrtc/api/webrtcsession.cc @@ -33,6 +33,7 @@ #include "webrtc/call/call.h" #include "webrtc/media/base/mediaconstants.h" #include "webrtc/media/base/videocapturer.h" +#include "webrtc/media/sctp/sctptransportinternal.h" #include "webrtc/p2p/base/portallocator.h" #include "webrtc/p2p/base/transportchannel.h" #include "webrtc/pc/channel.h" @@ -74,9 +75,9 @@ const char kSdpWithoutIceUfragPwd[] = "Called with SDP without ice-ufrag and ice-pwd."; const char kSessionError[] = "Session error code: "; const char kSessionErrorDesc[] = "Session error description: "; -const char kDtlsSetupFailureRtp[] = +const char kDtlsSrtpSetupFailureRtp[] = "Couldn't set up DTLS-SRTP on RTP channel."; -const char kDtlsSetupFailureRtcp[] = +const char kDtlsSrtpSetupFailureRtcp[] = "Couldn't set up DTLS-SRTP on RTCP channel."; const char kEnableBundleFailed[] = "Failed to enable BUNDLE."; @@ -291,6 +292,31 @@ static bool GetTrackIdBySsrc(const SessionDescription* session_description, return false; } +// Get the SCTP port out of a SessionDescription. +// Return -1 if not found. +static int GetSctpPort(const SessionDescription* session_description) { + const ContentInfo* content_info = GetFirstDataContent(session_description); + RTC_DCHECK(content_info); + if (!content_info) { + return -1; + } + const cricket::DataContentDescription* data = + static_cast( + (content_info->description)); + std::string value; + cricket::DataCodec match_pattern(cricket::kGoogleSctpDataCodecPlType, + cricket::kGoogleSctpDataCodecName); + for (const cricket::DataCodec& codec : data->codecs()) { + if (!codec.Matches(match_pattern)) { + continue; + } + if (codec.GetParam(cricket::kCodecParamPort, &value)) { + return rtc::FromString(value); + } + } + return -1; +} + static bool BadSdp(const std::string& source, const std::string& type, const std::string& reason, @@ -440,7 +466,8 @@ WebRtcSession::WebRtcSession( rtc::Thread* worker_thread, rtc::Thread* signaling_thread, cricket::PortAllocator* port_allocator, - std::unique_ptr transport_controller) + std::unique_ptr transport_controller, + std::unique_ptr sctp_factory) : network_thread_(network_thread), worker_thread_(worker_thread), signaling_thread_(signaling_thread), @@ -449,6 +476,7 @@ WebRtcSession::WebRtcSession( // Due to this constraint session id |sid_| is max limited to LLONG_MAX. sid_(rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX)), transport_controller_(std::move(transport_controller)), + sctp_factory_(std::move(sctp_factory)), media_controller_(media_controller), channel_manager_(media_controller_->channel_manager()), ice_observer_(NULL), @@ -470,7 +498,7 @@ WebRtcSession::WebRtcSession( transport_controller_->SignalCandidatesRemoved.connect( this, &WebRtcSession::OnTransportControllerCandidatesRemoved); transport_controller_->SignalDtlsHandshakeError.connect( - this, &WebRtcSession::OnDtlsHandshakeError); + this, &WebRtcSession::OnTransportControllerDtlsHandshakeError); } WebRtcSession::~WebRtcSession() { @@ -485,9 +513,14 @@ WebRtcSession::~WebRtcSession() { SignalVoiceChannelDestroyed(); channel_manager_->DestroyVoiceChannel(voice_channel_.release()); } - if (data_channel_) { + if (rtp_data_channel_) { SignalDataChannelDestroyed(); - channel_manager_->DestroyDataChannel(data_channel_.release()); + channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release()); + } + if (sctp_transport_) { + SignalDataChannelDestroyed(); + network_thread_->Invoke( + RTC_FROM_HERE, rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this)); } #ifdef HAVE_QUIC if (quic_data_transport_) { @@ -597,9 +630,10 @@ bool WebRtcSession::Initialize( void WebRtcSession::Close() { SetState(STATE_CLOSED); RemoveUnusedChannels(nullptr); - ASSERT(!voice_channel_); - ASSERT(!video_channel_); - ASSERT(!data_channel_); + RTC_DCHECK(!voice_channel_); + RTC_DCHECK(!video_channel_); + RTC_DCHECK(!rtp_data_channel_); + RTC_DCHECK(!sctp_transport_); media_controller_->Close(); } @@ -611,8 +645,9 @@ cricket::BaseChannel* WebRtcSession::GetChannel( if (video_channel() && video_channel()->content_name() == content_name) { return video_channel(); } - if (data_channel() && data_channel()->content_name() == content_name) { - return data_channel(); + if (rtp_data_channel() && + rtp_data_channel()->content_name() == content_name) { + return rtp_data_channel(); } return nullptr; } @@ -621,20 +656,31 @@ cricket::SecurePolicy WebRtcSession::SdesPolicy() const { return webrtc_session_desc_factory_->SdesPolicy(); } -bool WebRtcSession::GetSslRole(const std::string& transport_name, +bool WebRtcSession::GetSctpSslRole(rtc::SSLRole* role) { + if (!local_description() || !remote_description()) { + LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " + << "SSL Role of the SCTP transport."; + return false; + } + if (!sctp_transport_) { + LOG(LS_INFO) << "Non-rejected SCTP m= section is needed to get the " + << "SSL Role of the SCTP transport."; + return false; + } + + return transport_controller_->GetSslRole(*sctp_transport_name_, role); +} + +bool WebRtcSession::GetSslRole(const std::string& content_name, rtc::SSLRole* role) { if (!local_description() || !remote_description()) { - LOG(LS_INFO) << "Local and Remote descriptions must be applied to get " + LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " << "SSL Role of the session."; return false; } - return transport_controller_->GetSslRole(transport_name, role); -} - -bool WebRtcSession::GetSslRole(const cricket::BaseChannel* channel, - rtc::SSLRole* role) { - return channel && GetSslRole(channel->transport_name(), role); + return transport_controller_->GetSslRole(GetTransportName(content_name), + role); } void WebRtcSession::CreateOffer( @@ -918,9 +964,27 @@ bool WebRtcSession::PushdownMediaDescription( } }; - return (set_content(voice_channel()) && - set_content(video_channel()) && - set_content(data_channel())); + bool ret = (set_content(voice_channel()) && set_content(video_channel()) && + set_content(rtp_data_channel())); + // Need complete offer/answer before starting SCTP, according to + // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19 + if (sctp_transport_ && local_description() && remote_description()) { + ret &= network_thread_->Invoke( + RTC_FROM_HERE, + rtc::Bind(&WebRtcSession::PushdownSctpParameters_n, this, source)); + } + return ret; +} + +bool WebRtcSession::PushdownSctpParameters_n(cricket::ContentSource source) { + RTC_DCHECK(network_thread_->IsCurrent()); + RTC_DCHECK(local_description()); + RTC_DCHECK(remote_description()); + // Apply the SCTP port (which is hidden inside a DataCodec structure...) + // When we support "max-message-size", that would also be pushed down here. + return sctp_transport_->Start( + GetSctpPort(local_description()->description()), + GetSctpPort(remote_description()->description())); } bool WebRtcSession::PushdownTransportDescription(cricket::ContentSource source, @@ -992,46 +1056,6 @@ bool WebRtcSession::GetTransportDescription( return true; } -std::unique_ptr WebRtcSession::GetStats_s() { - ASSERT(signaling_thread()->IsCurrent()); - ChannelNamePairs channel_name_pairs; - if (voice_channel()) { - channel_name_pairs.voice = rtc::Optional(ChannelNamePair( - voice_channel()->content_name(), voice_channel()->transport_name())); - } - if (video_channel()) { - channel_name_pairs.video = rtc::Optional(ChannelNamePair( - video_channel()->content_name(), video_channel()->transport_name())); - } - if (data_channel()) { - channel_name_pairs.data = rtc::Optional(ChannelNamePair( - data_channel()->content_name(), data_channel()->transport_name())); - } - return GetStats(channel_name_pairs); -} - -std::unique_ptr WebRtcSession::GetStats( - const ChannelNamePairs& channel_name_pairs) { - if (network_thread()->IsCurrent()) { - return GetStats_n(channel_name_pairs); - } - return network_thread()->Invoke>( - RTC_FROM_HERE, - rtc::Bind(&WebRtcSession::GetStats_n, this, channel_name_pairs)); -} - -bool WebRtcSession::GetLocalCertificate( - const std::string& transport_name, - rtc::scoped_refptr* certificate) { - return transport_controller_->GetLocalCertificate(transport_name, - certificate); -} - -std::unique_ptr WebRtcSession::GetRemoteSSLCertificate( - const std::string& transport_name) { - return transport_controller_->GetRemoteSSLCertificate(transport_name); -} - bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { const std::string* first_content_name = bundle.FirstContentName(); if (!first_content_name) { @@ -1039,7 +1063,6 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { return false; } const std::string& transport_name = *first_content_name; - cricket::BaseChannel* first_channel = GetChannel(transport_name); #ifdef HAVE_QUIC if (quic_data_transport_ && @@ -1050,8 +1073,8 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { } #endif - auto maybe_set_transport = [this, bundle, transport_name, - first_channel](cricket::BaseChannel* ch) { + auto maybe_set_transport = [this, bundle, + transport_name](cricket::BaseChannel* ch) { if (!ch || !bundle.HasContentName(ch->content_name())) { return true; } @@ -1073,9 +1096,21 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { if (!maybe_set_transport(voice_channel()) || !maybe_set_transport(video_channel()) || - !maybe_set_transport(data_channel())) { + !maybe_set_transport(rtp_data_channel())) { return false; } + // For SCTP, transport creation/deletion happens here instead of in the + // object itself. + if (sctp_transport_) { + RTC_DCHECK(sctp_transport_name_); + RTC_DCHECK(sctp_content_name_); + if (transport_name != *sctp_transport_name_ && + bundle.HasContentName(*sctp_content_name_)) { + network_thread_->Invoke( + RTC_FROM_HERE, rtc::Bind(&WebRtcSession::ChangeSctpTransport_n, this, + transport_name)); + } + } return true; } @@ -1248,60 +1283,129 @@ sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() { bool WebRtcSession::SendData(const cricket::SendDataParams& params, const rtc::CopyOnWriteBuffer& payload, cricket::SendDataResult* result) { - if (!data_channel_) { - LOG(LS_ERROR) << "SendData called when data_channel_ is NULL."; + if (!rtp_data_channel_ && !sctp_transport_) { + LOG(LS_ERROR) << "SendData called when rtp_data_channel_ " + << "and sctp_transport_ are NULL."; return false; } - return data_channel_->SendData(params, payload, result); + return rtp_data_channel_ + ? rtp_data_channel_->SendData(params, payload, result) + : network_thread_->Invoke( + RTC_FROM_HERE, + Bind(&cricket::SctpTransportInternal::SendData, + sctp_transport_.get(), params, payload, result)); } bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) { - if (!data_channel_) { + if (!rtp_data_channel_ && !sctp_transport_) { // Don't log an error here, because DataChannels are expected to call // ConnectDataChannel in this state. It's the only way to initially tell // whether or not the underlying transport is ready. return false; } - data_channel_->SignalReadyToSendData.connect(webrtc_data_channel, - &DataChannel::OnChannelReady); - data_channel_->SignalDataReceived.connect(webrtc_data_channel, - &DataChannel::OnDataReceived); - data_channel_->SignalStreamClosedRemotely.connect( - webrtc_data_channel, &DataChannel::OnStreamClosedRemotely); + if (rtp_data_channel_) { + rtp_data_channel_->SignalReadyToSendData.connect( + webrtc_data_channel, &DataChannel::OnChannelReady); + rtp_data_channel_->SignalDataReceived.connect(webrtc_data_channel, + &DataChannel::OnDataReceived); + } else { + SignalSctpReadyToSendData.connect(webrtc_data_channel, + &DataChannel::OnChannelReady); + SignalSctpDataReceived.connect(webrtc_data_channel, + &DataChannel::OnDataReceived); + SignalSctpStreamClosedRemotely.connect( + webrtc_data_channel, &DataChannel::OnStreamClosedRemotely); + } return true; } void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) { - if (!data_channel_) { - LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL."; + if (!rtp_data_channel_ && !sctp_transport_) { + LOG(LS_ERROR) << "DisconnectDataChannel called when rtp_data_channel_ and " + "sctp_transport_ are NULL."; return; } - data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel); - data_channel_->SignalDataReceived.disconnect(webrtc_data_channel); - data_channel_->SignalStreamClosedRemotely.disconnect(webrtc_data_channel); + if (rtp_data_channel_) { + rtp_data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel); + rtp_data_channel_->SignalDataReceived.disconnect(webrtc_data_channel); + } else { + SignalSctpReadyToSendData.disconnect(webrtc_data_channel); + SignalSctpDataReceived.disconnect(webrtc_data_channel); + SignalSctpStreamClosedRemotely.disconnect(webrtc_data_channel); + } } void WebRtcSession::AddSctpDataStream(int sid) { - if (!data_channel_) { - LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL."; + if (!sctp_transport_) { + LOG(LS_ERROR) << "AddSctpDataStream called when sctp_transport_ is NULL."; return; } - data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(sid)); - data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(sid)); + network_thread_->Invoke( + RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream, + sctp_transport_.get(), sid)); } void WebRtcSession::RemoveSctpDataStream(int sid) { - if (!data_channel_) { - LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is " + if (!sctp_transport_) { + LOG(LS_ERROR) << "RemoveSctpDataStream called when sctp_transport_ is " << "NULL."; return; } - data_channel_->RemoveRecvStream(sid); - data_channel_->RemoveSendStream(sid); + network_thread_->Invoke( + RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream, + sctp_transport_.get(), sid)); } bool WebRtcSession::ReadyToSendData() const { - return data_channel_ && data_channel_->ready_to_send_data(); + return (rtp_data_channel_ && rtp_data_channel_->ready_to_send_data()) || + sctp_ready_to_send_data_; +} + +std::unique_ptr WebRtcSession::GetStats_s() { + ASSERT(signaling_thread()->IsCurrent()); + ChannelNamePairs channel_name_pairs; + if (voice_channel()) { + channel_name_pairs.voice = rtc::Optional(ChannelNamePair( + voice_channel()->content_name(), voice_channel()->transport_name())); + } + if (video_channel()) { + channel_name_pairs.video = rtc::Optional(ChannelNamePair( + video_channel()->content_name(), video_channel()->transport_name())); + } + if (rtp_data_channel()) { + channel_name_pairs.data = rtc::Optional( + ChannelNamePair(rtp_data_channel()->content_name(), + rtp_data_channel()->transport_name())); + } + if (sctp_transport_) { + RTC_DCHECK(sctp_content_name_); + RTC_DCHECK(sctp_transport_name_); + channel_name_pairs.data = rtc::Optional( + ChannelNamePair(*sctp_content_name_, *sctp_transport_name_)); + } + return GetStats(channel_name_pairs); +} + +std::unique_ptr WebRtcSession::GetStats( + const ChannelNamePairs& channel_name_pairs) { + if (network_thread()->IsCurrent()) { + return GetStats_n(channel_name_pairs); + } + return network_thread()->Invoke>( + RTC_FROM_HERE, + rtc::Bind(&WebRtcSession::GetStats_n, this, channel_name_pairs)); +} + +bool WebRtcSession::GetLocalCertificate( + const std::string& transport_name, + rtc::scoped_refptr* certificate) { + return transport_controller_->GetLocalCertificate(transport_name, + certificate); +} + +std::unique_ptr WebRtcSession::GetRemoteSSLCertificate( + const std::string& transport_name) { + return transport_controller_->GetRemoteSSLCertificate(transport_name); } cricket::DataChannelType WebRtcSession::data_channel_type() const { @@ -1326,6 +1430,11 @@ void WebRtcSession::OnCertificateReady( transport_controller_->SetLocalCertificate(certificate); } +void WebRtcSession::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) { + SetError(ERROR_TRANSPORT, + rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp); +} + bool WebRtcSession::waiting_for_certificate_for_testing() const { return webrtc_session_desc_factory_->waiting_for_certificate_for_testing(); } @@ -1455,7 +1564,16 @@ void WebRtcSession::OnTransportControllerCandidatesRemoved( } } -// Enabling voice and video channel. +void WebRtcSession::OnTransportControllerDtlsHandshakeError( + rtc::SSLHandshakeError error) { + if (metrics_observer_) { + metrics_observer_->IncrementEnumCounter( + webrtc::kEnumCounterDtlsHandshakeError, static_cast(error), + static_cast(rtc::SSLHandshakeError::MAX_VALUE)); + } +} + +// Enabling voice and video (and RTP data) channel. void WebRtcSession::EnableChannels() { if (voice_channel_ && !voice_channel_->enabled()) voice_channel_->Enable(true); @@ -1463,8 +1581,8 @@ void WebRtcSession::EnableChannels() { if (video_channel_ && !video_channel_->enabled()) video_channel_->Enable(true); - if (data_channel_ && !data_channel_->enabled()) - data_channel_->Enable(true); + if (rtp_data_channel_ && !rtp_data_channel_->enabled()) + rtp_data_channel_->Enable(true); } // Returns the media index for a local ice candidate given the content name. @@ -1574,9 +1692,15 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) { const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc); if (!data_info || data_info->rejected) { - if (data_channel_) { + if (rtp_data_channel_) { SignalDataChannelDestroyed(); - channel_manager_->DestroyDataChannel(data_channel_.release()); + channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release()); + } + if (sctp_transport_) { + SignalDataChannelDestroyed(); + network_thread_->Invoke( + RTC_FROM_HERE, + rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this)); } #ifdef HAVE_QUIC // Clean up the existing QuicDataTransport and its QuicTransportChannels. @@ -1637,8 +1761,8 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) { } const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc); - if (data_channel_type_ != cricket::DCT_NONE && - data && !data->rejected && !data_channel_) { + if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected && + !rtp_data_channel_ && !sctp_transport_) { if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) { LOG(LS_ERROR) << "Failed to create data channel."; return false; @@ -1664,8 +1788,8 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content, voice_channel_->ActivateRtcpMux(); } - voice_channel_->SignalDtlsSetupFailure.connect( - this, &WebRtcSession::OnDtlsSetupFailure); + voice_channel_->SignalDtlsSrtpSetupFailure.connect( + this, &WebRtcSession::OnDtlsSrtpSetupFailure); SignalVoiceChannelCreated(); voice_channel_->SignalSentPacket.connect(this, @@ -1688,8 +1812,8 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content, if (require_rtcp_mux) { video_channel_->ActivateRtcpMux(); } - video_channel_->SignalDtlsSetupFailure.connect( - this, &WebRtcSession::OnDtlsSetupFailure); + video_channel_->SignalDtlsSrtpSetupFailure.connect( + this, &WebRtcSession::OnDtlsSrtpSetupFailure); SignalVideoChannelCreated(); video_channel_->SignalSentPacket.connect(this, @@ -1699,40 +1823,48 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content, bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content, const std::string* bundle_transport) { + const std::string transport_name = + bundle_transport ? *bundle_transport : content->name; #ifdef HAVE_QUIC if (data_channel_type_ == cricket::DCT_QUIC) { RTC_DCHECK(transport_controller_->quic()); - const std::string transport_name = - bundle_transport ? *bundle_transport : content->name; quic_data_transport_->SetTransport(transport_name); return true; } #endif // HAVE_QUIC bool sctp = (data_channel_type_ == cricket::DCT_SCTP); - bool require_rtcp_mux = - rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire; - bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux; - data_channel_.reset(channel_manager_->CreateDataChannel( - media_controller_, transport_controller_.get(), content->name, - bundle_transport, create_rtcp_transport_channel, SrtpRequired(), - data_channel_type_)); - if (!data_channel_) { - return false; - } - if (require_rtcp_mux) { - data_channel_->ActivateRtcpMux(); - } - if (sctp) { - data_channel_->SignalDataReceived.connect( - this, &WebRtcSession::OnDataChannelMessageReceived); + if (!sctp_factory_) { + LOG(LS_ERROR) + << "Trying to create SCTP transport, but didn't compile with " + "SCTP support (HAVE_SCTP)"; + return false; + } + if (!network_thread_->Invoke( + RTC_FROM_HERE, rtc::Bind(&WebRtcSession::CreateSctpTransport_n, + this, content->name, transport_name))) { + return false; + }; + } else { + bool require_rtcp_mux = + rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire; + bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux; + rtp_data_channel_.reset(channel_manager_->CreateRtpDataChannel( + media_controller_, transport_controller_.get(), content->name, + bundle_transport, create_rtcp_transport_channel, SrtpRequired())); + if (!rtp_data_channel_) { + return false; + } + if (require_rtcp_mux) { + rtp_data_channel_->ActivateRtcpMux(); + } + rtp_data_channel_->SignalDtlsSrtpSetupFailure.connect( + this, &WebRtcSession::OnDtlsSrtpSetupFailure); + rtp_data_channel_->SignalSentPacket.connect(this, + &WebRtcSession::OnSentPacket_w); } - data_channel_->SignalDtlsSetupFailure.connect( - this, &WebRtcSession::OnDtlsSetupFailure); - SignalDataChannelCreated(); - data_channel_->SignalSentPacket.connect(this, &WebRtcSession::OnSentPacket_w); return true; } @@ -1758,16 +1890,79 @@ std::unique_ptr WebRtcSession::GetStats_n( return session_stats; } -void WebRtcSession::OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp) { - SetError(ERROR_TRANSPORT, - rtcp ? kDtlsSetupFailureRtcp : kDtlsSetupFailureRtp); +bool WebRtcSession::CreateSctpTransport_n(const std::string& content_name, + const std::string& transport_name) { + RTC_DCHECK(network_thread_->IsCurrent()); + RTC_DCHECK(sctp_factory_); + cricket::TransportChannel* tc = + transport_controller_->CreateTransportChannel_n( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + sctp_transport_ = sctp_factory_->CreateSctpTransport(tc); + RTC_DCHECK(sctp_transport_); + sctp_invoker_.reset(new rtc::AsyncInvoker()); + sctp_transport_->SignalReadyToSendData.connect( + this, &WebRtcSession::OnSctpTransportReadyToSendData_n); + sctp_transport_->SignalDataReceived.connect( + this, &WebRtcSession::OnSctpTransportDataReceived_n); + sctp_transport_->SignalStreamClosedRemotely.connect( + this, &WebRtcSession::OnSctpStreamClosedRemotely_n); + sctp_transport_name_ = rtc::Optional(transport_name); + sctp_content_name_ = rtc::Optional(content_name); + return true; } -void WebRtcSession::OnDataChannelMessageReceived( - cricket::DataChannel* channel, +void WebRtcSession::ChangeSctpTransport_n(const std::string& transport_name) { + RTC_DCHECK(network_thread_->IsCurrent()); + RTC_DCHECK(sctp_transport_); + RTC_DCHECK(sctp_transport_name_); + std::string old_sctp_transport_name = *sctp_transport_name_; + sctp_transport_name_ = rtc::Optional(transport_name); + cricket::TransportChannel* tc = + transport_controller_->CreateTransportChannel_n( + transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); + sctp_transport_->SetTransportChannel(tc); + transport_controller_->DestroyTransportChannel_n( + old_sctp_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP); +} + +void WebRtcSession::DestroySctpTransport_n() { + RTC_DCHECK(network_thread_->IsCurrent()); + sctp_transport_.reset(nullptr); + sctp_content_name_.reset(); + sctp_transport_name_.reset(); + sctp_invoker_.reset(nullptr); + sctp_ready_to_send_data_ = false; +} + +void WebRtcSession::OnSctpTransportReadyToSendData_n() { + RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); + RTC_DCHECK(network_thread_->IsCurrent()); + sctp_invoker_->AsyncInvoke( + RTC_FROM_HERE, signaling_thread_, + rtc::Bind(&WebRtcSession::OnSctpTransportReadyToSendData_s, this, true)); +} + +void WebRtcSession::OnSctpTransportReadyToSendData_s(bool ready) { + RTC_DCHECK(signaling_thread_->IsCurrent()); + sctp_ready_to_send_data_ = ready; + SignalSctpReadyToSendData(ready); +} + +void WebRtcSession::OnSctpTransportDataReceived_n( const cricket::ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& payload) { RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); + RTC_DCHECK(network_thread_->IsCurrent()); + sctp_invoker_->AsyncInvoke( + RTC_FROM_HERE, signaling_thread_, + rtc::Bind(&WebRtcSession::OnSctpTransportDataReceived_s, this, params, + payload)); +} + +void WebRtcSession::OnSctpTransportDataReceived_s( + const cricket::ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& payload) { + RTC_DCHECK(signaling_thread_->IsCurrent()); if (params.type == cricket::DMT_CONTROL && IsOpenMessage(payload)) { // Received OPEN message; parse and signal that a new data channel should // be created. @@ -1781,8 +1976,19 @@ void WebRtcSession::OnDataChannelMessageReceived( } config.open_handshake_role = InternalDataChannelInit::kAcker; SignalDataChannelOpenMessage(label, config); + } else { + // Otherwise just forward the signal. + SignalSctpDataReceived(params, payload); } - // Otherwise ignore the message. +} + +void WebRtcSession::OnSctpStreamClosedRemotely_n(int sid) { + RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP); + RTC_DCHECK(network_thread_->IsCurrent()); + sctp_invoker_->AsyncInvoke( + RTC_FROM_HERE, signaling_thread_, + rtc::Bind(&sigslot::signal1::operator(), + &SignalSctpStreamClosedRemotely, sid)); } // Returns false if bundle is enabled and rtcp_mux is disabled. @@ -1976,8 +2182,11 @@ void WebRtcSession::ReportTransportStats() { if (video_channel()) { transport_names.insert(video_channel()->transport_name()); } - if (data_channel()) { - transport_names.insert(data_channel()->transport_name()); + if (rtp_data_channel()) { + transport_names.insert(rtp_data_channel()->transport_name()); + } + if (sctp_transport_name_) { + transport_names.insert(*sctp_transport_name_); } for (const auto& name : transport_names) { cricket::TransportStats stats; @@ -2094,17 +2303,17 @@ const std::string WebRtcSession::GetTransportName( return quic_data_transport_->transport_name(); } #endif + if (sctp_transport_) { + RTC_DCHECK(sctp_content_name_); + RTC_DCHECK(sctp_transport_name_); + if (content_name == *sctp_content_name_) { + return *sctp_transport_name_; + } + } // Return an empty string if failed to retrieve the transport name. return ""; } return channel->transport_name(); } -void WebRtcSession::OnDtlsHandshakeError(rtc::SSLHandshakeError error) { - if (metrics_observer_) { - metrics_observer_->IncrementEnumCounter( - webrtc::kEnumCounterDtlsHandshakeError, static_cast(error), - static_cast(rtc::SSLHandshakeError::MAX_VALUE)); - } -} } // namespace webrtc diff --git a/webrtc/api/webrtcsession.h b/webrtc/api/webrtcsession.h index ef31560509..e346112cba 100644 --- a/webrtc/api/webrtcsession.h +++ b/webrtc/api/webrtcsession.h @@ -22,6 +22,7 @@ #include "webrtc/api/peerconnectioninterface.h" #include "webrtc/api/statstypes.h" #include "webrtc/base/constructormagic.h" +#include "webrtc/base/optional.h" #include "webrtc/base/sigslot.h" #include "webrtc/base/sslidentity.h" #include "webrtc/base/thread.h" @@ -37,7 +38,9 @@ namespace cricket { class ChannelManager; -class DataChannel; +class RtpDataChannel; +class SctpTransportInternal; +class SctpTransportInternalFactory; class StatsReport; class VideoChannel; class VoiceChannel; @@ -67,8 +70,8 @@ extern const char kSdpWithoutIceUfragPwd[]; extern const char kSdpWithoutSdesAndDtlsDisabled[]; extern const char kSessionError[]; extern const char kSessionErrorDesc[]; -extern const char kDtlsSetupFailureRtp[]; -extern const char kDtlsSetupFailureRtcp[]; +extern const char kDtlsSrtpSetupFailureRtp[]; +extern const char kDtlsSrtpSetupFailureRtcp[]; extern const char kEnableBundleFailed[]; // Maximum number of received video streams that will be processed by webrtc @@ -158,13 +161,15 @@ class WebRtcSession : ERROR_TRANSPORT = 2, // transport error of some kind }; + // |sctp_factory| may be null, in which case SCTP is treated as unsupported. WebRtcSession( webrtc::MediaControllerInterface* media_controller, rtc::Thread* network_thread, rtc::Thread* worker_thread, rtc::Thread* signaling_thread, cricket::PortAllocator* port_allocator, - std::unique_ptr transport_controller); + std::unique_ptr transport_controller, + std::unique_ptr sctp_factory); virtual ~WebRtcSession(); // These are const to allow them to be called from const methods. @@ -199,26 +204,34 @@ class WebRtcSession : ice_observer_ = observer; } + // Exposed for stats collecting. virtual cricket::VoiceChannel* voice_channel() { return voice_channel_.get(); } virtual cricket::VideoChannel* video_channel() { return video_channel_.get(); } - virtual cricket::DataChannel* data_channel() { - return data_channel_.get(); + // Only valid when using deprecated RTP data channels. + virtual cricket::RtpDataChannel* rtp_data_channel() { + return rtp_data_channel_.get(); + } + virtual rtc::Optional sctp_content_name() const { + return sctp_content_name_; + } + virtual rtc::Optional sctp_transport_name() const { + return sctp_transport_name_; } cricket::BaseChannel* GetChannel(const std::string& content_name); cricket::SecurePolicy SdesPolicy() const; - // Get current ssl role from transport. - bool GetSslRole(const std::string& transport_name, rtc::SSLRole* role); - - // Get current SSL role for this channel's transport. - // If |transport| is null, returns false. - bool GetSslRole(const cricket::BaseChannel* channel, rtc::SSLRole* role); + // Get current SSL role used by SCTP's underlying transport. + bool GetSctpSslRole(rtc::SSLRole* role); + // Get SSL role for an arbitrary m= section (handles bundling correctly). + // TODO(deadbeef): This is only used internally by the session description + // factory, it shouldn't really be public). + bool GetSslRole(const std::string& content_name, rtc::SSLRole* role); void CreateOffer( CreateSessionDescriptionObserver* observer, @@ -232,6 +245,7 @@ class WebRtcSession : // The ownership of |desc| will be transferred after this call. bool SetRemoteDescription(SessionDescriptionInterface* desc, std::string* err_desc); + bool ProcessIceMessage(const IceCandidateInterface* ice_candidate); bool RemoveRemoteIceCandidates( @@ -326,7 +340,7 @@ class WebRtcSession : // WebRTCSessionDescriptionFactory. Should happen before setLocalDescription. void OnCertificateReady( const rtc::scoped_refptr& certificate); - void OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp); + void OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp); // For unit test. bool waiting_for_certificate_for_testing() const; @@ -338,8 +352,9 @@ class WebRtcSession : transport_controller_->SetMetricsObserver(metrics_observer); } - // Called when voice_channel_, video_channel_ and data_channel_ are created - // and destroyed. As a result of, for example, setting a new description. + // Called when voice_channel_, video_channel_ and + // rtp_data_channel_/sctp_transport_ are created and destroyed. As a result + // of, for example, setting a new description. sigslot::signal0<> SignalVoiceChannelCreated; sigslot::signal0<> SignalVoiceChannelDestroyed; sigslot::signal0<> SignalVideoChannelCreated; @@ -397,6 +412,7 @@ class WebRtcSession : bool PushdownMediaDescription(cricket::ContentAction action, cricket::ContentSource source, std::string* error_desc); + bool PushdownSctpParameters_n(cricket::ContentSource source); bool PushdownTransportDescription(cricket::ContentSource source, cricket::ContentAction action, @@ -461,11 +477,24 @@ class WebRtcSession : std::unique_ptr GetStats_n( const ChannelNamePairs& channel_name_pairs); - // Listens to SCTP CONTROL messages on unused SIDs and process them as OPEN - // messages. - void OnDataChannelMessageReceived(cricket::DataChannel* channel, - const cricket::ReceiveDataParams& params, - const rtc::CopyOnWriteBuffer& payload); + bool CreateSctpTransport_n(const std::string& content_name, + const std::string& transport_name); + // For bundling. + void ChangeSctpTransport_n(const std::string& transport_name); + void DestroySctpTransport_n(); + // SctpTransport signal handlers. Needed to marshal signals from the network + // to signaling thread. + void OnSctpTransportReadyToSendData_n(); + // This may be called with "false" if the direction of the m= section causes + // us to tear down the SCTP connection. + void OnSctpTransportReadyToSendData_s(bool ready); + void OnSctpTransportDataReceived_n(const cricket::ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& payload); + // Beyond just firing the signal to the signaling thread, listens to SCTP + // CONTROL messages on unused SIDs and processes them as OPEN messages. + void OnSctpTransportDataReceived_s(const cricket::ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& payload); + void OnSctpStreamClosedRemotely_n(int sid); std::string BadStateErrMsg(State state); void SetIceConnectionState(PeerConnectionInterface::IceConnectionState state); @@ -498,6 +527,7 @@ class WebRtcSession : // this session. bool SrtpRequired() const; + // TransportController signal handlers. void OnTransportControllerConnectionState(cricket::IceConnectionState state); void OnTransportControllerReceiving(bool receiving); void OnTransportControllerGatheringState(cricket::IceGatheringState state); @@ -506,6 +536,7 @@ class WebRtcSession : const std::vector& candidates); void OnTransportControllerCandidatesRemoved( const std::vector& candidates); + void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error); std::string GetSessionErrorMsg(); @@ -522,8 +553,6 @@ class WebRtcSession : const std::string GetTransportName(const std::string& content_name); - void OnDtlsHandshakeError(rtc::SSLHandshakeError error); - rtc::Thread* const network_thread_; rtc::Thread* const worker_thread_; rtc::Thread* const signaling_thread_; @@ -536,10 +565,39 @@ class WebRtcSession : bool initial_offerer_ = false; const std::unique_ptr transport_controller_; + const std::unique_ptr sctp_factory_; MediaControllerInterface* media_controller_; std::unique_ptr voice_channel_; std::unique_ptr video_channel_; - std::unique_ptr data_channel_; + // |rtp_data_channel_| is used if in RTP data channel mode, |sctp_transport_| + // when using SCTP. + std::unique_ptr rtp_data_channel_; + + std::unique_ptr sctp_transport_; + // |sctp_transport_name_| keeps track of what DTLS transport the SCTP + // transport is using (which can change due to bundling). + rtc::Optional sctp_transport_name_; + // |sctp_content_name_| is the content name (MID) in SDP. + rtc::Optional sctp_content_name_; + // Value cached on signaling thread. Only updated when SctpReadyToSendData + // fires on the signaling thread. + bool sctp_ready_to_send_data_ = false; + // Same as signals provided by SctpTransport, but these are guaranteed to + // fire on the signaling thread, whereas SctpTransport fires on the networking + // thread. + // |sctp_invoker_| is used so that any signals queued on the signaling thread + // from the network thread are immediately discarded if the SctpTransport is + // destroyed (due to m= section being rejected). + // TODO(deadbeef): Use a proxy object to ensure that method calls/signals + // are marshalled to the right thread. Could almost use proxy.h for this, + // but it doesn't have a mechanism for marshalling sigslot::signals + std::unique_ptr sctp_invoker_; + sigslot::signal1 SignalSctpReadyToSendData; + sigslot::signal2 + SignalSctpDataReceived; + sigslot::signal1 SignalSctpStreamClosedRemotely; + cricket::ChannelManager* channel_manager_; IceObserver* ice_observer_; PeerConnectionInterface::IceConnectionState ice_connection_state_; diff --git a/webrtc/api/webrtcsession_unittest.cc b/webrtc/api/webrtcsession_unittest.cc index 116c8d5739..7c26d1db57 100644 --- a/webrtc/api/webrtcsession_unittest.cc +++ b/webrtc/api/webrtcsession_unittest.cc @@ -40,6 +40,7 @@ #include "webrtc/media/base/fakevideorenderer.h" #include "webrtc/media/base/mediachannel.h" #include "webrtc/media/engine/fakewebrtccall.h" +#include "webrtc/media/sctp/sctptransportinternal.h" #include "webrtc/p2p/base/packettransportinterface.h" #include "webrtc/p2p/base/stunserver.h" #include "webrtc/p2p/base/teststunserver.h" @@ -109,6 +110,7 @@ static const char kMediaContentName0[] = "audio"; static const int kMediaContentIndex1 = 1; static const char kMediaContentName1[] = "video"; +static const int kDefaultTimeout = 10000; // 10 seconds. static const int kIceCandidatesTimeout = 10000; // STUN timeout with all retransmissions is a total of 9500ms. static const int kStunTimeout = 9500; @@ -211,6 +213,52 @@ class MockIceObserver : public webrtc::IceObserver { size_t num_candidates_removed_ = 0; }; +// Used for tests in this file to verify that WebRtcSession responds to signals +// from the SctpTransport correctly, and calls Start with the correct +// local/remote ports. +class FakeSctpTransport : public cricket::SctpTransportInternal { + public: + void SetTransportChannel(cricket::TransportChannel* channel) override {} + bool Start(int local_port, int remote_port) override { + local_port_ = local_port; + remote_port_ = remote_port; + return true; + } + bool OpenStream(int sid) override { return true; } + bool ResetStream(int sid) override { return true; } + bool SendData(const cricket::SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + cricket::SendDataResult* result = nullptr) override { + return true; + } + bool ReadyToSendData() override { return true; } + void set_debug_name_for_testing(const char* debug_name) override {} + + int local_port() const { return local_port_; } + int remote_port() const { return remote_port_; } + + private: + int local_port_ = -1; + int remote_port_ = -1; +}; + +class FakeSctpTransportFactory : public cricket::SctpTransportInternalFactory { + public: + std::unique_ptr CreateSctpTransport( + cricket::TransportChannel*) override { + last_fake_sctp_transport_ = new FakeSctpTransport(); + return std::unique_ptr( + last_fake_sctp_transport_); + } + + FakeSctpTransport* last_fake_sctp_transport() { + return last_fake_sctp_transport_; + } + + private: + FakeSctpTransport* last_fake_sctp_transport_ = nullptr; +}; + class WebRtcSessionForTest : public webrtc::WebRtcSession { public: WebRtcSessionForTest( @@ -220,13 +268,15 @@ class WebRtcSessionForTest : public webrtc::WebRtcSession { rtc::Thread* signaling_thread, cricket::PortAllocator* port_allocator, webrtc::IceObserver* ice_observer, - std::unique_ptr transport_controller) + std::unique_ptr transport_controller, + std::unique_ptr sctp_factory) : WebRtcSession(media_controller, network_thread, worker_thread, signaling_thread, port_allocator, - std::move(transport_controller)) { + std::move(transport_controller), + std::move(sctp_factory)) { RegisterIceObserver(ice_observer); } virtual ~WebRtcSessionForTest() {} @@ -249,14 +299,6 @@ class WebRtcSessionForTest : public webrtc::WebRtcSession { return rtcp_transport_channel(video_channel()); } - rtc::PacketTransportInterface* data_rtp_transport_channel() { - return rtp_transport_channel(data_channel()); - } - - rtc::PacketTransportInterface* data_rtcp_transport_channel() { - return rtcp_transport_channel(data_channel()); - } - private: rtc::PacketTransportInterface* rtp_transport_channel( cricket::BaseChannel* ch) { @@ -386,13 +428,16 @@ class WebRtcSessionTest std::unique_ptr cert_generator, PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy) { ASSERT_TRUE(session_.get() == NULL); + fake_sctp_transport_factory_ = new FakeSctpTransportFactory(); session_.reset(new WebRtcSessionForTest( media_controller_.get(), rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(), allocator_.get(), &observer_, std::unique_ptr( new cricket::TransportController(rtc::Thread::Current(), rtc::Thread::Current(), - allocator_.get())))); + allocator_.get())), + std::unique_ptr( + fake_sctp_transport_factory_))); session_->SignalDataChannelOpenMessage.connect( this, &WebRtcSessionTest::OnDataChannelOpenMessage); session_->GetOnDestroyedSignal()->connect( @@ -1496,6 +1541,8 @@ class WebRtcSessionTest webrtc::RtcEventLogNullImpl event_log_; cricket::FakeMediaEngine* media_engine_; cricket::FakeDataEngine* data_engine_; + // Actually owned by session_. + FakeSctpTransportFactory* fake_sctp_transport_factory_ = nullptr; std::unique_ptr channel_manager_; cricket::FakeCall fake_call_; std::unique_ptr media_controller_; @@ -3875,7 +3922,7 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannel) { Init(); SetLocalDescriptionWithDataChannel(); ASSERT_TRUE(data_engine_); - EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type()); + EXPECT_NE(nullptr, data_engine_->GetChannel(0)); } TEST_P(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { @@ -3887,7 +3934,43 @@ TEST_P(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { InitWithDtls(GetParam()); SetLocalDescriptionWithDataChannel(); - EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type()); + EXPECT_NE(nullptr, data_engine_->GetChannel(0)); +} + +// Test that sctp_content_name/sctp_transport_name (used for stats) are correct +// before and after BUNDLE is negotiated. +TEST_P(WebRtcSessionTest, SctpContentAndTransportName) { + MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp); + SetFactoryDtlsSrtp(); + InitWithDtls(GetParam()); + + // Initially these fields should be empty. + EXPECT_FALSE(session_->sctp_content_name()); + EXPECT_FALSE(session_->sctp_transport_name()); + + // Create offer with audio/video/data. + // Default bundle policy is "balanced", so data should be using its own + // transport. + SendAudioVideoStream1(); + CreateDataChannel(); + InitiateCall(); + ASSERT_TRUE(session_->sctp_content_name()); + ASSERT_TRUE(session_->sctp_transport_name()); + EXPECT_EQ("data", *session_->sctp_content_name()); + EXPECT_EQ("data", *session_->sctp_transport_name()); + + // Create answer that finishes BUNDLE negotiation, which means everything + // should be bundled on the first transport (audio). + cricket::MediaSessionOptions answer_options; + answer_options.recv_video = true; + answer_options.bundle_enabled = true; + answer_options.data_channel_type = cricket::DCT_SCTP; + SetRemoteDescriptionWithoutError(CreateRemoteAnswer( + session_->local_description(), answer_options, cricket::SEC_DISABLED)); + ASSERT_TRUE(session_->sctp_content_name()); + ASSERT_TRUE(session_->sctp_transport_name()); + EXPECT_EQ("data", *session_->sctp_content_name()); + EXPECT_EQ("audio", *session_->sctp_transport_name()); } TEST_P(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) { @@ -3919,30 +4002,39 @@ TEST_P(WebRtcSessionTest, TestCreateAnswerWithSctpInOfferAndNoStreams) { EXPECT_TRUE(answer->description()->GetTransportInfoByName("data") != NULL); } +// Test that if DTLS is disabled, we don't end up with an SctpTransport +// created (or an RtpDataChannel). TEST_P(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) { configuration_.enable_dtls_srtp = rtc::Optional(false); InitWithDtls(GetParam()); SetLocalDescriptionWithDataChannel(); - EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type()); + EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); + EXPECT_EQ(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); } +// Test that if DTLS is enabled, we end up with an SctpTransport created +// (and not an RtpDataChannel). TEST_P(WebRtcSessionTest, TestSctpDataChannelWithDtls) { MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp); InitWithDtls(GetParam()); SetLocalDescriptionWithDataChannel(); - EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); + EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); + EXPECT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); } +// Test that if SCTP is disabled, we don't end up with an SctpTransport +// created (or an RtpDataChannel). TEST_P(WebRtcSessionTest, TestDisableSctpDataChannels) { MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp); options_.disable_sctp_data_channels = true; InitWithDtls(GetParam()); SetLocalDescriptionWithDataChannel(); - EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type()); + EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); + EXPECT_EQ(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); } TEST_P(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) { @@ -3973,31 +4065,19 @@ TEST_P(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) { // TEST PLAN: Set the port number to something new, set it in the SDP, // and pass it all the way down. - EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); + EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); CreateDataChannel(); - - cricket::FakeDataMediaChannel* ch = data_engine_->GetChannel(0); - int portnum = -1; - ASSERT_TRUE(ch != NULL); - ASSERT_EQ(1UL, ch->send_codecs().size()); - EXPECT_EQ(cricket::kGoogleSctpDataCodecPlType, ch->send_codecs()[0].id); - EXPECT_EQ(0, strcmp(cricket::kGoogleSctpDataCodecName, - ch->send_codecs()[0].name.c_str())); - EXPECT_TRUE(ch->send_codecs()[0].GetParam(cricket::kCodecParamPort, - &portnum)); - EXPECT_EQ(new_send_port, portnum); - - ASSERT_EQ(1UL, ch->recv_codecs().size()); - EXPECT_EQ(cricket::kGoogleSctpDataCodecPlType, ch->recv_codecs()[0].id); - EXPECT_EQ(0, strcmp(cricket::kGoogleSctpDataCodecName, - ch->recv_codecs()[0].name.c_str())); - EXPECT_TRUE(ch->recv_codecs()[0].GetParam(cricket::kCodecParamPort, - &portnum)); - EXPECT_EQ(new_recv_port, portnum); + ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); + EXPECT_EQ( + new_recv_port, + fake_sctp_transport_factory_->last_fake_sctp_transport()->local_port()); + EXPECT_EQ( + new_send_port, + fake_sctp_transport_factory_->last_fake_sctp_transport()->remote_port()); } -// Verifies that when a session's DataChannel receives an OPEN message, -// WebRtcSession signals the DataChannel creation request with the expected +// Verifies that when a session's SctpTransport receives an OPEN message, +// WebRtcSession signals the SctpTransport creation request with the expected // config. TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) { MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp); @@ -4005,8 +4085,10 @@ TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) { InitWithDtls(GetParam()); SetLocalDescriptionWithDataChannel(); - EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); + EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); + ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); + // Make the fake SCTP transport pretend it received an OPEN message. webrtc::DataChannelInit config; config.id = 1; rtc::CopyOnWriteBuffer payload; @@ -4014,11 +4096,10 @@ TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) { cricket::ReceiveDataParams params; params.ssrc = config.id; params.type = cricket::DMT_CONTROL; + fake_sctp_transport_factory_->last_fake_sctp_transport()->SignalDataReceived( + params, payload); - cricket::DataChannel* data_channel = session_->data_channel(); - data_channel->SignalDataReceived(data_channel, params, payload); - - EXPECT_EQ("a", last_data_channel_label_); + EXPECT_EQ_WAIT("a", last_data_channel_label_, kDefaultTimeout); EXPECT_EQ(config.id, last_data_channel_config_.id); EXPECT_FALSE(last_data_channel_config_.negotiated); EXPECT_EQ(webrtc::InternalDataChannelInit::kAcker, diff --git a/webrtc/api/webrtcsessiondescriptionfactory.cc b/webrtc/api/webrtcsessiondescriptionfactory.cc index 1f811f5911..0ab458bdfe 100644 --- a/webrtc/api/webrtcsessiondescriptionfactory.cc +++ b/webrtc/api/webrtcsessiondescriptionfactory.cc @@ -400,7 +400,7 @@ void WebRtcSessionDescriptionFactory::InternalCreateAnswer( // We should pass the current SSL role to the transport description // factory, if there is already an existing ongoing session. rtc::SSLRole ssl_role; - if (session_->GetSslRole(session_->GetChannel(content.name), &ssl_role)) { + if (session_->GetSslRole(content.name, &ssl_role)) { request.options.transport_options[content.name].prefer_passive_role = (rtc::SSL_SERVER == ssl_role); } diff --git a/webrtc/media/BUILD.gn b/webrtc/media/BUILD.gn index 04936de177..3df49da7b1 100644 --- a/webrtc/media/BUILD.gn +++ b/webrtc/media/BUILD.gn @@ -143,12 +143,13 @@ rtc_static_library("rtc_media") { "engine/webrtcvoe.h", "engine/webrtcvoiceengine.cc", "engine/webrtcvoiceengine.h", + "sctp/sctptransportinternal.h", ] if (rtc_enable_sctp) { sources += [ - "sctp/sctpdataengine.cc", - "sctp/sctpdataengine.h", + "sctp/sctptransport.cc", + "sctp/sctptransport.h", ] } @@ -346,7 +347,7 @@ if (rtc_include_tests) { ] if (rtc_enable_sctp) { - sources += [ "sctp/sctpdataengine_unittest.cc" ] + sources += [ "sctp/sctptransport_unittest.cc" ] } configs += [ ":rtc_media_unittests_config" ] diff --git a/webrtc/media/base/fakemediaengine.h b/webrtc/media/base/fakemediaengine.h index 932427b9b4..cbd4e6ef26 100644 --- a/webrtc/media/base/fakemediaengine.h +++ b/webrtc/media/base/fakemediaengine.h @@ -942,11 +942,9 @@ inline FakeVideoMediaChannel::~FakeVideoMediaChannel() { class FakeDataEngine : public DataEngineInterface { public: - FakeDataEngine() : last_channel_type_(DCT_NONE) {} + FakeDataEngine(){}; - virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type, - const MediaConfig& config) { - last_channel_type_ = data_channel_type; + virtual DataMediaChannel* CreateChannel(const MediaConfig& config) { FakeDataMediaChannel* ch = new FakeDataMediaChannel(this, DataOptions()); channels_.push_back(ch); return ch; @@ -966,12 +964,9 @@ class FakeDataEngine : public DataEngineInterface { virtual const std::vector& data_codecs() { return data_codecs_; } - DataChannelType last_channel_type() const { return last_channel_type_; } - private: std::vector channels_; std::vector data_codecs_; - DataChannelType last_channel_type_; }; } // namespace cricket diff --git a/webrtc/media/base/hybriddataengine.h b/webrtc/media/base/hybriddataengine.h deleted file mode 100644 index 341f054868..0000000000 --- a/webrtc/media/base/hybriddataengine.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MEDIA_BASE_HYBRIDDATAENGINE_H_ -#define WEBRTC_MEDIA_BASE_HYBRIDDATAENGINE_H_ - -#include -#include -#include - -#include "webrtc/media/base/codec.h" -#include "webrtc/media/base/mediachannel.h" -#include "webrtc/media/base/mediaengine.h" - -namespace cricket { - -class HybridDataEngine : public DataEngineInterface { - public: - // Takes ownership. - HybridDataEngine(DataEngineInterface* first, - DataEngineInterface* second) - : first_(first), - second_(second) { - codecs_ = first_->data_codecs(); - codecs_.insert( - codecs_.end(), - second_->data_codecs().begin(), - second_->data_codecs().end()); - } - - virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type, - const MediaConfig& config) { - DataMediaChannel* channel = NULL; - if (first_) { - channel = first_->CreateChannel(data_channel_type, config); - } - if (!channel && second_) { - channel = second_->CreateChannel(data_channel_type, config); - } - return channel; - } - - virtual const std::vector& data_codecs() { return codecs_; } - - private: - std::unique_ptr first_; - std::unique_ptr second_; - std::vector codecs_; -}; - -} // namespace cricket - -#endif // WEBRTC_MEDIA_BASE_HYBRIDDATAENGINE_H_ diff --git a/webrtc/media/base/mediachannel.h b/webrtc/media/base/mediachannel.h index d66424014b..4c6b024947 100644 --- a/webrtc/media/base/mediachannel.h +++ b/webrtc/media/base/mediachannel.h @@ -1077,8 +1077,11 @@ enum DataMessageType { // signal fires, on up the chain. struct ReceiveDataParams { // The in-packet stream indentifier. - // For SCTP, this is really SID, not SSRC. - uint32_t ssrc; + // RTP data channels use SSRCs, SCTP data channels use SIDs. + union { + uint32_t ssrc; + int sid; + }; // The type of message (binary, text, or control). DataMessageType type; // A per-stream value incremented per packet in the stream. @@ -1086,18 +1089,16 @@ struct ReceiveDataParams { // A per-stream value monotonically increasing with time. int timestamp; - ReceiveDataParams() : - ssrc(0), - type(DMT_TEXT), - seq_num(0), - timestamp(0) { - } + ReceiveDataParams() : sid(0), type(DMT_TEXT), seq_num(0), timestamp(0) {} }; struct SendDataParams { // The in-packet stream indentifier. - // For SCTP, this is really SID, not SSRC. - uint32_t ssrc; + // RTP data channels use SSRCs, SCTP data channels use SIDs. + union { + uint32_t ssrc; + int sid; + }; // The type of message (binary, text, or control). DataMessageType type; @@ -1116,15 +1117,14 @@ struct SendDataParams { // is supported, not both at the same time. int max_rtx_ms; - SendDataParams() : - ssrc(0), - type(DMT_TEXT), - // TODO(pthatcher): Make these true by default? - ordered(false), - reliable(false), - max_rtx_count(0), - max_rtx_ms(0) { - } + SendDataParams() + : sid(0), + type(DMT_TEXT), + // TODO(pthatcher): Make these true by default? + ordered(false), + reliable(false), + max_rtx_count(0), + max_rtx_ms(0) {} }; enum SendDataResult { SDR_SUCCESS, SDR_ERROR, SDR_BLOCK }; @@ -1183,8 +1183,6 @@ class DataMediaChannel : public MediaChannel { // Signal when the media channel is ready to send the stream. Arguments are: // writable(bool) sigslot::signal1 SignalReadyToSend; - // Signal for notifying that the remote side has closed the DataChannel. - sigslot::signal1 SignalStreamClosedRemotely; }; } // namespace cricket diff --git a/webrtc/media/base/mediaconstants.h b/webrtc/media/base/mediaconstants.h index 547839afdb..44d8c7ee01 100644 --- a/webrtc/media/base/mediaconstants.h +++ b/webrtc/media/base/mediaconstants.h @@ -106,9 +106,9 @@ extern const char kCodecParamStartBitrate[]; extern const char kCodecParamMaxQuantization[]; extern const char kCodecParamPort[]; -// We put the data codec names here so callers of -// DataEngine::CreateChannel don't have to import rtpdataengine.h or -// sctpdataengine.h to get the codec names they want to pass in. +// We put the data codec names here so callers of DataEngine::CreateChannel +// don't have to import rtpdataengine.h to get the codec names they want to +// pass in. extern const int kGoogleRtpDataCodecPlType; extern const char kGoogleRtpDataCodecName[]; diff --git a/webrtc/media/base/mediaengine.h b/webrtc/media/base/mediaengine.h index debc171360..0dbac55954 100644 --- a/webrtc/media/base/mediaengine.h +++ b/webrtc/media/base/mediaengine.h @@ -171,8 +171,7 @@ enum DataChannelType { DCT_NONE = 0, DCT_RTP = 1, DCT_SCTP = 2, DCT_QUIC = 3 }; class DataEngineInterface { public: virtual ~DataEngineInterface() {} - virtual DataMediaChannel* CreateChannel(DataChannelType type, - const MediaConfig& config) = 0; + virtual DataMediaChannel* CreateChannel(const MediaConfig& config) = 0; virtual const std::vector& data_codecs() = 0; }; diff --git a/webrtc/media/base/rtpdataengine.cc b/webrtc/media/base/rtpdataengine.cc index 12a37fa180..96f5a3b95f 100644 --- a/webrtc/media/base/rtpdataengine.cc +++ b/webrtc/media/base/rtpdataengine.cc @@ -40,11 +40,7 @@ RtpDataEngine::RtpDataEngine() { } DataMediaChannel* RtpDataEngine::CreateChannel( - DataChannelType data_channel_type, const MediaConfig& config) { - if (data_channel_type != DCT_RTP) { - return NULL; - } return new RtpDataMediaChannel(config); } diff --git a/webrtc/media/base/rtpdataengine.h b/webrtc/media/base/rtpdataengine.h index adf0bef6e9..ca434940dc 100644 --- a/webrtc/media/base/rtpdataengine.h +++ b/webrtc/media/base/rtpdataengine.h @@ -27,8 +27,7 @@ class RtpDataEngine : public DataEngineInterface { public: RtpDataEngine(); - virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type, - const MediaConfig& config); + virtual DataMediaChannel* CreateChannel(const MediaConfig& config); virtual const std::vector& data_codecs() { return data_codecs_; diff --git a/webrtc/media/base/rtpdataengine_unittest.cc b/webrtc/media/base/rtpdataengine_unittest.cc index 9d1e485916..dca509df8a 100644 --- a/webrtc/media/base/rtpdataengine_unittest.cc +++ b/webrtc/media/base/rtpdataengine_unittest.cc @@ -72,8 +72,7 @@ class RtpDataMediaChannelTest : public testing::Test { cricket::RtpDataMediaChannel* CreateChannel(cricket::RtpDataEngine* dme) { cricket::MediaConfig config; cricket::RtpDataMediaChannel* channel = - static_cast( - dme->CreateChannel(cricket::DCT_RTP, config)); + static_cast(dme->CreateChannel(config)); channel->SetInterface(iface_.get()); channel->SignalDataReceived.connect( receiver_.get(), &FakeDataReceiver::OnDataReceived); diff --git a/webrtc/media/sctp/sctpdataengine.cc b/webrtc/media/sctp/sctpdataengine.cc deleted file mode 100644 index 103aebdd9b..0000000000 --- a/webrtc/media/sctp/sctpdataengine.cc +++ /dev/null @@ -1,1066 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/media/sctp/sctpdataengine.h" - -#include -#include - -#include -#include -#include - -#include "usrsctplib/usrsctp.h" -#include "webrtc/base/arraysize.h" -#include "webrtc/base/copyonwritebuffer.h" -#include "webrtc/base/criticalsection.h" -#include "webrtc/base/helpers.h" -#include "webrtc/base/logging.h" -#include "webrtc/base/safe_conversions.h" -#include "webrtc/media/base/codec.h" -#include "webrtc/media/base/mediaconstants.h" -#include "webrtc/media/base/streamparams.h" - -namespace cricket { -// The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280, -// take off 80 bytes for DTLS/TURN/TCP/IP overhead. -static constexpr size_t kSctpMtu = 1200; - -// The size of the SCTP association send buffer. 256kB, the usrsctp default. -static constexpr int kSendBufferSize = 262144; - -struct SctpInboundPacket { - rtc::CopyOnWriteBuffer buffer; - ReceiveDataParams params; - // The |flags| parameter is used by SCTP to distinguish notification packets - // from other types of packets. - int flags; -}; - -namespace { -// Set the initial value of the static SCTP Data Engines reference count. -int g_usrsctp_usage_count = 0; -rtc::GlobalLockPod g_usrsctp_lock_; - -typedef SctpDataMediaChannel::StreamSet StreamSet; - -// Returns a comma-separated, human-readable list of the stream IDs in 's' -std::string ListStreams(const StreamSet& s) { - std::stringstream result; - bool first = true; - for (StreamSet::const_iterator it = s.begin(); it != s.end(); ++it) { - if (!first) { - result << ", " << *it; - } else { - result << *it; - first = false; - } - } - return result.str(); -} - -// Returns a pipe-separated, human-readable list of the SCTP_STREAM_RESET -// flags in 'flags' -std::string ListFlags(int flags) { - std::stringstream result; - bool first = true; - // Skip past the first 12 chars (strlen("SCTP_STREAM_")) -#define MAKEFLAG(X) { X, #X + 12} - struct flaginfo_t { - int value; - const char* name; - } flaginfo[] = { - MAKEFLAG(SCTP_STREAM_RESET_INCOMING_SSN), - MAKEFLAG(SCTP_STREAM_RESET_OUTGOING_SSN), - MAKEFLAG(SCTP_STREAM_RESET_DENIED), - MAKEFLAG(SCTP_STREAM_RESET_FAILED), - MAKEFLAG(SCTP_STREAM_CHANGE_DENIED) - }; -#undef MAKEFLAG - for (uint32_t i = 0; i < arraysize(flaginfo); ++i) { - if (flags & flaginfo[i].value) { - if (!first) result << " | "; - result << flaginfo[i].name; - first = false; - } - } - return result.str(); -} - -// Returns a comma-separated, human-readable list of the integers in 'array'. -// All 'num_elems' of them. -std::string ListArray(const uint16_t* array, int num_elems) { - std::stringstream result; - for (int i = 0; i < num_elems; ++i) { - if (i) { - result << ", " << array[i]; - } else { - result << array[i]; - } - } - return result.str(); -} - -typedef rtc::ScopedMessageData InboundPacketMessage; -typedef rtc::ScopedMessageData OutboundPacketMessage; - -enum { - MSG_SCTPINBOUNDPACKET = 1, // MessageData is SctpInboundPacket - MSG_SCTPOUTBOUNDPACKET = 2, // MessageData is rtc:Buffer -}; - -// Helper for logging SCTP messages. -void DebugSctpPrintf(const char* format, ...) { -#if RTC_DCHECK_IS_ON - char s[255]; - va_list ap; - va_start(ap, format); - vsnprintf(s, sizeof(s), format, ap); - LOG(LS_INFO) << "SCTP: " << s; - va_end(ap); -#endif -} - -// Get the PPID to use for the terminating fragment of this type. -SctpDataMediaChannel::PayloadProtocolIdentifier GetPpid(DataMessageType type) { - switch (type) { - default: - case DMT_NONE: - return SctpDataMediaChannel::PPID_NONE; - case DMT_CONTROL: - return SctpDataMediaChannel::PPID_CONTROL; - case DMT_BINARY: - return SctpDataMediaChannel::PPID_BINARY_LAST; - case DMT_TEXT: - return SctpDataMediaChannel::PPID_TEXT_LAST; - }; -} - -bool GetDataMediaType(SctpDataMediaChannel::PayloadProtocolIdentifier ppid, - DataMessageType* dest) { - ASSERT(dest != NULL); - switch (ppid) { - case SctpDataMediaChannel::PPID_BINARY_PARTIAL: - case SctpDataMediaChannel::PPID_BINARY_LAST: - *dest = DMT_BINARY; - return true; - - case SctpDataMediaChannel::PPID_TEXT_PARTIAL: - case SctpDataMediaChannel::PPID_TEXT_LAST: - *dest = DMT_TEXT; - return true; - - case SctpDataMediaChannel::PPID_CONTROL: - *dest = DMT_CONTROL; - return true; - - case SctpDataMediaChannel::PPID_NONE: - *dest = DMT_NONE; - return true; - - default: - return false; - } -} - -// Log the packet in text2pcap format, if log level is at LS_VERBOSE. -void VerboseLogPacket(const void* data, size_t length, int direction) { - if (LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) { - char *dump_buf; - // Some downstream project uses an older version of usrsctp that expects - // a non-const "void*" as first parameter when dumping the packet, so we - // need to cast the const away here to avoid a compiler error. - if ((dump_buf = usrsctp_dumppacket( - const_cast(data), length, direction)) != NULL) { - LOG(LS_VERBOSE) << dump_buf; - usrsctp_freedumpbuffer(dump_buf); - } - } -} - -// This is the callback usrsctp uses when there's data to send on the network -// that has been wrapped appropriatly for the SCTP protocol. -int OnSctpOutboundPacket(void* addr, - void* data, - size_t length, - uint8_t tos, - uint8_t set_df) { - SctpDataMediaChannel* channel = static_cast(addr); - LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():" - << "addr: " << addr << "; length: " << length - << "; tos: " << std::hex << static_cast(tos) - << "; set_df: " << std::hex << static_cast(set_df); - - VerboseLogPacket(data, length, SCTP_DUMP_OUTBOUND); - // Note: We have to copy the data; the caller will delete it. - auto* msg = new OutboundPacketMessage( - new rtc::CopyOnWriteBuffer(reinterpret_cast(data), length)); - channel->worker_thread()->Post(RTC_FROM_HERE, channel, MSG_SCTPOUTBOUNDPACKET, - msg); - return 0; -} - -// This is the callback called from usrsctp when data has been received, after -// a packet has been interpreted and parsed by usrsctp and found to contain -// payload data. It is called by a usrsctp thread. It is assumed this function -// will free the memory used by 'data'. -int OnSctpInboundPacket(struct socket* sock, - union sctp_sockstore addr, - void* data, - size_t length, - struct sctp_rcvinfo rcv, - int flags, - void* ulp_info) { - SctpDataMediaChannel* channel = static_cast(ulp_info); - // Post data to the channel's receiver thread (copying it). - // TODO(ldixon): Unclear if copy is needed as this method is responsible for - // memory cleanup. But this does simplify code. - const SctpDataMediaChannel::PayloadProtocolIdentifier ppid = - static_cast( - rtc::HostToNetwork32(rcv.rcv_ppid)); - DataMessageType type = DMT_NONE; - if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) { - // It's neither a notification nor a recognized data packet. Drop it. - LOG(LS_ERROR) << "Received an unknown PPID " << ppid - << " on an SCTP packet. Dropping."; - } else { - SctpInboundPacket* packet = new SctpInboundPacket; - packet->buffer.SetData(reinterpret_cast(data), length); - packet->params.ssrc = rcv.rcv_sid; - packet->params.seq_num = rcv.rcv_ssn; - packet->params.timestamp = rcv.rcv_tsn; - packet->params.type = type; - packet->flags = flags; - // The ownership of |packet| transfers to |msg|. - InboundPacketMessage* msg = new InboundPacketMessage(packet); - channel->worker_thread()->Post(RTC_FROM_HERE, channel, - MSG_SCTPINBOUNDPACKET, msg); - } - free(data); - return 1; -} - -void InitializeUsrSctp() { - LOG(LS_INFO) << __FUNCTION__; - // First argument is udp_encapsulation_port, which is not releveant for our - // AF_CONN use of sctp. - usrsctp_init(0, &OnSctpOutboundPacket, &DebugSctpPrintf); - - // To turn on/off detailed SCTP debugging. You will also need to have the - // SCTP_DEBUG cpp defines flag. - // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL); - - // TODO(ldixon): Consider turning this on/off. - usrsctp_sysctl_set_sctp_ecn_enable(0); - - // This is harmless, but we should find out when the library default - // changes. - int send_size = usrsctp_sysctl_get_sctp_sendspace(); - if (send_size != kSendBufferSize) { - LOG(LS_ERROR) << "Got different send size than expected: " << send_size; - } - - // TODO(ldixon): Consider turning this on/off. - // This is not needed right now (we don't do dynamic address changes): - // If SCTP Auto-ASCONF is enabled, the peer is informed automatically - // when a new address is added or removed. This feature is enabled by - // default. - // usrsctp_sysctl_set_sctp_auto_asconf(0); - - // TODO(ldixon): Consider turning this on/off. - // Add a blackhole sysctl. Setting it to 1 results in no ABORTs - // being sent in response to INITs, setting it to 2 results - // in no ABORTs being sent for received OOTB packets. - // This is similar to the TCP sysctl. - // - // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html - // See: http://svnweb.freebsd.org/base?view=revision&revision=229805 - // usrsctp_sysctl_set_sctp_blackhole(2); - - // Set the number of default outgoing streams. This is the number we'll - // send in the SCTP INIT message. - usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(kMaxSctpStreams); -} - -void UninitializeUsrSctp() { - LOG(LS_INFO) << __FUNCTION__; - // usrsctp_finish() may fail if it's called too soon after the channels are - // closed. Wait and try again until it succeeds for up to 3 seconds. - for (size_t i = 0; i < 300; ++i) { - if (usrsctp_finish() == 0) { - return; - } - - rtc::Thread::SleepMs(10); - } - LOG(LS_ERROR) << "Failed to shutdown usrsctp."; -} - -void IncrementUsrSctpUsageCount() { - rtc::GlobalLockScope lock(&g_usrsctp_lock_); - if (!g_usrsctp_usage_count) { - InitializeUsrSctp(); - } - ++g_usrsctp_usage_count; -} - -void DecrementUsrSctpUsageCount() { - rtc::GlobalLockScope lock(&g_usrsctp_lock_); - --g_usrsctp_usage_count; - if (!g_usrsctp_usage_count) { - UninitializeUsrSctp(); - } -} - -DataCodec GetSctpDataCodec() { - DataCodec codec(kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName); - codec.SetParam(kCodecParamPort, kSctpDefaultPort); - return codec; -} - -} // namespace - -SctpDataEngine::SctpDataEngine() : codecs_(1, GetSctpDataCodec()) {} - -SctpDataEngine::~SctpDataEngine() {} - -// Called on the worker thread. -DataMediaChannel* SctpDataEngine::CreateChannel( - DataChannelType data_channel_type, - const MediaConfig& config) { - if (data_channel_type != DCT_SCTP) { - return NULL; - } - return new SctpDataMediaChannel(rtc::Thread::Current(), config); -} - -// static -SctpDataMediaChannel* SctpDataMediaChannel::GetChannelFromSocket( - struct socket* sock) { - struct sockaddr* addrs = nullptr; - int naddrs = usrsctp_getladdrs(sock, 0, &addrs); - if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) { - return nullptr; - } - // usrsctp_getladdrs() returns the addresses bound to this socket, which - // contains the SctpDataMediaChannel* as sconn_addr. Read the pointer, - // then free the list of addresses once we have the pointer. We only open - // AF_CONN sockets, and they should all have the sconn_addr set to the - // pointer that created them, so [0] is as good as any other. - struct sockaddr_conn* sconn = - reinterpret_cast(&addrs[0]); - SctpDataMediaChannel* channel = - reinterpret_cast(sconn->sconn_addr); - usrsctp_freeladdrs(addrs); - - return channel; -} - -// static -int SctpDataMediaChannel::SendThresholdCallback(struct socket* sock, - uint32_t sb_free) { - // Fired on our I/O thread. SctpDataMediaChannel::OnPacketReceived() gets - // a packet containing acknowledgments, which goes into usrsctp_conninput, - // and then back here. - SctpDataMediaChannel* channel = GetChannelFromSocket(sock); - if (!channel) { - LOG(LS_ERROR) << "SendThresholdCallback: Failed to get channel for socket " - << sock; - return 0; - } - channel->OnSendThresholdCallback(); - return 0; -} - -SctpDataMediaChannel::SctpDataMediaChannel(rtc::Thread* thread, - const MediaConfig& config) - : DataMediaChannel(config), - worker_thread_(thread), - local_port_(kSctpDefaultPort), - remote_port_(kSctpDefaultPort), - sock_(NULL), - sending_(false), - receiving_(false), - debug_name_("SctpDataMediaChannel") {} - -SctpDataMediaChannel::~SctpDataMediaChannel() { - CloseSctpSocket(); -} - -void SctpDataMediaChannel::OnSendThresholdCallback() { - RTC_DCHECK(rtc::Thread::Current() == worker_thread_); - SignalReadyToSend(true); -} - -sockaddr_conn SctpDataMediaChannel::GetSctpSockAddr(int port) { - sockaddr_conn sconn = {0}; - sconn.sconn_family = AF_CONN; -#ifdef HAVE_SCONN_LEN - sconn.sconn_len = sizeof(sockaddr_conn); -#endif - // Note: conversion from int to uint16_t happens here. - sconn.sconn_port = rtc::HostToNetwork16(port); - sconn.sconn_addr = this; - return sconn; -} - -bool SctpDataMediaChannel::OpenSctpSocket() { - if (sock_) { - LOG(LS_VERBOSE) << debug_name_ - << "->Ignoring attempt to re-create existing socket."; - return false; - } - - IncrementUsrSctpUsageCount(); - - // If kSendBufferSize isn't reflective of reality, we log an error, but we - // still have to do something reasonable here. Look up what the buffer's - // real size is and set our threshold to something reasonable. - const static int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 2; - - sock_ = usrsctp_socket( - AF_CONN, SOCK_STREAM, IPPROTO_SCTP, OnSctpInboundPacket, - &SctpDataMediaChannel::SendThresholdCallback, kSendThreshold, this); - if (!sock_) { - LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to create SCTP socket."; - DecrementUsrSctpUsageCount(); - return false; - } - - // Make the socket non-blocking. Connect, close, shutdown etc will not block - // the thread waiting for the socket operation to complete. - if (usrsctp_set_non_blocking(sock_, 1) < 0) { - LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP to non blocking."; - return false; - } - - // This ensures that the usrsctp close call deletes the association. This - // prevents usrsctp from calling OnSctpOutboundPacket with references to - // this class as the address. - linger linger_opt; - linger_opt.l_onoff = 1; - linger_opt.l_linger = 0; - if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt, - sizeof(linger_opt))) { - LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SO_LINGER."; - return false; - } - - // Enable stream ID resets. - struct sctp_assoc_value stream_rst; - stream_rst.assoc_id = SCTP_ALL_ASSOC; - stream_rst.assoc_value = 1; - if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, - &stream_rst, sizeof(stream_rst))) { - LOG_ERRNO(LS_ERROR) << debug_name_ - << "Failed to set SCTP_ENABLE_STREAM_RESET."; - return false; - } - - // Nagle. - uint32_t nodelay = 1; - if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, - sizeof(nodelay))) { - LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_NODELAY."; - return false; - } - - // Subscribe to SCTP event notifications. - int event_types[] = {SCTP_ASSOC_CHANGE, - SCTP_PEER_ADDR_CHANGE, - SCTP_SEND_FAILED_EVENT, - SCTP_SENDER_DRY_EVENT, - SCTP_STREAM_RESET_EVENT}; - struct sctp_event event = {0}; - event.se_assoc_id = SCTP_ALL_ASSOC; - event.se_on = 1; - for (size_t i = 0; i < arraysize(event_types); i++) { - event.se_type = event_types[i]; - if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event, - sizeof(event)) < 0) { - LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to set SCTP_EVENT type: " - << event.se_type; - return false; - } - } - - // Register this class as an address for usrsctp. This is used by SCTP to - // direct the packets received (by the created socket) to this class. - usrsctp_register_address(this); - sending_ = true; - return true; -} - -void SctpDataMediaChannel::CloseSctpSocket() { - sending_ = false; - if (sock_) { - // We assume that SO_LINGER option is set to close the association when - // close is called. This means that any pending packets in usrsctp will be - // discarded instead of being sent. - usrsctp_close(sock_); - sock_ = NULL; - usrsctp_deregister_address(this); - - DecrementUsrSctpUsageCount(); - } -} - -bool SctpDataMediaChannel::Connect() { - LOG(LS_VERBOSE) << debug_name_ << "->Connect()."; - - // If we already have a socket connection, just return. - if (sock_) { - LOG(LS_WARNING) << debug_name_ << "->Connect(): Ignored as socket " - "is already established."; - return true; - } - - // If no socket (it was closed) try to start it again. This can happen when - // the socket we are connecting to closes, does an sctp shutdown handshake, - // or behaves unexpectedly causing us to perform a CloseSctpSocket. - if (!sock_ && !OpenSctpSocket()) { - return false; - } - - // Note: conversion from int to uint16_t happens on assignment. - sockaddr_conn local_sconn = GetSctpSockAddr(local_port_); - if (usrsctp_bind(sock_, reinterpret_cast(&local_sconn), - sizeof(local_sconn)) < 0) { - LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): " - << ("Failed usrsctp_bind"); - CloseSctpSocket(); - return false; - } - - // Note: conversion from int to uint16_t happens on assignment. - sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_); - int connect_result = usrsctp_connect( - sock_, reinterpret_cast(&remote_sconn), sizeof(remote_sconn)); - if (connect_result < 0 && errno != SCTP_EINPROGRESS) { - LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed usrsctp_connect. got errno=" - << errno << ", but wanted " << SCTP_EINPROGRESS; - CloseSctpSocket(); - return false; - } - // Set the MTU and disable MTU discovery. - // We can only do this after usrsctp_connect or it has no effect. - sctp_paddrparams params = {{0}}; - memcpy(¶ms.spp_address, &remote_sconn, sizeof(remote_sconn)); - params.spp_flags = SPP_PMTUD_DISABLE; - params.spp_pathmtu = kSctpMtu; - if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, ¶ms, - sizeof(params))) { - LOG_ERRNO(LS_ERROR) << debug_name_ - << "Failed to set SCTP_PEER_ADDR_PARAMS."; - } - return true; -} - -void SctpDataMediaChannel::Disconnect() { - // TODO(ldixon): Consider calling |usrsctp_shutdown(sock_, ...)| to do a - // shutdown handshake and remove the association. - CloseSctpSocket(); -} - -bool SctpDataMediaChannel::SetSend(bool send) { - if (!sending_ && send) { - return Connect(); - } - if (sending_ && !send) { - Disconnect(); - } - return true; -} - -bool SctpDataMediaChannel::SetReceive(bool receive) { - receiving_ = receive; - return true; -} - -bool SctpDataMediaChannel::SetSendParameters(const DataSendParameters& params) { - return SetSendCodecs(params.codecs); -} - -bool SctpDataMediaChannel::SetRecvParameters(const DataRecvParameters& params) { - return SetRecvCodecs(params.codecs); -} - -bool SctpDataMediaChannel::AddSendStream(const StreamParams& stream) { - return AddStream(stream); -} - -bool SctpDataMediaChannel::RemoveSendStream(uint32_t ssrc) { - return ResetStream(ssrc); -} - -bool SctpDataMediaChannel::AddRecvStream(const StreamParams& stream) { - // SCTP DataChannels are always bi-directional and calling AddSendStream will - // enable both sending and receiving on the stream. So AddRecvStream is a - // no-op. - return true; -} - -bool SctpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) { - // SCTP DataChannels are always bi-directional and calling RemoveSendStream - // will disable both sending and receiving on the stream. So RemoveRecvStream - // is a no-op. - return true; -} - -bool SctpDataMediaChannel::SendData( - const SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - SendDataResult* result) { - if (result) { - // Preset |result| to assume an error. If SendData succeeds, we'll - // overwrite |*result| once more at the end. - *result = SDR_ERROR; - } - - if (!sending_) { - LOG(LS_WARNING) << debug_name_ << "->SendData(...): " - << "Not sending packet with ssrc=" << params.ssrc - << " len=" << payload.size() << " before SetSend(true)."; - return false; - } - - if (params.type != DMT_CONTROL && - open_streams_.find(params.ssrc) == open_streams_.end()) { - LOG(LS_WARNING) << debug_name_ << "->SendData(...): " - << "Not sending data because ssrc is unknown: " - << params.ssrc; - return false; - } - - // - // Send data using SCTP. - ssize_t send_res = 0; // result from usrsctp_sendv. - struct sctp_sendv_spa spa = {0}; - spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID; - spa.sendv_sndinfo.snd_sid = params.ssrc; - spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32( - GetPpid(params.type)); - - // Ordered implies reliable. - if (!params.ordered) { - spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED; - if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) { - spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; - spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX; - spa.sendv_prinfo.pr_value = params.max_rtx_count; - } else { - spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; - spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL; - spa.sendv_prinfo.pr_value = params.max_rtx_ms; - } - } - - // We don't fragment. - send_res = usrsctp_sendv( - sock_, payload.data(), static_cast(payload.size()), NULL, 0, &spa, - rtc::checked_cast(sizeof(spa)), SCTP_SENDV_SPA, 0); - if (send_res < 0) { - if (errno == SCTP_EWOULDBLOCK) { - *result = SDR_BLOCK; - LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned"; - } else { - LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_ - << "->SendData(...): " - << " usrsctp_sendv: "; - } - return false; - } - if (result) { - // Only way out now is success. - *result = SDR_SUCCESS; - } - return true; -} - -// Called by network interface when a packet has been received. -void SctpDataMediaChannel::OnPacketReceived( - rtc::CopyOnWriteBuffer* packet, const rtc::PacketTime& packet_time) { - RTC_DCHECK(rtc::Thread::Current() == worker_thread_); - LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): " - << " length=" << packet->size() << ", sending: " << sending_; - // Only give receiving packets to usrsctp after if connected. This enables two - // peers to each make a connect call, but for them not to receive an INIT - // packet before they have called connect; least the last receiver of the INIT - // packet will have called connect, and a connection will be established. - if (sending_) { - // Pass received packet to SCTP stack. Once processed by usrsctp, the data - // will be will be given to the global OnSctpInboundData, and then, - // marshalled by a Post and handled with OnMessage. - VerboseLogPacket(packet->cdata(), packet->size(), SCTP_DUMP_INBOUND); - usrsctp_conninput(this, packet->cdata(), packet->size(), 0); - } else { - // TODO(ldixon): Consider caching the packet for very slightly better - // reliability. - } -} - -void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel( - SctpInboundPacket* packet) { - LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): " - << "Received SCTP data:" - << " ssrc=" << packet->params.ssrc - << " notification: " << (packet->flags & MSG_NOTIFICATION) - << " length=" << packet->buffer.size(); - // Sending a packet with data == NULL (no data) is SCTPs "close the - // connection" message. This sets sock_ = NULL; - if (!packet->buffer.size() || !packet->buffer.data()) { - LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): " - "No data, closing."; - return; - } - if (packet->flags & MSG_NOTIFICATION) { - OnNotificationFromSctp(packet->buffer); - } else { - OnDataFromSctpToChannel(packet->params, packet->buffer); - } -} - -void SctpDataMediaChannel::OnDataFromSctpToChannel( - const ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& buffer) { - if (receiving_) { - LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): " - << "Posting with length: " << buffer.size() - << " on stream " << params.ssrc; - // Reports all received messages to upper layers, no matter whether the sid - // is known. - SignalDataReceived(params, buffer.data(), buffer.size()); - } else { - LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): " - << "Not receiving packet with sid=" << params.ssrc - << " len=" << buffer.size() << " before SetReceive(true)."; - } -} - -bool SctpDataMediaChannel::AddStream(const StreamParams& stream) { - if (!stream.has_ssrcs()) { - return false; - } - - const uint32_t ssrc = stream.first_ssrc(); - if (ssrc > kMaxSctpSid) { - LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): " - << "Not adding data stream '" << stream.id - << "' with sid=" << ssrc << " because sid is too high."; - return false; - } else if (open_streams_.find(ssrc) != open_streams_.end()) { - LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): " - << "Not adding data stream '" << stream.id - << "' with sid=" << ssrc - << " because stream is already open."; - return false; - } else if (queued_reset_streams_.find(ssrc) != queued_reset_streams_.end() - || sent_reset_streams_.find(ssrc) != sent_reset_streams_.end()) { - LOG(LS_WARNING) << debug_name_ << "->Add(Send|Recv)Stream(...): " - << "Not adding data stream '" << stream.id - << "' with sid=" << ssrc - << " because stream is still closing."; - return false; - } - - open_streams_.insert(ssrc); - return true; -} - -bool SctpDataMediaChannel::ResetStream(uint32_t ssrc) { - // We typically get this called twice for the same stream, once each for - // Send and Recv. - StreamSet::iterator found = open_streams_.find(ssrc); - - if (found == open_streams_.end()) { - LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): " - << "stream not found."; - return false; - } else { - LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << ssrc << "): " - << "Removing and queuing RE-CONFIG chunk."; - open_streams_.erase(found); - } - - // SCTP won't let you have more than one stream reset pending at a time, but - // you can close multiple streams in a single reset. So, we keep an internal - // queue of streams-to-reset, and send them as one reset message in - // SendQueuedStreamResets(). - queued_reset_streams_.insert(ssrc); - - // Signal our stream-reset logic that it should try to send now, if it can. - SendQueuedStreamResets(); - - // The stream will actually get removed when we get the acknowledgment. - return true; -} - -void SctpDataMediaChannel::OnNotificationFromSctp( - const rtc::CopyOnWriteBuffer& buffer) { - const sctp_notification& notification = - reinterpret_cast(*buffer.data()); - ASSERT(notification.sn_header.sn_length == buffer.size()); - - // TODO(ldixon): handle notifications appropriately. - switch (notification.sn_header.sn_type) { - case SCTP_ASSOC_CHANGE: - LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE"; - OnNotificationAssocChange(notification.sn_assoc_change); - break; - case SCTP_REMOTE_ERROR: - LOG(LS_INFO) << "SCTP_REMOTE_ERROR"; - break; - case SCTP_SHUTDOWN_EVENT: - LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT"; - break; - case SCTP_ADAPTATION_INDICATION: - LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION"; - break; - case SCTP_PARTIAL_DELIVERY_EVENT: - LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT"; - break; - case SCTP_AUTHENTICATION_EVENT: - LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT"; - break; - case SCTP_SENDER_DRY_EVENT: - LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT"; - SignalReadyToSend(true); - break; - // TODO(ldixon): Unblock after congestion. - case SCTP_NOTIFICATIONS_STOPPED_EVENT: - LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT"; - break; - case SCTP_SEND_FAILED_EVENT: - LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT"; - break; - case SCTP_STREAM_RESET_EVENT: - OnStreamResetEvent(¬ification.sn_strreset_event); - break; - case SCTP_ASSOC_RESET_EVENT: - LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT"; - break; - case SCTP_STREAM_CHANGE_EVENT: - LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT"; - // An acknowledgment we get after our stream resets have gone through, - // if they've failed. We log the message, but don't react -- we don't - // keep around the last-transmitted set of SSIDs we wanted to close for - // error recovery. It doesn't seem likely to occur, and if so, likely - // harmless within the lifetime of a single SCTP association. - break; - default: - LOG(LS_WARNING) << "Unknown SCTP event: " - << notification.sn_header.sn_type; - break; - } -} - -void SctpDataMediaChannel::OnNotificationAssocChange( - const sctp_assoc_change& change) { - switch (change.sac_state) { - case SCTP_COMM_UP: - LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP"; - break; - case SCTP_COMM_LOST: - LOG(LS_INFO) << "Association change SCTP_COMM_LOST"; - break; - case SCTP_RESTART: - LOG(LS_INFO) << "Association change SCTP_RESTART"; - break; - case SCTP_SHUTDOWN_COMP: - LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP"; - break; - case SCTP_CANT_STR_ASSOC: - LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC"; - break; - default: - LOG(LS_INFO) << "Association change UNKNOWN"; - break; - } -} - -void SctpDataMediaChannel::OnStreamResetEvent( - const struct sctp_stream_reset_event* evt) { - // A stream reset always involves two RE-CONFIG chunks for us -- we always - // simultaneously reset a sid's sequence number in both directions. The - // requesting side transmits a RE-CONFIG chunk and waits for the peer to send - // one back. Both sides get this SCTP_STREAM_RESET_EVENT when they receive - // RE-CONFIGs. - const int num_ssrcs = (evt->strreset_length - sizeof(*evt)) / - sizeof(evt->strreset_stream_list[0]); - LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ - << "): Flags = 0x" - << std::hex << evt->strreset_flags << " (" - << ListFlags(evt->strreset_flags) << ")"; - LOG(LS_VERBOSE) << "Assoc = " << evt->strreset_assoc_id << ", Streams = [" - << ListArray(evt->strreset_stream_list, num_ssrcs) - << "], Open: [" - << ListStreams(open_streams_) << "], Q'd: [" - << ListStreams(queued_reset_streams_) << "], Sent: [" - << ListStreams(sent_reset_streams_) << "]"; - - // If both sides try to reset some streams at the same time (even if they're - // disjoint sets), we can get reset failures. - if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) { - // OK, just try again. The stream IDs sent over when the RESET_FAILED flag - // is set seem to be garbage values. Ignore them. - queued_reset_streams_.insert( - sent_reset_streams_.begin(), - sent_reset_streams_.end()); - sent_reset_streams_.clear(); - - } else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) { - // Each side gets an event for each direction of a stream. That is, - // closing sid k will make each side receive INCOMING and OUTGOING reset - // events for k. As per RFC6525, Section 5, paragraph 2, each side will - // get an INCOMING event first. - for (int i = 0; i < num_ssrcs; i++) { - const int stream_id = evt->strreset_stream_list[i]; - - // See if this stream ID was closed by our peer or ourselves. - StreamSet::iterator it = sent_reset_streams_.find(stream_id); - - // The reset was requested locally. - if (it != sent_reset_streams_.end()) { - LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ - << "): local sid " << stream_id << " acknowledged."; - sent_reset_streams_.erase(it); - - } else if ((it = open_streams_.find(stream_id)) - != open_streams_.end()) { - // The peer requested the reset. - LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ - << "): closing sid " << stream_id; - open_streams_.erase(it); - SignalStreamClosedRemotely(stream_id); - - } else if ((it = queued_reset_streams_.find(stream_id)) - != queued_reset_streams_.end()) { - // The peer requested the reset, but there was a local reset - // queued. - LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ - << "): double-sided close for sid " << stream_id; - // Both sides want the stream closed, and the peer got to send the - // RE-CONFIG first. Treat it like the local Remove(Send|Recv)Stream - // finished quickly. - queued_reset_streams_.erase(it); - - } else { - // This stream is unknown. Sometimes this can be from an - // RESET_FAILED-related retransmit. - LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ - << "): Unknown sid " << stream_id; - } - } - } - - // Always try to send the queued RESET because this call indicates that the - // last local RESET or remote RESET has made some progress. - SendQueuedStreamResets(); -} - -// Puts the specified |param| from the codec identified by |id| into |dest| -// and returns true. Or returns false if it wasn't there, leaving |dest| -// untouched. -static bool GetCodecIntParameter(const std::vector& codecs, - int id, const std::string& name, - const std::string& param, int* dest) { - std::string value; - DataCodec match_pattern(id, name); - for (size_t i = 0; i < codecs.size(); ++i) { - if (codecs[i].Matches(match_pattern)) { - if (codecs[i].GetParam(param, &value)) { - *dest = rtc::FromString(value); - return true; - } - } - } - return false; -} - -bool SctpDataMediaChannel::SetSendCodecs(const std::vector& codecs) { - return GetCodecIntParameter( - codecs, kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName, - kCodecParamPort, &remote_port_); -} - -bool SctpDataMediaChannel::SetRecvCodecs(const std::vector& codecs) { - return GetCodecIntParameter( - codecs, kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName, - kCodecParamPort, &local_port_); -} - -void SctpDataMediaChannel::OnPacketFromSctpToNetwork( - rtc::CopyOnWriteBuffer* buffer) { - if (buffer->size() > (kSctpMtu)) { - LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): " - << "SCTP seems to have made a packet that is bigger " - << "than its official MTU: " << buffer->size() - << " vs max of " << kSctpMtu; - } - MediaChannel::SendPacket(buffer, rtc::PacketOptions()); -} - -bool SctpDataMediaChannel::SendQueuedStreamResets() { - if (!sent_reset_streams_.empty() || queued_reset_streams_.empty()) { - return true; - } - - LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ << "]: Sending [" - << ListStreams(queued_reset_streams_) << "], Open: [" - << ListStreams(open_streams_) << "], Sent: [" - << ListStreams(sent_reset_streams_) << "]"; - - const size_t num_streams = queued_reset_streams_.size(); - const size_t num_bytes = - sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t)); - - std::vector reset_stream_buf(num_bytes, 0); - struct sctp_reset_streams* resetp = reinterpret_cast( - &reset_stream_buf[0]); - resetp->srs_assoc_id = SCTP_ALL_ASSOC; - resetp->srs_flags = SCTP_STREAM_RESET_INCOMING | SCTP_STREAM_RESET_OUTGOING; - resetp->srs_number_streams = rtc::checked_cast(num_streams); - int result_idx = 0; - for (StreamSet::iterator it = queued_reset_streams_.begin(); - it != queued_reset_streams_.end(); ++it) { - resetp->srs_stream_list[result_idx++] = *it; - } - - int ret = usrsctp_setsockopt( - sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp, - rtc::checked_cast(reset_stream_buf.size())); - if (ret < 0) { - LOG_ERRNO(LS_ERROR) << debug_name_ << "Failed to send a stream reset for " - << num_streams << " streams"; - return false; - } - - // sent_reset_streams_ is empty, and all the queued_reset_streams_ go into - // it now. - queued_reset_streams_.swap(sent_reset_streams_); - return true; -} - -void SctpDataMediaChannel::OnMessage(rtc::Message* msg) { - switch (msg->message_id) { - case MSG_SCTPINBOUNDPACKET: { - std::unique_ptr pdata( - static_cast(msg->pdata)); - OnInboundPacketFromSctpToChannel(pdata->data().get()); - break; - } - case MSG_SCTPOUTBOUNDPACKET: { - std::unique_ptr pdata( - static_cast(msg->pdata)); - OnPacketFromSctpToNetwork(pdata->data().get()); - break; - } - } -} -} // namespace cricket diff --git a/webrtc/media/sctp/sctpdataengine.h b/webrtc/media/sctp/sctpdataengine.h deleted file mode 100644 index 8b6f37d6ba..0000000000 --- a/webrtc/media/sctp/sctpdataengine.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_MEDIA_SCTP_SCTPDATAENGINE_H_ -#define WEBRTC_MEDIA_SCTP_SCTPDATAENGINE_H_ - -#include -#include -#include - -namespace cricket { -// Some ERRNO values get re-#defined to WSA* equivalents in some talk/ -// headers. We save the original ones in an enum. -enum PreservedErrno { - SCTP_EINPROGRESS = EINPROGRESS, - SCTP_EWOULDBLOCK = EWOULDBLOCK -}; -} // namespace cricket - -#include "webrtc/base/copyonwritebuffer.h" -#include "webrtc/base/gtest_prod_util.h" -#include "webrtc/media/base/codec.h" -#include "webrtc/media/base/mediachannel.h" -#include "webrtc/media/base/mediaengine.h" - -// Defined by "usrsctplib/usrsctp.h" -struct sockaddr_conn; -struct sctp_assoc_change; -struct sctp_stream_reset_event; -// Defined by -struct socket; -namespace cricket { -// The number of outgoing streams that we'll negotiate. Since stream IDs (SIDs) -// are 0-based, the highest usable SID is 1023. -// -// It's recommended to use the maximum of 65535 in: -// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.2 -// However, we use 1024 in order to save memory. usrsctp allocates 104 bytes -// for each pair of incoming/outgoing streams (on a 64-bit system), so 65535 -// streams would waste ~6MB. -// -// Note: "max" and "min" here are inclusive. -constexpr uint16_t kMaxSctpStreams = 1024; -constexpr uint16_t kMaxSctpSid = kMaxSctpStreams - 1; -constexpr uint16_t kMinSctpSid = 0; - -// This is the default SCTP port to use. It is passed along the wire and the -// connectee and connector must be using the same port. It is not related to the -// ports at the IP level. (Corresponds to: sockaddr_conn.sconn_port in -// usrsctp.h) -const int kSctpDefaultPort = 5000; - -class SctpDataMediaChannel; - -// A DataEngine that interacts with usrsctp. -// -// From channel calls, data flows like this: -// [worker thread (although it can in princple be another thread)] -// 1. SctpDataMediaChannel::SendData(data) -// 2. usrsctp_sendv(data) -// [worker thread returns; sctp thread then calls the following] -// 3. OnSctpOutboundPacket(wrapped_data) -// [sctp thread returns having posted a message for the worker thread] -// 4. SctpDataMediaChannel::OnMessage(wrapped_data) -// 5. SctpDataMediaChannel::OnPacketFromSctpToNetwork(wrapped_data) -// 6. NetworkInterface::SendPacket(wrapped_data) -// 7. ... across network ... a packet is sent back ... -// 8. SctpDataMediaChannel::OnPacketReceived(wrapped_data) -// 9. usrsctp_conninput(wrapped_data) -// [worker thread returns; sctp thread then calls the following] -// 10. OnSctpInboundData(data) -// [sctp thread returns having posted a message fot the worker thread] -// 11. SctpDataMediaChannel::OnMessage(inboundpacket) -// 12. SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(inboundpacket) -// 13. SctpDataMediaChannel::OnDataFromSctpToChannel(data) -// 14. SctpDataMediaChannel::SignalDataReceived(data) -// [from the same thread, methods registered/connected to -// SctpDataMediaChannel are called with the recieved data] -class SctpDataEngine : public DataEngineInterface, public sigslot::has_slots<> { - public: - SctpDataEngine(); - ~SctpDataEngine() override; - - DataMediaChannel* CreateChannel(DataChannelType data_channel_type, - const MediaConfig& config) override; - const std::vector& data_codecs() override { return codecs_; } - - private: - const std::vector codecs_; -}; - -// TODO(ldixon): Make into a special type of TypedMessageData. -// Holds data to be passed on to a channel. -struct SctpInboundPacket; - -class SctpDataMediaChannel : public DataMediaChannel, - public rtc::MessageHandler { - public: - // DataMessageType is used for the SCTP "Payload Protocol Identifier", as - // defined in http://tools.ietf.org/html/rfc4960#section-14.4 - // - // For the list of IANA approved values see: - // http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xml - // The value is not used by SCTP itself. It indicates the protocol running - // on top of SCTP. - enum PayloadProtocolIdentifier { - PPID_NONE = 0, // No protocol is specified. - // Matches the PPIDs in mozilla source and - // https://datatracker.ietf.org/doc/draft-ietf-rtcweb-data-protocol Sec. 9 - // They're not yet assigned by IANA. - PPID_CONTROL = 50, - PPID_BINARY_PARTIAL = 52, - PPID_BINARY_LAST = 53, - PPID_TEXT_PARTIAL = 54, - PPID_TEXT_LAST = 51 - }; - - typedef std::set StreamSet; - - // Given a thread which will be used to post messages (received data) to this - // SctpDataMediaChannel instance. - explicit SctpDataMediaChannel(rtc::Thread* thread, const MediaConfig& config); - virtual ~SctpDataMediaChannel(); - - // When SetSend is set to true, connects. When set to false, disconnects. - // Calling: "SetSend(true); SetSend(false); SetSend(true);" will connect, - // disconnect, and reconnect. - virtual bool SetSend(bool send); - // Unless SetReceive(true) is called, received packets will be discarded. - virtual bool SetReceive(bool receive); - - virtual bool SetSendParameters(const DataSendParameters& params); - virtual bool SetRecvParameters(const DataRecvParameters& params); - virtual bool AddSendStream(const StreamParams& sp); - virtual bool RemoveSendStream(uint32_t ssrc); - virtual bool AddRecvStream(const StreamParams& sp); - virtual bool RemoveRecvStream(uint32_t ssrc); - - // Called when Sctp gets data. The data may be a notification or data for - // OnSctpInboundData. Called from the worker thread. - virtual void OnMessage(rtc::Message* msg); - // Send data down this channel (will be wrapped as SCTP packets then given to - // sctp that will then post the network interface by OnMessage). - // Returns true iff successful data somewhere on the send-queue/network. - virtual bool SendData(const SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - SendDataResult* result = NULL); - // A packet is received from the network interface. Posted to OnMessage. - virtual void OnPacketReceived(rtc::CopyOnWriteBuffer* packet, - const rtc::PacketTime& packet_time); - - // Exposed to allow Post call from c-callbacks. - rtc::Thread* worker_thread() const { return worker_thread_; } - - // Many of these things are unused by SCTP, but are needed to fulfill - // the MediaChannel interface. - virtual void OnRtcpReceived(rtc::CopyOnWriteBuffer* packet, - const rtc::PacketTime& packet_time) {} - virtual void OnReadyToSend(bool ready) {} - virtual void OnTransportOverheadChanged(int transport_overhead_per_packet) {} - - void OnSendThresholdCallback(); - // Helper for debugging. - void set_debug_name_for_testing(const char* debug_name) { - debug_name_ = debug_name; - } - const struct socket* socket() const { return sock_; } - - private: - FRIEND_TEST_ALL_PREFIXES(SctpDataMediaChannelTest, EngineSignalsRightChannel); - static int SendThresholdCallback(struct socket* sock, uint32_t sb_free); - static SctpDataMediaChannel* GetChannelFromSocket(struct socket* sock); - - private: - sockaddr_conn GetSctpSockAddr(int port); - - bool SetSendCodecs(const std::vector& codecs); - bool SetRecvCodecs(const std::vector& codecs); - - // Creates the socket and connects. Sets sending_ to true. - bool Connect(); - // Closes the socket. Sets sending_ to false. - void Disconnect(); - - // Returns false when openning the socket failed; when successfull sets - // sending_ to true - bool OpenSctpSocket(); - // Sets sending_ to false and sock_ to NULL. - void CloseSctpSocket(); - - // Sends a SCTP_RESET_STREAM for all streams in closing_ssids_. - bool SendQueuedStreamResets(); - - // Adds a stream. - bool AddStream(const StreamParams &sp); - // Queues a stream for reset. - bool ResetStream(uint32_t ssrc); - - // Called by OnMessage to send packet on the network. - void OnPacketFromSctpToNetwork(rtc::CopyOnWriteBuffer* buffer); - // Called by OnMessage to decide what to do with the packet. - void OnInboundPacketFromSctpToChannel(SctpInboundPacket* packet); - void OnDataFromSctpToChannel(const ReceiveDataParams& params, - const rtc::CopyOnWriteBuffer& buffer); - void OnNotificationFromSctp(const rtc::CopyOnWriteBuffer& buffer); - void OnNotificationAssocChange(const sctp_assoc_change& change); - - void OnStreamResetEvent(const struct sctp_stream_reset_event* evt); - - // Responsible for marshalling incoming data to the channels listeners, and - // outgoing data to the network interface. - rtc::Thread* worker_thread_; - // The local and remote SCTP port to use. These are passed along the wire - // and the listener and connector must be using the same port. It is not - // related to the ports at the IP level. If set to -1, we default to - // kSctpDefaultPort. - int local_port_; - int remote_port_; - struct socket* sock_; // The socket created by usrsctp_socket(...). - - // sending_ is true iff there is a connected socket. - bool sending_; - // receiving_ controls whether inbound packets are thrown away. - bool receiving_; - - // When a data channel opens a stream, it goes into open_streams_. When we - // want to close it, the stream's ID goes into queued_reset_streams_. When - // we actually transmit a RE-CONFIG chunk with that stream ID, the ID goes - // into sent_reset_streams_. When we get a response RE-CONFIG chunk back - // acknowledging the reset, we remove the stream ID from - // sent_reset_streams_. We use sent_reset_streams_ to differentiate - // between acknowledgment RE-CONFIG and peer-initiated RE-CONFIGs. - StreamSet open_streams_; - StreamSet queued_reset_streams_; - StreamSet sent_reset_streams_; - - // A static human-readable name for debugging messages. - const char* debug_name_; -}; - -} // namespace cricket - -#endif // WEBRTC_MEDIA_SCTP_SCTPDATAENGINE_H_ diff --git a/webrtc/media/sctp/sctpdataengine_unittest.cc b/webrtc/media/sctp/sctpdataengine_unittest.cc deleted file mode 100644 index 060b2d9749..0000000000 --- a/webrtc/media/sctp/sctpdataengine_unittest.cc +++ /dev/null @@ -1,523 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -#include -#include - -#include -#include -#include - -#include "webrtc/base/bind.h" -#include "webrtc/base/copyonwritebuffer.h" -#include "webrtc/base/criticalsection.h" -#include "webrtc/base/gunit.h" -#include "webrtc/base/helpers.h" -#include "webrtc/base/messagehandler.h" -#include "webrtc/base/messagequeue.h" -#include "webrtc/base/ssladapter.h" -#include "webrtc/base/thread.h" -#include "webrtc/media/base/mediachannel.h" -#include "webrtc/media/base/mediaconstants.h" -#include "webrtc/media/sctp/sctpdataengine.h" - -namespace cricket { -enum { - MSG_PACKET = 1, -}; - -// Fake NetworkInterface that sends/receives sctp packets. The one in -// webrtc/media/base/fakenetworkinterface.h only works with rtp/rtcp. -class SctpFakeNetworkInterface : public MediaChannel::NetworkInterface, - public rtc::MessageHandler { - public: - explicit SctpFakeNetworkInterface(rtc::Thread* thread) - : thread_(thread), - dest_(NULL) { - } - - void SetDestination(DataMediaChannel* dest) { dest_ = dest; } - - protected: - // Called to send raw packet down the wire (e.g. SCTP an packet). - virtual bool SendPacket(rtc::CopyOnWriteBuffer* packet, - const rtc::PacketOptions& options) { - LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::SendPacket"; - - rtc::CopyOnWriteBuffer* buffer = new rtc::CopyOnWriteBuffer(*packet); - thread_->Post(RTC_FROM_HERE, this, MSG_PACKET, - rtc::WrapMessageData(buffer)); - LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::SendPacket, Posted message."; - return true; - } - - // Called when a raw packet has been recieved. This passes the data to the - // code that will interpret the packet. e.g. to get the content payload from - // an SCTP packet. - virtual void OnMessage(rtc::Message* msg) { - LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::OnMessage"; - std::unique_ptr buffer( - static_cast*>( - msg->pdata)->data()); - if (dest_) { - dest_->OnPacketReceived(buffer.get(), rtc::PacketTime()); - } - delete msg->pdata; - } - - // Unsupported functions required to exist by NetworkInterface. - // TODO(ldixon): Refactor parent NetworkInterface class so these are not - // required. They are RTC specific and should be in an appropriate subclass. - virtual bool SendRtcp(rtc::CopyOnWriteBuffer* packet, - const rtc::PacketOptions& options) { - LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SendRtcp."; - return false; - } - virtual int SetOption(SocketType type, rtc::Socket::Option opt, - int option) { - LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SetOption."; - return 0; - } - virtual void SetDefaultDSCPCode(rtc::DiffServCodePoint dscp) { - LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SetOption."; - } - - private: - // Not owned by this class. - rtc::Thread* thread_; - DataMediaChannel* dest_; -}; - -// This is essentially a buffer to hold recieved data. It stores only the last -// received data. Calling OnDataReceived twice overwrites old data with the -// newer one. -// TODO(ldixon): Implement constraints, and allow new data to be added to old -// instead of replacing it. -class SctpFakeDataReceiver : public sigslot::has_slots<> { - public: - SctpFakeDataReceiver() : received_(false) {} - - void Clear() { - received_ = false; - last_data_ = ""; - last_params_ = ReceiveDataParams(); - } - - virtual void OnDataReceived(const ReceiveDataParams& params, - const char* data, - size_t length) { - received_ = true; - last_data_ = std::string(data, length); - last_params_ = params; - } - - bool received() const { return received_; } - std::string last_data() const { return last_data_; } - ReceiveDataParams last_params() const { return last_params_; } - - private: - bool received_; - std::string last_data_; - ReceiveDataParams last_params_; -}; - -class SignalReadyToSendObserver : public sigslot::has_slots<> { - public: - SignalReadyToSendObserver() : signaled_(false), writable_(false) {} - - void OnSignaled(bool writable) { - signaled_ = true; - writable_ = writable; - } - - bool IsSignaled(bool writable) { - return signaled_ && (writable_ == writable); - } - - private: - bool signaled_; - bool writable_; -}; - -class SignalChannelClosedObserver : public sigslot::has_slots<> { - public: - SignalChannelClosedObserver() {} - void BindSelf(SctpDataMediaChannel* channel) { - channel->SignalStreamClosedRemotely.connect( - this, &SignalChannelClosedObserver::OnStreamClosed); - } - void OnStreamClosed(uint32_t stream) { streams_.push_back(stream); } - - int StreamCloseCount(uint32_t stream) { - return std::count(streams_.begin(), streams_.end(), stream); - } - - bool WasStreamClosed(uint32_t stream) { - return std::find(streams_.begin(), streams_.end(), stream) - != streams_.end(); - } - - private: - std::vector streams_; -}; - -class SignalChannelClosedReopener : public sigslot::has_slots<> { - public: - SignalChannelClosedReopener(SctpDataMediaChannel* channel, - SctpDataMediaChannel* peer) - : channel_(channel), peer_(peer) {} - - void OnStreamClosed(int stream) { - StreamParams p(StreamParams::CreateLegacy(stream)); - channel_->AddSendStream(p); - channel_->AddRecvStream(p); - peer_->AddSendStream(p); - peer_->AddRecvStream(p); - streams_.push_back(stream); - } - - int StreamCloseCount(int stream) { - return std::count(streams_.begin(), streams_.end(), stream); - } - - private: - SctpDataMediaChannel* channel_; - SctpDataMediaChannel* peer_; - std::vector streams_; -}; - -// SCTP Data Engine testing framework. -class SctpDataMediaChannelTest : public testing::Test, - public sigslot::has_slots<> { - protected: - // usrsctp uses the NSS random number generator on non-Android platforms, - // so we need to initialize SSL. - static void SetUpTestCase() { - } - - virtual void SetUp() { engine_.reset(new SctpDataEngine()); } - - void SetupConnectedChannels() { - net1_.reset(new SctpFakeNetworkInterface(rtc::Thread::Current())); - net2_.reset(new SctpFakeNetworkInterface(rtc::Thread::Current())); - recv1_.reset(new SctpFakeDataReceiver()); - recv2_.reset(new SctpFakeDataReceiver()); - chan1_ready_to_send_count_ = 0; - chan2_ready_to_send_count_ = 0; - chan1_.reset(CreateChannel(net1_.get(), recv1_.get())); - chan1_->set_debug_name_for_testing("chan1/connector"); - chan1_->SignalReadyToSend.connect( - this, &SctpDataMediaChannelTest::OnChan1ReadyToSend); - chan2_.reset(CreateChannel(net2_.get(), recv2_.get())); - chan2_->set_debug_name_for_testing("chan2/listener"); - chan2_->SignalReadyToSend.connect( - this, &SctpDataMediaChannelTest::OnChan2ReadyToSend); - // Setup two connected channels ready to send and receive. - net1_->SetDestination(chan2_.get()); - net2_->SetDestination(chan1_.get()); - - LOG(LS_VERBOSE) << "Channel setup ----------------------------- "; - AddStream(1); - AddStream(2); - - LOG(LS_VERBOSE) << "Connect the channels -----------------------------"; - // chan1 wants to setup a data connection. - chan1_->SetReceive(true); - // chan1 will have sent chan2 a request to setup a data connection. After - // chan2 accepts the offer, chan2 connects to chan1 with the following. - chan2_->SetReceive(true); - chan2_->SetSend(true); - // Makes sure that network packets are delivered and simulates a - // deterministic and realistic small timing delay between the SetSend calls. - ProcessMessagesUntilIdle(); - - // chan1 and chan2 are now connected so chan1 enables sending to complete - // the creation of the connection. - chan1_->SetSend(true); - } - - virtual void TearDown() { - channel1()->SetSend(false); - channel2()->SetSend(false); - - // Process messages until idle to prevent a sent packet from being dropped - // and causing memory leaks (not being deleted by the receiver). - ProcessMessagesUntilIdle(); - } - - bool AddStream(int ssrc) { - bool ret = true; - StreamParams p(StreamParams::CreateLegacy(ssrc)); - ret = ret && chan1_->AddSendStream(p); - ret = ret && chan1_->AddRecvStream(p); - ret = ret && chan2_->AddSendStream(p); - ret = ret && chan2_->AddRecvStream(p); - return ret; - } - - SctpDataMediaChannel* CreateChannel(SctpFakeNetworkInterface* net, - SctpFakeDataReceiver* recv) { - cricket::MediaConfig config; - SctpDataMediaChannel* channel = static_cast( - engine_->CreateChannel(DCT_SCTP, config)); - channel->SetInterface(net); - // When data is received, pass it to the SctpFakeDataReceiver. - channel->SignalDataReceived.connect( - recv, &SctpFakeDataReceiver::OnDataReceived); - return channel; - } - - bool SendData(SctpDataMediaChannel* chan, - uint32_t ssrc, - const std::string& msg, - SendDataResult* result) { - SendDataParams params; - params.ssrc = ssrc; - - return chan->SendData(params, rtc::CopyOnWriteBuffer( - &msg[0], msg.length()), result); - } - - bool ReceivedData(const SctpFakeDataReceiver* recv, - uint32_t ssrc, - const std::string& msg) { - return (recv->received() && - recv->last_params().ssrc == ssrc && - recv->last_data() == msg); - } - - bool ProcessMessagesUntilIdle() { - rtc::Thread* thread = rtc::Thread::Current(); - while (!thread->empty()) { - rtc::Message msg; - if (thread->Get(&msg, rtc::Thread::kForever)) { - thread->Dispatch(&msg); - } - } - return !thread->IsQuitting(); - } - - SctpDataMediaChannel* channel1() { return chan1_.get(); } - SctpDataMediaChannel* channel2() { return chan2_.get(); } - SctpFakeDataReceiver* receiver1() { return recv1_.get(); } - SctpFakeDataReceiver* receiver2() { return recv2_.get(); } - - int channel1_ready_to_send_count() { return chan1_ready_to_send_count_; } - int channel2_ready_to_send_count() { return chan2_ready_to_send_count_; } - private: - std::unique_ptr engine_; - std::unique_ptr net1_; - std::unique_ptr net2_; - std::unique_ptr recv1_; - std::unique_ptr recv2_; - std::unique_ptr chan1_; - std::unique_ptr chan2_; - - int chan1_ready_to_send_count_; - int chan2_ready_to_send_count_; - - void OnChan1ReadyToSend(bool send) { - if (send) - ++chan1_ready_to_send_count_; - } - void OnChan2ReadyToSend(bool send) { - if (send) - ++chan2_ready_to_send_count_; - } -}; - -// Verifies that SignalReadyToSend is fired. -TEST_F(SctpDataMediaChannelTest, SignalReadyToSend) { - SetupConnectedChannels(); - - SignalReadyToSendObserver signal_observer_1; - SignalReadyToSendObserver signal_observer_2; - - channel1()->SignalReadyToSend.connect(&signal_observer_1, - &SignalReadyToSendObserver::OnSignaled); - channel2()->SignalReadyToSend.connect(&signal_observer_2, - &SignalReadyToSendObserver::OnSignaled); - - SendDataResult result; - ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); - ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); - - EXPECT_TRUE_WAIT(signal_observer_1.IsSignaled(true), 1000); - EXPECT_TRUE_WAIT(signal_observer_2.IsSignaled(true), 1000); -} - -TEST_F(SctpDataMediaChannelTest, SendData) { - SetupConnectedChannels(); - - SendDataResult result; - LOG(LS_VERBOSE) << "chan1 sending: 'hello?' -----------------------------"; - ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); - LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received() - << ", recv2.last_params.ssrc=" - << receiver2()->last_params().ssrc - << ", recv2.last_params.timestamp=" - << receiver2()->last_params().ssrc - << ", recv2.last_params.seq_num=" - << receiver2()->last_params().seq_num - << ", recv2.last_data=" << receiver2()->last_data(); - - LOG(LS_VERBOSE) << "chan2 sending: 'hi chan1' -----------------------------"; - ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); - LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received() - << ", recv1.last_params.ssrc=" - << receiver1()->last_params().ssrc - << ", recv1.last_params.timestamp=" - << receiver1()->last_params().ssrc - << ", recv1.last_params.seq_num=" - << receiver1()->last_params().seq_num - << ", recv1.last_data=" << receiver1()->last_data(); -} - -// Sends a lot of large messages at once and verifies SDR_BLOCK is returned. -TEST_F(SctpDataMediaChannelTest, SendDataBlocked) { - SetupConnectedChannels(); - - SendDataResult result; - SendDataParams params; - params.ssrc = 1; - - std::vector buffer(1024 * 64, 0); - - for (size_t i = 0; i < 100; ++i) { - channel1()->SendData( - params, rtc::CopyOnWriteBuffer(&buffer[0], buffer.size()), &result); - if (result == SDR_BLOCK) - break; - } - - EXPECT_EQ(SDR_BLOCK, result); -} - -TEST_F(SctpDataMediaChannelTest, ClosesRemoteStream) { - SetupConnectedChannels(); - SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver; - chan_1_sig_receiver.BindSelf(channel1()); - chan_2_sig_receiver.BindSelf(channel2()); - - SendDataResult result; - ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); - ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); - - // Close channel 1. Channel 2 should notify us. - channel1()->RemoveSendStream(1); - EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000); -} - -TEST_F(SctpDataMediaChannelTest, ClosesTwoRemoteStreams) { - SetupConnectedChannels(); - AddStream(3); - SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver; - chan_1_sig_receiver.BindSelf(channel1()); - chan_2_sig_receiver.BindSelf(channel2()); - - SendDataResult result; - ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); - ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); - - // Close two streams on one side. - channel2()->RemoveSendStream(2); - channel2()->RemoveSendStream(3); - EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(2), 1000); - EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(3), 1000); -} - -TEST_F(SctpDataMediaChannelTest, ClosesStreamsOnBothSides) { - SetupConnectedChannels(); - AddStream(3); - AddStream(4); - SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver; - chan_1_sig_receiver.BindSelf(channel1()); - chan_2_sig_receiver.BindSelf(channel2()); - - SendDataResult result; - ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); - ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000); - - // Close one stream on channel1(), while closing three streams on - // channel2(). They will conflict (only one side can close anything at a - // time, apparently). Test the resolution of the conflict. - channel1()->RemoveSendStream(1); - - channel2()->RemoveSendStream(2); - channel2()->RemoveSendStream(3); - channel2()->RemoveSendStream(4); - EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000); - EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(2), 1000); - EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(3), 1000); - EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(4), 1000); -} - -TEST_F(SctpDataMediaChannelTest, EngineSignalsRightChannel) { - SetupConnectedChannels(); - EXPECT_TRUE_WAIT(channel1()->socket() != NULL, 1000); - struct socket *sock = const_cast(channel1()->socket()); - int prior_count = channel1_ready_to_send_count(); - SctpDataMediaChannel::SendThresholdCallback(sock, 0); - EXPECT_GT(channel1_ready_to_send_count(), prior_count); -} - -TEST_F(SctpDataMediaChannelTest, RefusesHighNumberedChannels) { - SetupConnectedChannels(); - EXPECT_TRUE(AddStream(kMaxSctpSid)); - EXPECT_FALSE(AddStream(kMaxSctpSid + 1)); -} - -// Flaky, see webrtc:4453. -TEST_F(SctpDataMediaChannelTest, DISABLED_ReusesAStream) { - // Shut down channel 1, then open it up again for reuse. - SetupConnectedChannels(); - SendDataResult result; - SignalChannelClosedObserver chan_2_sig_receiver; - chan_2_sig_receiver.BindSelf(channel2()); - - ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000); - - channel1()->RemoveSendStream(1); - EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000); - // Channel 1 is gone now. - - // Create a new channel 1. - AddStream(1); - ASSERT_TRUE(SendData(channel1(), 1, "hi?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hi?"), 1000); - channel1()->RemoveSendStream(1); - EXPECT_TRUE_WAIT(chan_2_sig_receiver.StreamCloseCount(1) == 2, 1000); -} - -} // namespace cricket diff --git a/webrtc/media/sctp/sctptransport.cc b/webrtc/media/sctp/sctptransport.cc new file mode 100644 index 0000000000..b95cf8a4ba --- /dev/null +++ b/webrtc/media/sctp/sctptransport.cc @@ -0,0 +1,1090 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +namespace { +// Some ERRNO values get re-#defined to WSA* equivalents in some talk/ +// headers. We save the original ones in an enum. +enum PreservedErrno { + SCTP_EINPROGRESS = EINPROGRESS, + SCTP_EWOULDBLOCK = EWOULDBLOCK +}; +} + +#include "webrtc/media/sctp/sctptransport.h" + +#include +#include + +#include +#include + +#include "usrsctplib/usrsctp.h" +#include "webrtc/base/arraysize.h" +#include "webrtc/base/copyonwritebuffer.h" +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/logging.h" +#include "webrtc/base/safe_conversions.h" +#include "webrtc/base/thread_checker.h" +#include "webrtc/base/trace_event.h" +#include "webrtc/media/base/codec.h" +#include "webrtc/media/base/mediaconstants.h" +#include "webrtc/media/base/rtputils.h" // For IsRtpPacket +#include "webrtc/media/base/streamparams.h" + +namespace { + +// The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280, +// take off 80 bytes for DTLS/TURN/TCP/IP overhead. +static constexpr size_t kSctpMtu = 1200; + +// The size of the SCTP association send buffer. 256kB, the usrsctp default. +static constexpr int kSendBufferSize = 262144; + +// Set the initial value of the static SCTP Data Engines reference count. +int g_usrsctp_usage_count = 0; +rtc::GlobalLockPod g_usrsctp_lock_; + +// DataMessageType is used for the SCTP "Payload Protocol Identifier", as +// defined in http://tools.ietf.org/html/rfc4960#section-14.4 +// +// For the list of IANA approved values see: +// http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xml +// The value is not used by SCTP itself. It indicates the protocol running +// on top of SCTP. +enum PayloadProtocolIdentifier { + PPID_NONE = 0, // No protocol is specified. + // Matches the PPIDs in mozilla source and + // https://datatracker.ietf.org/doc/draft-ietf-rtcweb-data-protocol Sec. 9 + // They're not yet assigned by IANA. + PPID_CONTROL = 50, + PPID_BINARY_PARTIAL = 52, + PPID_BINARY_LAST = 53, + PPID_TEXT_PARTIAL = 54, + PPID_TEXT_LAST = 51 +}; + +typedef std::set StreamSet; + +// Returns a comma-separated, human-readable list of the stream IDs in 's' +std::string ListStreams(const StreamSet& s) { + std::stringstream result; + bool first = true; + for (StreamSet::const_iterator it = s.begin(); it != s.end(); ++it) { + if (!first) { + result << ", " << *it; + } else { + result << *it; + first = false; + } + } + return result.str(); +} + +// Returns a pipe-separated, human-readable list of the SCTP_STREAM_RESET +// flags in 'flags' +std::string ListFlags(int flags) { + std::stringstream result; + bool first = true; +// Skip past the first 12 chars (strlen("SCTP_STREAM_")) +#define MAKEFLAG(X) \ + { X, #X + 12 } + struct flaginfo_t { + int value; + const char* name; + } flaginfo[] = {MAKEFLAG(SCTP_STREAM_RESET_INCOMING_SSN), + MAKEFLAG(SCTP_STREAM_RESET_OUTGOING_SSN), + MAKEFLAG(SCTP_STREAM_RESET_DENIED), + MAKEFLAG(SCTP_STREAM_RESET_FAILED), + MAKEFLAG(SCTP_STREAM_CHANGE_DENIED)}; +#undef MAKEFLAG + for (uint32_t i = 0; i < arraysize(flaginfo); ++i) { + if (flags & flaginfo[i].value) { + if (!first) + result << " | "; + result << flaginfo[i].name; + first = false; + } + } + return result.str(); +} + +// Returns a comma-separated, human-readable list of the integers in 'array'. +// All 'num_elems' of them. +std::string ListArray(const uint16_t* array, int num_elems) { + std::stringstream result; + for (int i = 0; i < num_elems; ++i) { + if (i) { + result << ", " << array[i]; + } else { + result << array[i]; + } + } + return result.str(); +} + +// Helper for logging SCTP messages. +void DebugSctpPrintf(const char* format, ...) { +#if RTC_DCHECK_IS_ON + char s[255]; + va_list ap; + va_start(ap, format); + vsnprintf(s, sizeof(s), format, ap); + LOG(LS_INFO) << "SCTP: " << s; + va_end(ap); +#endif +} + +// Get the PPID to use for the terminating fragment of this type. +PayloadProtocolIdentifier GetPpid(cricket::DataMessageType type) { + switch (type) { + default: + case cricket::DMT_NONE: + return PPID_NONE; + case cricket::DMT_CONTROL: + return PPID_CONTROL; + case cricket::DMT_BINARY: + return PPID_BINARY_LAST; + case cricket::DMT_TEXT: + return PPID_TEXT_LAST; + } +} + +bool GetDataMediaType(PayloadProtocolIdentifier ppid, + cricket::DataMessageType* dest) { + RTC_DCHECK(dest != NULL); + switch (ppid) { + case PPID_BINARY_PARTIAL: + case PPID_BINARY_LAST: + *dest = cricket::DMT_BINARY; + return true; + + case PPID_TEXT_PARTIAL: + case PPID_TEXT_LAST: + *dest = cricket::DMT_TEXT; + return true; + + case PPID_CONTROL: + *dest = cricket::DMT_CONTROL; + return true; + + case PPID_NONE: + *dest = cricket::DMT_NONE; + return true; + + default: + return false; + } +} + +// Log the packet in text2pcap format, if log level is at LS_VERBOSE. +void VerboseLogPacket(const void* data, size_t length, int direction) { + if (LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) { + char* dump_buf; + // Some downstream project uses an older version of usrsctp that expects + // a non-const "void*" as first parameter when dumping the packet, so we + // need to cast the const away here to avoid a compiler error. + if ((dump_buf = usrsctp_dumppacket(const_cast(data), length, + direction)) != NULL) { + LOG(LS_VERBOSE) << dump_buf; + usrsctp_freedumpbuffer(dump_buf); + } + } +} + +} // namespace + +namespace cricket { + +// Handles global init/deinit, and mapping from usrsctp callbacks to +// SctpTransport calls. +class SctpTransport::UsrSctpWrapper { + public: + static void InitializeUsrSctp() { + LOG(LS_INFO) << __FUNCTION__; + // First argument is udp_encapsulation_port, which is not releveant for our + // AF_CONN use of sctp. + usrsctp_init(0, &UsrSctpWrapper::OnSctpOutboundPacket, &DebugSctpPrintf); + + // To turn on/off detailed SCTP debugging. You will also need to have the + // SCTP_DEBUG cpp defines flag. + // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL); + + // TODO(ldixon): Consider turning this on/off. + usrsctp_sysctl_set_sctp_ecn_enable(0); + + // This is harmless, but we should find out when the library default + // changes. + int send_size = usrsctp_sysctl_get_sctp_sendspace(); + if (send_size != kSendBufferSize) { + LOG(LS_ERROR) << "Got different send size than expected: " << send_size; + } + + // TODO(ldixon): Consider turning this on/off. + // This is not needed right now (we don't do dynamic address changes): + // If SCTP Auto-ASCONF is enabled, the peer is informed automatically + // when a new address is added or removed. This feature is enabled by + // default. + // usrsctp_sysctl_set_sctp_auto_asconf(0); + + // TODO(ldixon): Consider turning this on/off. + // Add a blackhole sysctl. Setting it to 1 results in no ABORTs + // being sent in response to INITs, setting it to 2 results + // in no ABORTs being sent for received OOTB packets. + // This is similar to the TCP sysctl. + // + // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html + // See: http://svnweb.freebsd.org/base?view=revision&revision=229805 + // usrsctp_sysctl_set_sctp_blackhole(2); + + // Set the number of default outgoing streams. This is the number we'll + // send in the SCTP INIT message. + usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(kMaxSctpStreams); + } + + static void UninitializeUsrSctp() { + LOG(LS_INFO) << __FUNCTION__; + // usrsctp_finish() may fail if it's called too soon after the transports + // are + // closed. Wait and try again until it succeeds for up to 3 seconds. + for (size_t i = 0; i < 300; ++i) { + if (usrsctp_finish() == 0) { + return; + } + + rtc::Thread::SleepMs(10); + } + LOG(LS_ERROR) << "Failed to shutdown usrsctp."; + } + + static void IncrementUsrSctpUsageCount() { + rtc::GlobalLockScope lock(&g_usrsctp_lock_); + if (!g_usrsctp_usage_count) { + InitializeUsrSctp(); + } + ++g_usrsctp_usage_count; + } + + static void DecrementUsrSctpUsageCount() { + rtc::GlobalLockScope lock(&g_usrsctp_lock_); + --g_usrsctp_usage_count; + if (!g_usrsctp_usage_count) { + UninitializeUsrSctp(); + } + } + + // This is the callback usrsctp uses when there's data to send on the network + // that has been wrapped appropriatly for the SCTP protocol. + static int OnSctpOutboundPacket(void* addr, + void* data, + size_t length, + uint8_t tos, + uint8_t set_df) { + SctpTransport* transport = static_cast(addr); + LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():" + << "addr: " << addr << "; length: " << length + << "; tos: " << std::hex << static_cast(tos) + << "; set_df: " << std::hex << static_cast(set_df); + + VerboseLogPacket(data, length, SCTP_DUMP_OUTBOUND); + // Note: We have to copy the data; the caller will delete it. + rtc::CopyOnWriteBuffer buf(reinterpret_cast(data), length); + // TODO(deadbeef): Why do we need an AsyncInvoke here? We're already on the + // right thread and don't need to unwind the stack. + transport->invoker_.AsyncInvoke( + RTC_FROM_HERE, transport->network_thread_, + rtc::Bind(&SctpTransport::OnPacketFromSctpToNetwork, transport, buf)); + return 0; + } + + // This is the callback called from usrsctp when data has been received, after + // a packet has been interpreted and parsed by usrsctp and found to contain + // payload data. It is called by a usrsctp thread. It is assumed this function + // will free the memory used by 'data'. + static int OnSctpInboundPacket(struct socket* sock, + union sctp_sockstore addr, + void* data, + size_t length, + struct sctp_rcvinfo rcv, + int flags, + void* ulp_info) { + SctpTransport* transport = static_cast(ulp_info); + // Post data to the transport's receiver thread (copying it). + // TODO(ldixon): Unclear if copy is needed as this method is responsible for + // memory cleanup. But this does simplify code. + const PayloadProtocolIdentifier ppid = + static_cast( + rtc::HostToNetwork32(rcv.rcv_ppid)); + DataMessageType type = DMT_NONE; + if (!GetDataMediaType(ppid, &type) && !(flags & MSG_NOTIFICATION)) { + // It's neither a notification nor a recognized data packet. Drop it. + LOG(LS_ERROR) << "Received an unknown PPID " << ppid + << " on an SCTP packet. Dropping."; + } else { + rtc::CopyOnWriteBuffer buffer; + ReceiveDataParams params; + buffer.SetData(reinterpret_cast(data), length); + params.sid = rcv.rcv_sid; + params.seq_num = rcv.rcv_ssn; + params.timestamp = rcv.rcv_tsn; + params.type = type; + // The ownership of the packet transfers to |invoker_|. Using + // CopyOnWriteBuffer is the most convenient way to do this. + transport->invoker_.AsyncInvoke( + RTC_FROM_HERE, transport->network_thread_, + rtc::Bind(&SctpTransport::OnInboundPacketFromSctpToChannel, transport, + buffer, params, flags)); + } + free(data); + return 1; + } + + static SctpTransport* GetTransportFromSocket(struct socket* sock) { + struct sockaddr* addrs = nullptr; + int naddrs = usrsctp_getladdrs(sock, 0, &addrs); + if (naddrs <= 0 || addrs[0].sa_family != AF_CONN) { + return nullptr; + } + // usrsctp_getladdrs() returns the addresses bound to this socket, which + // contains the SctpTransport* as sconn_addr. Read the pointer, + // then free the list of addresses once we have the pointer. We only open + // AF_CONN sockets, and they should all have the sconn_addr set to the + // pointer that created them, so [0] is as good as any other. + struct sockaddr_conn* sconn = + reinterpret_cast(&addrs[0]); + SctpTransport* transport = + reinterpret_cast(sconn->sconn_addr); + usrsctp_freeladdrs(addrs); + + return transport; + } + + static int SendThresholdCallback(struct socket* sock, uint32_t sb_free) { + // Fired on our I/O thread. SctpTransport::OnPacketReceived() gets + // a packet containing acknowledgments, which goes into usrsctp_conninput, + // and then back here. + SctpTransport* transport = GetTransportFromSocket(sock); + if (!transport) { + LOG(LS_ERROR) + << "SendThresholdCallback: Failed to get transport for socket " + << sock; + return 0; + } + transport->OnSendThresholdCallback(); + return 0; + } +}; + +SctpTransport::SctpTransport(rtc::Thread* network_thread, + TransportChannel* channel) + : network_thread_(network_thread), + transport_channel_(channel), + was_ever_writable_(channel->writable()) { + RTC_DCHECK(network_thread_); + RTC_DCHECK(transport_channel_); + RTC_DCHECK_RUN_ON(network_thread_); + ConnectTransportChannelSignals(); +} + +SctpTransport::~SctpTransport() { + // Close abruptly; no reset procedure. + CloseSctpSocket(); +} + +void SctpTransport::SetTransportChannel(cricket::TransportChannel* channel) { + RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK(channel); + DisconnectTransportChannelSignals(); + transport_channel_ = channel; + ConnectTransportChannelSignals(); + if (!was_ever_writable_ && channel->writable()) { + was_ever_writable_ = true; + // New channel is writable, now we can start the SCTP connection if Start + // was called already. + if (started_) { + RTC_DCHECK(!sock_); + Connect(); + } + } +} + +bool SctpTransport::Start(int local_sctp_port, int remote_sctp_port) { + RTC_DCHECK_RUN_ON(network_thread_); + if (local_sctp_port == -1) { + local_sctp_port = kSctpDefaultPort; + } + if (remote_sctp_port == -1) { + remote_sctp_port = kSctpDefaultPort; + } + if (started_) { + if (local_sctp_port != local_port_ || remote_sctp_port != remote_port_) { + LOG(LS_ERROR) << "Can't change SCTP port after SCTP association formed."; + return false; + } + return true; + } + local_port_ = local_sctp_port; + remote_port_ = remote_sctp_port; + started_ = true; + RTC_DCHECK(!sock_); + // Only try to connect if the DTLS channel has been writable before + // (indicating that the DTLS handshake is complete). + if (was_ever_writable_) { + return Connect(); + } + return true; +} + +bool SctpTransport::OpenStream(int sid) { + RTC_DCHECK_RUN_ON(network_thread_); + if (sid > kMaxSctpSid) { + LOG(LS_WARNING) << debug_name_ << "->OpenStream(...): " + << "Not adding data stream " + << "with sid=" << sid << " because sid is too high."; + return false; + } else if (open_streams_.find(sid) != open_streams_.end()) { + LOG(LS_WARNING) << debug_name_ << "->OpenStream(...): " + << "Not adding data stream " + << "with sid=" << sid << " because stream is already open."; + return false; + } else if (queued_reset_streams_.find(sid) != queued_reset_streams_.end() || + sent_reset_streams_.find(sid) != sent_reset_streams_.end()) { + LOG(LS_WARNING) << debug_name_ << "->OpenStream(...): " + << "Not adding data stream " + << " with sid=" << sid + << " because stream is still closing."; + return false; + } + + open_streams_.insert(sid); + return true; +} + +bool SctpTransport::ResetStream(int sid) { + RTC_DCHECK_RUN_ON(network_thread_); + StreamSet::iterator found = open_streams_.find(sid); + if (found == open_streams_.end()) { + LOG(LS_WARNING) << debug_name_ << "->ResetStream(" << sid << "): " + << "stream not found."; + return false; + } else { + LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << sid << "): " + << "Removing and queuing RE-CONFIG chunk."; + open_streams_.erase(found); + } + + // SCTP won't let you have more than one stream reset pending at a time, but + // you can close multiple streams in a single reset. So, we keep an internal + // queue of streams-to-reset, and send them as one reset message in + // SendQueuedStreamResets(). + queued_reset_streams_.insert(sid); + + // Signal our stream-reset logic that it should try to send now, if it can. + SendQueuedStreamResets(); + + // The stream will actually get removed when we get the acknowledgment. + return true; +} + +bool SctpTransport::SendData(const SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + SendDataResult* result) { + RTC_DCHECK_RUN_ON(network_thread_); + if (result) { + // Preset |result| to assume an error. If SendData succeeds, we'll + // overwrite |*result| once more at the end. + *result = SDR_ERROR; + } + + if (!sock_) { + LOG(LS_WARNING) << debug_name_ << "->SendData(...): " + << "Not sending packet with sid=" << params.sid + << " len=" << payload.size() << " before Start()."; + return false; + } + + if (params.type != DMT_CONTROL && + open_streams_.find(params.sid) == open_streams_.end()) { + LOG(LS_WARNING) << debug_name_ << "->SendData(...): " + << "Not sending data because sid is unknown: " + << params.sid; + return false; + } + + // Send data using SCTP. + ssize_t send_res = 0; // result from usrsctp_sendv. + struct sctp_sendv_spa spa = {0}; + spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID; + spa.sendv_sndinfo.snd_sid = params.sid; + spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(GetPpid(params.type)); + + // Ordered implies reliable. + if (!params.ordered) { + spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED; + if (params.max_rtx_count >= 0 || params.max_rtx_ms == 0) { + spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; + spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX; + spa.sendv_prinfo.pr_value = params.max_rtx_count; + } else { + spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; + spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL; + spa.sendv_prinfo.pr_value = params.max_rtx_ms; + } + } + + // We don't fragment. + send_res = usrsctp_sendv( + sock_, payload.data(), static_cast(payload.size()), NULL, 0, &spa, + rtc::checked_cast(sizeof(spa)), SCTP_SENDV_SPA, 0); + if (send_res < 0) { + if (errno == SCTP_EWOULDBLOCK) { + *result = SDR_BLOCK; + ready_to_send_data_ = false; + LOG(LS_INFO) << debug_name_ << "->SendData(...): EWOULDBLOCK returned"; + } else { + LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_ << "->SendData(...): " + << " usrsctp_sendv: "; + } + return false; + } + if (result) { + // Only way out now is success. + *result = SDR_SUCCESS; + } + return true; +} + +bool SctpTransport::ReadyToSendData() { + RTC_DCHECK_RUN_ON(network_thread_); + return ready_to_send_data_; +} + +void SctpTransport::ConnectTransportChannelSignals() { + RTC_DCHECK_RUN_ON(network_thread_); + transport_channel_->SignalWritableState.connect( + this, &SctpTransport::OnWritableState); + transport_channel_->SignalReadPacket.connect(this, + &SctpTransport::OnPacketRead); +} + +void SctpTransport::DisconnectTransportChannelSignals() { + RTC_DCHECK_RUN_ON(network_thread_); + transport_channel_->SignalWritableState.disconnect(this); + transport_channel_->SignalReadPacket.disconnect(this); +} + +bool SctpTransport::Connect() { + RTC_DCHECK_RUN_ON(network_thread_); + LOG(LS_VERBOSE) << debug_name_ << "->Connect()."; + + // If we already have a socket connection (which shouldn't ever happen), just + // return. + RTC_DCHECK(!sock_); + if (sock_) { + LOG(LS_ERROR) << debug_name_ << "->Connect(): Ignored as socket " + "is already established."; + return true; + } + + // If no socket (it was closed) try to start it again. This can happen when + // the socket we are connecting to closes, does an sctp shutdown handshake, + // or behaves unexpectedly causing us to perform a CloseSctpSocket. + if (!OpenSctpSocket()) { + return false; + } + + // Note: conversion from int to uint16_t happens on assignment. + sockaddr_conn local_sconn = GetSctpSockAddr(local_port_); + if (usrsctp_bind(sock_, reinterpret_cast(&local_sconn), + sizeof(local_sconn)) < 0) { + LOG_ERRNO(LS_ERROR) << debug_name_ + << "->Connect(): " << ("Failed usrsctp_bind"); + CloseSctpSocket(); + return false; + } + + // Note: conversion from int to uint16_t happens on assignment. + sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_); + int connect_result = usrsctp_connect( + sock_, reinterpret_cast(&remote_sconn), sizeof(remote_sconn)); + if (connect_result < 0 && errno != SCTP_EINPROGRESS) { + LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): " + << "Failed usrsctp_connect. got errno=" << errno + << ", but wanted " << SCTP_EINPROGRESS; + CloseSctpSocket(); + return false; + } + // Set the MTU and disable MTU discovery. + // We can only do this after usrsctp_connect or it has no effect. + sctp_paddrparams params = {{0}}; + memcpy(¶ms.spp_address, &remote_sconn, sizeof(remote_sconn)); + params.spp_flags = SPP_PMTUD_DISABLE; + params.spp_pathmtu = kSctpMtu; + if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, ¶ms, + sizeof(params))) { + LOG_ERRNO(LS_ERROR) << debug_name_ << "->Connect(): " + << "Failed to set SCTP_PEER_ADDR_PARAMS."; + } + // Since this is a fresh SCTP association, we'll always start out with empty + // queues, so "ReadyToSendData" should be true. + SetReadyToSendData(); + return true; +} + +bool SctpTransport::OpenSctpSocket() { + RTC_DCHECK_RUN_ON(network_thread_); + if (sock_) { + LOG(LS_WARNING) << debug_name_ << "->OpenSctpSocket(): " + << "Ignoring attempt to re-create existing socket."; + return false; + } + + UsrSctpWrapper::IncrementUsrSctpUsageCount(); + + // If kSendBufferSize isn't reflective of reality, we log an error, but we + // still have to do something reasonable here. Look up what the buffer's + // real size is and set our threshold to something reasonable. + static const int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 2; + + sock_ = usrsctp_socket( + AF_CONN, SOCK_STREAM, IPPROTO_SCTP, &UsrSctpWrapper::OnSctpInboundPacket, + &UsrSctpWrapper::SendThresholdCallback, kSendThreshold, this); + if (!sock_) { + LOG_ERRNO(LS_ERROR) << debug_name_ << "->OpenSctpSocket(): " + << "Failed to create SCTP socket."; + UsrSctpWrapper::DecrementUsrSctpUsageCount(); + return false; + } + + if (!ConfigureSctpSocket()) { + usrsctp_close(sock_); + sock_ = nullptr; + UsrSctpWrapper::DecrementUsrSctpUsageCount(); + return false; + } + // Register this class as an address for usrsctp. This is used by SCTP to + // direct the packets received (by the created socket) to this class. + usrsctp_register_address(this); + return true; +} + +bool SctpTransport::ConfigureSctpSocket() { + RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK(sock_); + // Make the socket non-blocking. Connect, close, shutdown etc will not block + // the thread waiting for the socket operation to complete. + if (usrsctp_set_non_blocking(sock_, 1) < 0) { + LOG_ERRNO(LS_ERROR) << debug_name_ << "->ConfigureSctpSocket(): " + << "Failed to set SCTP to non blocking."; + return false; + } + + // This ensures that the usrsctp close call deletes the association. This + // prevents usrsctp from calling OnSctpOutboundPacket with references to + // this class as the address. + linger linger_opt; + linger_opt.l_onoff = 1; + linger_opt.l_linger = 0; + if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt, + sizeof(linger_opt))) { + LOG_ERRNO(LS_ERROR) << debug_name_ << "->ConfigureSctpSocket(): " + << "Failed to set SO_LINGER."; + return false; + } + + // Enable stream ID resets. + struct sctp_assoc_value stream_rst; + stream_rst.assoc_id = SCTP_ALL_ASSOC; + stream_rst.assoc_value = 1; + if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, + &stream_rst, sizeof(stream_rst))) { + LOG_ERRNO(LS_ERROR) << debug_name_ << "->ConfigureSctpSocket(): " + + << "Failed to set SCTP_ENABLE_STREAM_RESET."; + return false; + } + + // Nagle. + uint32_t nodelay = 1; + if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, + sizeof(nodelay))) { + LOG_ERRNO(LS_ERROR) << debug_name_ << "->ConfigureSctpSocket(): " + << "Failed to set SCTP_NODELAY."; + return false; + } + + // Subscribe to SCTP event notifications. + int event_types[] = {SCTP_ASSOC_CHANGE, SCTP_PEER_ADDR_CHANGE, + SCTP_SEND_FAILED_EVENT, SCTP_SENDER_DRY_EVENT, + SCTP_STREAM_RESET_EVENT}; + struct sctp_event event = {0}; + event.se_assoc_id = SCTP_ALL_ASSOC; + event.se_on = 1; + for (size_t i = 0; i < arraysize(event_types); i++) { + event.se_type = event_types[i]; + if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event, + sizeof(event)) < 0) { + LOG_ERRNO(LS_ERROR) << debug_name_ << "->ConfigureSctpSocket(): " + + << "Failed to set SCTP_EVENT type: " << event.se_type; + return false; + } + } + return true; +} + +void SctpTransport::CloseSctpSocket() { + RTC_DCHECK_RUN_ON(network_thread_); + if (sock_) { + // We assume that SO_LINGER option is set to close the association when + // close is called. This means that any pending packets in usrsctp will be + // discarded instead of being sent. + usrsctp_close(sock_); + sock_ = nullptr; + usrsctp_deregister_address(this); + UsrSctpWrapper::DecrementUsrSctpUsageCount(); + ready_to_send_data_ = false; + } +} + +bool SctpTransport::SendQueuedStreamResets() { + RTC_DCHECK_RUN_ON(network_thread_); + if (!sent_reset_streams_.empty() || queued_reset_streams_.empty()) { + return true; + } + + LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ << "]: Sending [" + << ListStreams(queued_reset_streams_) << "], Open: [" + << ListStreams(open_streams_) << "], Sent: [" + << ListStreams(sent_reset_streams_) << "]"; + + const size_t num_streams = queued_reset_streams_.size(); + const size_t num_bytes = + sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t)); + + std::vector reset_stream_buf(num_bytes, 0); + struct sctp_reset_streams* resetp = + reinterpret_cast(&reset_stream_buf[0]); + resetp->srs_assoc_id = SCTP_ALL_ASSOC; + resetp->srs_flags = SCTP_STREAM_RESET_INCOMING | SCTP_STREAM_RESET_OUTGOING; + resetp->srs_number_streams = rtc::checked_cast(num_streams); + int result_idx = 0; + for (StreamSet::iterator it = queued_reset_streams_.begin(); + it != queued_reset_streams_.end(); ++it) { + resetp->srs_stream_list[result_idx++] = *it; + } + + int ret = + usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp, + rtc::checked_cast(reset_stream_buf.size())); + if (ret < 0) { + LOG_ERRNO(LS_ERROR) << debug_name_ << "->SendQueuedStreamResets(): " + "Failed to send a stream reset for " + << num_streams << " streams"; + return false; + } + + // sent_reset_streams_ is empty, and all the queued_reset_streams_ go into + // it now. + queued_reset_streams_.swap(sent_reset_streams_); + return true; +} + +void SctpTransport::SetReadyToSendData() { + RTC_DCHECK_RUN_ON(network_thread_); + if (!ready_to_send_data_) { + ready_to_send_data_ = true; + SignalReadyToSendData(); + } +} + +void SctpTransport::OnWritableState(rtc::PacketTransportInterface* transport) { + RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK_EQ(transport_channel_, transport); + if (!was_ever_writable_ && transport->writable()) { + was_ever_writable_ = true; + if (started_) { + Connect(); + } + } +} + +// Called by network interface when a packet has been received. +void SctpTransport::OnPacketRead(rtc::PacketTransportInterface* transport, + const char* data, + size_t len, + const rtc::PacketTime& packet_time, + int flags) { + RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK_EQ(transport_channel_, transport); + TRACE_EVENT0("webrtc", "SctpTransport::OnPacketRead"); + + // TODO(pthatcher): Do this in a more robust way by checking for + // SCTP or DTLS. + if (IsRtpPacket(data, len)) { + return; + } + + LOG(LS_VERBOSE) << debug_name_ << "->OnPacketRead(...): " + << " length=" << len << ", started: " << started_; + // Only give receiving packets to usrsctp after if connected. This enables two + // peers to each make a connect call, but for them not to receive an INIT + // packet before they have called connect; least the last receiver of the INIT + // packet will have called connect, and a connection will be established. + if (sock_) { + // Pass received packet to SCTP stack. Once processed by usrsctp, the data + // will be will be given to the global OnSctpInboundData, and then, + // marshalled by the AsyncInvoker. + VerboseLogPacket(data, len, SCTP_DUMP_INBOUND); + usrsctp_conninput(this, data, len, 0); + } else { + // TODO(ldixon): Consider caching the packet for very slightly better + // reliability. + } +} + +void SctpTransport::OnSendThresholdCallback() { + RTC_DCHECK_RUN_ON(network_thread_); + SetReadyToSendData(); +} + +sockaddr_conn SctpTransport::GetSctpSockAddr(int port) { + sockaddr_conn sconn = {0}; + sconn.sconn_family = AF_CONN; +#ifdef HAVE_SCONN_LEN + sconn.sconn_len = sizeof(sockaddr_conn); +#endif + // Note: conversion from int to uint16_t happens here. + sconn.sconn_port = rtc::HostToNetwork16(port); + sconn.sconn_addr = this; + return sconn; +} + +void SctpTransport::OnPacketFromSctpToNetwork( + const rtc::CopyOnWriteBuffer& buffer) { + RTC_DCHECK_RUN_ON(network_thread_); + if (buffer.size() > (kSctpMtu)) { + LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): " + << "SCTP seems to have made a packet that is bigger " + << "than its official MTU: " << buffer.size() << " vs max of " + << kSctpMtu; + } + TRACE_EVENT0("webrtc", "SctpTransport::OnPacketFromSctpToNetwork"); + + // Don't create noise by trying to send a packet when the DTLS channel isn't + // even writable. + if (!transport_channel_->writable()) { + return; + } + + // Bon voyage. + transport_channel_->SendPacket(buffer.data(), buffer.size(), + rtc::PacketOptions(), PF_NORMAL); +} + +void SctpTransport::OnInboundPacketFromSctpToChannel( + const rtc::CopyOnWriteBuffer& buffer, + ReceiveDataParams params, + int flags) { + RTC_DCHECK_RUN_ON(network_thread_); + LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): " + << "Received SCTP data:" + << " sid=" << params.sid + << " notification: " << (flags & MSG_NOTIFICATION) + << " length=" << buffer.size(); + // Sending a packet with data == NULL (no data) is SCTPs "close the + // connection" message. This sets sock_ = NULL; + if (!buffer.size() || !buffer.data()) { + LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): " + "No data, closing."; + return; + } + if (flags & MSG_NOTIFICATION) { + OnNotificationFromSctp(buffer); + } else { + OnDataFromSctpToChannel(params, buffer); + } +} + +void SctpTransport::OnDataFromSctpToChannel( + const ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& buffer) { + RTC_DCHECK_RUN_ON(network_thread_); + LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): " + << "Posting with length: " << buffer.size() << " on stream " + << params.sid; + // Reports all received messages to upper layers, no matter whether the sid + // is known. + SignalDataReceived(params, buffer); +} + +void SctpTransport::OnNotificationFromSctp( + const rtc::CopyOnWriteBuffer& buffer) { + RTC_DCHECK_RUN_ON(network_thread_); + const sctp_notification& notification = + reinterpret_cast(*buffer.data()); + RTC_DCHECK(notification.sn_header.sn_length == buffer.size()); + + // TODO(ldixon): handle notifications appropriately. + switch (notification.sn_header.sn_type) { + case SCTP_ASSOC_CHANGE: + LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE"; + OnNotificationAssocChange(notification.sn_assoc_change); + break; + case SCTP_REMOTE_ERROR: + LOG(LS_INFO) << "SCTP_REMOTE_ERROR"; + break; + case SCTP_SHUTDOWN_EVENT: + LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT"; + break; + case SCTP_ADAPTATION_INDICATION: + LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION"; + break; + case SCTP_PARTIAL_DELIVERY_EVENT: + LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT"; + break; + case SCTP_AUTHENTICATION_EVENT: + LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT"; + break; + case SCTP_SENDER_DRY_EVENT: + LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT"; + SetReadyToSendData(); + break; + // TODO(ldixon): Unblock after congestion. + case SCTP_NOTIFICATIONS_STOPPED_EVENT: + LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT"; + break; + case SCTP_SEND_FAILED_EVENT: + LOG(LS_INFO) << "SCTP_SEND_FAILED_EVENT"; + break; + case SCTP_STREAM_RESET_EVENT: + OnStreamResetEvent(¬ification.sn_strreset_event); + break; + case SCTP_ASSOC_RESET_EVENT: + LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT"; + break; + case SCTP_STREAM_CHANGE_EVENT: + LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT"; + // An acknowledgment we get after our stream resets have gone through, + // if they've failed. We log the message, but don't react -- we don't + // keep around the last-transmitted set of SSIDs we wanted to close for + // error recovery. It doesn't seem likely to occur, and if so, likely + // harmless within the lifetime of a single SCTP association. + break; + default: + LOG(LS_WARNING) << "Unknown SCTP event: " + << notification.sn_header.sn_type; + break; + } +} + +void SctpTransport::OnNotificationAssocChange(const sctp_assoc_change& change) { + RTC_DCHECK_RUN_ON(network_thread_); + switch (change.sac_state) { + case SCTP_COMM_UP: + LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP"; + break; + case SCTP_COMM_LOST: + LOG(LS_INFO) << "Association change SCTP_COMM_LOST"; + break; + case SCTP_RESTART: + LOG(LS_INFO) << "Association change SCTP_RESTART"; + break; + case SCTP_SHUTDOWN_COMP: + LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP"; + break; + case SCTP_CANT_STR_ASSOC: + LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC"; + break; + default: + LOG(LS_INFO) << "Association change UNKNOWN"; + break; + } +} + +void SctpTransport::OnStreamResetEvent( + const struct sctp_stream_reset_event* evt) { + RTC_DCHECK_RUN_ON(network_thread_); + // A stream reset always involves two RE-CONFIG chunks for us -- we always + // simultaneously reset a sid's sequence number in both directions. The + // requesting side transmits a RE-CONFIG chunk and waits for the peer to send + // one back. Both sides get this SCTP_STREAM_RESET_EVENT when they receive + // RE-CONFIGs. + const int num_sids = (evt->strreset_length - sizeof(*evt)) / + sizeof(evt->strreset_stream_list[0]); + LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ + << "): Flags = 0x" << std::hex << evt->strreset_flags << " (" + << ListFlags(evt->strreset_flags) << ")"; + LOG(LS_VERBOSE) << "Assoc = " << evt->strreset_assoc_id << ", Streams = [" + << ListArray(evt->strreset_stream_list, num_sids) + << "], Open: [" << ListStreams(open_streams_) << "], Q'd: [" + << ListStreams(queued_reset_streams_) << "], Sent: [" + << ListStreams(sent_reset_streams_) << "]"; + + // If both sides try to reset some streams at the same time (even if they're + // disjoint sets), we can get reset failures. + if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) { + // OK, just try again. The stream IDs sent over when the RESET_FAILED flag + // is set seem to be garbage values. Ignore them. + queued_reset_streams_.insert(sent_reset_streams_.begin(), + sent_reset_streams_.end()); + sent_reset_streams_.clear(); + + } else if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) { + // Each side gets an event for each direction of a stream. That is, + // closing sid k will make each side receive INCOMING and OUTGOING reset + // events for k. As per RFC6525, Section 5, paragraph 2, each side will + // get an INCOMING event first. + for (int i = 0; i < num_sids; i++) { + const int stream_id = evt->strreset_stream_list[i]; + + // See if this stream ID was closed by our peer or ourselves. + StreamSet::iterator it = sent_reset_streams_.find(stream_id); + + // The reset was requested locally. + if (it != sent_reset_streams_.end()) { + LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ + << "): local sid " << stream_id << " acknowledged."; + sent_reset_streams_.erase(it); + + } else if ((it = open_streams_.find(stream_id)) != open_streams_.end()) { + // The peer requested the reset. + LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ + << "): closing sid " << stream_id; + open_streams_.erase(it); + SignalStreamClosedRemotely(stream_id); + + } else if ((it = queued_reset_streams_.find(stream_id)) != + queued_reset_streams_.end()) { + // The peer requested the reset, but there was a local reset + // queued. + LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ + << "): double-sided close for sid " << stream_id; + // Both sides want the stream closed, and the peer got to send the + // RE-CONFIG first. Treat it like the local Remove(Send|Recv)Stream + // finished quickly. + queued_reset_streams_.erase(it); + + } else { + // This stream is unknown. Sometimes this can be from an + // RESET_FAILED-related retransmit. + LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ + << "): Unknown sid " << stream_id; + } + } + } + + // Always try to send the queued RESET because this call indicates that the + // last local RESET or remote RESET has made some progress. + SendQueuedStreamResets(); +} + +} // namespace cricket diff --git a/webrtc/media/sctp/sctptransport.h b/webrtc/media/sctp/sctptransport.h new file mode 100644 index 0000000000..6d3a41a8f1 --- /dev/null +++ b/webrtc/media/sctp/sctptransport.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MEDIA_SCTP_SCTPTRANSPORT_H_ +#define WEBRTC_MEDIA_SCTP_SCTPTRANSPORT_H_ + +#include + +#include // for unique_ptr. +#include +#include +#include + +#include "webrtc/base/asyncinvoker.h" +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/copyonwritebuffer.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/thread.h" +// For SendDataParams/ReceiveDataParams. +#include "webrtc/media/base/mediachannel.h" +#include "webrtc/media/sctp/sctptransportinternal.h" +#include "webrtc/p2p/base/transportchannel.h" + +// Defined by "usrsctplib/usrsctp.h" +struct sockaddr_conn; +struct sctp_assoc_change; +struct sctp_stream_reset_event; +// Defined by +struct socket; +namespace cricket { + +// Holds data to be passed on to a channel. +struct SctpInboundPacket; + +// From channel calls, data flows like this: +// [network thread (although it can in princple be another thread)] +// 1. SctpTransport::SendData(data) +// 2. usrsctp_sendv(data) +// [network thread returns; sctp thread then calls the following] +// 3. OnSctpOutboundPacket(wrapped_data) +// [sctp thread returns having async invoked on the network thread] +// 4. SctpTransport::OnPacketFromSctpToNetwork(wrapped_data) +// 5. TransportChannel::SendPacket(wrapped_data) +// 6. ... across network ... a packet is sent back ... +// 7. SctpTransport::OnPacketReceived(wrapped_data) +// 8. usrsctp_conninput(wrapped_data) +// [network thread returns; sctp thread then calls the following] +// 9. OnSctpInboundData(data) +// [sctp thread returns having async invoked on the network thread] +// 10. SctpTransport::OnInboundPacketFromSctpToChannel(inboundpacket) +// 11. SctpTransport::OnDataFromSctpToChannel(data) +// 12. SctpTransport::SignalDataReceived(data) +// [from the same thread, methods registered/connected to +// SctpTransport are called with the recieved data] +class SctpTransport : public SctpTransportInternal, + public sigslot::has_slots<> { + public: + // |network_thread| is where packets will be processed and callbacks from + // this transport will be posted, and is the only thread on which public + // methods can be called. + // |channel| is required (must not be null). + SctpTransport(rtc::Thread* network_thread, + cricket::TransportChannel* channel); + ~SctpTransport() override; + + // SctpTransportInternal overrides (see sctptransportinternal.h for comments). + void SetTransportChannel(cricket::TransportChannel* channel) override; + bool Start(int local_port, int remote_port) override; + bool OpenStream(int sid) override; + bool ResetStream(int sid) override; + bool SendData(const SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + SendDataResult* result = nullptr) override; + bool ReadyToSendData() override; + void set_debug_name_for_testing(const char* debug_name) override { + debug_name_ = debug_name; + } + + // Exposed to allow Post call from c-callbacks. + // TODO(deadbeef): Remove this or at least make it return a const pointer. + rtc::Thread* network_thread() const { return network_thread_; } + + private: + void ConnectTransportChannelSignals(); + void DisconnectTransportChannelSignals(); + + // Creates the socket and connects. + bool Connect(); + + // Returns false when opening the socket failed. + bool OpenSctpSocket(); + // Helpet method to set socket options. + bool ConfigureSctpSocket(); + // Sets |sock_ |to nullptr. + void CloseSctpSocket(); + + // Sends a SCTP_RESET_STREAM for all streams in closing_ssids_. + bool SendQueuedStreamResets(); + + // Sets the "ready to send" flag and fires signal if needed. + void SetReadyToSendData(); + + // Callbacks from DTLS channel. + void OnWritableState(rtc::PacketTransportInterface* transport); + virtual void OnPacketRead(rtc::PacketTransportInterface* transport, + const char* data, + size_t len, + const rtc::PacketTime& packet_time, + int flags); + + // Methods related to usrsctp callbacks. + void OnSendThresholdCallback(); + sockaddr_conn GetSctpSockAddr(int port); + + // Called using |invoker_| to send packet on the network. + void OnPacketFromSctpToNetwork(const rtc::CopyOnWriteBuffer& buffer); + // Called using |invoker_| to decide what to do with the packet. + // The |flags| parameter is used by SCTP to distinguish notification packets + // from other types of packets. + void OnInboundPacketFromSctpToChannel(const rtc::CopyOnWriteBuffer& buffer, + ReceiveDataParams params, + int flags); + void OnDataFromSctpToChannel(const ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& buffer); + void OnNotificationFromSctp(const rtc::CopyOnWriteBuffer& buffer); + void OnNotificationAssocChange(const sctp_assoc_change& change); + + void OnStreamResetEvent(const struct sctp_stream_reset_event* evt); + + // Responsible for marshalling incoming data to the channels listeners, and + // outgoing data to the network interface. + rtc::Thread* network_thread_; + // Helps pass inbound/outbound packets asynchronously to the network thread. + rtc::AsyncInvoker invoker_; + // Underlying DTLS channel. + TransportChannel* transport_channel_; + bool was_ever_writable_ = false; + int local_port_ = kSctpDefaultPort; + int remote_port_ = kSctpDefaultPort; + struct socket* sock_ = nullptr; // The socket created by usrsctp_socket(...). + + // Has Start been called? Don't create SCTP socket until it has. + bool started_ = false; + // Are we ready to queue data (SCTP socket created, and not blocked due to + // congestion control)? Different than |transport_channel_|'s "ready to + // send". + bool ready_to_send_data_ = false; + + typedef std::set StreamSet; + // When a data channel opens a stream, it goes into open_streams_. When we + // want to close it, the stream's ID goes into queued_reset_streams_. When + // we actually transmit a RE-CONFIG chunk with that stream ID, the ID goes + // into sent_reset_streams_. When we get a response RE-CONFIG chunk back + // acknowledging the reset, we remove the stream ID from + // sent_reset_streams_. We use sent_reset_streams_ to differentiate + // between acknowledgment RE-CONFIG and peer-initiated RE-CONFIGs. + StreamSet open_streams_; + StreamSet queued_reset_streams_; + StreamSet sent_reset_streams_; + + // A static human-readable name for debugging messages. + const char* debug_name_ = "SctpTransport"; + // Hides usrsctp interactions from this header file. + class UsrSctpWrapper; + + RTC_DISALLOW_COPY_AND_ASSIGN(SctpTransport); +}; + +class SctpTransportFactory : public SctpTransportInternalFactory { + public: + explicit SctpTransportFactory(rtc::Thread* network_thread) + : network_thread_(network_thread) {} + + std::unique_ptr CreateSctpTransport( + TransportChannel* channel) override { + return std::unique_ptr( + new SctpTransport(network_thread_, channel)); + } + + private: + rtc::Thread* network_thread_; +}; + +} // namespace cricket + +#endif // WEBRTC_MEDIA_SCTP_SCTPTRANSPORT_H_ diff --git a/webrtc/media/sctp/sctptransport_unittest.cc b/webrtc/media/sctp/sctptransport_unittest.cc new file mode 100644 index 0000000000..42e4dc646f --- /dev/null +++ b/webrtc/media/sctp/sctptransport_unittest.cc @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include +#include + +#include +#include +#include + +#include "webrtc/base/bind.h" +#include "webrtc/base/copyonwritebuffer.h" +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/helpers.h" +#include "webrtc/base/ssladapter.h" +#include "webrtc/base/thread.h" +#include "webrtc/media/sctp/sctptransport.h" +#include "webrtc/p2p/base/faketransportcontroller.h" + +namespace { +static const int kDefaultTimeout = 10000; // 10 seconds. +// Use ports other than the default 5000 for testing. +static const int kTransport1Port = 5001; +static const int kTransport2Port = 5002; +} + +namespace cricket { + +// This is essentially a buffer to hold recieved data. It stores only the last +// received data. Calling OnDataReceived twice overwrites old data with the +// newer one. +// TODO(ldixon): Implement constraints, and allow new data to be added to old +// instead of replacing it. +class SctpFakeDataReceiver : public sigslot::has_slots<> { + public: + SctpFakeDataReceiver() : received_(false) {} + + void Clear() { + received_ = false; + last_data_ = ""; + last_params_ = ReceiveDataParams(); + } + + virtual void OnDataReceived(const ReceiveDataParams& params, + const rtc::CopyOnWriteBuffer& data) { + received_ = true; + last_data_ = std::string(data.data(), data.size()); + last_params_ = params; + } + + bool received() const { return received_; } + std::string last_data() const { return last_data_; } + ReceiveDataParams last_params() const { return last_params_; } + + private: + bool received_; + std::string last_data_; + ReceiveDataParams last_params_; +}; + +class SignalReadyToSendObserver : public sigslot::has_slots<> { + public: + SignalReadyToSendObserver() : signaled_(false) {} + + void OnSignaled() { signaled_ = true; } + + bool IsSignaled() { return signaled_; } + + private: + bool signaled_; +}; + +class SignalTransportClosedObserver : public sigslot::has_slots<> { + public: + SignalTransportClosedObserver() {} + void BindSelf(SctpTransport* transport) { + transport->SignalStreamClosedRemotely.connect( + this, &SignalTransportClosedObserver::OnStreamClosed); + } + void OnStreamClosed(int stream) { streams_.push_back(stream); } + + int StreamCloseCount(int stream) { + return std::count(streams_.begin(), streams_.end(), stream); + } + + bool WasStreamClosed(int stream) { + return std::find(streams_.begin(), streams_.end(), stream) != + streams_.end(); + } + + private: + std::vector streams_; +}; + +class SignalTransportClosedReopener : public sigslot::has_slots<> { + public: + SignalTransportClosedReopener(SctpTransport* transport, SctpTransport* peer) + : transport_(transport), peer_(peer) {} + + void OnStreamClosed(int stream) { + transport_->OpenStream(stream); + peer_->OpenStream(stream); + streams_.push_back(stream); + } + + int StreamCloseCount(int stream) { + return std::count(streams_.begin(), streams_.end(), stream); + } + + private: + SctpTransport* transport_; + SctpTransport* peer_; + std::vector streams_; +}; + +// SCTP Data Engine testing framework. +class SctpTransportTest : public testing::Test, public sigslot::has_slots<> { + protected: + // usrsctp uses the NSS random number generator on non-Android platforms, + // so we need to initialize SSL. + static void SetUpTestCase() {} + + void SetupConnectedTransportsWithTwoStreams() { + fake_dtls1_.reset(new FakeTransportChannel("fake dtls 1", 0)); + fake_dtls2_.reset(new FakeTransportChannel("fake dtls 2", 0)); + recv1_.reset(new SctpFakeDataReceiver()); + recv2_.reset(new SctpFakeDataReceiver()); + transport1_.reset(CreateTransport(fake_dtls1_.get(), recv1_.get())); + transport1_->set_debug_name_for_testing("transport1"); + transport1_->SignalReadyToSendData.connect( + this, &SctpTransportTest::OnChan1ReadyToSend); + transport2_.reset(CreateTransport(fake_dtls2_.get(), recv2_.get())); + transport2_->set_debug_name_for_testing("transport2"); + transport2_->SignalReadyToSendData.connect( + this, &SctpTransportTest::OnChan2ReadyToSend); + // Setup two connected transports ready to send and receive. + bool asymmetric = false; + fake_dtls1_->SetDestination(fake_dtls2_.get(), asymmetric); + + LOG(LS_VERBOSE) << "Transport setup ----------------------------- "; + AddStream(1); + AddStream(2); + + LOG(LS_VERBOSE) << "Connect the transports -----------------------------"; + // Both transports need to have started (with matching ports) for an + // association to be formed. + transport1_->Start(kTransport1Port, kTransport2Port); + transport2_->Start(kTransport2Port, kTransport1Port); + } + + bool AddStream(int sid) { + bool ret = true; + ret = ret && transport1_->OpenStream(sid); + ret = ret && transport2_->OpenStream(sid); + return ret; + } + + SctpTransport* CreateTransport(FakeTransportChannel* fake_dtls, + SctpFakeDataReceiver* recv) { + SctpTransport* transport = + new SctpTransport(rtc::Thread::Current(), fake_dtls); + // When data is received, pass it to the SctpFakeDataReceiver. + transport->SignalDataReceived.connect( + recv, &SctpFakeDataReceiver::OnDataReceived); + return transport; + } + + bool SendData(SctpTransport* chan, + int sid, + const std::string& msg, + SendDataResult* result) { + SendDataParams params; + params.sid = sid; + + return chan->SendData(params, rtc::CopyOnWriteBuffer(&msg[0], msg.length()), + result); + } + + bool ReceivedData(const SctpFakeDataReceiver* recv, + int sid, + const std::string& msg) { + return (recv->received() && recv->last_params().sid == sid && + recv->last_data() == msg); + } + + bool ProcessMessagesUntilIdle() { + rtc::Thread* thread = rtc::Thread::Current(); + while (!thread->empty()) { + rtc::Message msg; + if (thread->Get(&msg, rtc::Thread::kForever)) { + thread->Dispatch(&msg); + } + } + return !thread->IsQuitting(); + } + + SctpTransport* transport1() { return transport1_.get(); } + SctpTransport* transport2() { return transport2_.get(); } + SctpFakeDataReceiver* receiver1() { return recv1_.get(); } + SctpFakeDataReceiver* receiver2() { return recv2_.get(); } + FakeTransportChannel* fake_dtls1() { return fake_dtls1_.get(); } + FakeTransportChannel* fake_dtls2() { return fake_dtls2_.get(); } + + int transport1_ready_to_send_count() { + return transport1_ready_to_send_count_; + } + int transport2_ready_to_send_count() { + return transport2_ready_to_send_count_; + } + + private: + std::unique_ptr fake_dtls1_; + std::unique_ptr fake_dtls2_; + std::unique_ptr recv1_; + std::unique_ptr recv2_; + std::unique_ptr transport1_; + std::unique_ptr transport2_; + + int transport1_ready_to_send_count_ = 0; + int transport2_ready_to_send_count_ = 0; + + void OnChan1ReadyToSend() { ++transport1_ready_to_send_count_; } + void OnChan2ReadyToSend() { ++transport2_ready_to_send_count_; } +}; + +// Test that data can be sent end-to-end when an SCTP transport starts with one +// transport channel (which is unwritable), and then switches to another +// channel. A common scenario due to how BUNDLE works. +TEST_F(SctpTransportTest, SwitchTransportChannel) { + FakeTransportChannel black_hole("black hole", 0); + FakeTransportChannel fake_dtls1("fake dtls 1", 0); + FakeTransportChannel fake_dtls2("fake dtls 2", 0); + SctpFakeDataReceiver recv1; + SctpFakeDataReceiver recv2; + + // Construct transport1 with the "black hole" channel. + std::unique_ptr transport1( + CreateTransport(&black_hole, &recv1)); + std::unique_ptr transport2( + CreateTransport(&fake_dtls2, &recv2)); + + // Add a stream. + transport1->OpenStream(1); + transport2->OpenStream(1); + + // Tell them both to start (though transport1_ is connected to black_hole). + transport1->Start(kTransport1Port, kTransport2Port); + transport2->Start(kTransport2Port, kTransport1Port); + + // Switch transport1_ to the normal fake_dtls1_ channel. + transport1->SetTransportChannel(&fake_dtls1); + + // Connect the two fake DTLS channels. + bool asymmetric = false; + fake_dtls1.SetDestination(&fake_dtls2, asymmetric); + + // Make sure we end up able to send data. + SendDataResult result; + ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result)); + ASSERT_TRUE(SendData(transport2.get(), 1, "bar", &result)); + EXPECT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout); + EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "bar"), kDefaultTimeout); +} + +// Calling Start twice shouldn't do anything bad, if with the same parameters. +TEST_F(SctpTransportTest, DuplicateStartCallsIgnored) { + SetupConnectedTransportsWithTwoStreams(); + EXPECT_TRUE(transport1()->Start(kTransport1Port, kTransport2Port)); + + // Make sure we can still send/recv data. + SendDataResult result; + ASSERT_TRUE(SendData(transport1(), 1, "foo", &result)); + ASSERT_TRUE(SendData(transport2(), 1, "bar", &result)); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "foo"), kDefaultTimeout); + EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 1, "bar"), kDefaultTimeout); +} + +// Calling Start a second time with a different port should fail. +TEST_F(SctpTransportTest, CallingStartWithDifferentPortFails) { + SetupConnectedTransportsWithTwoStreams(); + EXPECT_FALSE(transport1()->Start(kTransport1Port, 1234)); + EXPECT_FALSE(transport1()->Start(1234, kTransport2Port)); +} + +// A value of -1 for the local/remote port should be treated as the default +// (5000). +TEST_F(SctpTransportTest, NegativeOnePortTreatedAsDefault) { + FakeTransportChannel fake_dtls1("fake dtls 1", 0); + FakeTransportChannel fake_dtls2("fake dtls 2", 0); + SctpFakeDataReceiver recv1; + SctpFakeDataReceiver recv2; + std::unique_ptr transport1( + CreateTransport(&fake_dtls1, &recv1)); + std::unique_ptr transport2( + CreateTransport(&fake_dtls2, &recv2)); + + // Add a stream. + transport1->OpenStream(1); + transport2->OpenStream(1); + + // Tell them both to start, giving one transport the default port and the + // other transport -1. + transport1->Start(kSctpDefaultPort, kSctpDefaultPort); + transport2->Start(-1, -1); + + // Connect the two fake DTLS channels. + bool asymmetric = false; + fake_dtls1.SetDestination(&fake_dtls2, asymmetric); + + // Make sure we end up able to send data. + SendDataResult result; + ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result)); + ASSERT_TRUE(SendData(transport2.get(), 1, "bar", &result)); + EXPECT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout); + EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "bar"), kDefaultTimeout); +} + +TEST_F(SctpTransportTest, OpenStreamWithAlreadyOpenedStreamFails) { + FakeTransportChannel fake_dtls("fake dtls", 0); + SctpFakeDataReceiver recv; + std::unique_ptr transport(CreateTransport(&fake_dtls, &recv)); + EXPECT_TRUE(transport->OpenStream(1)); + EXPECT_FALSE(transport->OpenStream(1)); +} + +TEST_F(SctpTransportTest, ResetStreamWithAlreadyResetStreamFails) { + FakeTransportChannel fake_dtls("fake dtls", 0); + SctpFakeDataReceiver recv; + std::unique_ptr transport(CreateTransport(&fake_dtls, &recv)); + EXPECT_TRUE(transport->OpenStream(1)); + EXPECT_TRUE(transport->ResetStream(1)); + EXPECT_FALSE(transport->ResetStream(1)); +} + +// Test that SignalReadyToSendData is fired after Start has been called and the +// DTLS channel is writable. +TEST_F(SctpTransportTest, SignalReadyToSendDataAfterDtlsWritable) { + FakeTransportChannel fake_dtls("fake dtls", 0); + SctpFakeDataReceiver recv; + std::unique_ptr transport(CreateTransport(&fake_dtls, &recv)); + + SignalReadyToSendObserver signal_observer; + transport->SignalReadyToSendData.connect( + &signal_observer, &SignalReadyToSendObserver::OnSignaled); + + transport->Start(kSctpDefaultPort, kSctpDefaultPort); + fake_dtls.SetWritable(true); + EXPECT_TRUE_WAIT(signal_observer.IsSignaled(), kDefaultTimeout); +} + +// Test that after an SCTP socket's buffer is filled, SignalReadyToSendData +// is fired after it begins to be drained. +TEST_F(SctpTransportTest, SignalReadyToSendDataAfterBlocked) { + SetupConnectedTransportsWithTwoStreams(); + // Wait for initial SCTP association to be formed. + EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout); + // Make the fake transport unwritable so that messages pile up for the SCTP + // socket. + fake_dtls1()->SetWritable(false); + // Send messages until we get EWOULDBLOCK. + static const int kMaxMessages = 1024; + SendDataParams params; + params.sid = 1; + rtc::CopyOnWriteBuffer buf(1024); + memset(buf.data(), 0, 1024); + SendDataResult result; + int message_count; + for (message_count = 0; message_count < kMaxMessages; ++message_count) { + if (!transport1()->SendData(params, buf, &result) && result == SDR_BLOCK) { + break; + } + } + ASSERT_NE(kMaxMessages, message_count) + << "Sent max number of messages without getting SDR_BLOCK?"; + // Make sure the ready-to-send count hasn't changed. + EXPECT_EQ(1, transport1_ready_to_send_count()); + // Make the transport writable again and expect a "SignalReadyToSendData" at + // some point. + fake_dtls1()->SetWritable(true); + EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout); +} + +TEST_F(SctpTransportTest, SendData) { + SetupConnectedTransportsWithTwoStreams(); + + SendDataResult result; + LOG(LS_VERBOSE) + << "transport1 sending: 'hello?' -----------------------------"; + ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result)); + EXPECT_EQ(SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); + LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received() + << ", recv2.last_params.sid=" + << receiver2()->last_params().sid + << ", recv2.last_params.timestamp=" + << receiver2()->last_params().timestamp + << ", recv2.last_params.seq_num=" + << receiver2()->last_params().seq_num + << ", recv2.last_data=" << receiver2()->last_data(); + + LOG(LS_VERBOSE) + << "transport2 sending: 'hi transport1' -----------------------------"; + ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result)); + EXPECT_EQ(SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"), + kDefaultTimeout); + LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received() + << ", recv1.last_params.sid=" + << receiver1()->last_params().sid + << ", recv1.last_params.timestamp=" + << receiver1()->last_params().timestamp + << ", recv1.last_params.seq_num=" + << receiver1()->last_params().seq_num + << ", recv1.last_data=" << receiver1()->last_data(); +} + +// Sends a lot of large messages at once and verifies SDR_BLOCK is returned. +TEST_F(SctpTransportTest, SendDataBlocked) { + SetupConnectedTransportsWithTwoStreams(); + + SendDataResult result; + SendDataParams params; + params.sid = 1; + + std::vector buffer(1024 * 64, 0); + + for (size_t i = 0; i < 100; ++i) { + transport1()->SendData( + params, rtc::CopyOnWriteBuffer(&buffer[0], buffer.size()), &result); + if (result == SDR_BLOCK) + break; + } + + EXPECT_EQ(SDR_BLOCK, result); +} + +// Trying to send data for a nonexistent stream should fail. +TEST_F(SctpTransportTest, SendDataWithNonexistentStreamFails) { + SetupConnectedTransportsWithTwoStreams(); + SendDataResult result; + EXPECT_FALSE(SendData(transport2(), 123, "some data", &result)); + EXPECT_EQ(SDR_ERROR, result); +} + +TEST_F(SctpTransportTest, ClosesRemoteStream) { + SetupConnectedTransportsWithTwoStreams(); + SignalTransportClosedObserver transport1_sig_receiver, + transport2_sig_receiver; + transport1_sig_receiver.BindSelf(transport1()); + transport2_sig_receiver.BindSelf(transport2()); + + SendDataResult result; + ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result)); + EXPECT_EQ(SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); + ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result)); + EXPECT_EQ(SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"), + kDefaultTimeout); + + // Close transport 1. Transport 2 should notify us. + transport1()->ResetStream(1); + EXPECT_TRUE_WAIT(transport2_sig_receiver.WasStreamClosed(1), kDefaultTimeout); +} + +TEST_F(SctpTransportTest, ClosesTwoRemoteStreams) { + SetupConnectedTransportsWithTwoStreams(); + AddStream(3); + SignalTransportClosedObserver transport1_sig_receiver, + transport2_sig_receiver; + transport1_sig_receiver.BindSelf(transport1()); + transport2_sig_receiver.BindSelf(transport2()); + + SendDataResult result; + ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result)); + EXPECT_EQ(SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); + ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result)); + EXPECT_EQ(SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"), + kDefaultTimeout); + + // Close two streams on one side. + transport2()->ResetStream(2); + transport2()->ResetStream(3); + EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(2), kDefaultTimeout); + EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(3), kDefaultTimeout); +} + +TEST_F(SctpTransportTest, ClosesStreamsOnBothSides) { + SetupConnectedTransportsWithTwoStreams(); + AddStream(3); + AddStream(4); + SignalTransportClosedObserver transport1_sig_receiver, + transport2_sig_receiver; + transport1_sig_receiver.BindSelf(transport1()); + transport2_sig_receiver.BindSelf(transport2()); + + SendDataResult result; + ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result)); + EXPECT_EQ(SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); + ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result)); + EXPECT_EQ(SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"), + kDefaultTimeout); + + // Close one stream on transport1(), while closing three streams on + // transport2(). They will conflict (only one side can close anything at a + // time, apparently). Test the resolution of the conflict. + transport1()->ResetStream(1); + + transport2()->ResetStream(2); + transport2()->ResetStream(3); + transport2()->ResetStream(4); + EXPECT_TRUE_WAIT(transport2_sig_receiver.WasStreamClosed(1), kDefaultTimeout); + EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(2), kDefaultTimeout); + EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(3), kDefaultTimeout); + EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(4), kDefaultTimeout); +} + +TEST_F(SctpTransportTest, RefusesHighNumberedTransports) { + SetupConnectedTransportsWithTwoStreams(); + EXPECT_TRUE(AddStream(kMaxSctpSid)); + EXPECT_FALSE(AddStream(kMaxSctpSid + 1)); +} + +// Flaky, see webrtc:4453. +TEST_F(SctpTransportTest, DISABLED_ReusesAStream) { + // Shut down transport 1, then open it up again for reuse. + SetupConnectedTransportsWithTwoStreams(); + SendDataResult result; + SignalTransportClosedObserver transport2_sig_receiver; + transport2_sig_receiver.BindSelf(transport2()); + + ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result)); + EXPECT_EQ(SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); + + transport1()->ResetStream(1); + EXPECT_TRUE_WAIT(transport2_sig_receiver.WasStreamClosed(1), kDefaultTimeout); + // Transport 1 is gone now. + + // Create a new transport 1. + AddStream(1); + ASSERT_TRUE(SendData(transport1(), 1, "hi?", &result)); + EXPECT_EQ(SDR_SUCCESS, result); + EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hi?"), kDefaultTimeout); + transport1()->ResetStream(1); + EXPECT_TRUE_WAIT(transport2_sig_receiver.StreamCloseCount(1) == 2, + kDefaultTimeout); +} + +} // namespace cricket diff --git a/webrtc/media/sctp/sctptransportinternal.h b/webrtc/media/sctp/sctptransportinternal.h new file mode 100644 index 0000000000..7dd6bc7ea7 --- /dev/null +++ b/webrtc/media/sctp/sctptransportinternal.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_ +#define WEBRTC_MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_ + +// TODO(deadbeef): Move SCTP code out of media/, and make it not depend on +// anything in media/. + +#include // for unique_ptr +#include +#include + +#include "webrtc/base/copyonwritebuffer.h" +#include "webrtc/base/thread.h" +// For SendDataParams/ReceiveDataParams. +// TODO(deadbeef): Use something else for SCTP. It's confusing that we use an +// SSRC field for SID. +#include "webrtc/media/base/mediachannel.h" +#include "webrtc/p2p/base/transportchannel.h" + +namespace cricket { + +// The number of outgoing streams that we'll negotiate. Since stream IDs (SIDs) +// are 0-based, the highest usable SID is 1023. +// +// It's recommended to use the maximum of 65535 in: +// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.2 +// However, we use 1024 in order to save memory. usrsctp allocates 104 bytes +// for each pair of incoming/outgoing streams (on a 64-bit system), so 65535 +// streams would waste ~6MB. +// +// Note: "max" and "min" here are inclusive. +constexpr uint16_t kMaxSctpStreams = 1024; +constexpr uint16_t kMaxSctpSid = kMaxSctpStreams - 1; +constexpr uint16_t kMinSctpSid = 0; + +// This is the default SCTP port to use. It is passed along the wire and the +// connectee and connector must be using the same port. It is not related to the +// ports at the IP level. (Corresponds to: sockaddr_conn.sconn_port in +// usrsctp.h) +const int kSctpDefaultPort = 5000; + +// Abstract SctpTransport interface for use internally (by +// PeerConnection/WebRtcSession/etc.). Exists to allow mock/fake SctpTransports +// to be created. +class SctpTransportInternal { + public: + virtual ~SctpTransportInternal() {} + + // Changes what underlying DTLS channel is uses. Used when switching which + // bundled transport the SctpTransport uses. + // Assumes |channel| is non-null. + virtual void SetTransportChannel(TransportChannel* channel) = 0; + + // When Start is called, connects as soon as possible; this can be called + // before DTLS completes, in which case the connection will begin when DTLS + // completes. This method can be called multiple times, though not if either + // of the ports are changed. + // + // |local_sctp_port| and |remote_sctp_port| are passed along the wire and the + // listener and connector must be using the same port. They are not related + // to the ports at the IP level. If set to -1, we default to + // kSctpDefaultPort. + // + // TODO(deadbeef): Add remote max message size as parameter to Start, once we + // start supporting it. + // TODO(deadbeef): Support calling Start with different local/remote ports + // and create a new association? Not clear if this is something we need to + // support though. See: https://github.com/w3c/webrtc-pc/issues/979 + virtual bool Start(int local_sctp_port, int remote_sctp_port) = 0; + + // NOTE: Initially there was a "Stop" method here, but it was never used, so + // it was removed. + + // Informs SctpTransport that |sid| will start being used. Returns false if + // it is impossible to use |sid|, or if it's already in use. + // Until calling this, can't send data using |sid|. + // TODO(deadbeef): Actually implement the "returns false if |sid| can't be + // used" part. See: + // https://bugs.chromium.org/p/chromium/issues/detail?id=619849 + virtual bool OpenStream(int sid) = 0; + // The inverse of OpenStream. When this method returns, the reset process may + // have not finished but it will have begun. + // TODO(deadbeef): We need a way to tell when it's done. See: + // https://bugs.chromium.org/p/webrtc/issues/detail?id=4453 + virtual bool ResetStream(int sid) = 0; + // Send data down this channel (will be wrapped as SCTP packets then given to + // usrsctp that will then post the network interface). + // Returns true iff successful data somewhere on the send-queue/network. + // Uses |params.ssrc| as the SCTP sid. + virtual bool SendData(const SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + SendDataResult* result = nullptr) = 0; + + // Indicates when the SCTP socket is created and not blocked by congestion + // control. This changes to false when SDR_BLOCK is returned from SendData, + // and + // changes to true when SignalReadyToSendData is fired. The underlying DTLS/ + // ICE channels may be unwritable while ReadyToSendData is true, because data + // can still be queued in usrsctp. + virtual bool ReadyToSendData() = 0; + + sigslot::signal0<> SignalReadyToSendData; + // ReceiveDataParams includes SID, seq num, timestamp, etc. CopyOnWriteBuffer + // contains message payload. + sigslot::signal2 + SignalDataReceived; + // Parameter is SID of closed stream. + sigslot::signal1 SignalStreamClosedRemotely; + + // Helper for debugging. + virtual void set_debug_name_for_testing(const char* debug_name) = 0; +}; + +// Factory class which can be used to allow fake SctpTransports to be injected +// for testing. Or, theoretically, SctpTransportInternal implementations that +// use something other than usrsctp. +class SctpTransportInternalFactory { + public: + virtual ~SctpTransportInternalFactory() {} + + // Create an SCTP transport using |channel| for the underlying transport. + virtual std::unique_ptr CreateSctpTransport( + TransportChannel* channel) = 0; +}; + +} // namespace cricket + +#endif // WEBRTC_MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_ diff --git a/webrtc/pc/channel.cc b/webrtc/pc/channel.cc index 1f29261ed2..56335e24d0 100644 --- a/webrtc/pc/channel.cc +++ b/webrtc/pc/channel.cc @@ -69,7 +69,6 @@ enum { MSG_READYTOSENDDATA, MSG_DATARECEIVED, MSG_FIRSTPACKETRECEIVED, - MSG_STREAMCLOSEDREMOTELY, }; // Value specified in RFC 5764. @@ -931,16 +930,16 @@ void BaseChannel::ChannelWritable_n() { UpdateMediaSendRecvState(); } -void BaseChannel::SignalDtlsSetupFailure_n(bool rtcp) { +void BaseChannel::SignalDtlsSrtpSetupFailure_n(bool rtcp) { RTC_DCHECK(network_thread_->IsCurrent()); invoker_.AsyncInvoke( RTC_FROM_HERE, signaling_thread(), - Bind(&BaseChannel::SignalDtlsSetupFailure_s, this, rtcp)); + Bind(&BaseChannel::SignalDtlsSrtpSetupFailure_s, this, rtcp)); } -void BaseChannel::SignalDtlsSetupFailure_s(bool rtcp) { +void BaseChannel::SignalDtlsSrtpSetupFailure_s(bool rtcp) { RTC_DCHECK(signaling_thread() == rtc::Thread::Current()); - SignalDtlsSetupFailure(this, rtcp); + SignalDtlsSrtpSetupFailure(this, rtcp); } bool BaseChannel::SetDtlsSrtpCryptoSuites_n(TransportChannel* tc, bool rtcp) { @@ -1061,13 +1060,13 @@ void BaseChannel::MaybeSetupDtlsSrtp_n() { } if (!SetupDtlsSrtp_n(false)) { - SignalDtlsSetupFailure_n(false); + SignalDtlsSrtpSetupFailure_n(false); return; } if (rtcp_transport_channel_) { if (!SetupDtlsSrtp_n(true)) { - SignalDtlsSetupFailure_n(true); + SignalDtlsSrtpSetupFailure_n(true); return; } } @@ -2147,25 +2146,23 @@ void VideoChannel::GetSrtpCryptoSuites_n( GetSupportedVideoCryptoSuites(crypto_options(), crypto_suites); } -DataChannel::DataChannel(rtc::Thread* worker_thread, - rtc::Thread* network_thread, - DataMediaChannel* media_channel, - TransportController* transport_controller, - const std::string& content_name, - bool rtcp, - bool srtp_required) +RtpDataChannel::RtpDataChannel(rtc::Thread* worker_thread, + rtc::Thread* network_thread, + DataMediaChannel* media_channel, + TransportController* transport_controller, + const std::string& content_name, + bool rtcp, + bool srtp_required) : BaseChannel(worker_thread, network_thread, media_channel, transport_controller, content_name, rtcp, - srtp_required), - data_channel_type_(cricket::DCT_NONE), - ready_to_send_data_(false) {} + srtp_required) {} -DataChannel::~DataChannel() { - TRACE_EVENT0("webrtc", "DataChannel::~DataChannel"); +RtpDataChannel::~RtpDataChannel() { + TRACE_EVENT0("webrtc", "RtpDataChannel::~RtpDataChannel"); StopMediaMonitor(); // this can't be done in the base class, since it calls a virtual DisableMedia_w(); @@ -2173,78 +2170,48 @@ DataChannel::~DataChannel() { Deinit(); } -bool DataChannel::Init_w(const std::string* bundle_transport_name) { +bool RtpDataChannel::Init_w(const std::string* bundle_transport_name) { if (!BaseChannel::Init_w(bundle_transport_name)) { return false; } - media_channel()->SignalDataReceived.connect( - this, &DataChannel::OnDataReceived); + media_channel()->SignalDataReceived.connect(this, + &RtpDataChannel::OnDataReceived); media_channel()->SignalReadyToSend.connect( - this, &DataChannel::OnDataChannelReadyToSend); - media_channel()->SignalStreamClosedRemotely.connect( - this, &DataChannel::OnStreamClosedRemotely); + this, &RtpDataChannel::OnDataChannelReadyToSend); return true; } -bool DataChannel::SendData(const SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - SendDataResult* result) { +bool RtpDataChannel::SendData(const SendDataParams& params, + const rtc::CopyOnWriteBuffer& payload, + SendDataResult* result) { return InvokeOnWorker( RTC_FROM_HERE, Bind(&DataMediaChannel::SendData, media_channel(), params, payload, result)); } -const ContentInfo* DataChannel::GetFirstContent( +const ContentInfo* RtpDataChannel::GetFirstContent( const SessionDescription* sdesc) { return GetFirstDataContent(sdesc); } -bool DataChannel::WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet) { - if (data_channel_type_ == DCT_SCTP) { - // TODO(pthatcher): Do this in a more robust way by checking for - // SCTP or DTLS. - return !IsRtpPacket(packet->data(), packet->size()); - } else if (data_channel_type_ == DCT_RTP) { - return BaseChannel::WantsPacket(rtcp, packet); - } - return false; -} - -bool DataChannel::SetDataChannelType(DataChannelType new_data_channel_type, - std::string* error_desc) { - // It hasn't been set before, so set it now. - if (data_channel_type_ == DCT_NONE) { - data_channel_type_ = new_data_channel_type; - return true; - } - - // It's been set before, but doesn't match. That's bad. - if (data_channel_type_ != new_data_channel_type) { - std::ostringstream desc; - desc << "Data channel type mismatch." - << " Expected " << data_channel_type_ - << " Got " << new_data_channel_type; - SafeSetError(desc.str(), error_desc); - return false; - } - - // It's hasn't changed. Nothing to do. - return true; -} - -bool DataChannel::SetDataChannelTypeFromContent( +bool RtpDataChannel::CheckDataChannelTypeFromContent( const DataContentDescription* content, std::string* error_desc) { bool is_sctp = ((content->protocol() == kMediaProtocolSctp) || (content->protocol() == kMediaProtocolDtlsSctp)); - DataChannelType data_channel_type = is_sctp ? DCT_SCTP : DCT_RTP; - return SetDataChannelType(data_channel_type, error_desc); + // It's been set before, but doesn't match. That's bad. + if (is_sctp) { + SafeSetError("Data channel type mismatch. Expected RTP, got SCTP.", + error_desc); + return false; + } + return true; } -bool DataChannel::SetLocalContent_w(const MediaContentDescription* content, - ContentAction action, - std::string* error_desc) { - TRACE_EVENT0("webrtc", "DataChannel::SetLocalContent_w"); +bool RtpDataChannel::SetLocalContent_w(const MediaContentDescription* content, + ContentAction action, + std::string* error_desc) { + TRACE_EVENT0("webrtc", "RtpDataChannel::SetLocalContent_w"); RTC_DCHECK(worker_thread() == rtc::Thread::Current()); LOG(LS_INFO) << "Setting local data description"; @@ -2256,19 +2223,14 @@ bool DataChannel::SetLocalContent_w(const MediaContentDescription* content, return false; } - if (!SetDataChannelTypeFromContent(data, error_desc)) { + if (!CheckDataChannelTypeFromContent(data, error_desc)) { return false; } - if (data_channel_type_ == DCT_RTP) { - if (!SetRtpTransportParameters(content, action, CS_LOCAL, error_desc)) { - return false; - } + if (!SetRtpTransportParameters(content, action, CS_LOCAL, error_desc)) { + return false; } - // FYI: We send the SCTP port number (not to be confused with the - // underlying UDP port number) as a codec parameter. So even SCTP - // data channels need codecs. DataRecvParameters recv_params = last_recv_params_; RtpParametersFromMediaDescription(data, &recv_params); if (!media_channel()->SetRecvParameters(recv_params)) { @@ -2276,10 +2238,8 @@ bool DataChannel::SetLocalContent_w(const MediaContentDescription* content, error_desc); return false; } - if (data_channel_type_ == DCT_RTP) { - for (const DataCodec& codec : data->codecs()) { - bundle_filter()->AddPayloadType(codec.id); - } + for (const DataCodec& codec : data->codecs()) { + bundle_filter()->AddPayloadType(codec.id); } last_recv_params_ = recv_params; @@ -2297,10 +2257,10 @@ bool DataChannel::SetLocalContent_w(const MediaContentDescription* content, return true; } -bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action, - std::string* error_desc) { - TRACE_EVENT0("webrtc", "DataChannel::SetRemoteContent_w"); +bool RtpDataChannel::SetRemoteContent_w(const MediaContentDescription* content, + ContentAction action, + std::string* error_desc) { + TRACE_EVENT0("webrtc", "RtpDataChannel::SetRemoteContent_w"); RTC_DCHECK(worker_thread() == rtc::Thread::Current()); const DataContentDescription* data = @@ -2317,17 +2277,15 @@ bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content, return true; } - if (!SetDataChannelTypeFromContent(data, error_desc)) { + if (!CheckDataChannelTypeFromContent(data, error_desc)) { return false; } LOG(LS_INFO) << "Setting remote data description"; - if (data_channel_type_ == DCT_RTP && - !SetRtpTransportParameters(content, action, CS_REMOTE, error_desc)) { + if (!SetRtpTransportParameters(content, action, CS_REMOTE, error_desc)) { return false; } - DataSendParameters send_params = last_send_params_; RtpSendParametersFromMediaDescription(data, &send_params); if (!media_channel()->SetSendParameters(send_params)) { @@ -2352,7 +2310,7 @@ bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content, return true; } -void DataChannel::UpdateMediaSendRecvState_w() { +void RtpDataChannel::UpdateMediaSendRecvState_w() { // Render incoming data if we're the active call, and we have the local // content. We receive data on the default channel and multiplexed streams. bool recv = IsReadyToReceiveMedia_w(); @@ -2373,7 +2331,7 @@ void DataChannel::UpdateMediaSendRecvState_w() { LOG(LS_INFO) << "Changing data state, recv=" << recv << " send=" << send; } -void DataChannel::OnMessage(rtc::Message *pmsg) { +void RtpDataChannel::OnMessage(rtc::Message* pmsg) { switch (pmsg->message_id) { case MSG_READYTOSENDDATA: { DataChannelReadyToSendMessageData* data = @@ -2386,7 +2344,7 @@ void DataChannel::OnMessage(rtc::Message *pmsg) { case MSG_DATARECEIVED: { DataReceivedMessageData* data = static_cast(pmsg->pdata); - SignalDataReceived(this, data->params, data->payload); + SignalDataReceived(data->params, data->payload); delete data; break; } @@ -2396,33 +2354,27 @@ void DataChannel::OnMessage(rtc::Message *pmsg) { delete data; break; } - case MSG_STREAMCLOSEDREMOTELY: { - rtc::TypedMessageData* data = - static_cast*>(pmsg->pdata); - SignalStreamClosedRemotely(data->data()); - delete data; - break; - } default: BaseChannel::OnMessage(pmsg); break; } } -void DataChannel::OnConnectionMonitorUpdate( - ConnectionMonitor* monitor, const std::vector& infos) { +void RtpDataChannel::OnConnectionMonitorUpdate( + ConnectionMonitor* monitor, + const std::vector& infos) { SignalConnectionMonitor(this, infos); } -void DataChannel::StartMediaMonitor(int cms) { +void RtpDataChannel::StartMediaMonitor(int cms) { media_monitor_.reset(new DataMediaMonitor(media_channel(), worker_thread(), rtc::Thread::Current())); - media_monitor_->SignalUpdate.connect( - this, &DataChannel::OnMediaMonitorUpdate); + media_monitor_->SignalUpdate.connect(this, + &RtpDataChannel::OnMediaMonitorUpdate); media_monitor_->Start(cms); } -void DataChannel::StopMediaMonitor() { +void RtpDataChannel::StopMediaMonitor() { if (media_monitor_) { media_monitor_->Stop(); media_monitor_->SignalUpdate.disconnect(this); @@ -2430,27 +2382,28 @@ void DataChannel::StopMediaMonitor() { } } -void DataChannel::OnMediaMonitorUpdate( - DataMediaChannel* media_channel, const DataMediaInfo& info) { +void RtpDataChannel::OnMediaMonitorUpdate(DataMediaChannel* media_channel, + const DataMediaInfo& info) { RTC_DCHECK(media_channel == this->media_channel()); SignalMediaMonitor(this, info); } -void DataChannel::OnDataReceived( - const ReceiveDataParams& params, const char* data, size_t len) { +void RtpDataChannel::OnDataReceived(const ReceiveDataParams& params, + const char* data, + size_t len) { DataReceivedMessageData* msg = new DataReceivedMessageData( params, data, len); signaling_thread()->Post(RTC_FROM_HERE, this, MSG_DATARECEIVED, msg); } -void DataChannel::OnDataChannelError(uint32_t ssrc, - DataMediaChannel::Error err) { +void RtpDataChannel::OnDataChannelError(uint32_t ssrc, + DataMediaChannel::Error err) { DataChannelErrorMessageData* data = new DataChannelErrorMessageData( ssrc, err); signaling_thread()->Post(RTC_FROM_HERE, this, MSG_CHANNEL_ERROR, data); } -void DataChannel::OnDataChannelReadyToSend(bool writable) { +void RtpDataChannel::OnDataChannelReadyToSend(bool writable) { // This is usded for congestion control to indicate that the stream is ready // to send by the MediaChannel, as opposed to OnReadyToSend, which indicates // that the transport channel is ready. @@ -2458,19 +2411,9 @@ void DataChannel::OnDataChannelReadyToSend(bool writable) { new DataChannelReadyToSendMessageData(writable)); } -void DataChannel::GetSrtpCryptoSuites_n(std::vector* crypto_suites) const { +void RtpDataChannel::GetSrtpCryptoSuites_n( + std::vector* crypto_suites) const { GetSupportedDataCryptoSuites(crypto_options(), crypto_suites); } -bool DataChannel::ShouldSetupDtlsSrtp_n() const { - return data_channel_type_ == DCT_RTP && BaseChannel::ShouldSetupDtlsSrtp_n(); -} - -void DataChannel::OnStreamClosedRemotely(uint32_t sid) { - rtc::TypedMessageData* message = - new rtc::TypedMessageData(sid); - signaling_thread()->Post(RTC_FROM_HERE, this, MSG_STREAMCLOSEDREMOTELY, - message); -} - } // namespace cricket diff --git a/webrtc/pc/channel.h b/webrtc/pc/channel.h index 64c5f8253f..ff98a2fb27 100644 --- a/webrtc/pc/channel.h +++ b/webrtc/pc/channel.h @@ -149,9 +149,9 @@ class BaseChannel return remote_streams_; } - sigslot::signal2 SignalDtlsSetupFailure; - void SignalDtlsSetupFailure_n(bool rtcp); - void SignalDtlsSetupFailure_s(bool rtcp); + sigslot::signal2 SignalDtlsSrtpSetupFailure; + void SignalDtlsSrtpSetupFailure_n(bool rtcp); + void SignalDtlsSrtpSetupFailure_s(bool rtcp); // Used for latency measurements. sigslot::signal1 SignalFirstPacketReceived; @@ -261,7 +261,7 @@ class BaseChannel rtc::CopyOnWriteBuffer* packet, const rtc::PacketOptions& options); - virtual bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet); + bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet); void HandlePacket(bool rtcp, rtc::CopyOnWriteBuffer* packet, const rtc::PacketTime& packet_time); void OnPacketReceived(bool rtcp, @@ -282,7 +282,7 @@ class BaseChannel bool RemoveRecvStream_w(uint32_t ssrc); bool AddSendStream_w(const StreamParams& sp); bool RemoveSendStream_w(uint32_t ssrc); - virtual bool ShouldSetupDtlsSrtp_n() const; + bool ShouldSetupDtlsSrtp_n() const; // Do the DTLS key expansion and impose it on the SRTP/SRTCP filters. // |rtcp_channel| indicates whether to set up the RTP or RTCP filter. bool SetupDtlsSrtp_n(bool rtcp_channel); @@ -615,17 +615,17 @@ class VideoChannel : public BaseChannel { VideoRecvParameters last_recv_params_; }; -// DataChannel is a specialization for data. -class DataChannel : public BaseChannel { +// RtpDataChannel is a specialization for data. +class RtpDataChannel : public BaseChannel { public: - DataChannel(rtc::Thread* worker_thread, - rtc::Thread* network_thread, - DataMediaChannel* media_channel, - TransportController* transport_controller, - const std::string& content_name, - bool rtcp, - bool srtp_required); - ~DataChannel(); + RtpDataChannel(rtc::Thread* worker_thread, + rtc::Thread* network_thread, + DataMediaChannel* media_channel, + TransportController* transport_controller, + const std::string& content_name, + bool rtcp, + bool srtp_required); + ~RtpDataChannel(); bool Init_w(const std::string* bundle_transport_name); virtual bool SendData(const SendDataParams& params, @@ -640,17 +640,16 @@ class DataChannel : public BaseChannel { return ready_to_send_data_; } - sigslot::signal2 SignalMediaMonitor; - sigslot::signal2&> + sigslot::signal2 SignalMediaMonitor; + sigslot::signal2&> SignalConnectionMonitor; - sigslot::signal3 SignalDataReceived; + + sigslot::signal2 + SignalDataReceived; // Signal for notifying when the channel becomes ready to send data. // That occurs when the channel is enabled, the transport is writable, // both local and remote descriptions are set, and the channel is unblocked. sigslot::signal1 SignalReadyToSendData; - // Signal for notifying that the remote side has closed the DataChannel. - sigslot::signal1 SignalStreamClosedRemotely; cricket::MediaType media_type() override { return cricket::MEDIA_TYPE_DATA; } protected: @@ -693,15 +692,9 @@ class DataChannel : public BaseChannel { // overrides from BaseChannel const ContentInfo* GetFirstContent(const SessionDescription* sdesc) override; - // If data_channel_type_ is DCT_NONE, set it. Otherwise, check that - // it's the same as what was set previously. Returns false if it's - // set to one type one type and changed to another type later. - bool SetDataChannelType(DataChannelType new_data_channel_type, - std::string* error_desc); - // Same as SetDataChannelType, but extracts the type from the - // DataContentDescription. - bool SetDataChannelTypeFromContent(const DataContentDescription* content, - std::string* error_desc); + // Checks that data channel type is RTP. + bool CheckDataChannelTypeFromContent(const DataContentDescription* content, + std::string* error_desc); bool SetLocalContent_w(const MediaContentDescription* content, ContentAction action, std::string* error_desc) override; @@ -709,7 +702,6 @@ class DataChannel : public BaseChannel { ContentAction action, std::string* error_desc) override; void UpdateMediaSendRecvState_w() override; - bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet) override; void OnMessage(rtc::Message* pmsg) override; void GetSrtpCryptoSuites_n(std::vector* crypto_suites) const override; @@ -718,18 +710,13 @@ class DataChannel : public BaseChannel { const std::vector& infos) override; void OnMediaMonitorUpdate(DataMediaChannel* media_channel, const DataMediaInfo& info); - bool ShouldSetupDtlsSrtp_n() const override; void OnDataReceived( const ReceiveDataParams& params, const char* data, size_t len); void OnDataChannelError(uint32_t ssrc, DataMediaChannel::Error error); void OnDataChannelReadyToSend(bool writable); - void OnStreamClosedRemotely(uint32_t sid); std::unique_ptr media_monitor_; - // TODO(pthatcher): Make a separate SctpDataChannel and - // RtpDataChannel instead of using this. - DataChannelType data_channel_type_; - bool ready_to_send_data_; + bool ready_to_send_data_ = false; // Last DataSendParameters sent down to the media_channel() via // SetSendParameters. diff --git a/webrtc/pc/channel_unittest.cc b/webrtc/pc/channel_unittest.cc index 5de3c94555..589ebd54d1 100644 --- a/webrtc/pc/channel_unittest.cc +++ b/webrtc/pc/channel_unittest.cc @@ -84,14 +84,14 @@ class VideoTraits : public Traits {}; -class DataTraits : public Traits {}; -// Base class for Voice/Video/DataChannel tests +// Base class for Voice/Video/RtpDataChannel tests template class ChannelTest : public testing::Test, public sigslot::has_slots<> { public: @@ -3288,32 +3288,32 @@ TEST_F(VideoChannelDoubleThreadTest, CanChangeMaxBitrate) { Base::CanChangeMaxBitrate(); } -// DataChannelSingleThreadTest -class DataChannelSingleThreadTest : public ChannelTest { +// RtpDataChannelSingleThreadTest +class RtpDataChannelSingleThreadTest : public ChannelTest { public: typedef ChannelTest Base; - DataChannelSingleThreadTest() + RtpDataChannelSingleThreadTest() : Base(true, kDataPacket, kRtcpReport, NetworkIsWorker::Yes) {} }; -// DataChannelDoubleThreadTest -class DataChannelDoubleThreadTest : public ChannelTest { +// RtpDataChannelDoubleThreadTest +class RtpDataChannelDoubleThreadTest : public ChannelTest { public: typedef ChannelTest Base; - DataChannelDoubleThreadTest() + RtpDataChannelDoubleThreadTest() : Base(true, kDataPacket, kRtcpReport, NetworkIsWorker::No) {} }; // Override to avoid engine channel parameter. template <> -cricket::DataChannel* ChannelTest::CreateChannel( +cricket::RtpDataChannel* ChannelTest::CreateChannel( rtc::Thread* worker_thread, rtc::Thread* network_thread, cricket::MediaEngineInterface* engine, cricket::FakeDataMediaChannel* ch, cricket::TransportController* transport_controller, int flags) { - cricket::DataChannel* channel = new cricket::DataChannel( + cricket::RtpDataChannel* channel = new cricket::RtpDataChannel( worker_thread, network_thread, ch, transport_controller, cricket::CN_DATA, (flags & RTCP) != 0, (flags & SECURE) != 0); rtc::CryptoOptions crypto_options; @@ -3362,136 +3362,136 @@ void ChannelTest::AddLegacyStreamInContent( data->AddLegacyStream(ssrc); } -TEST_F(DataChannelSingleThreadTest, TestInit) { +TEST_F(RtpDataChannelSingleThreadTest, TestInit) { Base::TestInit(); EXPECT_FALSE(media_channel1_->IsStreamMuted(0)); } -TEST_F(DataChannelSingleThreadTest, TestDeinit) { +TEST_F(RtpDataChannelSingleThreadTest, TestDeinit) { Base::TestDeinit(); } -TEST_F(DataChannelSingleThreadTest, TestSetContents) { +TEST_F(RtpDataChannelSingleThreadTest, TestSetContents) { Base::TestSetContents(); } -TEST_F(DataChannelSingleThreadTest, TestSetContentsNullOffer) { +TEST_F(RtpDataChannelSingleThreadTest, TestSetContentsNullOffer) { Base::TestSetContentsNullOffer(); } -TEST_F(DataChannelSingleThreadTest, TestSetContentsRtcpMux) { +TEST_F(RtpDataChannelSingleThreadTest, TestSetContentsRtcpMux) { Base::TestSetContentsRtcpMux(); } -TEST_F(DataChannelSingleThreadTest, TestSetRemoteContentUpdate) { +TEST_F(RtpDataChannelSingleThreadTest, TestSetRemoteContentUpdate) { Base::TestSetRemoteContentUpdate(); } -TEST_F(DataChannelSingleThreadTest, TestStreams) { +TEST_F(RtpDataChannelSingleThreadTest, TestStreams) { Base::TestStreams(); } -TEST_F(DataChannelSingleThreadTest, TestUpdateStreamsInLocalContent) { +TEST_F(RtpDataChannelSingleThreadTest, TestUpdateStreamsInLocalContent) { Base::TestUpdateStreamsInLocalContent(); } -TEST_F(DataChannelSingleThreadTest, TestUpdateRemoteStreamsInContent) { +TEST_F(RtpDataChannelSingleThreadTest, TestUpdateRemoteStreamsInContent) { Base::TestUpdateStreamsInRemoteContent(); } -TEST_F(DataChannelSingleThreadTest, TestChangeStreamParamsInContent) { +TEST_F(RtpDataChannelSingleThreadTest, TestChangeStreamParamsInContent) { Base::TestChangeStreamParamsInContent(); } -TEST_F(DataChannelSingleThreadTest, TestPlayoutAndSendingStates) { +TEST_F(RtpDataChannelSingleThreadTest, TestPlayoutAndSendingStates) { Base::TestPlayoutAndSendingStates(); } -TEST_F(DataChannelSingleThreadTest, TestMediaContentDirection) { +TEST_F(RtpDataChannelSingleThreadTest, TestMediaContentDirection) { Base::TestMediaContentDirection(); } -TEST_F(DataChannelSingleThreadTest, TestCallSetup) { +TEST_F(RtpDataChannelSingleThreadTest, TestCallSetup) { Base::TestCallSetup(); } -TEST_F(DataChannelSingleThreadTest, TestCallTeardownRtcpMux) { +TEST_F(RtpDataChannelSingleThreadTest, TestCallTeardownRtcpMux) { Base::TestCallTeardownRtcpMux(); } -TEST_F(DataChannelSingleThreadTest, TestOnReadyToSend) { +TEST_F(RtpDataChannelSingleThreadTest, TestOnReadyToSend) { Base::TestOnReadyToSend(); } -TEST_F(DataChannelSingleThreadTest, TestOnReadyToSendWithRtcpMux) { +TEST_F(RtpDataChannelSingleThreadTest, TestOnReadyToSendWithRtcpMux) { Base::TestOnReadyToSendWithRtcpMux(); } -TEST_F(DataChannelSingleThreadTest, SendRtpToRtp) { +TEST_F(RtpDataChannelSingleThreadTest, SendRtpToRtp) { Base::SendRtpToRtp(); } -TEST_F(DataChannelSingleThreadTest, SendNoRtcpToNoRtcp) { +TEST_F(RtpDataChannelSingleThreadTest, SendNoRtcpToNoRtcp) { Base::SendNoRtcpToNoRtcp(); } -TEST_F(DataChannelSingleThreadTest, SendNoRtcpToRtcp) { +TEST_F(RtpDataChannelSingleThreadTest, SendNoRtcpToRtcp) { Base::SendNoRtcpToRtcp(); } -TEST_F(DataChannelSingleThreadTest, SendRtcpToNoRtcp) { +TEST_F(RtpDataChannelSingleThreadTest, SendRtcpToNoRtcp) { Base::SendRtcpToNoRtcp(); } -TEST_F(DataChannelSingleThreadTest, SendRtcpToRtcp) { +TEST_F(RtpDataChannelSingleThreadTest, SendRtcpToRtcp) { Base::SendRtcpToRtcp(); } -TEST_F(DataChannelSingleThreadTest, SendRtcpMuxToRtcp) { +TEST_F(RtpDataChannelSingleThreadTest, SendRtcpMuxToRtcp) { Base::SendRtcpMuxToRtcp(); } -TEST_F(DataChannelSingleThreadTest, SendRtcpMuxToRtcpMux) { +TEST_F(RtpDataChannelSingleThreadTest, SendRtcpMuxToRtcpMux) { Base::SendRtcpMuxToRtcpMux(); } -TEST_F(DataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcp) { +TEST_F(RtpDataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcp) { Base::SendEarlyRtcpMuxToRtcp(); } -TEST_F(DataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcpMux) { +TEST_F(RtpDataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcpMux) { Base::SendEarlyRtcpMuxToRtcpMux(); } -TEST_F(DataChannelSingleThreadTest, SendSrtpToSrtp) { +TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToSrtp) { Base::SendSrtpToSrtp(); } -TEST_F(DataChannelSingleThreadTest, SendSrtpToRtp) { +TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToRtp) { Base::SendSrtpToSrtp(); } -TEST_F(DataChannelSingleThreadTest, SendSrtcpMux) { +TEST_F(RtpDataChannelSingleThreadTest, SendSrtcpMux) { Base::SendSrtpToSrtp(RTCP_MUX, RTCP_MUX); } -TEST_F(DataChannelSingleThreadTest, SendRtpToRtpOnThread) { +TEST_F(RtpDataChannelSingleThreadTest, SendRtpToRtpOnThread) { Base::SendRtpToRtpOnThread(); } -TEST_F(DataChannelSingleThreadTest, SendSrtpToSrtpOnThread) { +TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToSrtpOnThread) { Base::SendSrtpToSrtpOnThread(); } -TEST_F(DataChannelSingleThreadTest, SendWithWritabilityLoss) { +TEST_F(RtpDataChannelSingleThreadTest, SendWithWritabilityLoss) { Base::SendWithWritabilityLoss(); } -TEST_F(DataChannelSingleThreadTest, TestMediaMonitor) { +TEST_F(RtpDataChannelSingleThreadTest, TestMediaMonitor) { Base::TestMediaMonitor(); } -TEST_F(DataChannelSingleThreadTest, TestSendData) { +TEST_F(RtpDataChannelSingleThreadTest, TestSendData) { CreateChannels(0, 0); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); @@ -3506,136 +3506,136 @@ TEST_F(DataChannelSingleThreadTest, TestSendData) { EXPECT_EQ("foo", media_channel1_->last_sent_data()); } -TEST_F(DataChannelDoubleThreadTest, TestInit) { +TEST_F(RtpDataChannelDoubleThreadTest, TestInit) { Base::TestInit(); EXPECT_FALSE(media_channel1_->IsStreamMuted(0)); } -TEST_F(DataChannelDoubleThreadTest, TestDeinit) { +TEST_F(RtpDataChannelDoubleThreadTest, TestDeinit) { Base::TestDeinit(); } -TEST_F(DataChannelDoubleThreadTest, TestSetContents) { +TEST_F(RtpDataChannelDoubleThreadTest, TestSetContents) { Base::TestSetContents(); } -TEST_F(DataChannelDoubleThreadTest, TestSetContentsNullOffer) { +TEST_F(RtpDataChannelDoubleThreadTest, TestSetContentsNullOffer) { Base::TestSetContentsNullOffer(); } -TEST_F(DataChannelDoubleThreadTest, TestSetContentsRtcpMux) { +TEST_F(RtpDataChannelDoubleThreadTest, TestSetContentsRtcpMux) { Base::TestSetContentsRtcpMux(); } -TEST_F(DataChannelDoubleThreadTest, TestSetRemoteContentUpdate) { +TEST_F(RtpDataChannelDoubleThreadTest, TestSetRemoteContentUpdate) { Base::TestSetRemoteContentUpdate(); } -TEST_F(DataChannelDoubleThreadTest, TestStreams) { +TEST_F(RtpDataChannelDoubleThreadTest, TestStreams) { Base::TestStreams(); } -TEST_F(DataChannelDoubleThreadTest, TestUpdateStreamsInLocalContent) { +TEST_F(RtpDataChannelDoubleThreadTest, TestUpdateStreamsInLocalContent) { Base::TestUpdateStreamsInLocalContent(); } -TEST_F(DataChannelDoubleThreadTest, TestUpdateRemoteStreamsInContent) { +TEST_F(RtpDataChannelDoubleThreadTest, TestUpdateRemoteStreamsInContent) { Base::TestUpdateStreamsInRemoteContent(); } -TEST_F(DataChannelDoubleThreadTest, TestChangeStreamParamsInContent) { +TEST_F(RtpDataChannelDoubleThreadTest, TestChangeStreamParamsInContent) { Base::TestChangeStreamParamsInContent(); } -TEST_F(DataChannelDoubleThreadTest, TestPlayoutAndSendingStates) { +TEST_F(RtpDataChannelDoubleThreadTest, TestPlayoutAndSendingStates) { Base::TestPlayoutAndSendingStates(); } -TEST_F(DataChannelDoubleThreadTest, TestMediaContentDirection) { +TEST_F(RtpDataChannelDoubleThreadTest, TestMediaContentDirection) { Base::TestMediaContentDirection(); } -TEST_F(DataChannelDoubleThreadTest, TestCallSetup) { +TEST_F(RtpDataChannelDoubleThreadTest, TestCallSetup) { Base::TestCallSetup(); } -TEST_F(DataChannelDoubleThreadTest, TestCallTeardownRtcpMux) { +TEST_F(RtpDataChannelDoubleThreadTest, TestCallTeardownRtcpMux) { Base::TestCallTeardownRtcpMux(); } -TEST_F(DataChannelDoubleThreadTest, TestOnReadyToSend) { +TEST_F(RtpDataChannelDoubleThreadTest, TestOnReadyToSend) { Base::TestOnReadyToSend(); } -TEST_F(DataChannelDoubleThreadTest, TestOnReadyToSendWithRtcpMux) { +TEST_F(RtpDataChannelDoubleThreadTest, TestOnReadyToSendWithRtcpMux) { Base::TestOnReadyToSendWithRtcpMux(); } -TEST_F(DataChannelDoubleThreadTest, SendRtpToRtp) { +TEST_F(RtpDataChannelDoubleThreadTest, SendRtpToRtp) { Base::SendRtpToRtp(); } -TEST_F(DataChannelDoubleThreadTest, SendNoRtcpToNoRtcp) { +TEST_F(RtpDataChannelDoubleThreadTest, SendNoRtcpToNoRtcp) { Base::SendNoRtcpToNoRtcp(); } -TEST_F(DataChannelDoubleThreadTest, SendNoRtcpToRtcp) { +TEST_F(RtpDataChannelDoubleThreadTest, SendNoRtcpToRtcp) { Base::SendNoRtcpToRtcp(); } -TEST_F(DataChannelDoubleThreadTest, SendRtcpToNoRtcp) { +TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpToNoRtcp) { Base::SendRtcpToNoRtcp(); } -TEST_F(DataChannelDoubleThreadTest, SendRtcpToRtcp) { +TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpToRtcp) { Base::SendRtcpToRtcp(); } -TEST_F(DataChannelDoubleThreadTest, SendRtcpMuxToRtcp) { +TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpMuxToRtcp) { Base::SendRtcpMuxToRtcp(); } -TEST_F(DataChannelDoubleThreadTest, SendRtcpMuxToRtcpMux) { +TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpMuxToRtcpMux) { Base::SendRtcpMuxToRtcpMux(); } -TEST_F(DataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcp) { +TEST_F(RtpDataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcp) { Base::SendEarlyRtcpMuxToRtcp(); } -TEST_F(DataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcpMux) { +TEST_F(RtpDataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcpMux) { Base::SendEarlyRtcpMuxToRtcpMux(); } -TEST_F(DataChannelDoubleThreadTest, SendSrtpToSrtp) { +TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToSrtp) { Base::SendSrtpToSrtp(); } -TEST_F(DataChannelDoubleThreadTest, SendSrtpToRtp) { +TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToRtp) { Base::SendSrtpToSrtp(); } -TEST_F(DataChannelDoubleThreadTest, SendSrtcpMux) { +TEST_F(RtpDataChannelDoubleThreadTest, SendSrtcpMux) { Base::SendSrtpToSrtp(RTCP_MUX, RTCP_MUX); } -TEST_F(DataChannelDoubleThreadTest, SendRtpToRtpOnThread) { +TEST_F(RtpDataChannelDoubleThreadTest, SendRtpToRtpOnThread) { Base::SendRtpToRtpOnThread(); } -TEST_F(DataChannelDoubleThreadTest, SendSrtpToSrtpOnThread) { +TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToSrtpOnThread) { Base::SendSrtpToSrtpOnThread(); } -TEST_F(DataChannelDoubleThreadTest, SendWithWritabilityLoss) { +TEST_F(RtpDataChannelDoubleThreadTest, SendWithWritabilityLoss) { Base::SendWithWritabilityLoss(); } -TEST_F(DataChannelDoubleThreadTest, TestMediaMonitor) { +TEST_F(RtpDataChannelDoubleThreadTest, TestMediaMonitor) { Base::TestMediaMonitor(); } -TEST_F(DataChannelDoubleThreadTest, TestSendData) { +TEST_F(RtpDataChannelDoubleThreadTest, TestSendData) { CreateChannels(0, 0); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); diff --git a/webrtc/pc/channelmanager.cc b/webrtc/pc/channelmanager.cc index a0b3609ecf..cdfed68bb6 100644 --- a/webrtc/pc/channelmanager.cc +++ b/webrtc/pc/channelmanager.cc @@ -20,11 +20,7 @@ #include "webrtc/base/stringutils.h" #include "webrtc/base/trace_event.h" #include "webrtc/media/base/device.h" -#include "webrtc/media/base/hybriddataengine.h" #include "webrtc/media/base/rtpdataengine.h" -#ifdef HAVE_SCTP -#include "webrtc/media/sctp/sctpdataengine.h" -#endif #include "webrtc/pc/srtpfilter.h" namespace cricket { @@ -33,11 +29,7 @@ namespace cricket { using rtc::Bind; static DataEngineInterface* ConstructDataEngine() { -#ifdef HAVE_SCTP - return new HybridDataEngine(new RtpDataEngine(), new SctpDataEngine()); -#else return new RtpDataEngine(); -#endif } ChannelManager::ChannelManager(MediaEngineInterface* me, @@ -344,73 +336,66 @@ void ChannelManager::DestroyVideoChannel_w(VideoChannel* video_channel) { delete video_channel; } -DataChannel* ChannelManager::CreateDataChannel( +RtpDataChannel* ChannelManager::CreateRtpDataChannel( webrtc::MediaControllerInterface* media_controller, TransportController* transport_controller, const std::string& content_name, const std::string* bundle_transport_name, bool rtcp, - bool srtp_required, - DataChannelType channel_type) { - return worker_thread_->Invoke( - RTC_FROM_HERE, - Bind(&ChannelManager::CreateDataChannel_w, this, media_controller, - transport_controller, content_name, bundle_transport_name, rtcp, - srtp_required, channel_type)); + bool srtp_required) { + return worker_thread_->Invoke( + RTC_FROM_HERE, Bind(&ChannelManager::CreateRtpDataChannel_w, this, + media_controller, transport_controller, content_name, + bundle_transport_name, rtcp, srtp_required)); } -DataChannel* ChannelManager::CreateDataChannel_w( +RtpDataChannel* ChannelManager::CreateRtpDataChannel_w( webrtc::MediaControllerInterface* media_controller, TransportController* transport_controller, const std::string& content_name, const std::string* bundle_transport_name, bool rtcp, - bool srtp_required, - DataChannelType data_channel_type) { + bool srtp_required) { // This is ok to alloc from a thread other than the worker thread. ASSERT(initialized_); MediaConfig config; if (media_controller) { config = media_controller->config(); } - DataMediaChannel* media_channel = - data_media_engine_->CreateChannel(data_channel_type, config); + DataMediaChannel* media_channel = data_media_engine_->CreateChannel(config); if (!media_channel) { - LOG(LS_WARNING) << "Failed to create data channel of type " - << data_channel_type; - return NULL; + LOG(LS_WARNING) << "Failed to create RTP data channel."; + return nullptr; } - // Only RTP data channels need SRTP. - srtp_required = srtp_required && data_channel_type == DCT_RTP; - DataChannel* data_channel = - new DataChannel(worker_thread_, network_thread_, media_channel, - transport_controller, content_name, rtcp, srtp_required); + RtpDataChannel* data_channel = new RtpDataChannel( + worker_thread_, network_thread_, media_channel, transport_controller, + content_name, rtcp, srtp_required); data_channel->SetCryptoOptions(crypto_options_); if (!data_channel->Init_w(bundle_transport_name)) { LOG(LS_WARNING) << "Failed to init data channel."; delete data_channel; - return NULL; + return nullptr; } data_channels_.push_back(data_channel); return data_channel; } -void ChannelManager::DestroyDataChannel(DataChannel* data_channel) { - TRACE_EVENT0("webrtc", "ChannelManager::DestroyDataChannel"); +void ChannelManager::DestroyRtpDataChannel(RtpDataChannel* data_channel) { + TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel"); if (data_channel) { worker_thread_->Invoke( RTC_FROM_HERE, - Bind(&ChannelManager::DestroyDataChannel_w, this, data_channel)); + Bind(&ChannelManager::DestroyRtpDataChannel_w, this, data_channel)); } } -void ChannelManager::DestroyDataChannel_w(DataChannel* data_channel) { - TRACE_EVENT0("webrtc", "ChannelManager::DestroyDataChannel_w"); +void ChannelManager::DestroyRtpDataChannel_w(RtpDataChannel* data_channel) { + TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel_w"); // Destroy data channel. ASSERT(initialized_); - DataChannels::iterator it = std::find(data_channels_.begin(), - data_channels_.end(), data_channel); + RtpDataChannels::iterator it = + std::find(data_channels_.begin(), data_channels_.end(), data_channel); ASSERT(it != data_channels_.end()); if (it == data_channels_.end()) return; diff --git a/webrtc/pc/channelmanager.h b/webrtc/pc/channelmanager.h index 7df49bb07d..3022eca8aa 100644 --- a/webrtc/pc/channelmanager.h +++ b/webrtc/pc/channelmanager.h @@ -110,16 +110,15 @@ class ChannelManager { const VideoOptions& options); // Destroys a video channel created with the Create API. void DestroyVideoChannel(VideoChannel* video_channel); - DataChannel* CreateDataChannel( + RtpDataChannel* CreateRtpDataChannel( webrtc::MediaControllerInterface* media_controller, TransportController* transport_controller, const std::string& content_name, const std::string* bundle_transport_name, bool rtcp, - bool srtp_required, - DataChannelType data_channel_type); + bool srtp_required); // Destroys a data channel created with the Create API. - void DestroyDataChannel(DataChannel* data_channel); + void DestroyRtpDataChannel(RtpDataChannel* data_channel); // Indicates whether any channels exist. bool has_channels() const { @@ -150,7 +149,7 @@ class ChannelManager { private: typedef std::vector VoiceChannels; typedef std::vector VideoChannels; - typedef std::vector DataChannels; + typedef std::vector RtpDataChannels; void Construct(MediaEngineInterface* me, DataEngineInterface* dme, @@ -178,15 +177,14 @@ class ChannelManager { bool srtp_required, const VideoOptions& options); void DestroyVideoChannel_w(VideoChannel* video_channel); - DataChannel* CreateDataChannel_w( + RtpDataChannel* CreateRtpDataChannel_w( webrtc::MediaControllerInterface* media_controller, TransportController* transport_controller, const std::string& content_name, const std::string* bundle_transport_name, bool rtcp, - bool srtp_required, - DataChannelType data_channel_type); - void DestroyDataChannel_w(DataChannel* data_channel); + bool srtp_required); + void DestroyRtpDataChannel_w(RtpDataChannel* data_channel); std::unique_ptr media_engine_; std::unique_ptr data_media_engine_; @@ -197,7 +195,7 @@ class ChannelManager { VoiceChannels voice_channels_; VideoChannels video_channels_; - DataChannels data_channels_; + RtpDataChannels data_channels_; bool enable_rtx_; rtc::CryptoOptions crypto_options_; diff --git a/webrtc/pc/channelmanager_unittest.cc b/webrtc/pc/channelmanager_unittest.cc index df3e9e5ba3..30d2fe5879 100644 --- a/webrtc/pc/channelmanager_unittest.cc +++ b/webrtc/pc/channelmanager_unittest.cc @@ -110,13 +110,13 @@ TEST_F(ChannelManagerTest, CreateDestroyChannels) { &fake_mc_, transport_controller_, cricket::CN_VIDEO, nullptr, kDefaultRtcpEnabled, kDefaultSrtpRequired, VideoOptions()); EXPECT_TRUE(video_channel != nullptr); - cricket::DataChannel* data_channel = cm_->CreateDataChannel( + cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel( &fake_mc_, transport_controller_, cricket::CN_DATA, nullptr, - kDefaultRtcpEnabled, kDefaultSrtpRequired, cricket::DCT_RTP); - EXPECT_TRUE(data_channel != nullptr); + kDefaultRtcpEnabled, kDefaultSrtpRequired); + EXPECT_TRUE(rtp_data_channel != nullptr); cm_->DestroyVideoChannel(video_channel); cm_->DestroyVoiceChannel(voice_channel); - cm_->DestroyDataChannel(data_channel); + cm_->DestroyRtpDataChannel(rtp_data_channel); cm_->Terminate(); } @@ -138,13 +138,13 @@ TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) { &fake_mc_, transport_controller_, cricket::CN_VIDEO, nullptr, kDefaultRtcpEnabled, kDefaultSrtpRequired, VideoOptions()); EXPECT_TRUE(video_channel != nullptr); - cricket::DataChannel* data_channel = cm_->CreateDataChannel( + cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel( &fake_mc_, transport_controller_, cricket::CN_DATA, nullptr, - kDefaultRtcpEnabled, kDefaultSrtpRequired, cricket::DCT_RTP); - EXPECT_TRUE(data_channel != nullptr); + kDefaultRtcpEnabled, kDefaultSrtpRequired); + EXPECT_TRUE(rtp_data_channel != nullptr); cm_->DestroyVideoChannel(video_channel); cm_->DestroyVoiceChannel(voice_channel); - cm_->DestroyDataChannel(data_channel); + cm_->DestroyRtpDataChannel(rtp_data_channel); cm_->Terminate(); }