Reland of: 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-Original-Commit-Position: refs/heads/master@{#15906}
Committed: https://chromium.googlesource.com/external/webrtc/+/67b3bbe639645ab719972682359acda303d94454
Review-Url: https://codereview.webrtc.org/2564333002
Cr-Commit-Position: refs/heads/master@{#15973}
diff --git a/webrtc/api/datachannel.cc b/webrtc/api/datachannel.cc
index 9812e9b..ad7cb57 100644
--- a/webrtc/api/datachannel.cc
+++ b/webrtc/api/datachannel.cc
@@ -16,7 +16,7 @@
#include "webrtc/api/sctputils.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/refcount.h"
-#include "webrtc/media/sctp/sctpdataengine.h"
+#include "webrtc/media/sctp/sctptransportinternal.h"
namespace webrtc {
@@ -328,12 +328,12 @@
}
}
-void DataChannel::OnDataReceived(cricket::DataChannel* channel,
- const cricket::ReceiveDataParams& params,
+void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& payload) {
- uint32_t expected_ssrc =
- (data_channel_type_ == cricket::DCT_RTP) ? receive_ssrc_ : config_.id;
- if (params.ssrc != expected_ssrc) {
+ if (data_channel_type_ == cricket::DCT_RTP && params.ssrc != receive_ssrc_) {
+ return;
+ }
+ if (data_channel_type_ == cricket::DCT_SCTP && params.sid != config_.id) {
return;
}
@@ -342,17 +342,17 @@
if (handshake_state_ != kHandshakeWaitingForAck) {
// Ignore it if we are not expecting an ACK message.
LOG(LS_WARNING) << "DataChannel received unexpected CONTROL message, "
- << "sid = " << params.ssrc;
+ << "sid = " << params.sid;
return;
}
if (ParseDataChannelOpenAckMessage(payload)) {
// We can send unordered as soon as we receive the ACK message.
handshake_state_ = kHandshakeReady;
LOG(LS_INFO) << "DataChannel received OPEN_ACK message, sid = "
- << params.ssrc;
+ << params.sid;
} else {
LOG(LS_WARNING) << "DataChannel failed to parse OPEN_ACK message, sid = "
- << params.ssrc;
+ << params.sid;
}
return;
}
@@ -360,7 +360,7 @@
ASSERT(params.type == cricket::DMT_BINARY ||
params.type == cricket::DMT_TEXT);
- LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.ssrc;
+ LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.sid;
// We can send unordered as soon as we receive any DATA message since the
// remote side must have received the OPEN (and old clients do not send
// OPEN_ACK).
@@ -390,9 +390,8 @@
}
}
-void DataChannel::OnStreamClosedRemotely(uint32_t sid) {
- if (data_channel_type_ == cricket::DCT_SCTP &&
- sid == static_cast<uint32_t>(config_.id)) {
+void DataChannel::OnStreamClosedRemotely(int sid) {
+ if (data_channel_type_ == cricket::DCT_SCTP && sid == config_.id) {
Close();
}
}
@@ -551,7 +550,7 @@
send_params.max_rtx_count = config_.maxRetransmits;
send_params.max_rtx_ms = config_.maxRetransmitTime;
- send_params.ssrc = config_.id;
+ send_params.sid = config_.id;
} else {
send_params.ssrc = send_ssrc_;
}
@@ -623,7 +622,7 @@
(!is_open_message || !config_.negotiated));
cricket::SendDataParams send_params;
- send_params.ssrc = config_.id;
+ send_params.sid = config_.id;
// Send data as ordered before we receive any message from the remote peer to
// make sure the remote peer will not receive any data before it receives the
// OPEN message.
diff --git a/webrtc/api/datachannel.h b/webrtc/api/datachannel.h
index 9208ada..19f95df 100644
--- a/webrtc/api/datachannel.h
+++ b/webrtc/api/datachannel.h
@@ -144,11 +144,10 @@
// stream on an existing DataMediaChannel, and we've finished negotiation.
void OnChannelReady(bool writable);
- // Sigslots from cricket::DataChannel
- void OnDataReceived(cricket::DataChannel* channel,
- const cricket::ReceiveDataParams& params,
+ // Slots for provider to connect signals to.
+ void OnDataReceived(const cricket::ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& payload);
- void OnStreamClosedRemotely(uint32_t sid);
+ void OnStreamClosedRemotely(int sid);
// The remote peer request that this channel should be closed.
void RemotePeerRequestClose();
diff --git a/webrtc/api/datachannel_unittest.cc b/webrtc/api/datachannel_unittest.cc
index a8c8361..4ce1be5 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(NULL, params, payload);
+ dc->OnDataReceived(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(NULL, params, buffer.data);
+ dc->OnDataReceived(params, buffer.data);
// Sends a message and verifies it's unordered.
ASSERT_TRUE(dc->Send(buffer));
@@ -414,7 +414,7 @@
cricket::ReceiveDataParams params;
params.ssrc = 0;
webrtc::DataBuffer buffer("abcd");
- webrtc_data_channel_->OnDataReceived(NULL, params, buffer.data);
+ webrtc_data_channel_->OnDataReceived(params, buffer.data);
EXPECT_EQ(0U, observer_->messages_received());
}
@@ -430,7 +430,7 @@
params.ssrc = 1;
webrtc::DataBuffer buffer("abcd");
- webrtc_data_channel_->OnDataReceived(NULL, params, buffer.data);
+ webrtc_data_channel_->OnDataReceived(params, buffer.data);
EXPECT_EQ(1U, observer_->messages_received());
}
@@ -472,9 +472,9 @@
EXPECT_EQ(0U, webrtc_data_channel_->bytes_received());
// Receive three buffers while data channel isn't open.
- webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[0].data);
- webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[1].data);
- webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[2].data);
+ webrtc_data_channel_->OnDataReceived(params, buffers[0].data);
+ webrtc_data_channel_->OnDataReceived(params, buffers[1].data);
+ webrtc_data_channel_->OnDataReceived(params, buffers[2].data);
EXPECT_EQ(0U, observer_->messages_received());
EXPECT_EQ(0U, webrtc_data_channel_->messages_received());
EXPECT_EQ(0U, webrtc_data_channel_->bytes_received());
@@ -488,9 +488,9 @@
EXPECT_EQ(bytes_received, webrtc_data_channel_->bytes_received());
// Receive three buffers while open.
- webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[3].data);
- webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[4].data);
- webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[5].data);
+ webrtc_data_channel_->OnDataReceived(params, buffers[3].data);
+ webrtc_data_channel_->OnDataReceived(params, buffers[4].data);
+ webrtc_data_channel_->OnDataReceived(params, buffers[5].data);
bytes_received += buffers[3].size() + buffers[4].size() + buffers[5].size();
EXPECT_EQ(6U, observer_->messages_received());
EXPECT_EQ(6U, webrtc_data_channel_->messages_received());
@@ -593,7 +593,7 @@
// Receiving data without having an observer will overflow the buffer.
for (size_t i = 0; i < 16 * 1024 + 1; ++i) {
- webrtc_data_channel_->OnDataReceived(NULL, params, buffer);
+ webrtc_data_channel_->OnDataReceived(params, buffer);
}
EXPECT_EQ(webrtc::DataChannelInterface::kClosed,
webrtc_data_channel_->state());
diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc
index f0efe90..4f731da 100644
--- a/webrtc/api/peerconnection.cc
+++ b/webrtc/api/peerconnection.cc
@@ -38,7 +38,7 @@
#include "webrtc/base/trace_event.h"
#include "webrtc/call/call.h"
#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
-#include "webrtc/media/sctp/sctpdataengine.h"
+#include "webrtc/media/sctp/sctptransport.h"
#include "webrtc/pc/channelmanager.h"
#include "webrtc/system_wrappers/include/field_trial.h"
@@ -658,7 +658,14 @@
std::unique_ptr<cricket::TransportController>(
factory_->CreateTransportController(
port_allocator_.get(),
- configuration.redetermine_role_on_ice_restart))));
+ configuration.redetermine_role_on_ice_restart)),
+#ifdef HAVE_SCTP
+ std::unique_ptr<cricket::SctpTransportInternalFactory>(
+ new cricket::SctpTransportFactory(factory_->network_thread()))
+#else
+ nullptr
+#endif
+ ));
stats_.reset(new StatsCollector(this));
stats_collector_ = RTCStatsCollector::Create(this);
@@ -1125,7 +1132,7 @@
// SCTP sids.
rtc::SSLRole role;
if (session_->data_channel_type() == cricket::DCT_SCTP &&
- session_->GetSslRole(session_->data_channel(), &role)) {
+ session_->GetSctpSslRole(&role)) {
AllocateSctpSids(role);
}
@@ -1207,7 +1214,7 @@
// SCTP sids.
rtc::SSLRole role;
if (session_->data_channel_type() == cricket::DCT_SCTP &&
- session_->GetSslRole(session_->data_channel(), &role)) {
+ session_->GetSctpSslRole(&role)) {
AllocateSctpSids(role);
}
@@ -2149,7 +2156,7 @@
if (session_->data_channel_type() == cricket::DCT_SCTP) {
if (new_config.id < 0) {
rtc::SSLRole role;
- if ((session_->GetSslRole(session_->data_channel(), &role)) &&
+ if ((session_->GetSctpSslRole(&role)) &&
!sid_allocator_.AllocateSid(role, &new_config.id)) {
LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
return nullptr;
diff --git a/webrtc/api/peerconnectioninterface_unittest.cc b/webrtc/api/peerconnectioninterface_unittest.cc
index c52df41..d6da24e 100644
--- a/webrtc/api/peerconnectioninterface_unittest.cc
+++ b/webrtc/api/peerconnectioninterface_unittest.cc
@@ -35,7 +35,7 @@
#include "webrtc/base/stringutils.h"
#include "webrtc/base/thread.h"
#include "webrtc/media/base/fakevideocapturer.h"
-#include "webrtc/media/sctp/sctpdataengine.h"
+#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/p2p/base/fakeportallocator.h"
#include "webrtc/p2p/base/faketransportcontroller.h"
#include "webrtc/pc/mediasession.h"
diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc
index 3ab7ac9..dc14970 100644
--- a/webrtc/api/rtcstatscollector.cc
+++ b/webrtc/api/rtcstatscollector.cc
@@ -463,10 +463,16 @@
ChannelNamePair(pc_->session()->video_channel()->content_name(),
pc_->session()->video_channel()->transport_name()));
}
- if (pc_->session()->data_channel()) {
+ if (pc_->session()->rtp_data_channel()) {
+ channel_name_pairs_->data =
+ rtc::Optional<ChannelNamePair>(ChannelNamePair(
+ pc_->session()->rtp_data_channel()->content_name(),
+ pc_->session()->rtp_data_channel()->transport_name()));
+ }
+ if (pc_->session()->sctp_content_name()) {
channel_name_pairs_->data = rtc::Optional<ChannelNamePair>(
- ChannelNamePair(pc_->session()->data_channel()->content_name(),
- pc_->session()->data_channel()->transport_name()));
+ ChannelNamePair(*pc_->session()->sctp_content_name(),
+ *pc_->session()->sctp_transport_name()));
}
media_info_.reset(PrepareMediaInfo_s().release());
diff --git a/webrtc/api/test/mock_webrtcsession.h b/webrtc/api/test/mock_webrtcsession.h
index 7fefad8..ae75035 100644
--- a/webrtc/api/test/mock_webrtcsession.h
+++ b/webrtc/api/test/mock_webrtcsession.h
@@ -15,6 +15,7 @@
#include <string>
#include "webrtc/api/webrtcsession.h"
+#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/test/gmock.h"
namespace webrtc {
@@ -35,7 +36,8 @@
std::unique_ptr<cricket::TransportController>(
new cricket::TransportController(rtc::Thread::Current(),
rtc::Thread::Current(),
- nullptr))) {}
+ nullptr)),
+ std::unique_ptr<cricket::SctpTransportInternalFactory>()) {}
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 e5ee416..2c1ba64 100644
--- a/webrtc/api/webrtcsdp.cc
+++ b/webrtc/api/webrtcsdp.cc
@@ -33,7 +33,7 @@
#include "webrtc/media/base/cryptoparams.h"
#include "webrtc/media/base/mediaconstants.h"
#include "webrtc/media/base/rtputils.h"
-#include "webrtc/media/sctp/sctpdataengine.h"
+#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/p2pconstants.h"
#include "webrtc/p2p/base/port.h"
diff --git a/webrtc/api/webrtcsession.cc b/webrtc/api/webrtcsession.cc
index 5d414ba..5d67fef 100644
--- a/webrtc/api/webrtcsession.cc
+++ b/webrtc/api/webrtcsession.cc
@@ -33,6 +33,7 @@
#include "webrtc/call/call.h"
#include "webrtc/media/base/mediaconstants.h"
#include "webrtc/media/base/videocapturer.h"
+#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/transportchannel.h"
#include "webrtc/pc/channel.h"
@@ -74,9 +75,9 @@
"Called with SDP without ice-ufrag and ice-pwd.";
const char kSessionError[] = "Session error code: ";
const char kSessionErrorDesc[] = "Session error description: ";
-const char kDtlsSetupFailureRtp[] =
+const char kDtlsSrtpSetupFailureRtp[] =
"Couldn't set up DTLS-SRTP on RTP channel.";
-const char kDtlsSetupFailureRtcp[] =
+const char kDtlsSrtpSetupFailureRtcp[] =
"Couldn't set up DTLS-SRTP on RTCP channel.";
const char kEnableBundleFailed[] = "Failed to enable BUNDLE.";
@@ -291,6 +292,31 @@
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,
@@ -440,7 +466,8 @@
rtc::Thread* worker_thread,
rtc::Thread* signaling_thread,
cricket::PortAllocator* port_allocator,
- std::unique_ptr<cricket::TransportController> transport_controller)
+ std::unique_ptr<cricket::TransportController> transport_controller,
+ std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory)
: network_thread_(network_thread),
worker_thread_(worker_thread),
signaling_thread_(signaling_thread),
@@ -449,6 +476,7 @@
// Due to this constraint session id |sid_| is max limited to LLONG_MAX.
sid_(rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX)),
transport_controller_(std::move(transport_controller)),
+ sctp_factory_(std::move(sctp_factory)),
media_controller_(media_controller),
channel_manager_(media_controller_->channel_manager()),
ice_observer_(NULL),
@@ -470,7 +498,7 @@
transport_controller_->SignalCandidatesRemoved.connect(
this, &WebRtcSession::OnTransportControllerCandidatesRemoved);
transport_controller_->SignalDtlsHandshakeError.connect(
- this, &WebRtcSession::OnDtlsHandshakeError);
+ this, &WebRtcSession::OnTransportControllerDtlsHandshakeError);
}
WebRtcSession::~WebRtcSession() {
@@ -485,9 +513,14 @@
SignalVoiceChannelDestroyed();
channel_manager_->DestroyVoiceChannel(voice_channel_.release());
}
- if (data_channel_) {
+ if (rtp_data_channel_) {
SignalDataChannelDestroyed();
- channel_manager_->DestroyDataChannel(data_channel_.release());
+ channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release());
+ }
+ if (sctp_transport_) {
+ SignalDataChannelDestroyed();
+ network_thread_->Invoke<void>(
+ RTC_FROM_HERE, rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this));
}
#ifdef HAVE_QUIC
if (quic_data_transport_) {
@@ -597,9 +630,10 @@
void WebRtcSession::Close() {
SetState(STATE_CLOSED);
RemoveUnusedChannels(nullptr);
- ASSERT(!voice_channel_);
- ASSERT(!video_channel_);
- ASSERT(!data_channel_);
+ RTC_DCHECK(!voice_channel_);
+ RTC_DCHECK(!video_channel_);
+ RTC_DCHECK(!rtp_data_channel_);
+ RTC_DCHECK(!sctp_transport_);
media_controller_->Close();
}
@@ -611,8 +645,9 @@
if (video_channel() && video_channel()->content_name() == content_name) {
return video_channel();
}
- if (data_channel() && data_channel()->content_name() == content_name) {
- return data_channel();
+ if (rtp_data_channel() &&
+ rtp_data_channel()->content_name() == content_name) {
+ return rtp_data_channel();
}
return nullptr;
}
@@ -621,20 +656,31 @@
return webrtc_session_desc_factory_->SdesPolicy();
}
-bool WebRtcSession::GetSslRole(const std::string& transport_name,
+bool WebRtcSession::GetSctpSslRole(rtc::SSLRole* role) {
+ if (!local_description() || !remote_description()) {
+ LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the "
+ << "SSL Role of the SCTP transport.";
+ return false;
+ }
+ if (!sctp_transport_) {
+ LOG(LS_INFO) << "Non-rejected SCTP m= section is needed to get the "
+ << "SSL Role of the SCTP transport.";
+ return false;
+ }
+
+ return transport_controller_->GetSslRole(*sctp_transport_name_, role);
+}
+
+bool WebRtcSession::GetSslRole(const std::string& content_name,
rtc::SSLRole* role) {
if (!local_description() || !remote_description()) {
- LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
+ LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the "
<< "SSL Role of the session.";
return false;
}
- return transport_controller_->GetSslRole(transport_name, role);
-}
-
-bool WebRtcSession::GetSslRole(const cricket::BaseChannel* channel,
- rtc::SSLRole* role) {
- return channel && GetSslRole(channel->transport_name(), role);
+ return transport_controller_->GetSslRole(GetTransportName(content_name),
+ role);
}
void WebRtcSession::CreateOffer(
@@ -918,9 +964,29 @@
}
};
- return (set_content(voice_channel()) &&
- set_content(video_channel()) &&
- set_content(data_channel()));
+ bool ret = (set_content(voice_channel()) && set_content(video_channel()) &&
+ set_content(rtp_data_channel()));
+ // Need complete offer/answer with an SCTP m= section before starting SCTP,
+ // according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19
+ if (sctp_transport_ && local_description() && remote_description() &&
+ cricket::GetFirstDataContent(local_description()->description()) &&
+ cricket::GetFirstDataContent(remote_description()->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()));
}
bool WebRtcSession::PushdownTransportDescription(cricket::ContentSource source,
@@ -992,46 +1058,6 @@
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) {
@@ -1039,7 +1065,6 @@
return false;
}
const std::string& transport_name = *first_content_name;
- cricket::BaseChannel* first_channel = GetChannel(transport_name);
#ifdef HAVE_QUIC
if (quic_data_transport_ &&
@@ -1050,8 +1075,8 @@
}
#endif
- auto maybe_set_transport = [this, bundle, transport_name,
- first_channel](cricket::BaseChannel* ch) {
+ auto maybe_set_transport = [this, bundle,
+ transport_name](cricket::BaseChannel* ch) {
if (!ch || !bundle.HasContentName(ch->content_name())) {
return true;
}
@@ -1073,9 +1098,21 @@
if (!maybe_set_transport(voice_channel()) ||
!maybe_set_transport(video_channel()) ||
- !maybe_set_transport(data_channel())) {
+ !maybe_set_transport(rtp_data_channel())) {
return false;
}
+ // For SCTP, transport creation/deletion happens here instead of in the
+ // object itself.
+ if (sctp_transport_) {
+ RTC_DCHECK(sctp_transport_name_);
+ RTC_DCHECK(sctp_content_name_);
+ if (transport_name != *sctp_transport_name_ &&
+ bundle.HasContentName(*sctp_content_name_)) {
+ network_thread_->Invoke<void>(
+ RTC_FROM_HERE, rtc::Bind(&WebRtcSession::ChangeSctpTransport_n, this,
+ transport_name));
+ }
+ }
return true;
}
@@ -1248,60 +1285,129 @@
bool WebRtcSession::SendData(const cricket::SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
cricket::SendDataResult* result) {
- if (!data_channel_) {
- LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
+ if (!rtp_data_channel_ && !sctp_transport_) {
+ LOG(LS_ERROR) << "SendData called when rtp_data_channel_ "
+ << "and sctp_transport_ are NULL.";
return false;
}
- return data_channel_->SendData(params, payload, result);
+ return rtp_data_channel_
+ ? rtp_data_channel_->SendData(params, payload, result)
+ : network_thread_->Invoke<bool>(
+ RTC_FROM_HERE,
+ Bind(&cricket::SctpTransportInternal::SendData,
+ sctp_transport_.get(), params, payload, result));
}
bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
- if (!data_channel_) {
+ if (!rtp_data_channel_ && !sctp_transport_) {
// Don't log an error here, because DataChannels are expected to call
// ConnectDataChannel in this state. It's the only way to initially tell
// whether or not the underlying transport is ready.
return false;
}
- data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
- &DataChannel::OnChannelReady);
- data_channel_->SignalDataReceived.connect(webrtc_data_channel,
- &DataChannel::OnDataReceived);
- data_channel_->SignalStreamClosedRemotely.connect(
- webrtc_data_channel, &DataChannel::OnStreamClosedRemotely);
+ if (rtp_data_channel_) {
+ rtp_data_channel_->SignalReadyToSendData.connect(
+ webrtc_data_channel, &DataChannel::OnChannelReady);
+ rtp_data_channel_->SignalDataReceived.connect(webrtc_data_channel,
+ &DataChannel::OnDataReceived);
+ } else {
+ SignalSctpReadyToSendData.connect(webrtc_data_channel,
+ &DataChannel::OnChannelReady);
+ SignalSctpDataReceived.connect(webrtc_data_channel,
+ &DataChannel::OnDataReceived);
+ SignalSctpStreamClosedRemotely.connect(
+ webrtc_data_channel, &DataChannel::OnStreamClosedRemotely);
+ }
return true;
}
void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
- if (!data_channel_) {
- LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL.";
+ if (!rtp_data_channel_ && !sctp_transport_) {
+ LOG(LS_ERROR) << "DisconnectDataChannel called when rtp_data_channel_ and "
+ "sctp_transport_ are NULL.";
return;
}
- data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
- data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
- data_channel_->SignalStreamClosedRemotely.disconnect(webrtc_data_channel);
+ if (rtp_data_channel_) {
+ rtp_data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
+ rtp_data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
+ } else {
+ SignalSctpReadyToSendData.disconnect(webrtc_data_channel);
+ SignalSctpDataReceived.disconnect(webrtc_data_channel);
+ SignalSctpStreamClosedRemotely.disconnect(webrtc_data_channel);
+ }
}
void WebRtcSession::AddSctpDataStream(int sid) {
- if (!data_channel_) {
- LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL.";
+ if (!sctp_transport_) {
+ LOG(LS_ERROR) << "AddSctpDataStream called when sctp_transport_ is NULL.";
return;
}
- data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(sid));
- data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(sid));
+ network_thread_->Invoke<void>(
+ RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream,
+ sctp_transport_.get(), sid));
}
void WebRtcSession::RemoveSctpDataStream(int sid) {
- if (!data_channel_) {
- LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is "
+ if (!sctp_transport_) {
+ LOG(LS_ERROR) << "RemoveSctpDataStream called when sctp_transport_ is "
<< "NULL.";
return;
}
- data_channel_->RemoveRecvStream(sid);
- data_channel_->RemoveSendStream(sid);
+ network_thread_->Invoke<void>(
+ RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream,
+ sctp_transport_.get(), sid));
}
bool WebRtcSession::ReadyToSendData() const {
- return data_channel_ && data_channel_->ready_to_send_data();
+ return (rtp_data_channel_ && rtp_data_channel_->ready_to_send_data()) ||
+ sctp_ready_to_send_data_;
+}
+
+std::unique_ptr<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);
}
cricket::DataChannelType WebRtcSession::data_channel_type() const {
@@ -1326,6 +1432,11 @@
transport_controller_->SetLocalCertificate(certificate);
}
+void WebRtcSession::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) {
+ SetError(ERROR_TRANSPORT,
+ rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp);
+}
+
bool WebRtcSession::waiting_for_certificate_for_testing() const {
return webrtc_session_desc_factory_->waiting_for_certificate_for_testing();
}
@@ -1455,7 +1566,16 @@
}
}
-// Enabling voice and video channel.
+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.
void WebRtcSession::EnableChannels() {
if (voice_channel_ && !voice_channel_->enabled())
voice_channel_->Enable(true);
@@ -1463,8 +1583,8 @@
if (video_channel_ && !video_channel_->enabled())
video_channel_->Enable(true);
- if (data_channel_ && !data_channel_->enabled())
- data_channel_->Enable(true);
+ if (rtp_data_channel_ && !rtp_data_channel_->enabled())
+ rtp_data_channel_->Enable(true);
}
// Returns the media index for a local ice candidate given the content name.
@@ -1574,9 +1694,15 @@
const cricket::ContentInfo* data_info =
cricket::GetFirstDataContent(desc);
if (!data_info || data_info->rejected) {
- if (data_channel_) {
+ if (rtp_data_channel_) {
SignalDataChannelDestroyed();
- channel_manager_->DestroyDataChannel(data_channel_.release());
+ channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release());
+ }
+ if (sctp_transport_) {
+ SignalDataChannelDestroyed();
+ network_thread_->Invoke<void>(
+ RTC_FROM_HERE,
+ rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this));
}
#ifdef HAVE_QUIC
// Clean up the existing QuicDataTransport and its QuicTransportChannels.
@@ -1637,8 +1763,8 @@
}
const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
- if (data_channel_type_ != cricket::DCT_NONE &&
- data && !data->rejected && !data_channel_) {
+ if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected &&
+ !rtp_data_channel_ && !sctp_transport_) {
if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) {
LOG(LS_ERROR) << "Failed to create data channel.";
return false;
@@ -1664,8 +1790,8 @@
voice_channel_->ActivateRtcpMux();
}
- voice_channel_->SignalDtlsSetupFailure.connect(
- this, &WebRtcSession::OnDtlsSetupFailure);
+ voice_channel_->SignalDtlsSrtpSetupFailure.connect(
+ this, &WebRtcSession::OnDtlsSrtpSetupFailure);
SignalVoiceChannelCreated();
voice_channel_->SignalSentPacket.connect(this,
@@ -1688,8 +1814,8 @@
if (require_rtcp_mux) {
video_channel_->ActivateRtcpMux();
}
- video_channel_->SignalDtlsSetupFailure.connect(
- this, &WebRtcSession::OnDtlsSetupFailure);
+ video_channel_->SignalDtlsSrtpSetupFailure.connect(
+ this, &WebRtcSession::OnDtlsSrtpSetupFailure);
SignalVideoChannelCreated();
video_channel_->SignalSentPacket.connect(this,
@@ -1699,40 +1825,48 @@
bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport) {
+ const std::string transport_name =
+ bundle_transport ? *bundle_transport : content->name;
#ifdef HAVE_QUIC
if (data_channel_type_ == cricket::DCT_QUIC) {
RTC_DCHECK(transport_controller_->quic());
- const std::string transport_name =
- bundle_transport ? *bundle_transport : content->name;
quic_data_transport_->SetTransport(transport_name);
return true;
}
#endif // HAVE_QUIC
bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
- bool require_rtcp_mux =
- rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire;
- bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux;
- data_channel_.reset(channel_manager_->CreateDataChannel(
- media_controller_, transport_controller_.get(), content->name,
- bundle_transport, create_rtcp_transport_channel, SrtpRequired(),
- data_channel_type_));
- if (!data_channel_) {
- return false;
- }
- if (require_rtcp_mux) {
- data_channel_->ActivateRtcpMux();
- }
-
if (sctp) {
- data_channel_->SignalDataReceived.connect(
- this, &WebRtcSession::OnDataChannelMessageReceived);
+ if (!sctp_factory_) {
+ LOG(LS_ERROR)
+ << "Trying to create SCTP transport, but didn't compile with "
+ "SCTP support (HAVE_SCTP)";
+ return false;
+ }
+ if (!network_thread_->Invoke<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);
}
- data_channel_->SignalDtlsSetupFailure.connect(
- this, &WebRtcSession::OnDtlsSetupFailure);
-
SignalDataChannelCreated();
- data_channel_->SignalSentPacket.connect(this, &WebRtcSession::OnSentPacket_w);
return true;
}
@@ -1758,16 +1892,79 @@
return session_stats;
}
-void WebRtcSession::OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp) {
- SetError(ERROR_TRANSPORT,
- rtcp ? kDtlsSetupFailureRtcp : kDtlsSetupFailureRtp);
+bool WebRtcSession::CreateSctpTransport_n(const std::string& content_name,
+ const std::string& transport_name) {
+ RTC_DCHECK(network_thread_->IsCurrent());
+ RTC_DCHECK(sctp_factory_);
+ cricket::TransportChannel* tc =
+ transport_controller_->CreateTransportChannel_n(
+ transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
+ sctp_transport_ = sctp_factory_->CreateSctpTransport(tc);
+ RTC_DCHECK(sctp_transport_);
+ sctp_invoker_.reset(new rtc::AsyncInvoker());
+ sctp_transport_->SignalReadyToSendData.connect(
+ this, &WebRtcSession::OnSctpTransportReadyToSendData_n);
+ sctp_transport_->SignalDataReceived.connect(
+ this, &WebRtcSession::OnSctpTransportDataReceived_n);
+ sctp_transport_->SignalStreamClosedRemotely.connect(
+ this, &WebRtcSession::OnSctpStreamClosedRemotely_n);
+ sctp_transport_name_ = rtc::Optional<std::string>(transport_name);
+ sctp_content_name_ = rtc::Optional<std::string>(content_name);
+ return true;
}
-void WebRtcSession::OnDataChannelMessageReceived(
- cricket::DataChannel* channel,
+void WebRtcSession::ChangeSctpTransport_n(const std::string& transport_name) {
+ RTC_DCHECK(network_thread_->IsCurrent());
+ RTC_DCHECK(sctp_transport_);
+ RTC_DCHECK(sctp_transport_name_);
+ std::string old_sctp_transport_name = *sctp_transport_name_;
+ sctp_transport_name_ = rtc::Optional<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(
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.
@@ -1781,8 +1978,19 @@
}
config.open_handshake_role = InternalDataChannelInit::kAcker;
SignalDataChannelOpenMessage(label, config);
+ } else {
+ // Otherwise just forward the signal.
+ SignalSctpDataReceived(params, payload);
}
- // Otherwise ignore the message.
+}
+
+void WebRtcSession::OnSctpStreamClosedRemotely_n(int sid) {
+ RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP);
+ RTC_DCHECK(network_thread_->IsCurrent());
+ sctp_invoker_->AsyncInvoke<void>(
+ RTC_FROM_HERE, signaling_thread_,
+ rtc::Bind(&sigslot::signal1<int>::operator(),
+ &SignalSctpStreamClosedRemotely, sid));
}
// Returns false if bundle is enabled and rtcp_mux is disabled.
@@ -1976,8 +2184,11 @@
if (video_channel()) {
transport_names.insert(video_channel()->transport_name());
}
- if (data_channel()) {
- transport_names.insert(data_channel()->transport_name());
+ if (rtp_data_channel()) {
+ transport_names.insert(rtp_data_channel()->transport_name());
+ }
+ if (sctp_transport_name_) {
+ transport_names.insert(*sctp_transport_name_);
}
for (const auto& name : transport_names) {
cricket::TransportStats stats;
@@ -2094,17 +2305,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 ef31560..e346112 100644
--- a/webrtc/api/webrtcsession.h
+++ b/webrtc/api/webrtcsession.h
@@ -22,6 +22,7 @@
#include "webrtc/api/peerconnectioninterface.h"
#include "webrtc/api/statstypes.h"
#include "webrtc/base/constructormagic.h"
+#include "webrtc/base/optional.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/sslidentity.h"
#include "webrtc/base/thread.h"
@@ -37,7 +38,9 @@
namespace cricket {
class ChannelManager;
-class DataChannel;
+class RtpDataChannel;
+class SctpTransportInternal;
+class SctpTransportInternalFactory;
class StatsReport;
class VideoChannel;
class VoiceChannel;
@@ -67,8 +70,8 @@
extern const char kSdpWithoutSdesAndDtlsDisabled[];
extern const char kSessionError[];
extern const char kSessionErrorDesc[];
-extern const char kDtlsSetupFailureRtp[];
-extern const char kDtlsSetupFailureRtcp[];
+extern const char kDtlsSrtpSetupFailureRtp[];
+extern const char kDtlsSrtpSetupFailureRtcp[];
extern const char kEnableBundleFailed[];
// Maximum number of received video streams that will be processed by webrtc
@@ -158,13 +161,15 @@
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::TransportController> transport_controller,
+ std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory);
virtual ~WebRtcSession();
// These are const to allow them to be called from const methods.
@@ -199,26 +204,34 @@
ice_observer_ = observer;
}
+ // Exposed for stats collecting.
virtual cricket::VoiceChannel* voice_channel() {
return voice_channel_.get();
}
virtual cricket::VideoChannel* video_channel() {
return video_channel_.get();
}
- virtual cricket::DataChannel* data_channel() {
- return data_channel_.get();
+ // Only valid when using deprecated RTP data channels.
+ virtual cricket::RtpDataChannel* rtp_data_channel() {
+ return rtp_data_channel_.get();
+ }
+ virtual rtc::Optional<std::string> sctp_content_name() const {
+ return sctp_content_name_;
+ }
+ virtual rtc::Optional<std::string> sctp_transport_name() const {
+ return sctp_transport_name_;
}
cricket::BaseChannel* GetChannel(const std::string& content_name);
cricket::SecurePolicy SdesPolicy() const;
- // Get current ssl role from transport.
- bool GetSslRole(const std::string& transport_name, rtc::SSLRole* role);
-
- // Get current SSL role for this channel's transport.
- // If |transport| is null, returns false.
- bool GetSslRole(const cricket::BaseChannel* channel, rtc::SSLRole* role);
+ // Get current SSL role used by SCTP's underlying transport.
+ bool GetSctpSslRole(rtc::SSLRole* role);
+ // Get SSL role for an arbitrary m= section (handles bundling correctly).
+ // TODO(deadbeef): This is only used internally by the session description
+ // factory, it shouldn't really be public).
+ bool GetSslRole(const std::string& content_name, rtc::SSLRole* role);
void CreateOffer(
CreateSessionDescriptionObserver* observer,
@@ -232,6 +245,7 @@
// The ownership of |desc| will be transferred after this call.
bool SetRemoteDescription(SessionDescriptionInterface* desc,
std::string* err_desc);
+
bool ProcessIceMessage(const IceCandidateInterface* ice_candidate);
bool RemoveRemoteIceCandidates(
@@ -326,7 +340,7 @@
// WebRTCSessionDescriptionFactory. Should happen before setLocalDescription.
void OnCertificateReady(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
- void OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp);
+ void OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp);
// For unit test.
bool waiting_for_certificate_for_testing() const;
@@ -338,8 +352,9 @@
transport_controller_->SetMetricsObserver(metrics_observer);
}
- // Called when voice_channel_, video_channel_ and data_channel_ are created
- // and destroyed. As a result of, for example, setting a new description.
+ // Called when voice_channel_, video_channel_ and
+ // rtp_data_channel_/sctp_transport_ are created and destroyed. As a result
+ // of, for example, setting a new description.
sigslot::signal0<> SignalVoiceChannelCreated;
sigslot::signal0<> SignalVoiceChannelDestroyed;
sigslot::signal0<> SignalVideoChannelCreated;
@@ -397,6 +412,7 @@
bool PushdownMediaDescription(cricket::ContentAction action,
cricket::ContentSource source,
std::string* error_desc);
+ bool PushdownSctpParameters_n(cricket::ContentSource source);
bool PushdownTransportDescription(cricket::ContentSource source,
cricket::ContentAction action,
@@ -461,11 +477,24 @@
std::unique_ptr<SessionStats> GetStats_n(
const ChannelNamePairs& channel_name_pairs);
- // Listens to SCTP CONTROL messages on unused SIDs and process them as OPEN
- // messages.
- void OnDataChannelMessageReceived(cricket::DataChannel* channel,
- const cricket::ReceiveDataParams& params,
- const rtc::CopyOnWriteBuffer& payload);
+ bool CreateSctpTransport_n(const std::string& content_name,
+ const std::string& transport_name);
+ // For bundling.
+ void ChangeSctpTransport_n(const std::string& transport_name);
+ void DestroySctpTransport_n();
+ // SctpTransport signal handlers. Needed to marshal signals from the network
+ // to signaling thread.
+ void OnSctpTransportReadyToSendData_n();
+ // This may be called with "false" if the direction of the m= section causes
+ // us to tear down the SCTP connection.
+ void OnSctpTransportReadyToSendData_s(bool ready);
+ void OnSctpTransportDataReceived_n(const cricket::ReceiveDataParams& params,
+ const rtc::CopyOnWriteBuffer& payload);
+ // Beyond just firing the signal to the signaling thread, listens to SCTP
+ // CONTROL messages on unused SIDs and processes them as OPEN messages.
+ void OnSctpTransportDataReceived_s(const cricket::ReceiveDataParams& params,
+ const rtc::CopyOnWriteBuffer& payload);
+ void OnSctpStreamClosedRemotely_n(int sid);
std::string BadStateErrMsg(State state);
void SetIceConnectionState(PeerConnectionInterface::IceConnectionState state);
@@ -498,6 +527,7 @@
// this session.
bool SrtpRequired() const;
+ // TransportController signal handlers.
void OnTransportControllerConnectionState(cricket::IceConnectionState state);
void OnTransportControllerReceiving(bool receiving);
void OnTransportControllerGatheringState(cricket::IceGatheringState state);
@@ -506,6 +536,7 @@
const std::vector<cricket::Candidate>& candidates);
void OnTransportControllerCandidatesRemoved(
const std::vector<cricket::Candidate>& candidates);
+ void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error);
std::string GetSessionErrorMsg();
@@ -522,8 +553,6 @@
const std::string GetTransportName(const std::string& content_name);
- void OnDtlsHandshakeError(rtc::SSLHandshakeError error);
-
rtc::Thread* const network_thread_;
rtc::Thread* const worker_thread_;
rtc::Thread* const signaling_thread_;
@@ -536,10 +565,39 @@
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_;
- std::unique_ptr<cricket::DataChannel> data_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;
+
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 116c8d5..7c26d1d 100644
--- a/webrtc/api/webrtcsession_unittest.cc
+++ b/webrtc/api/webrtcsession_unittest.cc
@@ -40,6 +40,7 @@
#include "webrtc/media/base/fakevideorenderer.h"
#include "webrtc/media/base/mediachannel.h"
#include "webrtc/media/engine/fakewebrtccall.h"
+#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/p2p/base/packettransportinterface.h"
#include "webrtc/p2p/base/stunserver.h"
#include "webrtc/p2p/base/teststunserver.h"
@@ -109,6 +110,7 @@
static const int kMediaContentIndex1 = 1;
static const char kMediaContentName1[] = "video";
+static const int kDefaultTimeout = 10000; // 10 seconds.
static const int kIceCandidatesTimeout = 10000;
// STUN timeout with all retransmissions is a total of 9500ms.
static const int kStunTimeout = 9500;
@@ -211,6 +213,52 @@
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(
@@ -220,13 +268,15 @@
rtc::Thread* signaling_thread,
cricket::PortAllocator* port_allocator,
webrtc::IceObserver* ice_observer,
- std::unique_ptr<cricket::TransportController> transport_controller)
+ std::unique_ptr<cricket::TransportController> transport_controller,
+ std::unique_ptr<FakeSctpTransportFactory> sctp_factory)
: WebRtcSession(media_controller,
network_thread,
worker_thread,
signaling_thread,
port_allocator,
- std::move(transport_controller)) {
+ std::move(transport_controller),
+ std::move(sctp_factory)) {
RegisterIceObserver(ice_observer);
}
virtual ~WebRtcSessionForTest() {}
@@ -249,14 +299,6 @@
return rtcp_transport_channel(video_channel());
}
- rtc::PacketTransportInterface* data_rtp_transport_channel() {
- return rtp_transport_channel(data_channel());
- }
-
- rtc::PacketTransportInterface* data_rtcp_transport_channel() {
- return rtcp_transport_channel(data_channel());
- }
-
private:
rtc::PacketTransportInterface* rtp_transport_channel(
cricket::BaseChannel* ch) {
@@ -386,13 +428,16 @@
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()))));
+ allocator_.get())),
+ std::unique_ptr<FakeSctpTransportFactory>(
+ fake_sctp_transport_factory_)));
session_->SignalDataChannelOpenMessage.connect(
this, &WebRtcSessionTest::OnDataChannelOpenMessage);
session_->GetOnDestroyedSignal()->connect(
@@ -1496,6 +1541,8 @@
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_;
@@ -3875,7 +3922,7 @@
Init();
SetLocalDescriptionWithDataChannel();
ASSERT_TRUE(data_engine_);
- EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type());
+ EXPECT_NE(nullptr, data_engine_->GetChannel(0));
}
TEST_P(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) {
@@ -3887,7 +3934,43 @@
InitWithDtls(GetParam());
SetLocalDescriptionWithDataChannel();
- EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type());
+ EXPECT_NE(nullptr, data_engine_->GetChannel(0));
+}
+
+// Test that sctp_content_name/sctp_transport_name (used for stats) are correct
+// before and after BUNDLE is negotiated.
+TEST_P(WebRtcSessionTest, SctpContentAndTransportName) {
+ MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
+ SetFactoryDtlsSrtp();
+ InitWithDtls(GetParam());
+
+ // Initially these fields should be empty.
+ EXPECT_FALSE(session_->sctp_content_name());
+ EXPECT_FALSE(session_->sctp_transport_name());
+
+ // Create offer with audio/video/data.
+ // Default bundle policy is "balanced", so data should be using its own
+ // transport.
+ SendAudioVideoStream1();
+ CreateDataChannel();
+ InitiateCall();
+ ASSERT_TRUE(session_->sctp_content_name());
+ ASSERT_TRUE(session_->sctp_transport_name());
+ EXPECT_EQ("data", *session_->sctp_content_name());
+ EXPECT_EQ("data", *session_->sctp_transport_name());
+
+ // Create answer that finishes BUNDLE negotiation, which means everything
+ // should be bundled on the first transport (audio).
+ cricket::MediaSessionOptions answer_options;
+ answer_options.recv_video = true;
+ answer_options.bundle_enabled = true;
+ answer_options.data_channel_type = cricket::DCT_SCTP;
+ SetRemoteDescriptionWithoutError(CreateRemoteAnswer(
+ session_->local_description(), answer_options, cricket::SEC_DISABLED));
+ ASSERT_TRUE(session_->sctp_content_name());
+ ASSERT_TRUE(session_->sctp_transport_name());
+ EXPECT_EQ("data", *session_->sctp_content_name());
+ EXPECT_EQ("audio", *session_->sctp_transport_name());
}
TEST_P(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) {
@@ -3919,30 +4002,39 @@
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(cricket::DCT_NONE, data_engine_->last_channel_type());
+ EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
+ EXPECT_EQ(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
}
+// Test that if DTLS is enabled, we end up with an SctpTransport created
+// (and not an RtpDataChannel).
TEST_P(WebRtcSessionTest, TestSctpDataChannelWithDtls) {
MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
InitWithDtls(GetParam());
SetLocalDescriptionWithDataChannel();
- EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
+ EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
+ EXPECT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
}
+// Test that if SCTP is disabled, we don't end up with an SctpTransport
+// created (or an RtpDataChannel).
TEST_P(WebRtcSessionTest, TestDisableSctpDataChannels) {
MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
options_.disable_sctp_data_channels = true;
InitWithDtls(GetParam());
SetLocalDescriptionWithDataChannel();
- EXPECT_EQ(cricket::DCT_NONE, data_engine_->last_channel_type());
+ EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
+ EXPECT_EQ(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
}
TEST_P(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) {
@@ -3973,31 +4065,19 @@
// TEST PLAN: Set the port number to something new, set it in the SDP,
// and pass it all the way down.
- EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
+ EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
CreateDataChannel();
-
- cricket::FakeDataMediaChannel* ch = data_engine_->GetChannel(0);
- int portnum = -1;
- ASSERT_TRUE(ch != NULL);
- ASSERT_EQ(1UL, ch->send_codecs().size());
- EXPECT_EQ(cricket::kGoogleSctpDataCodecPlType, ch->send_codecs()[0].id);
- EXPECT_EQ(0, strcmp(cricket::kGoogleSctpDataCodecName,
- ch->send_codecs()[0].name.c_str()));
- EXPECT_TRUE(ch->send_codecs()[0].GetParam(cricket::kCodecParamPort,
- &portnum));
- EXPECT_EQ(new_send_port, portnum);
-
- ASSERT_EQ(1UL, ch->recv_codecs().size());
- EXPECT_EQ(cricket::kGoogleSctpDataCodecPlType, ch->recv_codecs()[0].id);
- EXPECT_EQ(0, strcmp(cricket::kGoogleSctpDataCodecName,
- ch->recv_codecs()[0].name.c_str()));
- EXPECT_TRUE(ch->recv_codecs()[0].GetParam(cricket::kCodecParamPort,
- &portnum));
- EXPECT_EQ(new_recv_port, portnum);
+ ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
+ EXPECT_EQ(
+ new_recv_port,
+ fake_sctp_transport_factory_->last_fake_sctp_transport()->local_port());
+ EXPECT_EQ(
+ new_send_port,
+ fake_sctp_transport_factory_->last_fake_sctp_transport()->remote_port());
}
-// Verifies that when a session's DataChannel receives an OPEN message,
-// WebRtcSession signals the DataChannel creation request with the expected
+// Verifies that when a session's SctpTransport receives an OPEN message,
+// WebRtcSession signals the SctpTransport creation request with the expected
// config.
TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) {
MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
@@ -4005,8 +4085,10 @@
InitWithDtls(GetParam());
SetLocalDescriptionWithDataChannel();
- EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
+ EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
+ ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
+ // Make the fake SCTP transport pretend it received an OPEN message.
webrtc::DataChannelInit config;
config.id = 1;
rtc::CopyOnWriteBuffer payload;
@@ -4014,11 +4096,10 @@
cricket::ReceiveDataParams params;
params.ssrc = config.id;
params.type = cricket::DMT_CONTROL;
+ fake_sctp_transport_factory_->last_fake_sctp_transport()->SignalDataReceived(
+ params, payload);
- cricket::DataChannel* data_channel = session_->data_channel();
- data_channel->SignalDataReceived(data_channel, params, payload);
-
- EXPECT_EQ("a", last_data_channel_label_);
+ EXPECT_EQ_WAIT("a", last_data_channel_label_, kDefaultTimeout);
EXPECT_EQ(config.id, last_data_channel_config_.id);
EXPECT_FALSE(last_data_channel_config_.negotiated);
EXPECT_EQ(webrtc::InternalDataChannelInit::kAcker,
diff --git a/webrtc/api/webrtcsessiondescriptionfactory.cc b/webrtc/api/webrtcsessiondescriptionfactory.cc
index 1f811f5..0ab458b 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(session_->GetChannel(content.name), &ssl_role)) {
+ if (session_->GetSslRole(content.name, &ssl_role)) {
request.options.transport_options[content.name].prefer_passive_role =
(rtc::SSL_SERVER == ssl_role);
}