Revert of Separating SCTP code from BaseChannel/MediaChannel. (patchset #14 id:240001 of https://codereview.webrtc.org/2564333002/ )
Reason for revert:
Hitting DCHECK in chromium's WebrtcTransportTest.TerminateDataChannel and WebrtcTransportTest.DataStreamLate. Will investigate and reland.
Original issue's description:
> Separating SCTP code from BaseChannel/MediaChannel.
>
> The BaseChannel code is geared around RTP; the presence of media engines,
> send and receive streams, SRTP, SDP directional attribute negotiation, etc.
> It doesn't make sense to use it for SCTP as well. This separation should make
> future work both on BaseChannel and the SCTP code paths easier.
>
> SctpDataEngine now becomes SctpTransport, and is used by WebRtcSession
> directly. cricket::DataChannel is also renamed, to RtpDataChannel, so it
> doesn't get confused with webrtc::DataChannel any more.
>
> Beyond just moving code around, some consequences of this CL:
> - We'll now stop using the worker thread for SCTP. Packets will be
> processed right on the network thread instead.
> - The SDP directional attribute is ignored, as it's supposed to be.
>
> BUG=None
>
> Review-Url: https://codereview.webrtc.org/2564333002
> Cr-Commit-Position: refs/heads/master@{#15906}
> Committed: https://chromium.googlesource.com/external/webrtc/+/67b3bbe639645ab719972682359acda303d94454
TBR=pthatcher@webrtc.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=None
Review-Url: https://codereview.webrtc.org/2614813003
Cr-Commit-Position: refs/heads/master@{#15908}
diff --git a/webrtc/api/datachannel.cc b/webrtc/api/datachannel.cc
index ad7cb57..9812e9b 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::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 @@
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 @@
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::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<uint32_t>(config_.id)) {
Close();
}
}
@@ -550,7 +551,7 @@
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 @@
(!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 19f95df..9208ada 100644
--- a/webrtc/api/datachannel.h
+++ b/webrtc/api/datachannel.h
@@ -144,10 +144,11 @@
// 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 4ce1be5..a8c8361 100644
--- a/webrtc/api/datachannel_unittest.cc
+++ b/webrtc/api/datachannel_unittest.cc
@@ -329,7 +329,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 f8bea1b..cdf5a97 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 @@
std::unique_ptr<cricket::TransportController>(
factory_->CreateTransportController(
port_allocator_.get(),
- configuration.redetermine_role_on_ice_restart)),
-#ifdef HAVE_SCTP
- std::unique_ptr<cricket::SctpTransportInternalFactory>(
- 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 @@
// 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 @@
// 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 @@
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 d6da24e..c52df41 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 dc14970..3ab7ac9 100644
--- a/webrtc/api/rtcstatscollector.cc
+++ b/webrtc/api/rtcstatscollector.cc
@@ -463,16 +463,10 @@
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>(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>(
- 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 ae75035..7fefad8 100644
--- a/webrtc/api/test/mock_webrtcsession.h
+++ b/webrtc/api/test/mock_webrtcsession.h
@@ -15,7 +15,6 @@
#include <string>
#include "webrtc/api/webrtcsession.h"
-#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/test/gmock.h"
namespace webrtc {
@@ -36,8 +35,7 @@
std::unique_ptr<cricket::TransportController>(
new cricket::TransportController(rtc::Thread::Current(),
rtc::Thread::Current(),
- nullptr)),
- std::unique_ptr<cricket::SctpTransportInternalFactory>()) {}
+ 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 2c1ba64..e5ee416 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 dad485c..5d414ba 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 @@
"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 @@
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<const cricket::DataContentDescription*>(
- (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<int>(value);
- }
- }
- return -1;
-}
-
static bool BadSdp(const std::string& source,
const std::string& type,
const std::string& reason,
@@ -466,8 +440,7 @@
rtc::Thread* worker_thread,
rtc::Thread* signaling_thread,
cricket::PortAllocator* port_allocator,
- std::unique_ptr<cricket::TransportController> transport_controller,
- std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory)
+ std::unique_ptr<cricket::TransportController> transport_controller)
: network_thread_(network_thread),
worker_thread_(worker_thread),
signaling_thread_(signaling_thread),
@@ -476,7 +449,6 @@
// 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 @@
transport_controller_->SignalCandidatesRemoved.connect(
this, &WebRtcSession::OnTransportControllerCandidatesRemoved);
transport_controller_->SignalDtlsHandshakeError.connect(
- this, &WebRtcSession::OnTransportControllerDtlsHandshakeError);
+ this, &WebRtcSession::OnDtlsHandshakeError);
}
WebRtcSession::~WebRtcSession() {
@@ -513,14 +485,9 @@
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<void>(
- 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 @@
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 @@
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 @@
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 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<bool>(
- 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 @@
return true;
}
+std::unique_ptr<SessionStats> WebRtcSession::GetStats_s() {
+ ASSERT(signaling_thread()->IsCurrent());
+ ChannelNamePairs channel_name_pairs;
+ if (voice_channel()) {
+ channel_name_pairs.voice = rtc::Optional<ChannelNamePair>(ChannelNamePair(
+ voice_channel()->content_name(), voice_channel()->transport_name()));
+ }
+ if (video_channel()) {
+ channel_name_pairs.video = rtc::Optional<ChannelNamePair>(ChannelNamePair(
+ video_channel()->content_name(), video_channel()->transport_name()));
+ }
+ if (data_channel()) {
+ channel_name_pairs.data = rtc::Optional<ChannelNamePair>(ChannelNamePair(
+ data_channel()->content_name(), data_channel()->transport_name()));
+ }
+ return GetStats(channel_name_pairs);
+}
+
+std::unique_ptr<SessionStats> WebRtcSession::GetStats(
+ const ChannelNamePairs& channel_name_pairs) {
+ if (network_thread()->IsCurrent()) {
+ return GetStats_n(channel_name_pairs);
+ }
+ return network_thread()->Invoke<std::unique_ptr<SessionStats>>(
+ RTC_FROM_HERE,
+ rtc::Bind(&WebRtcSession::GetStats_n, this, channel_name_pairs));
+}
+
+bool WebRtcSession::GetLocalCertificate(
+ const std::string& transport_name,
+ rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
+ return transport_controller_->GetLocalCertificate(transport_name,
+ certificate);
+}
+
+std::unique_ptr<rtc::SSLCertificate> 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 @@
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 @@
}
#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 @@
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<void>(
- RTC_FROM_HERE, rtc::Bind(&WebRtcSession::ChangeSctpTransport_n, this,
- transport_name));
- }
- }
return true;
}
@@ -1283,129 +1248,60 @@
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<bool>(
- 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<void>(
- 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<void>(
- 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<SessionStats> WebRtcSession::GetStats_s() {
- ASSERT(signaling_thread()->IsCurrent());
- ChannelNamePairs channel_name_pairs;
- if (voice_channel()) {
- channel_name_pairs.voice = rtc::Optional<ChannelNamePair>(ChannelNamePair(
- voice_channel()->content_name(), voice_channel()->transport_name()));
- }
- if (video_channel()) {
- channel_name_pairs.video = rtc::Optional<ChannelNamePair>(ChannelNamePair(
- video_channel()->content_name(), video_channel()->transport_name()));
- }
- if (rtp_data_channel()) {
- channel_name_pairs.data = rtc::Optional<ChannelNamePair>(
- 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>(
- ChannelNamePair(*sctp_content_name_, *sctp_transport_name_));
- }
- return GetStats(channel_name_pairs);
-}
-
-std::unique_ptr<SessionStats> WebRtcSession::GetStats(
- const ChannelNamePairs& channel_name_pairs) {
- if (network_thread()->IsCurrent()) {
- return GetStats_n(channel_name_pairs);
- }
- return network_thread()->Invoke<std::unique_ptr<SessionStats>>(
- RTC_FROM_HERE,
- rtc::Bind(&WebRtcSession::GetStats_n, this, channel_name_pairs));
-}
-
-bool WebRtcSession::GetLocalCertificate(
- const std::string& transport_name,
- rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
- return transport_controller_->GetLocalCertificate(transport_name,
- certificate);
-}
-
-std::unique_ptr<rtc::SSLCertificate> 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 @@
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::OnTransportControllerDtlsHandshakeError(
- rtc::SSLHandshakeError error) {
- if (metrics_observer_) {
- metrics_observer_->IncrementEnumCounter(
- webrtc::kEnumCounterDtlsHandshakeError, static_cast<int>(error),
- static_cast<int>(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 @@
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 @@
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<void>(
- 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 @@
}
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 @@
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 @@
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::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<bool>(
- 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 @@
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<std::string>(transport_name);
- sctp_content_name_ = rtc::Optional<std::string>(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<std::string>(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<void>(
- 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<void>(
- 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 @@
}
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<void>(
- RTC_FROM_HERE, signaling_thread_,
- rtc::Bind(&sigslot::signal1<int>::operator(),
- &SignalSctpStreamClosedRemotely, sid));
+ // Otherwise ignore the message.
}
// Returns false if bundle is enabled and rtcp_mux is disabled.
@@ -2182,11 +1976,8 @@
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 @@
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<int>(error),
+ static_cast<int>(rtc::SSLHandshakeError::MAX_VALUE));
+ }
+}
} // namespace webrtc
diff --git a/webrtc/api/webrtcsession.h b/webrtc/api/webrtcsession.h
index e346112..ef31560 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 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 @@
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<cricket::TransportController> transport_controller,
- std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory);
+ std::unique_ptr<cricket::TransportController> transport_controller);
virtual ~WebRtcSession();
// These are const to allow them to be called from const methods.
@@ -204,34 +199,26 @@
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<std::string> sctp_content_name() const {
- return sctp_content_name_;
- }
- virtual rtc::Optional<std::string> 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 @@
// 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 @@
// WebRTCSessionDescriptionFactory. Should happen before setLocalDescription.
void OnCertificateReady(
const rtc::scoped_refptr<rtc::RTCCertificate>& 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 @@
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 @@
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 @@
std::unique_ptr<SessionStats> 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 @@
// 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 @@
const std::vector<cricket::Candidate>& candidates);
void OnTransportControllerCandidatesRemoved(
const std::vector<cricket::Candidate>& candidates);
- void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error);
std::string GetSessionErrorMsg();
@@ -553,6 +522,8 @@
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 @@
bool initial_offerer_ = false;
const std::unique_ptr<cricket::TransportController> transport_controller_;
- const std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory_;
MediaControllerInterface* media_controller_;
std::unique_ptr<cricket::VoiceChannel> voice_channel_;
std::unique_ptr<cricket::VideoChannel> video_channel_;
- // |rtp_data_channel_| is used if in RTP data channel mode, |sctp_transport_|
- // when using SCTP.
- std::unique_ptr<cricket::RtpDataChannel> rtp_data_channel_;
-
- std::unique_ptr<cricket::SctpTransportInternal> sctp_transport_;
- // |sctp_transport_name_| keeps track of what DTLS transport the SCTP
- // transport is using (which can change due to bundling).
- rtc::Optional<std::string> sctp_transport_name_;
- // |sctp_content_name_| is the content name (MID) in SDP.
- rtc::Optional<std::string> 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<rtc::AsyncInvoker> sctp_invoker_;
- sigslot::signal1<bool> SignalSctpReadyToSendData;
- sigslot::signal2<const cricket::ReceiveDataParams&,
- const rtc::CopyOnWriteBuffer&>
- SignalSctpDataReceived;
- sigslot::signal1<int> SignalSctpStreamClosedRemotely;
-
+ std::unique_ptr<cricket::DataChannel> 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 7c26d1d..116c8d5 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 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 @@
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<cricket::SctpTransportInternal> CreateSctpTransport(
- cricket::TransportChannel*) override {
- last_fake_sctp_transport_ = new FakeSctpTransport();
- return std::unique_ptr<cricket::SctpTransportInternal>(
- 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 @@
rtc::Thread* signaling_thread,
cricket::PortAllocator* port_allocator,
webrtc::IceObserver* ice_observer,
- std::unique_ptr<cricket::TransportController> transport_controller,
- std::unique_ptr<FakeSctpTransportFactory> sctp_factory)
+ std::unique_ptr<cricket::TransportController> 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 @@
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 @@
std::unique_ptr<rtc::RTCCertificateGeneratorInterface> 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<cricket::TransportController>(
new cricket::TransportController(rtc::Thread::Current(),
rtc::Thread::Current(),
- allocator_.get())),
- std::unique_ptr<FakeSctpTransportFactory>(
- fake_sctp_transport_factory_)));
+ allocator_.get()))));
session_->SignalDataChannelOpenMessage.connect(
this, &WebRtcSessionTest::OnDataChannelOpenMessage);
session_->GetOnDestroyedSignal()->connect(
@@ -1541,8 +1496,6 @@
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<cricket::ChannelManager> channel_manager_;
cricket::FakeCall fake_call_;
std::unique_ptr<webrtc::MediaControllerInterface> media_controller_;
@@ -3922,7 +3875,7 @@
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 @@
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 @@
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<bool>(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 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 @@
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 @@
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 0ab458b..1f811f5 100644
--- a/webrtc/api/webrtcsessiondescriptionfactory.cc
+++ b/webrtc/api/webrtcsessiondescriptionfactory.cc
@@ -400,7 +400,7 @@
// 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 3aee5b7..04936de 100644
--- a/webrtc/media/BUILD.gn
+++ b/webrtc/media/BUILD.gn
@@ -54,6 +54,7 @@
"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 @@
"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_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 cbd4e6e..932427b 100644
--- a/webrtc/media/base/fakemediaengine.h
+++ b/webrtc/media/base/fakemediaengine.h
@@ -942,9 +942,11 @@
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 @@
virtual const std::vector<DataCodec>& data_codecs() { return data_codecs_; }
+ DataChannelType last_channel_type() const { return last_channel_type_; }
+
private:
std::vector<FakeDataMediaChannel*> channels_;
std::vector<DataCodec> 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 0000000..341f054
--- /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 <memory>
+#include <string>
+#include <vector>
+
+#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<DataCodec>& data_codecs() { return codecs_; }
+
+ private:
+ std::unique_ptr<DataEngineInterface> first_;
+ std::unique_ptr<DataEngineInterface> second_;
+ std::vector<DataCodec> codecs_;
+};
+
+} // namespace cricket
+
+#endif // WEBRTC_MEDIA_BASE_HYBRIDDATAENGINE_H_
diff --git a/webrtc/media/base/mediachannel.h b/webrtc/media/base/mediachannel.h
index 4c6b024..d664240 100644
--- a/webrtc/media/base/mediachannel.h
+++ b/webrtc/media/base/mediachannel.h
@@ -1077,11 +1077,8 @@
// 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 @@
// 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 @@
// 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 @@
// Signal when the media channel is ready to send the stream. Arguments are:
// writable(bool)
sigslot::signal1<bool> SignalReadyToSend;
+ // Signal for notifying that the remote side has closed the DataChannel.
+ sigslot::signal1<uint32_t> SignalStreamClosedRemotely;
};
} // namespace cricket
diff --git a/webrtc/media/base/mediaconstants.h b/webrtc/media/base/mediaconstants.h
index 44d8c7e..547839a 100644
--- a/webrtc/media/base/mediaconstants.h
+++ b/webrtc/media/base/mediaconstants.h
@@ -106,9 +106,9 @@
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 0dbac55..debc171 100644
--- a/webrtc/media/base/mediaengine.h
+++ b/webrtc/media/base/mediaengine.h
@@ -171,7 +171,8 @@
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<DataCodec>& data_codecs() = 0;
};
diff --git a/webrtc/media/base/rtpdataengine.cc b/webrtc/media/base/rtpdataengine.cc
index 96f5a3b..12a37fa 100644
--- a/webrtc/media/base/rtpdataengine.cc
+++ b/webrtc/media/base/rtpdataengine.cc
@@ -40,7 +40,11 @@
}
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 ca43494..adf0bef 100644
--- a/webrtc/media/base/rtpdataengine.h
+++ b/webrtc/media/base/rtpdataengine.h
@@ -27,7 +27,8 @@
public:
RtpDataEngine();
- virtual DataMediaChannel* CreateChannel(const MediaConfig& config);
+ virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type,
+ const MediaConfig& config);
virtual const std::vector<DataCodec>& data_codecs() {
return data_codecs_;
diff --git a/webrtc/media/base/rtpdataengine_unittest.cc b/webrtc/media/base/rtpdataengine_unittest.cc
index dca509d..9d1e485 100644
--- a/webrtc/media/base/rtpdataengine_unittest.cc
+++ b/webrtc/media/base/rtpdataengine_unittest.cc
@@ -72,7 +72,8 @@
cricket::RtpDataMediaChannel* CreateChannel(cricket::RtpDataEngine* dme) {
cricket::MediaConfig config;
cricket::RtpDataMediaChannel* channel =
- static_cast<cricket::RtpDataMediaChannel*>(dme->CreateChannel(config));
+ static_cast<cricket::RtpDataMediaChannel*>(
+ 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 0000000..103aebd
--- /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 <stdarg.h>
+#include <stdio.h>
+
+#include <memory>
+#include <sstream>
+#include <vector>
+
+#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<SctpInboundPacket> InboundPacketMessage;
+typedef rtc::ScopedMessageData<rtc::CopyOnWriteBuffer> 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<void*>(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<SctpDataMediaChannel*>(addr);
+ LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
+ << "addr: " << addr << "; length: " << length
+ << "; tos: " << std::hex << static_cast<int>(tos)
+ << "; set_df: " << std::hex << static_cast<int>(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<uint8_t*>(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<SctpDataMediaChannel*>(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<SctpDataMediaChannel::PayloadProtocolIdentifier>(
+ 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<uint8_t*>(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<struct sockaddr_conn*>(&addrs[0]);
+ SctpDataMediaChannel* channel =
+ reinterpret_cast<SctpDataMediaChannel*>(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<sockaddr *>(&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<sockaddr *>(&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<size_t>(payload.size()), NULL, 0, &spa,
+ rtc::checked_cast<socklen_t>(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<char>(), 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<const sctp_notification&>(*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<DataCodec>& 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<int>(value);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool SctpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) {
+ return GetCodecIntParameter(
+ codecs, kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName,
+ kCodecParamPort, &remote_port_);
+}
+
+bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& 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<uint8_t> reset_stream_buf(num_bytes, 0);
+ struct sctp_reset_streams* resetp = reinterpret_cast<sctp_reset_streams*>(
+ &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<uint16_t>(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<socklen_t>(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<InboundPacketMessage> pdata(
+ static_cast<InboundPacketMessage*>(msg->pdata));
+ OnInboundPacketFromSctpToChannel(pdata->data().get());
+ break;
+ }
+ case MSG_SCTPOUTBOUNDPACKET: {
+ std::unique_ptr<OutboundPacketMessage> pdata(
+ static_cast<OutboundPacketMessage*>(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 0000000..8b6f37d
--- /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 <errno.h>
+#include <string>
+#include <vector>
+
+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 <sys/socket.h>
+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<DataCodec>& data_codecs() override { return codecs_; }
+
+ private:
+ const std::vector<DataCodec> 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<uint32_t> 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<DataCodec>& codecs);
+ bool SetRecvCodecs(const std::vector<DataCodec>& 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 0000000..060b2d9
--- /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 <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#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<rtc::CopyOnWriteBuffer> buffer(
+ static_cast<rtc::TypedMessageData<rtc::CopyOnWriteBuffer*>*>(
+ 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<uint32_t> 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<int> 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<SctpDataMediaChannel*>(
+ 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<SctpDataEngine> engine_;
+ std::unique_ptr<SctpFakeNetworkInterface> net1_;
+ std::unique_ptr<SctpFakeNetworkInterface> net2_;
+ std::unique_ptr<SctpFakeDataReceiver> recv1_;
+ std::unique_ptr<SctpFakeDataReceiver> recv2_;
+ std::unique_ptr<SctpDataMediaChannel> chan1_;
+ std::unique_ptr<SctpDataMediaChannel> 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<char> 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<struct socket*>(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 b95cf8a..0000000
--- 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 <errno.h>
-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 <stdarg.h>
-#include <stdio.h>
-
-#include <memory>
-#include <sstream>
-
-#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<uint32_t> 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<void*>(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<SctpTransport*>(addr);
- LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():"
- << "addr: " << addr << "; length: " << length
- << "; tos: " << std::hex << static_cast<int>(tos)
- << "; set_df: " << std::hex << static_cast<int>(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<uint8_t*>(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<void>(
- 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<SctpTransport*>(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<PayloadProtocolIdentifier>(
- 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<uint8_t*>(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<void>(
- 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<struct sockaddr_conn*>(&addrs[0]);
- SctpTransport* transport =
- reinterpret_cast<SctpTransport*>(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<size_t>(payload.size()), NULL, 0, &spa,
- rtc::checked_cast<socklen_t>(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<sockaddr*>(&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<sockaddr*>(&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<uint8_t> reset_stream_buf(num_bytes, 0);
- struct sctp_reset_streams* resetp =
- reinterpret_cast<sctp_reset_streams*>(&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<uint16_t>(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<socklen_t>(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<char>(), 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<const sctp_notification&>(*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 6d3a41a..0000000
--- 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 <errno.h>
-
-#include <memory> // for unique_ptr.
-#include <set>
-#include <string>
-#include <vector>
-
-#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 <sys/socket.h>
-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<uint32_t> 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<SctpTransportInternal> CreateSctpTransport(
- TransportChannel* channel) override {
- return std::unique_ptr<SctpTransportInternal>(
- 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 42e4dc6..0000000
--- 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 <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#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<char>(), 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<int> 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<int> 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<FakeTransportChannel> fake_dtls1_;
- std::unique_ptr<FakeTransportChannel> fake_dtls2_;
- std::unique_ptr<SctpFakeDataReceiver> recv1_;
- std::unique_ptr<SctpFakeDataReceiver> recv2_;
- std::unique_ptr<SctpTransport> transport1_;
- std::unique_ptr<SctpTransport> 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<SctpTransport> transport1(
- CreateTransport(&black_hole, &recv1));
- std::unique_ptr<SctpTransport> 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<SctpTransport> transport1(
- CreateTransport(&fake_dtls1, &recv1));
- std::unique_ptr<SctpTransport> 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<SctpTransport> 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<SctpTransport> 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<SctpTransport> 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<uint8_t>(), 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<char> 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 7dd6bc7..0000000
--- 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 <memory> // for unique_ptr
-#include <string>
-#include <vector>
-
-#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<const ReceiveDataParams&, const rtc::CopyOnWriteBuffer&>
- SignalDataReceived;
- // Parameter is SID of closed stream.
- sigslot::signal1<int> 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<SctpTransportInternal> 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 56335e2..1f29261 100644
--- a/webrtc/pc/channel.cc
+++ b/webrtc/pc/channel.cc
@@ -69,6 +69,7 @@
MSG_READYTOSENDDATA,
MSG_DATARECEIVED,
MSG_FIRSTPACKETRECEIVED,
+ MSG_STREAMCLOSEDREMOTELY,
};
// Value specified in RFC 5764.
@@ -930,16 +931,16 @@
UpdateMediaSendRecvState();
}
-void BaseChannel::SignalDtlsSrtpSetupFailure_n(bool rtcp) {
+void BaseChannel::SignalDtlsSetupFailure_n(bool rtcp) {
RTC_DCHECK(network_thread_->IsCurrent());
invoker_.AsyncInvoke<void>(
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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<DataCodec>(data, &send_params);
if (!media_channel()->SetSendParameters(send_params)) {
@@ -2310,7 +2352,7 @@
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 @@
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 @@
case MSG_DATARECEIVED: {
DataReceivedMessageData* data =
static_cast<DataReceivedMessageData*>(pmsg->pdata);
- SignalDataReceived(data->params, data->payload);
+ SignalDataReceived(this, data->params, data->payload);
delete data;
break;
}
@@ -2354,27 +2396,33 @@
delete data;
break;
}
+ case MSG_STREAMCLOSEDREMOTELY: {
+ rtc::TypedMessageData<uint32_t>* data =
+ static_cast<rtc::TypedMessageData<uint32_t>*>(pmsg->pdata);
+ SignalStreamClosedRemotely(data->data());
+ delete data;
+ break;
+ }
default:
BaseChannel::OnMessage(pmsg);
break;
}
}
-void RtpDataChannel::OnConnectionMonitorUpdate(
- ConnectionMonitor* monitor,
- const std::vector<ConnectionInfo>& infos) {
+void DataChannel::OnConnectionMonitorUpdate(
+ ConnectionMonitor* monitor, const std::vector<ConnectionInfo>& 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::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 @@
new DataChannelReadyToSendMessageData(writable));
}
-void RtpDataChannel::GetSrtpCryptoSuites_n(
- std::vector<int>* crypto_suites) const {
+void DataChannel::GetSrtpCryptoSuites_n(std::vector<int>* 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<uint32_t>* message =
+ new rtc::TypedMessageData<uint32_t>(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 ff98a2f..64c5f82 100644
--- a/webrtc/pc/channel.h
+++ b/webrtc/pc/channel.h
@@ -149,9 +149,9 @@
return remote_streams_;
}
- sigslot::signal2<BaseChannel*, bool> SignalDtlsSrtpSetupFailure;
- void SignalDtlsSrtpSetupFailure_n(bool rtcp);
- void SignalDtlsSrtpSetupFailure_s(bool rtcp);
+ sigslot::signal2<BaseChannel*, bool> SignalDtlsSetupFailure;
+ void SignalDtlsSetupFailure_n(bool rtcp);
+ void SignalDtlsSetupFailure_s(bool rtcp);
// Used for latency measurements.
sigslot::signal1<BaseChannel*> SignalFirstPacketReceived;
@@ -261,7 +261,7 @@
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 @@
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 @@
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 @@
return ready_to_send_data_;
}
- sigslot::signal2<RtpDataChannel*, const DataMediaInfo&> SignalMediaMonitor;
- sigslot::signal2<RtpDataChannel*, const std::vector<ConnectionInfo>&>
+ sigslot::signal2<DataChannel*, const DataMediaInfo&> SignalMediaMonitor;
+ sigslot::signal2<DataChannel*, const std::vector<ConnectionInfo>&>
SignalConnectionMonitor;
-
- sigslot::signal2<const ReceiveDataParams&, const rtc::CopyOnWriteBuffer&>
- SignalDataReceived;
+ sigslot::signal3<DataChannel*, const ReceiveDataParams&,
+ const rtc::CopyOnWriteBuffer&> 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<bool> SignalReadyToSendData;
+ // Signal for notifying that the remote side has closed the DataChannel.
+ sigslot::signal1<uint32_t> SignalStreamClosedRemotely;
cricket::MediaType media_type() override { return cricket::MEDIA_TYPE_DATA; }
protected:
@@ -692,9 +693,15 @@
// 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 @@
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<int>* crypto_suites) const override;
@@ -710,13 +718,18 @@
const std::vector<ConnectionInfo>& 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<DataMediaMonitor> 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 589ebd5..5de3c94 100644
--- a/webrtc/pc/channel_unittest.cc
+++ b/webrtc/pc/channel_unittest.cc
@@ -84,14 +84,14 @@
cricket::VideoMediaInfo,
cricket::VideoOptions> {};
-class DataTraits : public Traits<cricket::RtpDataChannel,
+class DataTraits : public Traits<cricket::DataChannel,
cricket::FakeDataMediaChannel,
cricket::DataContentDescription,
cricket::DataCodec,
cricket::DataMediaInfo,
cricket::DataOptions> {};
-// Base class for Voice/Video/RtpDataChannel tests
+// Base class for Voice/Video/DataChannel tests
template<class T>
class ChannelTest : public testing::Test, public sigslot::has_slots<> {
public:
@@ -3288,32 +3288,32 @@
Base::CanChangeMaxBitrate();
}
-// RtpDataChannelSingleThreadTest
-class RtpDataChannelSingleThreadTest : public ChannelTest<DataTraits> {
+// DataChannelSingleThreadTest
+class DataChannelSingleThreadTest : public ChannelTest<DataTraits> {
public:
typedef ChannelTest<DataTraits> Base;
- RtpDataChannelSingleThreadTest()
+ DataChannelSingleThreadTest()
: Base(true, kDataPacket, kRtcpReport, NetworkIsWorker::Yes) {}
};
-// RtpDataChannelDoubleThreadTest
-class RtpDataChannelDoubleThreadTest : public ChannelTest<DataTraits> {
+// DataChannelDoubleThreadTest
+class DataChannelDoubleThreadTest : public ChannelTest<DataTraits> {
public:
typedef ChannelTest<DataTraits> Base;
- RtpDataChannelDoubleThreadTest()
+ DataChannelDoubleThreadTest()
: Base(true, kDataPacket, kRtcpReport, NetworkIsWorker::No) {}
};
// Override to avoid engine channel parameter.
template <>
-cricket::RtpDataChannel* ChannelTest<DataTraits>::CreateChannel(
+cricket::DataChannel* ChannelTest<DataTraits>::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 @@
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 @@
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 cdfed68..a0b3609 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 @@
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 @@
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<RtpDataChannel*>(
- 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<DataChannel*>(
+ 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<void>(
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 3022eca..7df49bb 100644
--- a/webrtc/pc/channelmanager.h
+++ b/webrtc/pc/channelmanager.h
@@ -110,15 +110,16 @@
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 @@
private:
typedef std::vector<VoiceChannel*> VoiceChannels;
typedef std::vector<VideoChannel*> VideoChannels;
- typedef std::vector<RtpDataChannel*> RtpDataChannels;
+ typedef std::vector<DataChannel*> DataChannels;
void Construct(MediaEngineInterface* me,
DataEngineInterface* dme,
@@ -177,14 +178,15 @@
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<MediaEngineInterface> media_engine_;
std::unique_ptr<DataEngineInterface> data_media_engine_;
@@ -195,7 +197,7 @@
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 30d2fe5..df3e9e5 100644
--- a/webrtc/pc/channelmanager_unittest.cc
+++ b/webrtc/pc/channelmanager_unittest.cc
@@ -110,13 +110,13 @@
&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 @@
&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();
}