diff --git a/webrtc/api/datachannel.cc b/webrtc/api/datachannel.cc index ad7cb57199..9812e9bb6a 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/sctptransportinternal.h" +#include "webrtc/media/sctp/sctpdataengine.h" namespace webrtc { @@ -328,12 +328,12 @@ void DataChannel::OnMessage(rtc::Message* msg) { } } -void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params, +void DataChannel::OnDataReceived(cricket::DataChannel* channel, + const cricket::ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& payload) { - if (data_channel_type_ == cricket::DCT_RTP && params.ssrc != receive_ssrc_) { - return; - } - if (data_channel_type_ == cricket::DCT_SCTP && params.sid != config_.id) { + uint32_t expected_ssrc = + (data_channel_type_ == cricket::DCT_RTP) ? receive_ssrc_ : config_.id; + if (params.ssrc != expected_ssrc) { return; } @@ -342,17 +342,17 @@ void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params, if (handshake_state_ != kHandshakeWaitingForAck) { // Ignore it if we are not expecting an ACK message. LOG(LS_WARNING) << "DataChannel received unexpected CONTROL message, " - << "sid = " << params.sid; + << "sid = " << params.ssrc; 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.sid; + << params.ssrc; } else { LOG(LS_WARNING) << "DataChannel failed to parse OPEN_ACK message, sid = " - << params.sid; + << params.ssrc; } return; } @@ -360,7 +360,7 @@ void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params, ASSERT(params.type == cricket::DMT_BINARY || params.type == cricket::DMT_TEXT); - LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.sid; + LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.ssrc; // 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,8 +390,9 @@ void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params, } } -void DataChannel::OnStreamClosedRemotely(int sid) { - if (data_channel_type_ == cricket::DCT_SCTP && sid == config_.id) { +void DataChannel::OnStreamClosedRemotely(uint32_t sid) { + if (data_channel_type_ == cricket::DCT_SCTP && + sid == static_cast(config_.id)) { Close(); } } @@ -550,7 +551,7 @@ bool DataChannel::SendDataMessage(const DataBuffer& buffer, send_params.max_rtx_count = config_.maxRetransmits; send_params.max_rtx_ms = config_.maxRetransmitTime; - send_params.sid = config_.id; + send_params.ssrc = config_.id; } else { send_params.ssrc = send_ssrc_; } @@ -622,7 +623,7 @@ bool DataChannel::SendControlMessage(const rtc::CopyOnWriteBuffer& buffer) { (!is_open_message || !config_.negotiated)); cricket::SendDataParams send_params; - send_params.sid = config_.id; + send_params.ssrc = 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 19f95df444..9208ada5ca 100644 --- a/webrtc/api/datachannel.h +++ b/webrtc/api/datachannel.h @@ -144,10 +144,11 @@ class DataChannel : public DataChannelInterface, // stream on an existing DataMediaChannel, and we've finished negotiation. void OnChannelReady(bool writable); - // Slots for provider to connect signals to. - void OnDataReceived(const cricket::ReceiveDataParams& params, + // Sigslots from cricket::DataChannel + void OnDataReceived(cricket::DataChannel* channel, + const cricket::ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& payload); - void OnStreamClosedRemotely(int sid); + void OnStreamClosedRemotely(uint32_t 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 4ce1be5736..a8c8361cb1 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(params, payload); + dc->OnDataReceived(NULL, 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(params, buffer.data); + dc->OnDataReceived(NULL, 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(params, buffer.data); + webrtc_data_channel_->OnDataReceived(NULL, 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(params, buffer.data); + webrtc_data_channel_->OnDataReceived(NULL, 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(params, buffers[0].data); - webrtc_data_channel_->OnDataReceived(params, buffers[1].data); - webrtc_data_channel_->OnDataReceived(params, buffers[2].data); + 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); 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(params, buffers[3].data); - webrtc_data_channel_->OnDataReceived(params, buffers[4].data); - webrtc_data_channel_->OnDataReceived(params, buffers[5].data); + 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); 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(params, buffer); + webrtc_data_channel_->OnDataReceived(NULL, params, buffer); } EXPECT_EQ(webrtc::DataChannelInterface::kClosed, webrtc_data_channel_->state()); diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc index f8bea1bd37..cdf5a97b68 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/sctptransport.h" +#include "webrtc/media/sctp/sctpdataengine.h" #include "webrtc/pc/channelmanager.h" #include "webrtc/system_wrappers/include/field_trial.h" @@ -652,14 +652,7 @@ bool PeerConnection::Initialize( std::unique_ptr( factory_->CreateTransportController( port_allocator_.get(), - configuration.redetermine_role_on_ice_restart)), -#ifdef HAVE_SCTP - std::unique_ptr( - new cricket::SctpTransportFactory(factory_->network_thread())) -#else - nullptr -#endif - )); + configuration.redetermine_role_on_ice_restart)))); stats_.reset(new StatsCollector(this)); stats_collector_ = RTCStatsCollector::Create(this); @@ -1126,7 +1119,7 @@ void PeerConnection::SetLocalDescription( // SCTP sids. rtc::SSLRole role; if (session_->data_channel_type() == cricket::DCT_SCTP && - session_->GetSctpSslRole(&role)) { + session_->GetSslRole(session_->data_channel(), &role)) { AllocateSctpSids(role); } @@ -1208,7 +1201,7 @@ void PeerConnection::SetRemoteDescription( // SCTP sids. rtc::SSLRole role; if (session_->data_channel_type() == cricket::DCT_SCTP && - session_->GetSctpSslRole(&role)) { + session_->GetSslRole(session_->data_channel(), &role)) { AllocateSctpSids(role); } @@ -2150,7 +2143,7 @@ rtc::scoped_refptr PeerConnection::InternalCreateDataChannel( if (session_->data_channel_type() == cricket::DCT_SCTP) { if (new_config.id < 0) { rtc::SSLRole role; - if ((session_->GetSctpSslRole(&role)) && + if ((session_->GetSslRole(session_->data_channel(), &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 d6da24ee19..c52df41f2b 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/sctptransportinternal.h" +#include "webrtc/media/sctp/sctpdataengine.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 dc14970854..3ab7ac94e1 100644 --- a/webrtc/api/rtcstatscollector.cc +++ b/webrtc/api/rtcstatscollector.cc @@ -463,16 +463,10 @@ void RTCStatsCollector::GetStatsReport( ChannelNamePair(pc_->session()->video_channel()->content_name(), pc_->session()->video_channel()->transport_name())); } - 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()) { + if (pc_->session()->data_channel()) { channel_name_pairs_->data = rtc::Optional( - ChannelNamePair(*pc_->session()->sctp_content_name(), - *pc_->session()->sctp_transport_name())); + ChannelNamePair(pc_->session()->data_channel()->content_name(), + pc_->session()->data_channel()->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 ae75035608..7fefad857d 100644 --- a/webrtc/api/test/mock_webrtcsession.h +++ b/webrtc/api/test/mock_webrtcsession.h @@ -15,7 +15,6 @@ #include #include "webrtc/api/webrtcsession.h" -#include "webrtc/media/sctp/sctptransportinternal.h" #include "webrtc/test/gmock.h" namespace webrtc { @@ -36,8 +35,7 @@ class MockWebRtcSession : public webrtc::WebRtcSession { std::unique_ptr( new cricket::TransportController(rtc::Thread::Current(), rtc::Thread::Current(), - nullptr)), - std::unique_ptr()) {} + nullptr))) {} 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 2c1ba640f8..e5ee4167ee 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/sctptransportinternal.h" +#include "webrtc/media/sctp/sctpdataengine.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 dad485c9bd..5d414baac2 100644 --- a/webrtc/api/webrtcsession.cc +++ b/webrtc/api/webrtcsession.cc @@ -33,7 +33,6 @@ #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" @@ -75,9 +74,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 kDtlsSrtpSetupFailureRtp[] = +const char kDtlsSetupFailureRtp[] = "Couldn't set up DTLS-SRTP on RTP channel."; -const char kDtlsSrtpSetupFailureRtcp[] = +const char kDtlsSetupFailureRtcp[] = "Couldn't set up DTLS-SRTP on RTCP channel."; const char kEnableBundleFailed[] = "Failed to enable BUNDLE."; @@ -292,31 +291,6 @@ 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, @@ -466,8 +440,7 @@ WebRtcSession::WebRtcSession( rtc::Thread* worker_thread, rtc::Thread* signaling_thread, cricket::PortAllocator* port_allocator, - std::unique_ptr transport_controller, - std::unique_ptr sctp_factory) + std::unique_ptr transport_controller) : network_thread_(network_thread), worker_thread_(worker_thread), signaling_thread_(signaling_thread), @@ -476,7 +449,6 @@ 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), @@ -498,7 +470,7 @@ WebRtcSession::WebRtcSession( transport_controller_->SignalCandidatesRemoved.connect( this, &WebRtcSession::OnTransportControllerCandidatesRemoved); transport_controller_->SignalDtlsHandshakeError.connect( - this, &WebRtcSession::OnTransportControllerDtlsHandshakeError); + this, &WebRtcSession::OnDtlsHandshakeError); } WebRtcSession::~WebRtcSession() { @@ -513,14 +485,9 @@ WebRtcSession::~WebRtcSession() { SignalVoiceChannelDestroyed(); channel_manager_->DestroyVoiceChannel(voice_channel_.release()); } - if (rtp_data_channel_) { + if (data_channel_) { SignalDataChannelDestroyed(); - channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release()); - } - if (sctp_transport_) { - SignalDataChannelDestroyed(); - network_thread_->Invoke( - RTC_FROM_HERE, rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this)); + channel_manager_->DestroyDataChannel(data_channel_.release()); } #ifdef HAVE_QUIC if (quic_data_transport_) { @@ -630,10 +597,9 @@ bool WebRtcSession::Initialize( void WebRtcSession::Close() { SetState(STATE_CLOSED); RemoveUnusedChannels(nullptr); - RTC_DCHECK(!voice_channel_); - RTC_DCHECK(!video_channel_); - RTC_DCHECK(!rtp_data_channel_); - RTC_DCHECK(!sctp_transport_); + ASSERT(!voice_channel_); + ASSERT(!video_channel_); + ASSERT(!data_channel_); media_controller_->Close(); } @@ -645,9 +611,8 @@ cricket::BaseChannel* WebRtcSession::GetChannel( if (video_channel() && video_channel()->content_name() == content_name) { return video_channel(); } - if (rtp_data_channel() && - rtp_data_channel()->content_name() == content_name) { - return rtp_data_channel(); + if (data_channel() && data_channel()->content_name() == content_name) { + return data_channel(); } return nullptr; } @@ -656,31 +621,20 @@ cricket::SecurePolicy WebRtcSession::SdesPolicy() const { return webrtc_session_desc_factory_->SdesPolicy(); } -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, +bool WebRtcSession::GetSslRole(const std::string& transport_name, rtc::SSLRole* role) { if (!local_description() || !remote_description()) { - LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the " + LOG(LS_INFO) << "Local and Remote descriptions must be applied to get " << "SSL Role of the session."; return false; } - return transport_controller_->GetSslRole(GetTransportName(content_name), - role); + return transport_controller_->GetSslRole(transport_name, role); +} + +bool WebRtcSession::GetSslRole(const cricket::BaseChannel* channel, + rtc::SSLRole* role) { + return channel && GetSslRole(channel->transport_name(), role); } void WebRtcSession::CreateOffer( @@ -964,27 +918,9 @@ bool WebRtcSession::PushdownMediaDescription( } }; - 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())); + return (set_content(voice_channel()) && + set_content(video_channel()) && + set_content(data_channel())); } bool WebRtcSession::PushdownTransportDescription(cricket::ContentSource source, @@ -1056,6 +992,46 @@ 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) { @@ -1063,6 +1039,7 @@ 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_ && @@ -1073,8 +1050,8 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { } #endif - auto maybe_set_transport = [this, bundle, - transport_name](cricket::BaseChannel* ch) { + auto maybe_set_transport = [this, bundle, transport_name, + first_channel](cricket::BaseChannel* ch) { if (!ch || !bundle.HasContentName(ch->content_name())) { return true; } @@ -1096,21 +1073,9 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { if (!maybe_set_transport(voice_channel()) || !maybe_set_transport(video_channel()) || - !maybe_set_transport(rtp_data_channel())) { + !maybe_set_transport(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; } @@ -1283,129 +1248,60 @@ sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() { bool WebRtcSession::SendData(const cricket::SendDataParams& params, const rtc::CopyOnWriteBuffer& payload, cricket::SendDataResult* result) { - if (!rtp_data_channel_ && !sctp_transport_) { - LOG(LS_ERROR) << "SendData called when rtp_data_channel_ " - << "and sctp_transport_ are NULL."; + if (!data_channel_) { + LOG(LS_ERROR) << "SendData called when data_channel_ is NULL."; return false; } - 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)); + return data_channel_->SendData(params, payload, result); } bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) { - if (!rtp_data_channel_ && !sctp_transport_) { + if (!data_channel_) { // 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; } - 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); - } + 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); return true; } void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) { - if (!rtp_data_channel_ && !sctp_transport_) { - LOG(LS_ERROR) << "DisconnectDataChannel called when rtp_data_channel_ and " - "sctp_transport_ are NULL."; + if (!data_channel_) { + LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL."; return; } - 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); - } + data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel); + data_channel_->SignalDataReceived.disconnect(webrtc_data_channel); + data_channel_->SignalStreamClosedRemotely.disconnect(webrtc_data_channel); } void WebRtcSession::AddSctpDataStream(int sid) { - if (!sctp_transport_) { - LOG(LS_ERROR) << "AddSctpDataStream called when sctp_transport_ is NULL."; + if (!data_channel_) { + LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL."; return; } - network_thread_->Invoke( - RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream, - sctp_transport_.get(), sid)); + data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(sid)); + data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(sid)); } void WebRtcSession::RemoveSctpDataStream(int sid) { - if (!sctp_transport_) { - LOG(LS_ERROR) << "RemoveSctpDataStream called when sctp_transport_ is " + if (!data_channel_) { + LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is " << "NULL."; return; } - network_thread_->Invoke( - RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream, - sctp_transport_.get(), sid)); + data_channel_->RemoveRecvStream(sid); + data_channel_->RemoveSendStream(sid); } bool WebRtcSession::ReadyToSendData() const { - 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); + return data_channel_ && data_channel_->ready_to_send_data(); } cricket::DataChannelType WebRtcSession::data_channel_type() const { @@ -1430,11 +1326,6 @@ 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(); } @@ -1564,16 +1455,7 @@ void WebRtcSession::OnTransportControllerCandidatesRemoved( } } -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. +// Enabling voice and video channel. void WebRtcSession::EnableChannels() { if (voice_channel_ && !voice_channel_->enabled()) voice_channel_->Enable(true); @@ -1581,8 +1463,8 @@ void WebRtcSession::EnableChannels() { if (video_channel_ && !video_channel_->enabled()) video_channel_->Enable(true); - if (rtp_data_channel_ && !rtp_data_channel_->enabled()) - rtp_data_channel_->Enable(true); + if (data_channel_ && !data_channel_->enabled()) + data_channel_->Enable(true); } // Returns the media index for a local ice candidate given the content name. @@ -1692,15 +1574,9 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) { const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc); if (!data_info || data_info->rejected) { - if (rtp_data_channel_) { + if (data_channel_) { SignalDataChannelDestroyed(); - channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release()); - } - if (sctp_transport_) { - SignalDataChannelDestroyed(); - network_thread_->Invoke( - RTC_FROM_HERE, - rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this)); + channel_manager_->DestroyDataChannel(data_channel_.release()); } #ifdef HAVE_QUIC // Clean up the existing QuicDataTransport and its QuicTransportChannels. @@ -1761,8 +1637,8 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) { } const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc); - if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected && - !rtp_data_channel_ && !sctp_transport_) { + if (data_channel_type_ != cricket::DCT_NONE && + data && !data->rejected && !data_channel_) { if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) { LOG(LS_ERROR) << "Failed to create data channel."; return false; @@ -1788,8 +1664,8 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content, voice_channel_->ActivateRtcpMux(); } - voice_channel_->SignalDtlsSrtpSetupFailure.connect( - this, &WebRtcSession::OnDtlsSrtpSetupFailure); + voice_channel_->SignalDtlsSetupFailure.connect( + this, &WebRtcSession::OnDtlsSetupFailure); SignalVoiceChannelCreated(); voice_channel_->SignalSentPacket.connect(this, @@ -1812,8 +1688,8 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content, if (require_rtcp_mux) { video_channel_->ActivateRtcpMux(); } - video_channel_->SignalDtlsSrtpSetupFailure.connect( - this, &WebRtcSession::OnDtlsSrtpSetupFailure); + video_channel_->SignalDtlsSetupFailure.connect( + this, &WebRtcSession::OnDtlsSetupFailure); SignalVideoChannelCreated(); video_channel_->SignalSentPacket.connect(this, @@ -1823,48 +1699,40 @@ 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); - if (sctp) { - 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); + 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); + } + + data_channel_->SignalDtlsSetupFailure.connect( + this, &WebRtcSession::OnDtlsSetupFailure); + SignalDataChannelCreated(); + data_channel_->SignalSentPacket.connect(this, &WebRtcSession::OnSentPacket_w); return true; } @@ -1890,79 +1758,16 @@ std::unique_ptr WebRtcSession::GetStats_n( return session_stats; } -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::OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp) { + SetError(ERROR_TRANSPORT, + rtcp ? kDtlsSetupFailureRtcp : kDtlsSetupFailureRtp); } -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( +void WebRtcSession::OnDataChannelMessageReceived( + cricket::DataChannel* channel, 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. @@ -1976,19 +1781,8 @@ void WebRtcSession::OnSctpTransportDataReceived_s( } config.open_handshake_role = InternalDataChannelInit::kAcker; SignalDataChannelOpenMessage(label, config); - } else { - // Otherwise just forward the signal. - SignalSctpDataReceived(params, payload); } -} - -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)); + // Otherwise ignore the message. } // Returns false if bundle is enabled and rtcp_mux is disabled. @@ -2182,11 +1976,8 @@ void WebRtcSession::ReportTransportStats() { if (video_channel()) { transport_names.insert(video_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_); + if (data_channel()) { + transport_names.insert(data_channel()->transport_name()); } for (const auto& name : transport_names) { cricket::TransportStats stats; @@ -2303,17 +2094,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 e346112cba..ef31560509 100644 --- a/webrtc/api/webrtcsession.h +++ b/webrtc/api/webrtcsession.h @@ -22,7 +22,6 @@ #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" @@ -38,9 +37,7 @@ namespace cricket { class ChannelManager; -class RtpDataChannel; -class SctpTransportInternal; -class SctpTransportInternalFactory; +class DataChannel; class StatsReport; class VideoChannel; class VoiceChannel; @@ -70,8 +67,8 @@ extern const char kSdpWithoutIceUfragPwd[]; extern const char kSdpWithoutSdesAndDtlsDisabled[]; extern const char kSessionError[]; extern const char kSessionErrorDesc[]; -extern const char kDtlsSrtpSetupFailureRtp[]; -extern const char kDtlsSrtpSetupFailureRtcp[]; +extern const char kDtlsSetupFailureRtp[]; +extern const char kDtlsSetupFailureRtcp[]; extern const char kEnableBundleFailed[]; // Maximum number of received video streams that will be processed by webrtc @@ -161,15 +158,13 @@ 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 sctp_factory); + std::unique_ptr transport_controller); virtual ~WebRtcSession(); // These are const to allow them to be called from const methods. @@ -204,34 +199,26 @@ 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(); } - // 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_; + virtual cricket::DataChannel* data_channel() { + return data_channel_.get(); } cricket::BaseChannel* GetChannel(const std::string& content_name); cricket::SecurePolicy SdesPolicy() const; - // 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); + // 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); void CreateOffer( CreateSessionDescriptionObserver* observer, @@ -245,7 +232,6 @@ 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( @@ -340,7 +326,7 @@ class WebRtcSession : // WebRTCSessionDescriptionFactory. Should happen before setLocalDescription. void OnCertificateReady( const rtc::scoped_refptr& certificate); - void OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp); + void OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp); // For unit test. bool waiting_for_certificate_for_testing() const; @@ -352,9 +338,8 @@ class WebRtcSession : transport_controller_->SetMetricsObserver(metrics_observer); } - // 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. + // Called when voice_channel_, video_channel_ and data_channel_ are created + // and destroyed. As a result of, for example, setting a new description. sigslot::signal0<> SignalVoiceChannelCreated; sigslot::signal0<> SignalVoiceChannelDestroyed; sigslot::signal0<> SignalVideoChannelCreated; @@ -412,7 +397,6 @@ 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, @@ -477,24 +461,11 @@ class WebRtcSession : std::unique_ptr GetStats_n( const ChannelNamePairs& channel_name_pairs); - 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); + // 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); std::string BadStateErrMsg(State state); void SetIceConnectionState(PeerConnectionInterface::IceConnectionState state); @@ -527,7 +498,6 @@ class WebRtcSession : // this session. bool SrtpRequired() const; - // TransportController signal handlers. void OnTransportControllerConnectionState(cricket::IceConnectionState state); void OnTransportControllerReceiving(bool receiving); void OnTransportControllerGatheringState(cricket::IceGatheringState state); @@ -536,7 +506,6 @@ class WebRtcSession : const std::vector& candidates); void OnTransportControllerCandidatesRemoved( const std::vector& candidates); - void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error); std::string GetSessionErrorMsg(); @@ -553,6 +522,8 @@ 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_; @@ -565,39 +536,10 @@ 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_; - // |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; - + std::unique_ptr data_channel_; 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 7c26d1db57..116c8d5739 100644 --- a/webrtc/api/webrtcsession_unittest.cc +++ b/webrtc/api/webrtcsession_unittest.cc @@ -40,7 +40,6 @@ #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" @@ -110,7 +109,6 @@ 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; @@ -213,52 +211,6 @@ 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( @@ -268,15 +220,13 @@ 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 sctp_factory) + std::unique_ptr transport_controller) : WebRtcSession(media_controller, network_thread, worker_thread, signaling_thread, port_allocator, - std::move(transport_controller), - std::move(sctp_factory)) { + std::move(transport_controller)) { RegisterIceObserver(ice_observer); } virtual ~WebRtcSessionForTest() {} @@ -299,6 +249,14 @@ 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) { @@ -428,16 +386,13 @@ 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())), - std::unique_ptr( - fake_sctp_transport_factory_))); + allocator_.get())))); session_->SignalDataChannelOpenMessage.connect( this, &WebRtcSessionTest::OnDataChannelOpenMessage); session_->GetOnDestroyedSignal()->connect( @@ -1541,8 +1496,6 @@ 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_; @@ -3922,7 +3875,7 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannel) { Init(); SetLocalDescriptionWithDataChannel(); ASSERT_TRUE(data_engine_); - EXPECT_NE(nullptr, data_engine_->GetChannel(0)); + EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type()); } TEST_P(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { @@ -3934,43 +3887,7 @@ TEST_P(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) { InitWithDtls(GetParam()); SetLocalDescriptionWithDataChannel(); - 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()); + EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type()); } TEST_P(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) { @@ -4002,39 +3919,30 @@ 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(nullptr, data_engine_->GetChannel(0)); - EXPECT_EQ(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); + EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type()); } -// 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(nullptr, data_engine_->GetChannel(0)); - EXPECT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); + EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); } -// 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(nullptr, data_engine_->GetChannel(0)); - EXPECT_EQ(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); + EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type()); } TEST_P(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) { @@ -4065,19 +3973,31 @@ 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(nullptr, data_engine_->GetChannel(0)); + EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); CreateDataChannel(); - 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()); + + 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); } -// Verifies that when a session's SctpTransport receives an OPEN message, -// WebRtcSession signals the SctpTransport creation request with the expected +// Verifies that when a session's DataChannel receives an OPEN message, +// WebRtcSession signals the DataChannel creation request with the expected // config. TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) { MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp); @@ -4085,10 +4005,8 @@ TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) { InitWithDtls(GetParam()); SetLocalDescriptionWithDataChannel(); - EXPECT_EQ(nullptr, data_engine_->GetChannel(0)); - ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport()); + EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type()); - // Make the fake SCTP transport pretend it received an OPEN message. webrtc::DataChannelInit config; config.id = 1; rtc::CopyOnWriteBuffer payload; @@ -4096,10 +4014,11 @@ 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); - EXPECT_EQ_WAIT("a", last_data_channel_label_, kDefaultTimeout); + cricket::DataChannel* data_channel = session_->data_channel(); + data_channel->SignalDataReceived(data_channel, params, payload); + + EXPECT_EQ("a", last_data_channel_label_); 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 0ab458bdfe..1f811f5911 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(content.name, &ssl_role)) { + if (session_->GetSslRole(session_->GetChannel(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 3aee5b7fe7..04936de177 100644 --- a/webrtc/media/BUILD.gn +++ b/webrtc/media/BUILD.gn @@ -54,6 +54,7 @@ rtc_static_library("rtc_media_base") { "base/codec.h", "base/cryptoparams.h", "base/device.h", + "base/hybriddataengine.h", "base/mediachannel.h", "base/mediaconstants.cc", "base/mediaconstants.h", @@ -142,13 +143,12 @@ rtc_static_library("rtc_media") { "engine/webrtcvoe.h", "engine/webrtcvoiceengine.cc", "engine/webrtcvoiceengine.h", - "sctp/sctptransportinternal.h", ] if (rtc_enable_sctp) { sources += [ - "sctp/sctptransport.cc", - "sctp/sctptransport.h", + "sctp/sctpdataengine.cc", + "sctp/sctpdataengine.h", ] } @@ -346,7 +346,7 @@ if (rtc_include_tests) { ] if (rtc_enable_sctp) { - sources += [ "sctp/sctptransport_unittest.cc" ] + sources += [ "sctp/sctpdataengine_unittest.cc" ] } configs += [ ":rtc_media_unittests_config" ] diff --git a/webrtc/media/base/fakemediaengine.h b/webrtc/media/base/fakemediaengine.h index cbd4e6ef26..932427b9b4 100644 --- a/webrtc/media/base/fakemediaengine.h +++ b/webrtc/media/base/fakemediaengine.h @@ -942,9 +942,11 @@ inline FakeVideoMediaChannel::~FakeVideoMediaChannel() { class FakeDataEngine : public DataEngineInterface { public: - FakeDataEngine(){}; + FakeDataEngine() : last_channel_type_(DCT_NONE) {} - virtual DataMediaChannel* CreateChannel(const MediaConfig& config) { + virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type, + const MediaConfig& config) { + last_channel_type_ = data_channel_type; FakeDataMediaChannel* ch = new FakeDataMediaChannel(this, DataOptions()); channels_.push_back(ch); return ch; @@ -964,9 +966,12 @@ 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 new file mode 100644 index 0000000000..341f054868 --- /dev/null +++ b/webrtc/media/base/hybriddataengine.h @@ -0,0 +1,60 @@ +/* + * 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 4c6b024947..d66424014b 100644 --- a/webrtc/media/base/mediachannel.h +++ b/webrtc/media/base/mediachannel.h @@ -1077,11 +1077,8 @@ enum DataMessageType { // signal fires, on up the chain. struct ReceiveDataParams { // The in-packet stream indentifier. - // RTP data channels use SSRCs, SCTP data channels use SIDs. - union { - uint32_t ssrc; - int sid; - }; + // For SCTP, this is really SID, not SSRC. + uint32_t ssrc; // The type of message (binary, text, or control). DataMessageType type; // A per-stream value incremented per packet in the stream. @@ -1089,16 +1086,18 @@ struct ReceiveDataParams { // A per-stream value monotonically increasing with time. int timestamp; - ReceiveDataParams() : sid(0), type(DMT_TEXT), seq_num(0), timestamp(0) {} + ReceiveDataParams() : + ssrc(0), + type(DMT_TEXT), + seq_num(0), + timestamp(0) { + } }; struct SendDataParams { // The in-packet stream indentifier. - // RTP data channels use SSRCs, SCTP data channels use SIDs. - union { - uint32_t ssrc; - int sid; - }; + // For SCTP, this is really SID, not SSRC. + uint32_t ssrc; // The type of message (binary, text, or control). DataMessageType type; @@ -1117,14 +1116,15 @@ struct SendDataParams { // is supported, not both at the same time. int max_rtx_ms; - 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) {} + 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) { + } }; enum SendDataResult { SDR_SUCCESS, SDR_ERROR, SDR_BLOCK }; @@ -1183,6 +1183,8 @@ 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 44d8c7ee01..547839afdb 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 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 or +// sctpdataengine.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 0dbac55954..debc171360 100644 --- a/webrtc/media/base/mediaengine.h +++ b/webrtc/media/base/mediaengine.h @@ -171,7 +171,8 @@ enum DataChannelType { DCT_NONE = 0, DCT_RTP = 1, DCT_SCTP = 2, DCT_QUIC = 3 }; class DataEngineInterface { public: virtual ~DataEngineInterface() {} - virtual DataMediaChannel* CreateChannel(const MediaConfig& config) = 0; + virtual DataMediaChannel* CreateChannel(DataChannelType type, + 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 96f5a3b95f..12a37fa180 100644 --- a/webrtc/media/base/rtpdataengine.cc +++ b/webrtc/media/base/rtpdataengine.cc @@ -40,7 +40,11 @@ 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 ca434940dc..adf0bef6e9 100644 --- a/webrtc/media/base/rtpdataengine.h +++ b/webrtc/media/base/rtpdataengine.h @@ -27,7 +27,8 @@ class RtpDataEngine : public DataEngineInterface { public: RtpDataEngine(); - virtual DataMediaChannel* CreateChannel(const MediaConfig& config); + virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type, + 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 dca509df8a..9d1e485916 100644 --- a/webrtc/media/base/rtpdataengine_unittest.cc +++ b/webrtc/media/base/rtpdataengine_unittest.cc @@ -72,7 +72,8 @@ class RtpDataMediaChannelTest : public testing::Test { cricket::RtpDataMediaChannel* CreateChannel(cricket::RtpDataEngine* dme) { cricket::MediaConfig config; cricket::RtpDataMediaChannel* channel = - static_cast(dme->CreateChannel(config)); + static_cast( + dme->CreateChannel(cricket::DCT_RTP, 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 new file mode 100644 index 0000000000..103aebdd9b --- /dev/null +++ b/webrtc/media/sctp/sctpdataengine.cc @@ -0,0 +1,1066 @@ +/* + * 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 new file mode 100644 index 0000000000..8b6f37d6ba --- /dev/null +++ b/webrtc/media/sctp/sctpdataengine.h @@ -0,0 +1,250 @@ +/* + * 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 new file mode 100644 index 0000000000..060b2d9749 --- /dev/null +++ b/webrtc/media/sctp/sctpdataengine_unittest.cc @@ -0,0 +1,523 @@ +/* + * 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 deleted file mode 100644 index b95cf8a4ba..0000000000 --- a/webrtc/media/sctp/sctptransport.cc +++ /dev/null @@ -1,1090 +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 -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 deleted file mode 100644 index 6d3a41a8f1..0000000000 --- a/webrtc/media/sctp/sctptransport.h +++ /dev/null @@ -1,193 +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_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 deleted file mode 100644 index 42e4dc646f..0000000000 --- a/webrtc/media/sctp/sctptransport_unittest.cc +++ /dev/null @@ -1,563 +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/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 deleted file mode 100644 index 7dd6bc7ea7..0000000000 --- a/webrtc/media/sctp/sctptransportinternal.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 56335e24d0..1f29261ed2 100644 --- a/webrtc/pc/channel.cc +++ b/webrtc/pc/channel.cc @@ -69,6 +69,7 @@ enum { MSG_READYTOSENDDATA, MSG_DATARECEIVED, MSG_FIRSTPACKETRECEIVED, + MSG_STREAMCLOSEDREMOTELY, }; // Value specified in RFC 5764. @@ -930,16 +931,16 @@ void BaseChannel::ChannelWritable_n() { UpdateMediaSendRecvState(); } -void BaseChannel::SignalDtlsSrtpSetupFailure_n(bool rtcp) { +void BaseChannel::SignalDtlsSetupFailure_n(bool rtcp) { RTC_DCHECK(network_thread_->IsCurrent()); invoker_.AsyncInvoke( RTC_FROM_HERE, signaling_thread(), - Bind(&BaseChannel::SignalDtlsSrtpSetupFailure_s, this, rtcp)); + Bind(&BaseChannel::SignalDtlsSetupFailure_s, this, rtcp)); } -void BaseChannel::SignalDtlsSrtpSetupFailure_s(bool rtcp) { +void BaseChannel::SignalDtlsSetupFailure_s(bool rtcp) { RTC_DCHECK(signaling_thread() == rtc::Thread::Current()); - SignalDtlsSrtpSetupFailure(this, rtcp); + SignalDtlsSetupFailure(this, rtcp); } bool BaseChannel::SetDtlsSrtpCryptoSuites_n(TransportChannel* tc, bool rtcp) { @@ -1060,13 +1061,13 @@ void BaseChannel::MaybeSetupDtlsSrtp_n() { } if (!SetupDtlsSrtp_n(false)) { - SignalDtlsSrtpSetupFailure_n(false); + SignalDtlsSetupFailure_n(false); return; } if (rtcp_transport_channel_) { if (!SetupDtlsSrtp_n(true)) { - SignalDtlsSrtpSetupFailure_n(true); + SignalDtlsSetupFailure_n(true); return; } } @@ -2146,23 +2147,25 @@ void VideoChannel::GetSrtpCryptoSuites_n( GetSupportedVideoCryptoSuites(crypto_options(), crypto_suites); } -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) +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) : BaseChannel(worker_thread, network_thread, media_channel, transport_controller, content_name, rtcp, - srtp_required) {} + srtp_required), + data_channel_type_(cricket::DCT_NONE), + ready_to_send_data_(false) {} -RtpDataChannel::~RtpDataChannel() { - TRACE_EVENT0("webrtc", "RtpDataChannel::~RtpDataChannel"); +DataChannel::~DataChannel() { + TRACE_EVENT0("webrtc", "DataChannel::~DataChannel"); StopMediaMonitor(); // this can't be done in the base class, since it calls a virtual DisableMedia_w(); @@ -2170,48 +2173,78 @@ RtpDataChannel::~RtpDataChannel() { Deinit(); } -bool RtpDataChannel::Init_w(const std::string* bundle_transport_name) { +bool DataChannel::Init_w(const std::string* bundle_transport_name) { if (!BaseChannel::Init_w(bundle_transport_name)) { return false; } - media_channel()->SignalDataReceived.connect(this, - &RtpDataChannel::OnDataReceived); + media_channel()->SignalDataReceived.connect( + this, &DataChannel::OnDataReceived); media_channel()->SignalReadyToSend.connect( - this, &RtpDataChannel::OnDataChannelReadyToSend); + this, &DataChannel::OnDataChannelReadyToSend); + media_channel()->SignalStreamClosedRemotely.connect( + this, &DataChannel::OnStreamClosedRemotely); return true; } -bool RtpDataChannel::SendData(const SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - SendDataResult* result) { +bool DataChannel::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* RtpDataChannel::GetFirstContent( +const ContentInfo* DataChannel::GetFirstContent( const SessionDescription* sdesc) { return GetFirstDataContent(sdesc); } -bool RtpDataChannel::CheckDataChannelTypeFromContent( +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( const DataContentDescription* content, std::string* error_desc) { bool is_sctp = ((content->protocol() == kMediaProtocolSctp) || (content->protocol() == kMediaProtocolDtlsSctp)); - // 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; + DataChannelType data_channel_type = is_sctp ? DCT_SCTP : DCT_RTP; + return SetDataChannelType(data_channel_type, error_desc); } -bool RtpDataChannel::SetLocalContent_w(const MediaContentDescription* content, - ContentAction action, - std::string* error_desc) { - TRACE_EVENT0("webrtc", "RtpDataChannel::SetLocalContent_w"); +bool DataChannel::SetLocalContent_w(const MediaContentDescription* content, + ContentAction action, + std::string* error_desc) { + TRACE_EVENT0("webrtc", "DataChannel::SetLocalContent_w"); RTC_DCHECK(worker_thread() == rtc::Thread::Current()); LOG(LS_INFO) << "Setting local data description"; @@ -2223,14 +2256,19 @@ bool RtpDataChannel::SetLocalContent_w(const MediaContentDescription* content, return false; } - if (!CheckDataChannelTypeFromContent(data, error_desc)) { + if (!SetDataChannelTypeFromContent(data, error_desc)) { return false; } - if (!SetRtpTransportParameters(content, action, CS_LOCAL, error_desc)) { - return false; + if (data_channel_type_ == DCT_RTP) { + 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)) { @@ -2238,8 +2276,10 @@ bool RtpDataChannel::SetLocalContent_w(const MediaContentDescription* content, error_desc); return false; } - for (const DataCodec& codec : data->codecs()) { - bundle_filter()->AddPayloadType(codec.id); + if (data_channel_type_ == DCT_RTP) { + for (const DataCodec& codec : data->codecs()) { + bundle_filter()->AddPayloadType(codec.id); + } } last_recv_params_ = recv_params; @@ -2257,10 +2297,10 @@ bool RtpDataChannel::SetLocalContent_w(const MediaContentDescription* content, return true; } -bool RtpDataChannel::SetRemoteContent_w(const MediaContentDescription* content, - ContentAction action, - std::string* error_desc) { - TRACE_EVENT0("webrtc", "RtpDataChannel::SetRemoteContent_w"); +bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content, + ContentAction action, + std::string* error_desc) { + TRACE_EVENT0("webrtc", "DataChannel::SetRemoteContent_w"); RTC_DCHECK(worker_thread() == rtc::Thread::Current()); const DataContentDescription* data = @@ -2277,15 +2317,17 @@ bool RtpDataChannel::SetRemoteContent_w(const MediaContentDescription* content, return true; } - if (!CheckDataChannelTypeFromContent(data, error_desc)) { + if (!SetDataChannelTypeFromContent(data, error_desc)) { return false; } LOG(LS_INFO) << "Setting remote data description"; - if (!SetRtpTransportParameters(content, action, CS_REMOTE, error_desc)) { + if (data_channel_type_ == DCT_RTP && + !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)) { @@ -2310,7 +2352,7 @@ bool RtpDataChannel::SetRemoteContent_w(const MediaContentDescription* content, return true; } -void RtpDataChannel::UpdateMediaSendRecvState_w() { +void DataChannel::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(); @@ -2331,7 +2373,7 @@ void RtpDataChannel::UpdateMediaSendRecvState_w() { LOG(LS_INFO) << "Changing data state, recv=" << recv << " send=" << send; } -void RtpDataChannel::OnMessage(rtc::Message* pmsg) { +void DataChannel::OnMessage(rtc::Message *pmsg) { switch (pmsg->message_id) { case MSG_READYTOSENDDATA: { DataChannelReadyToSendMessageData* data = @@ -2344,7 +2386,7 @@ void RtpDataChannel::OnMessage(rtc::Message* pmsg) { case MSG_DATARECEIVED: { DataReceivedMessageData* data = static_cast(pmsg->pdata); - SignalDataReceived(data->params, data->payload); + SignalDataReceived(this, data->params, data->payload); delete data; break; } @@ -2354,27 +2396,33 @@ void RtpDataChannel::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 RtpDataChannel::OnConnectionMonitorUpdate( - ConnectionMonitor* monitor, - const std::vector& infos) { +void DataChannel::OnConnectionMonitorUpdate( + ConnectionMonitor* monitor, const std::vector& infos) { SignalConnectionMonitor(this, infos); } -void RtpDataChannel::StartMediaMonitor(int cms) { +void DataChannel::StartMediaMonitor(int cms) { media_monitor_.reset(new DataMediaMonitor(media_channel(), worker_thread(), rtc::Thread::Current())); - media_monitor_->SignalUpdate.connect(this, - &RtpDataChannel::OnMediaMonitorUpdate); + media_monitor_->SignalUpdate.connect( + this, &DataChannel::OnMediaMonitorUpdate); media_monitor_->Start(cms); } -void RtpDataChannel::StopMediaMonitor() { +void DataChannel::StopMediaMonitor() { if (media_monitor_) { media_monitor_->Stop(); media_monitor_->SignalUpdate.disconnect(this); @@ -2382,28 +2430,27 @@ void RtpDataChannel::StopMediaMonitor() { } } -void RtpDataChannel::OnMediaMonitorUpdate(DataMediaChannel* media_channel, - const DataMediaInfo& info) { +void DataChannel::OnMediaMonitorUpdate( + DataMediaChannel* media_channel, const DataMediaInfo& info) { RTC_DCHECK(media_channel == this->media_channel()); SignalMediaMonitor(this, info); } -void RtpDataChannel::OnDataReceived(const ReceiveDataParams& params, - const char* data, - size_t len) { +void DataChannel::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 RtpDataChannel::OnDataChannelError(uint32_t ssrc, - DataMediaChannel::Error err) { +void DataChannel::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 RtpDataChannel::OnDataChannelReadyToSend(bool writable) { +void DataChannel::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. @@ -2411,9 +2458,19 @@ void RtpDataChannel::OnDataChannelReadyToSend(bool writable) { new DataChannelReadyToSendMessageData(writable)); } -void RtpDataChannel::GetSrtpCryptoSuites_n( - std::vector* crypto_suites) const { +void DataChannel::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 ff98a2fb27..64c5f8253f 100644 --- a/webrtc/pc/channel.h +++ b/webrtc/pc/channel.h @@ -149,9 +149,9 @@ class BaseChannel return remote_streams_; } - sigslot::signal2 SignalDtlsSrtpSetupFailure; - void SignalDtlsSrtpSetupFailure_n(bool rtcp); - void SignalDtlsSrtpSetupFailure_s(bool rtcp); + sigslot::signal2 SignalDtlsSetupFailure; + void SignalDtlsSetupFailure_n(bool rtcp); + void SignalDtlsSetupFailure_s(bool rtcp); // Used for latency measurements. sigslot::signal1 SignalFirstPacketReceived; @@ -261,7 +261,7 @@ class BaseChannel rtc::CopyOnWriteBuffer* packet, const rtc::PacketOptions& options); - bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet); + virtual 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); - bool ShouldSetupDtlsSrtp_n() const; + virtual 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_; }; -// RtpDataChannel is a specialization for data. -class RtpDataChannel : public BaseChannel { +// DataChannel is a specialization for data. +class DataChannel : public BaseChannel { public: - 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(); + 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(); bool Init_w(const std::string* bundle_transport_name); virtual bool SendData(const SendDataParams& params, @@ -640,16 +640,17 @@ class RtpDataChannel : public BaseChannel { return ready_to_send_data_; } - sigslot::signal2 SignalMediaMonitor; - sigslot::signal2&> + sigslot::signal2 SignalMediaMonitor; + sigslot::signal2&> SignalConnectionMonitor; - - sigslot::signal2 - SignalDataReceived; + sigslot::signal3 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: @@ -692,9 +693,15 @@ class RtpDataChannel : public BaseChannel { // overrides from BaseChannel const ContentInfo* GetFirstContent(const SessionDescription* sdesc) override; - // Checks that data channel type is RTP. - bool CheckDataChannelTypeFromContent(const DataContentDescription* content, - std::string* error_desc); + // 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); bool SetLocalContent_w(const MediaContentDescription* content, ContentAction action, std::string* error_desc) override; @@ -702,6 +709,7 @@ class RtpDataChannel : 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; @@ -710,13 +718,18 @@ class RtpDataChannel : 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_; - bool ready_to_send_data_ = false; + // TODO(pthatcher): Make a separate SctpDataChannel and + // RtpDataChannel instead of using this. + DataChannelType data_channel_type_; + bool ready_to_send_data_; // 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 589ebd54d1..5de3c94555 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/RtpDataChannel tests +// Base class for Voice/Video/DataChannel tests template class ChannelTest : public testing::Test, public sigslot::has_slots<> { public: @@ -3288,32 +3288,32 @@ TEST_F(VideoChannelDoubleThreadTest, CanChangeMaxBitrate) { Base::CanChangeMaxBitrate(); } -// RtpDataChannelSingleThreadTest -class RtpDataChannelSingleThreadTest : public ChannelTest { +// DataChannelSingleThreadTest +class DataChannelSingleThreadTest : public ChannelTest { public: typedef ChannelTest Base; - RtpDataChannelSingleThreadTest() + DataChannelSingleThreadTest() : Base(true, kDataPacket, kRtcpReport, NetworkIsWorker::Yes) {} }; -// RtpDataChannelDoubleThreadTest -class RtpDataChannelDoubleThreadTest : public ChannelTest { +// DataChannelDoubleThreadTest +class DataChannelDoubleThreadTest : public ChannelTest { public: typedef ChannelTest Base; - RtpDataChannelDoubleThreadTest() + DataChannelDoubleThreadTest() : Base(true, kDataPacket, kRtcpReport, NetworkIsWorker::No) {} }; // Override to avoid engine channel parameter. template <> -cricket::RtpDataChannel* ChannelTest::CreateChannel( +cricket::DataChannel* ChannelTest::CreateChannel( rtc::Thread* worker_thread, rtc::Thread* network_thread, cricket::MediaEngineInterface* engine, cricket::FakeDataMediaChannel* ch, cricket::TransportController* transport_controller, int flags) { - cricket::RtpDataChannel* channel = new cricket::RtpDataChannel( + cricket::DataChannel* channel = new cricket::DataChannel( 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(RtpDataChannelSingleThreadTest, TestInit) { +TEST_F(DataChannelSingleThreadTest, TestInit) { Base::TestInit(); EXPECT_FALSE(media_channel1_->IsStreamMuted(0)); } -TEST_F(RtpDataChannelSingleThreadTest, TestDeinit) { +TEST_F(DataChannelSingleThreadTest, TestDeinit) { Base::TestDeinit(); } -TEST_F(RtpDataChannelSingleThreadTest, TestSetContents) { +TEST_F(DataChannelSingleThreadTest, TestSetContents) { Base::TestSetContents(); } -TEST_F(RtpDataChannelSingleThreadTest, TestSetContentsNullOffer) { +TEST_F(DataChannelSingleThreadTest, TestSetContentsNullOffer) { Base::TestSetContentsNullOffer(); } -TEST_F(RtpDataChannelSingleThreadTest, TestSetContentsRtcpMux) { +TEST_F(DataChannelSingleThreadTest, TestSetContentsRtcpMux) { Base::TestSetContentsRtcpMux(); } -TEST_F(RtpDataChannelSingleThreadTest, TestSetRemoteContentUpdate) { +TEST_F(DataChannelSingleThreadTest, TestSetRemoteContentUpdate) { Base::TestSetRemoteContentUpdate(); } -TEST_F(RtpDataChannelSingleThreadTest, TestStreams) { +TEST_F(DataChannelSingleThreadTest, TestStreams) { Base::TestStreams(); } -TEST_F(RtpDataChannelSingleThreadTest, TestUpdateStreamsInLocalContent) { +TEST_F(DataChannelSingleThreadTest, TestUpdateStreamsInLocalContent) { Base::TestUpdateStreamsInLocalContent(); } -TEST_F(RtpDataChannelSingleThreadTest, TestUpdateRemoteStreamsInContent) { +TEST_F(DataChannelSingleThreadTest, TestUpdateRemoteStreamsInContent) { Base::TestUpdateStreamsInRemoteContent(); } -TEST_F(RtpDataChannelSingleThreadTest, TestChangeStreamParamsInContent) { +TEST_F(DataChannelSingleThreadTest, TestChangeStreamParamsInContent) { Base::TestChangeStreamParamsInContent(); } -TEST_F(RtpDataChannelSingleThreadTest, TestPlayoutAndSendingStates) { +TEST_F(DataChannelSingleThreadTest, TestPlayoutAndSendingStates) { Base::TestPlayoutAndSendingStates(); } -TEST_F(RtpDataChannelSingleThreadTest, TestMediaContentDirection) { +TEST_F(DataChannelSingleThreadTest, TestMediaContentDirection) { Base::TestMediaContentDirection(); } -TEST_F(RtpDataChannelSingleThreadTest, TestCallSetup) { +TEST_F(DataChannelSingleThreadTest, TestCallSetup) { Base::TestCallSetup(); } -TEST_F(RtpDataChannelSingleThreadTest, TestCallTeardownRtcpMux) { +TEST_F(DataChannelSingleThreadTest, TestCallTeardownRtcpMux) { Base::TestCallTeardownRtcpMux(); } -TEST_F(RtpDataChannelSingleThreadTest, TestOnReadyToSend) { +TEST_F(DataChannelSingleThreadTest, TestOnReadyToSend) { Base::TestOnReadyToSend(); } -TEST_F(RtpDataChannelSingleThreadTest, TestOnReadyToSendWithRtcpMux) { +TEST_F(DataChannelSingleThreadTest, TestOnReadyToSendWithRtcpMux) { Base::TestOnReadyToSendWithRtcpMux(); } -TEST_F(RtpDataChannelSingleThreadTest, SendRtpToRtp) { +TEST_F(DataChannelSingleThreadTest, SendRtpToRtp) { Base::SendRtpToRtp(); } -TEST_F(RtpDataChannelSingleThreadTest, SendNoRtcpToNoRtcp) { +TEST_F(DataChannelSingleThreadTest, SendNoRtcpToNoRtcp) { Base::SendNoRtcpToNoRtcp(); } -TEST_F(RtpDataChannelSingleThreadTest, SendNoRtcpToRtcp) { +TEST_F(DataChannelSingleThreadTest, SendNoRtcpToRtcp) { Base::SendNoRtcpToRtcp(); } -TEST_F(RtpDataChannelSingleThreadTest, SendRtcpToNoRtcp) { +TEST_F(DataChannelSingleThreadTest, SendRtcpToNoRtcp) { Base::SendRtcpToNoRtcp(); } -TEST_F(RtpDataChannelSingleThreadTest, SendRtcpToRtcp) { +TEST_F(DataChannelSingleThreadTest, SendRtcpToRtcp) { Base::SendRtcpToRtcp(); } -TEST_F(RtpDataChannelSingleThreadTest, SendRtcpMuxToRtcp) { +TEST_F(DataChannelSingleThreadTest, SendRtcpMuxToRtcp) { Base::SendRtcpMuxToRtcp(); } -TEST_F(RtpDataChannelSingleThreadTest, SendRtcpMuxToRtcpMux) { +TEST_F(DataChannelSingleThreadTest, SendRtcpMuxToRtcpMux) { Base::SendRtcpMuxToRtcpMux(); } -TEST_F(RtpDataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcp) { +TEST_F(DataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcp) { Base::SendEarlyRtcpMuxToRtcp(); } -TEST_F(RtpDataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcpMux) { +TEST_F(DataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcpMux) { Base::SendEarlyRtcpMuxToRtcpMux(); } -TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToSrtp) { +TEST_F(DataChannelSingleThreadTest, SendSrtpToSrtp) { Base::SendSrtpToSrtp(); } -TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToRtp) { +TEST_F(DataChannelSingleThreadTest, SendSrtpToRtp) { Base::SendSrtpToSrtp(); } -TEST_F(RtpDataChannelSingleThreadTest, SendSrtcpMux) { +TEST_F(DataChannelSingleThreadTest, SendSrtcpMux) { Base::SendSrtpToSrtp(RTCP_MUX, RTCP_MUX); } -TEST_F(RtpDataChannelSingleThreadTest, SendRtpToRtpOnThread) { +TEST_F(DataChannelSingleThreadTest, SendRtpToRtpOnThread) { Base::SendRtpToRtpOnThread(); } -TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToSrtpOnThread) { +TEST_F(DataChannelSingleThreadTest, SendSrtpToSrtpOnThread) { Base::SendSrtpToSrtpOnThread(); } -TEST_F(RtpDataChannelSingleThreadTest, SendWithWritabilityLoss) { +TEST_F(DataChannelSingleThreadTest, SendWithWritabilityLoss) { Base::SendWithWritabilityLoss(); } -TEST_F(RtpDataChannelSingleThreadTest, TestMediaMonitor) { +TEST_F(DataChannelSingleThreadTest, TestMediaMonitor) { Base::TestMediaMonitor(); } -TEST_F(RtpDataChannelSingleThreadTest, TestSendData) { +TEST_F(DataChannelSingleThreadTest, TestSendData) { CreateChannels(0, 0); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); @@ -3506,136 +3506,136 @@ TEST_F(RtpDataChannelSingleThreadTest, TestSendData) { EXPECT_EQ("foo", media_channel1_->last_sent_data()); } -TEST_F(RtpDataChannelDoubleThreadTest, TestInit) { +TEST_F(DataChannelDoubleThreadTest, TestInit) { Base::TestInit(); EXPECT_FALSE(media_channel1_->IsStreamMuted(0)); } -TEST_F(RtpDataChannelDoubleThreadTest, TestDeinit) { +TEST_F(DataChannelDoubleThreadTest, TestDeinit) { Base::TestDeinit(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestSetContents) { +TEST_F(DataChannelDoubleThreadTest, TestSetContents) { Base::TestSetContents(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestSetContentsNullOffer) { +TEST_F(DataChannelDoubleThreadTest, TestSetContentsNullOffer) { Base::TestSetContentsNullOffer(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestSetContentsRtcpMux) { +TEST_F(DataChannelDoubleThreadTest, TestSetContentsRtcpMux) { Base::TestSetContentsRtcpMux(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestSetRemoteContentUpdate) { +TEST_F(DataChannelDoubleThreadTest, TestSetRemoteContentUpdate) { Base::TestSetRemoteContentUpdate(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestStreams) { +TEST_F(DataChannelDoubleThreadTest, TestStreams) { Base::TestStreams(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestUpdateStreamsInLocalContent) { +TEST_F(DataChannelDoubleThreadTest, TestUpdateStreamsInLocalContent) { Base::TestUpdateStreamsInLocalContent(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestUpdateRemoteStreamsInContent) { +TEST_F(DataChannelDoubleThreadTest, TestUpdateRemoteStreamsInContent) { Base::TestUpdateStreamsInRemoteContent(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestChangeStreamParamsInContent) { +TEST_F(DataChannelDoubleThreadTest, TestChangeStreamParamsInContent) { Base::TestChangeStreamParamsInContent(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestPlayoutAndSendingStates) { +TEST_F(DataChannelDoubleThreadTest, TestPlayoutAndSendingStates) { Base::TestPlayoutAndSendingStates(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestMediaContentDirection) { +TEST_F(DataChannelDoubleThreadTest, TestMediaContentDirection) { Base::TestMediaContentDirection(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestCallSetup) { +TEST_F(DataChannelDoubleThreadTest, TestCallSetup) { Base::TestCallSetup(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestCallTeardownRtcpMux) { +TEST_F(DataChannelDoubleThreadTest, TestCallTeardownRtcpMux) { Base::TestCallTeardownRtcpMux(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestOnReadyToSend) { +TEST_F(DataChannelDoubleThreadTest, TestOnReadyToSend) { Base::TestOnReadyToSend(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestOnReadyToSendWithRtcpMux) { +TEST_F(DataChannelDoubleThreadTest, TestOnReadyToSendWithRtcpMux) { Base::TestOnReadyToSendWithRtcpMux(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendRtpToRtp) { +TEST_F(DataChannelDoubleThreadTest, SendRtpToRtp) { Base::SendRtpToRtp(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendNoRtcpToNoRtcp) { +TEST_F(DataChannelDoubleThreadTest, SendNoRtcpToNoRtcp) { Base::SendNoRtcpToNoRtcp(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendNoRtcpToRtcp) { +TEST_F(DataChannelDoubleThreadTest, SendNoRtcpToRtcp) { Base::SendNoRtcpToRtcp(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpToNoRtcp) { +TEST_F(DataChannelDoubleThreadTest, SendRtcpToNoRtcp) { Base::SendRtcpToNoRtcp(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpToRtcp) { +TEST_F(DataChannelDoubleThreadTest, SendRtcpToRtcp) { Base::SendRtcpToRtcp(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpMuxToRtcp) { +TEST_F(DataChannelDoubleThreadTest, SendRtcpMuxToRtcp) { Base::SendRtcpMuxToRtcp(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpMuxToRtcpMux) { +TEST_F(DataChannelDoubleThreadTest, SendRtcpMuxToRtcpMux) { Base::SendRtcpMuxToRtcpMux(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcp) { +TEST_F(DataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcp) { Base::SendEarlyRtcpMuxToRtcp(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcpMux) { +TEST_F(DataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcpMux) { Base::SendEarlyRtcpMuxToRtcpMux(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToSrtp) { +TEST_F(DataChannelDoubleThreadTest, SendSrtpToSrtp) { Base::SendSrtpToSrtp(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToRtp) { +TEST_F(DataChannelDoubleThreadTest, SendSrtpToRtp) { Base::SendSrtpToSrtp(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendSrtcpMux) { +TEST_F(DataChannelDoubleThreadTest, SendSrtcpMux) { Base::SendSrtpToSrtp(RTCP_MUX, RTCP_MUX); } -TEST_F(RtpDataChannelDoubleThreadTest, SendRtpToRtpOnThread) { +TEST_F(DataChannelDoubleThreadTest, SendRtpToRtpOnThread) { Base::SendRtpToRtpOnThread(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToSrtpOnThread) { +TEST_F(DataChannelDoubleThreadTest, SendSrtpToSrtpOnThread) { Base::SendSrtpToSrtpOnThread(); } -TEST_F(RtpDataChannelDoubleThreadTest, SendWithWritabilityLoss) { +TEST_F(DataChannelDoubleThreadTest, SendWithWritabilityLoss) { Base::SendWithWritabilityLoss(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestMediaMonitor) { +TEST_F(DataChannelDoubleThreadTest, TestMediaMonitor) { Base::TestMediaMonitor(); } -TEST_F(RtpDataChannelDoubleThreadTest, TestSendData) { +TEST_F(DataChannelDoubleThreadTest, TestSendData) { CreateChannels(0, 0); EXPECT_TRUE(SendInitiate()); EXPECT_TRUE(SendAccept()); diff --git a/webrtc/pc/channelmanager.cc b/webrtc/pc/channelmanager.cc index cdfed68bb6..a0b3609ecf 100644 --- a/webrtc/pc/channelmanager.cc +++ b/webrtc/pc/channelmanager.cc @@ -20,7 +20,11 @@ #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 { @@ -29,7 +33,11 @@ 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, @@ -336,66 +344,73 @@ void ChannelManager::DestroyVideoChannel_w(VideoChannel* video_channel) { delete video_channel; } -RtpDataChannel* ChannelManager::CreateRtpDataChannel( +DataChannel* ChannelManager::CreateDataChannel( webrtc::MediaControllerInterface* media_controller, TransportController* transport_controller, const std::string& content_name, const std::string* bundle_transport_name, bool rtcp, - 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)); + 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)); } -RtpDataChannel* ChannelManager::CreateRtpDataChannel_w( +DataChannel* ChannelManager::CreateDataChannel_w( webrtc::MediaControllerInterface* media_controller, TransportController* transport_controller, const std::string& content_name, const std::string* bundle_transport_name, bool rtcp, - bool srtp_required) { + bool srtp_required, + DataChannelType data_channel_type) { // 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(config); + DataMediaChannel* media_channel = + data_media_engine_->CreateChannel(data_channel_type, config); if (!media_channel) { - LOG(LS_WARNING) << "Failed to create RTP data channel."; - return nullptr; + LOG(LS_WARNING) << "Failed to create data channel of type " + << data_channel_type; + return NULL; } - RtpDataChannel* data_channel = new RtpDataChannel( - worker_thread_, network_thread_, media_channel, transport_controller, - content_name, rtcp, srtp_required); + // 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); 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 nullptr; + return NULL; } data_channels_.push_back(data_channel); return data_channel; } -void ChannelManager::DestroyRtpDataChannel(RtpDataChannel* data_channel) { - TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel"); +void ChannelManager::DestroyDataChannel(DataChannel* data_channel) { + TRACE_EVENT0("webrtc", "ChannelManager::DestroyDataChannel"); if (data_channel) { worker_thread_->Invoke( RTC_FROM_HERE, - Bind(&ChannelManager::DestroyRtpDataChannel_w, this, data_channel)); + Bind(&ChannelManager::DestroyDataChannel_w, this, data_channel)); } } -void ChannelManager::DestroyRtpDataChannel_w(RtpDataChannel* data_channel) { - TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel_w"); +void ChannelManager::DestroyDataChannel_w(DataChannel* data_channel) { + TRACE_EVENT0("webrtc", "ChannelManager::DestroyDataChannel_w"); // Destroy data channel. ASSERT(initialized_); - RtpDataChannels::iterator it = - std::find(data_channels_.begin(), data_channels_.end(), data_channel); + DataChannels::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 3022eca8aa..7df49bb07d 100644 --- a/webrtc/pc/channelmanager.h +++ b/webrtc/pc/channelmanager.h @@ -110,15 +110,16 @@ class ChannelManager { const VideoOptions& options); // Destroys a video channel created with the Create API. void DestroyVideoChannel(VideoChannel* video_channel); - RtpDataChannel* CreateRtpDataChannel( + DataChannel* CreateDataChannel( webrtc::MediaControllerInterface* media_controller, TransportController* transport_controller, const std::string& content_name, const std::string* bundle_transport_name, bool rtcp, - bool srtp_required); + bool srtp_required, + DataChannelType data_channel_type); // Destroys a data channel created with the Create API. - void DestroyRtpDataChannel(RtpDataChannel* data_channel); + void DestroyDataChannel(DataChannel* data_channel); // Indicates whether any channels exist. bool has_channels() const { @@ -149,7 +150,7 @@ class ChannelManager { private: typedef std::vector VoiceChannels; typedef std::vector VideoChannels; - typedef std::vector RtpDataChannels; + typedef std::vector DataChannels; void Construct(MediaEngineInterface* me, DataEngineInterface* dme, @@ -177,14 +178,15 @@ class ChannelManager { bool srtp_required, const VideoOptions& options); void DestroyVideoChannel_w(VideoChannel* video_channel); - RtpDataChannel* CreateRtpDataChannel_w( + DataChannel* CreateDataChannel_w( webrtc::MediaControllerInterface* media_controller, TransportController* transport_controller, const std::string& content_name, const std::string* bundle_transport_name, bool rtcp, - bool srtp_required); - void DestroyRtpDataChannel_w(RtpDataChannel* data_channel); + bool srtp_required, + DataChannelType data_channel_type); + void DestroyDataChannel_w(DataChannel* data_channel); std::unique_ptr media_engine_; std::unique_ptr data_media_engine_; @@ -195,7 +197,7 @@ class ChannelManager { VoiceChannels voice_channels_; VideoChannels video_channels_; - RtpDataChannels data_channels_; + DataChannels data_channels_; bool enable_rtx_; rtc::CryptoOptions crypto_options_; diff --git a/webrtc/pc/channelmanager_unittest.cc b/webrtc/pc/channelmanager_unittest.cc index 30d2fe5879..df3e9e5ba3 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::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel( + cricket::DataChannel* data_channel = cm_->CreateDataChannel( &fake_mc_, transport_controller_, cricket::CN_DATA, nullptr, - kDefaultRtcpEnabled, kDefaultSrtpRequired); - EXPECT_TRUE(rtp_data_channel != nullptr); + kDefaultRtcpEnabled, kDefaultSrtpRequired, cricket::DCT_RTP); + EXPECT_TRUE(data_channel != nullptr); cm_->DestroyVideoChannel(video_channel); cm_->DestroyVoiceChannel(voice_channel); - cm_->DestroyRtpDataChannel(rtp_data_channel); + cm_->DestroyDataChannel(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::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel( + cricket::DataChannel* data_channel = cm_->CreateDataChannel( &fake_mc_, transport_controller_, cricket::CN_DATA, nullptr, - kDefaultRtcpEnabled, kDefaultSrtpRequired); - EXPECT_TRUE(rtp_data_channel != nullptr); + kDefaultRtcpEnabled, kDefaultSrtpRequired, cricket::DCT_RTP); + EXPECT_TRUE(data_channel != nullptr); cm_->DestroyVideoChannel(video_channel); cm_->DestroyVoiceChannel(voice_channel); - cm_->DestroyRtpDataChannel(rtp_data_channel); + cm_->DestroyDataChannel(data_channel); cm_->Terminate(); }