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(); }