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: 67b3bbe639
Review-Url: https://codereview.webrtc.org/2564333002
Cr-Commit-Position: refs/heads/master@{#15973}
This commit is contained in:
deadbeef
2017-01-09 14:53:41 -08:00
committed by Commit bot
parent 1f035c0f2a
commit 953c2cea5e
34 changed files with 2828 additions and 2482 deletions

View File

@ -16,7 +16,7 @@
#include "webrtc/api/sctputils.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/refcount.h"
#include "webrtc/media/sctp/sctpdataengine.h"
#include "webrtc/media/sctp/sctptransportinternal.h"
namespace webrtc {
@ -328,12 +328,12 @@ void DataChannel::OnMessage(rtc::Message* msg) {
}
}
void DataChannel::OnDataReceived(cricket::DataChannel* channel,
const cricket::ReceiveDataParams& params,
void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& payload) {
uint32_t expected_ssrc =
(data_channel_type_ == cricket::DCT_RTP) ? receive_ssrc_ : config_.id;
if (params.ssrc != expected_ssrc) {
if (data_channel_type_ == cricket::DCT_RTP && params.ssrc != receive_ssrc_) {
return;
}
if (data_channel_type_ == cricket::DCT_SCTP && params.sid != config_.id) {
return;
}
@ -342,17 +342,17 @@ void DataChannel::OnDataReceived(cricket::DataChannel* channel,
if (handshake_state_ != kHandshakeWaitingForAck) {
// Ignore it if we are not expecting an ACK message.
LOG(LS_WARNING) << "DataChannel received unexpected CONTROL message, "
<< "sid = " << params.ssrc;
<< "sid = " << params.sid;
return;
}
if (ParseDataChannelOpenAckMessage(payload)) {
// We can send unordered as soon as we receive the ACK message.
handshake_state_ = kHandshakeReady;
LOG(LS_INFO) << "DataChannel received OPEN_ACK message, sid = "
<< params.ssrc;
<< params.sid;
} else {
LOG(LS_WARNING) << "DataChannel failed to parse OPEN_ACK message, sid = "
<< params.ssrc;
<< params.sid;
}
return;
}
@ -360,7 +360,7 @@ void DataChannel::OnDataReceived(cricket::DataChannel* channel,
ASSERT(params.type == cricket::DMT_BINARY ||
params.type == cricket::DMT_TEXT);
LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.ssrc;
LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.sid;
// We can send unordered as soon as we receive any DATA message since the
// remote side must have received the OPEN (and old clients do not send
// OPEN_ACK).
@ -390,9 +390,8 @@ void DataChannel::OnDataReceived(cricket::DataChannel* channel,
}
}
void DataChannel::OnStreamClosedRemotely(uint32_t sid) {
if (data_channel_type_ == cricket::DCT_SCTP &&
sid == static_cast<uint32_t>(config_.id)) {
void DataChannel::OnStreamClosedRemotely(int sid) {
if (data_channel_type_ == cricket::DCT_SCTP && sid == config_.id) {
Close();
}
}
@ -551,7 +550,7 @@ bool DataChannel::SendDataMessage(const DataBuffer& buffer,
send_params.max_rtx_count = config_.maxRetransmits;
send_params.max_rtx_ms = config_.maxRetransmitTime;
send_params.ssrc = config_.id;
send_params.sid = config_.id;
} else {
send_params.ssrc = send_ssrc_;
}
@ -623,7 +622,7 @@ bool DataChannel::SendControlMessage(const rtc::CopyOnWriteBuffer& buffer) {
(!is_open_message || !config_.negotiated));
cricket::SendDataParams send_params;
send_params.ssrc = config_.id;
send_params.sid = config_.id;
// Send data as ordered before we receive any message from the remote peer to
// make sure the remote peer will not receive any data before it receives the
// OPEN message.

View File

@ -144,11 +144,10 @@ class DataChannel : public DataChannelInterface,
// stream on an existing DataMediaChannel, and we've finished negotiation.
void OnChannelReady(bool writable);
// Sigslots from cricket::DataChannel
void OnDataReceived(cricket::DataChannel* channel,
const cricket::ReceiveDataParams& params,
// Slots for provider to connect signals to.
void OnDataReceived(const cricket::ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& payload);
void OnStreamClosedRemotely(uint32_t sid);
void OnStreamClosedRemotely(int sid);
// The remote peer request that this channel should be closed.
void RemotePeerRequestClose();

View File

@ -329,7 +329,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceivesOpenAck) {
params.type = cricket::DMT_CONTROL;
rtc::CopyOnWriteBuffer payload;
webrtc::WriteDataChannelOpenAckMessage(&payload);
dc->OnDataReceived(NULL, params, payload);
dc->OnDataReceived(params, payload);
// Sends another message and verifies it's unordered.
ASSERT_TRUE(dc->Send(buffer));
@ -353,7 +353,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceiveData) {
params.ssrc = init.id;
params.type = cricket::DMT_TEXT;
webrtc::DataBuffer buffer("data");
dc->OnDataReceived(NULL, params, buffer.data);
dc->OnDataReceived(params, buffer.data);
// Sends a message and verifies it's unordered.
ASSERT_TRUE(dc->Send(buffer));
@ -414,7 +414,7 @@ TEST_F(SctpDataChannelTest, ReceiveDataWithInvalidSsrc) {
cricket::ReceiveDataParams params;
params.ssrc = 0;
webrtc::DataBuffer buffer("abcd");
webrtc_data_channel_->OnDataReceived(NULL, params, buffer.data);
webrtc_data_channel_->OnDataReceived(params, buffer.data);
EXPECT_EQ(0U, observer_->messages_received());
}
@ -430,7 +430,7 @@ TEST_F(SctpDataChannelTest, ReceiveDataWithValidSsrc) {
params.ssrc = 1;
webrtc::DataBuffer buffer("abcd");
webrtc_data_channel_->OnDataReceived(NULL, params, buffer.data);
webrtc_data_channel_->OnDataReceived(params, buffer.data);
EXPECT_EQ(1U, observer_->messages_received());
}
@ -472,9 +472,9 @@ TEST_F(SctpDataChannelTest, VerifyMessagesAndBytesReceived) {
EXPECT_EQ(0U, webrtc_data_channel_->bytes_received());
// Receive three buffers while data channel isn't open.
webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[0].data);
webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[1].data);
webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[2].data);
webrtc_data_channel_->OnDataReceived(params, buffers[0].data);
webrtc_data_channel_->OnDataReceived(params, buffers[1].data);
webrtc_data_channel_->OnDataReceived(params, buffers[2].data);
EXPECT_EQ(0U, observer_->messages_received());
EXPECT_EQ(0U, webrtc_data_channel_->messages_received());
EXPECT_EQ(0U, webrtc_data_channel_->bytes_received());
@ -488,9 +488,9 @@ TEST_F(SctpDataChannelTest, VerifyMessagesAndBytesReceived) {
EXPECT_EQ(bytes_received, webrtc_data_channel_->bytes_received());
// Receive three buffers while open.
webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[3].data);
webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[4].data);
webrtc_data_channel_->OnDataReceived(nullptr, params, buffers[5].data);
webrtc_data_channel_->OnDataReceived(params, buffers[3].data);
webrtc_data_channel_->OnDataReceived(params, buffers[4].data);
webrtc_data_channel_->OnDataReceived(params, buffers[5].data);
bytes_received += buffers[3].size() + buffers[4].size() + buffers[5].size();
EXPECT_EQ(6U, observer_->messages_received());
EXPECT_EQ(6U, webrtc_data_channel_->messages_received());
@ -593,7 +593,7 @@ TEST_F(SctpDataChannelTest, ClosedWhenReceivedBufferFull) {
// Receiving data without having an observer will overflow the buffer.
for (size_t i = 0; i < 16 * 1024 + 1; ++i) {
webrtc_data_channel_->OnDataReceived(NULL, params, buffer);
webrtc_data_channel_->OnDataReceived(params, buffer);
}
EXPECT_EQ(webrtc::DataChannelInterface::kClosed,
webrtc_data_channel_->state());

View File

@ -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 @@ bool PeerConnection::Initialize(
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 @@ void PeerConnection::SetLocalDescription(
// SCTP sids.
rtc::SSLRole role;
if (session_->data_channel_type() == cricket::DCT_SCTP &&
session_->GetSslRole(session_->data_channel(), &role)) {
session_->GetSctpSslRole(&role)) {
AllocateSctpSids(role);
}
@ -1207,7 +1214,7 @@ void PeerConnection::SetRemoteDescription(
// SCTP sids.
rtc::SSLRole role;
if (session_->data_channel_type() == cricket::DCT_SCTP &&
session_->GetSslRole(session_->data_channel(), &role)) {
session_->GetSctpSslRole(&role)) {
AllocateSctpSids(role);
}
@ -2149,7 +2156,7 @@ rtc::scoped_refptr<DataChannel> PeerConnection::InternalCreateDataChannel(
if (session_->data_channel_type() == cricket::DCT_SCTP) {
if (new_config.id < 0) {
rtc::SSLRole role;
if ((session_->GetSslRole(session_->data_channel(), &role)) &&
if ((session_->GetSctpSslRole(&role)) &&
!sid_allocator_.AllocateSid(role, &new_config.id)) {
LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
return nullptr;

View File

@ -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"

View File

@ -463,10 +463,16 @@ void RTCStatsCollector::GetStatsReport(
ChannelNamePair(pc_->session()->video_channel()->content_name(),
pc_->session()->video_channel()->transport_name()));
}
if (pc_->session()->data_channel()) {
if (pc_->session()->rtp_data_channel()) {
channel_name_pairs_->data =
rtc::Optional<ChannelNamePair>(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());

View File

@ -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 @@ class MockWebRtcSession : public webrtc::WebRtcSession {
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

View File

@ -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"

View File

@ -33,6 +33,7 @@
#include "webrtc/call/call.h"
#include "webrtc/media/base/mediaconstants.h"
#include "webrtc/media/base/videocapturer.h"
#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/transportchannel.h"
#include "webrtc/pc/channel.h"
@ -74,9 +75,9 @@ const char kSdpWithoutIceUfragPwd[] =
"Called with SDP without ice-ufrag and ice-pwd.";
const char kSessionError[] = "Session error code: ";
const char kSessionErrorDesc[] = "Session error description: ";
const char kDtlsSetupFailureRtp[] =
const char kDtlsSrtpSetupFailureRtp[] =
"Couldn't set up DTLS-SRTP on RTP channel.";
const char kDtlsSetupFailureRtcp[] =
const char kDtlsSrtpSetupFailureRtcp[] =
"Couldn't set up DTLS-SRTP on RTCP channel.";
const char kEnableBundleFailed[] = "Failed to enable BUNDLE.";
@ -291,6 +292,31 @@ static bool GetTrackIdBySsrc(const SessionDescription* session_description,
return false;
}
// Get the SCTP port out of a SessionDescription.
// Return -1 if not found.
static int GetSctpPort(const SessionDescription* session_description) {
const ContentInfo* content_info = GetFirstDataContent(session_description);
RTC_DCHECK(content_info);
if (!content_info) {
return -1;
}
const cricket::DataContentDescription* data =
static_cast<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 @@ WebRtcSession::WebRtcSession(
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 @@ WebRtcSession::WebRtcSession(
// Due to this constraint session id |sid_| is max limited to LLONG_MAX.
sid_(rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX)),
transport_controller_(std::move(transport_controller)),
sctp_factory_(std::move(sctp_factory)),
media_controller_(media_controller),
channel_manager_(media_controller_->channel_manager()),
ice_observer_(NULL),
@ -470,7 +498,7 @@ WebRtcSession::WebRtcSession(
transport_controller_->SignalCandidatesRemoved.connect(
this, &WebRtcSession::OnTransportControllerCandidatesRemoved);
transport_controller_->SignalDtlsHandshakeError.connect(
this, &WebRtcSession::OnDtlsHandshakeError);
this, &WebRtcSession::OnTransportControllerDtlsHandshakeError);
}
WebRtcSession::~WebRtcSession() {
@ -485,9 +513,14 @@ WebRtcSession::~WebRtcSession() {
SignalVoiceChannelDestroyed();
channel_manager_->DestroyVoiceChannel(voice_channel_.release());
}
if (data_channel_) {
if (rtp_data_channel_) {
SignalDataChannelDestroyed();
channel_manager_->DestroyDataChannel(data_channel_.release());
channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release());
}
if (sctp_transport_) {
SignalDataChannelDestroyed();
network_thread_->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this));
}
#ifdef HAVE_QUIC
if (quic_data_transport_) {
@ -597,9 +630,10 @@ bool WebRtcSession::Initialize(
void WebRtcSession::Close() {
SetState(STATE_CLOSED);
RemoveUnusedChannels(nullptr);
ASSERT(!voice_channel_);
ASSERT(!video_channel_);
ASSERT(!data_channel_);
RTC_DCHECK(!voice_channel_);
RTC_DCHECK(!video_channel_);
RTC_DCHECK(!rtp_data_channel_);
RTC_DCHECK(!sctp_transport_);
media_controller_->Close();
}
@ -611,8 +645,9 @@ cricket::BaseChannel* WebRtcSession::GetChannel(
if (video_channel() && video_channel()->content_name() == content_name) {
return video_channel();
}
if (data_channel() && data_channel()->content_name() == content_name) {
return data_channel();
if (rtp_data_channel() &&
rtp_data_channel()->content_name() == content_name) {
return rtp_data_channel();
}
return nullptr;
}
@ -621,20 +656,31 @@ cricket::SecurePolicy WebRtcSession::SdesPolicy() const {
return webrtc_session_desc_factory_->SdesPolicy();
}
bool WebRtcSession::GetSslRole(const std::string& transport_name,
bool WebRtcSession::GetSctpSslRole(rtc::SSLRole* role) {
if (!local_description() || !remote_description()) {
LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the "
<< "SSL Role of the SCTP transport.";
return false;
}
if (!sctp_transport_) {
LOG(LS_INFO) << "Non-rejected SCTP m= section is needed to get the "
<< "SSL Role of the SCTP transport.";
return false;
}
return transport_controller_->GetSslRole(*sctp_transport_name_, role);
}
bool WebRtcSession::GetSslRole(const std::string& content_name,
rtc::SSLRole* role) {
if (!local_description() || !remote_description()) {
LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the "
<< "SSL Role of the session.";
return false;
}
return transport_controller_->GetSslRole(transport_name, role);
}
bool WebRtcSession::GetSslRole(const cricket::BaseChannel* channel,
rtc::SSLRole* role) {
return channel && GetSslRole(channel->transport_name(), role);
return transport_controller_->GetSslRole(GetTransportName(content_name),
role);
}
void WebRtcSession::CreateOffer(
@ -918,9 +964,29 @@ bool WebRtcSession::PushdownMediaDescription(
}
};
return (set_content(voice_channel()) &&
set_content(video_channel()) &&
set_content(data_channel()));
bool ret = (set_content(voice_channel()) && set_content(video_channel()) &&
set_content(rtp_data_channel()));
// Need complete offer/answer 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 @@ bool WebRtcSession::GetTransportDescription(
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 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
return false;
}
const std::string& transport_name = *first_content_name;
cricket::BaseChannel* first_channel = GetChannel(transport_name);
#ifdef HAVE_QUIC
if (quic_data_transport_ &&
@ -1050,8 +1075,8 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
}
#endif
auto maybe_set_transport = [this, bundle, transport_name,
first_channel](cricket::BaseChannel* ch) {
auto maybe_set_transport = [this, bundle,
transport_name](cricket::BaseChannel* ch) {
if (!ch || !bundle.HasContentName(ch->content_name())) {
return true;
}
@ -1073,9 +1098,21 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
if (!maybe_set_transport(voice_channel()) ||
!maybe_set_transport(video_channel()) ||
!maybe_set_transport(data_channel())) {
!maybe_set_transport(rtp_data_channel())) {
return false;
}
// For SCTP, transport creation/deletion happens here instead of in the
// object itself.
if (sctp_transport_) {
RTC_DCHECK(sctp_transport_name_);
RTC_DCHECK(sctp_content_name_);
if (transport_name != *sctp_transport_name_ &&
bundle.HasContentName(*sctp_content_name_)) {
network_thread_->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&WebRtcSession::ChangeSctpTransport_n, this,
transport_name));
}
}
return true;
}
@ -1248,60 +1285,129 @@ sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
bool WebRtcSession::SendData(const cricket::SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
cricket::SendDataResult* result) {
if (!data_channel_) {
LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
if (!rtp_data_channel_ && !sctp_transport_) {
LOG(LS_ERROR) << "SendData called when rtp_data_channel_ "
<< "and sctp_transport_ are NULL.";
return false;
}
return data_channel_->SendData(params, payload, result);
return rtp_data_channel_
? rtp_data_channel_->SendData(params, payload, result)
: network_thread_->Invoke<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 @@ void WebRtcSession::OnCertificateReady(
transport_controller_->SetLocalCertificate(certificate);
}
void WebRtcSession::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) {
SetError(ERROR_TRANSPORT,
rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp);
}
bool WebRtcSession::waiting_for_certificate_for_testing() const {
return webrtc_session_desc_factory_->waiting_for_certificate_for_testing();
}
@ -1455,7 +1566,16 @@ void WebRtcSession::OnTransportControllerCandidatesRemoved(
}
}
// Enabling voice and video channel.
void WebRtcSession::OnTransportControllerDtlsHandshakeError(
rtc::SSLHandshakeError error) {
if (metrics_observer_) {
metrics_observer_->IncrementEnumCounter(
webrtc::kEnumCounterDtlsHandshakeError, static_cast<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 @@ void WebRtcSession::EnableChannels() {
if (video_channel_ && !video_channel_->enabled())
video_channel_->Enable(true);
if (data_channel_ && !data_channel_->enabled())
data_channel_->Enable(true);
if (rtp_data_channel_ && !rtp_data_channel_->enabled())
rtp_data_channel_->Enable(true);
}
// Returns the media index for a local ice candidate given the content name.
@ -1574,9 +1694,15 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) {
const cricket::ContentInfo* data_info =
cricket::GetFirstDataContent(desc);
if (!data_info || data_info->rejected) {
if (data_channel_) {
if (rtp_data_channel_) {
SignalDataChannelDestroyed();
channel_manager_->DestroyDataChannel(data_channel_.release());
channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release());
}
if (sctp_transport_) {
SignalDataChannelDestroyed();
network_thread_->Invoke<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 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
}
const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
if (data_channel_type_ != cricket::DCT_NONE &&
data && !data->rejected && !data_channel_) {
if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected &&
!rtp_data_channel_ && !sctp_transport_) {
if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) {
LOG(LS_ERROR) << "Failed to create data channel.";
return false;
@ -1664,8 +1790,8 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content,
voice_channel_->ActivateRtcpMux();
}
voice_channel_->SignalDtlsSetupFailure.connect(
this, &WebRtcSession::OnDtlsSetupFailure);
voice_channel_->SignalDtlsSrtpSetupFailure.connect(
this, &WebRtcSession::OnDtlsSrtpSetupFailure);
SignalVoiceChannelCreated();
voice_channel_->SignalSentPacket.connect(this,
@ -1688,8 +1814,8 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content,
if (require_rtcp_mux) {
video_channel_->ActivateRtcpMux();
}
video_channel_->SignalDtlsSetupFailure.connect(
this, &WebRtcSession::OnDtlsSetupFailure);
video_channel_->SignalDtlsSrtpSetupFailure.connect(
this, &WebRtcSession::OnDtlsSrtpSetupFailure);
SignalVideoChannelCreated();
video_channel_->SignalSentPacket.connect(this,
@ -1699,40 +1825,48 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content,
bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport) {
const std::string transport_name =
bundle_transport ? *bundle_transport : content->name;
#ifdef HAVE_QUIC
if (data_channel_type_ == cricket::DCT_QUIC) {
RTC_DCHECK(transport_controller_->quic());
const std::string transport_name =
bundle_transport ? *bundle_transport : content->name;
quic_data_transport_->SetTransport(transport_name);
return true;
}
#endif // HAVE_QUIC
bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
bool require_rtcp_mux =
rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire;
bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux;
data_channel_.reset(channel_manager_->CreateDataChannel(
media_controller_, transport_controller_.get(), content->name,
bundle_transport, create_rtcp_transport_channel, SrtpRequired(),
data_channel_type_));
if (!data_channel_) {
return false;
}
if (require_rtcp_mux) {
data_channel_->ActivateRtcpMux();
}
if (sctp) {
data_channel_->SignalDataReceived.connect(
this, &WebRtcSession::OnDataChannelMessageReceived);
if (!sctp_factory_) {
LOG(LS_ERROR)
<< "Trying to create SCTP transport, but didn't compile with "
"SCTP support (HAVE_SCTP)";
return false;
}
if (!network_thread_->Invoke<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 @@ std::unique_ptr<SessionStats> WebRtcSession::GetStats_n(
return session_stats;
}
void WebRtcSession::OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp) {
SetError(ERROR_TRANSPORT,
rtcp ? kDtlsSetupFailureRtcp : kDtlsSetupFailureRtp);
bool WebRtcSession::CreateSctpTransport_n(const std::string& content_name,
const std::string& transport_name) {
RTC_DCHECK(network_thread_->IsCurrent());
RTC_DCHECK(sctp_factory_);
cricket::TransportChannel* tc =
transport_controller_->CreateTransportChannel_n(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
sctp_transport_ = sctp_factory_->CreateSctpTransport(tc);
RTC_DCHECK(sctp_transport_);
sctp_invoker_.reset(new rtc::AsyncInvoker());
sctp_transport_->SignalReadyToSendData.connect(
this, &WebRtcSession::OnSctpTransportReadyToSendData_n);
sctp_transport_->SignalDataReceived.connect(
this, &WebRtcSession::OnSctpTransportDataReceived_n);
sctp_transport_->SignalStreamClosedRemotely.connect(
this, &WebRtcSession::OnSctpStreamClosedRemotely_n);
sctp_transport_name_ = rtc::Optional<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 @@ void WebRtcSession::OnDataChannelMessageReceived(
}
config.open_handshake_role = InternalDataChannelInit::kAcker;
SignalDataChannelOpenMessage(label, config);
} else {
// Otherwise just forward the signal.
SignalSctpDataReceived(params, payload);
}
// Otherwise ignore the message.
}
void WebRtcSession::OnSctpStreamClosedRemotely_n(int sid) {
RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP);
RTC_DCHECK(network_thread_->IsCurrent());
sctp_invoker_->AsyncInvoke<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 @@ void WebRtcSession::ReportTransportStats() {
if (video_channel()) {
transport_names.insert(video_channel()->transport_name());
}
if (data_channel()) {
transport_names.insert(data_channel()->transport_name());
if (rtp_data_channel()) {
transport_names.insert(rtp_data_channel()->transport_name());
}
if (sctp_transport_name_) {
transport_names.insert(*sctp_transport_name_);
}
for (const auto& name : transport_names) {
cricket::TransportStats stats;
@ -2094,17 +2305,17 @@ const std::string WebRtcSession::GetTransportName(
return quic_data_transport_->transport_name();
}
#endif
if (sctp_transport_) {
RTC_DCHECK(sctp_content_name_);
RTC_DCHECK(sctp_transport_name_);
if (content_name == *sctp_content_name_) {
return *sctp_transport_name_;
}
}
// Return an empty string if failed to retrieve the transport name.
return "";
}
return channel->transport_name();
}
void WebRtcSession::OnDtlsHandshakeError(rtc::SSLHandshakeError error) {
if (metrics_observer_) {
metrics_observer_->IncrementEnumCounter(
webrtc::kEnumCounterDtlsHandshakeError, static_cast<int>(error),
static_cast<int>(rtc::SSLHandshakeError::MAX_VALUE));
}
}
} // namespace webrtc

View File

@ -22,6 +22,7 @@
#include "webrtc/api/peerconnectioninterface.h"
#include "webrtc/api/statstypes.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/optional.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/sslidentity.h"
#include "webrtc/base/thread.h"
@ -37,7 +38,9 @@
namespace cricket {
class ChannelManager;
class DataChannel;
class RtpDataChannel;
class SctpTransportInternal;
class SctpTransportInternalFactory;
class StatsReport;
class VideoChannel;
class VoiceChannel;
@ -67,8 +70,8 @@ extern const char kSdpWithoutIceUfragPwd[];
extern const char kSdpWithoutSdesAndDtlsDisabled[];
extern const char kSessionError[];
extern const char kSessionErrorDesc[];
extern const char kDtlsSetupFailureRtp[];
extern const char kDtlsSetupFailureRtcp[];
extern const char kDtlsSrtpSetupFailureRtp[];
extern const char kDtlsSrtpSetupFailureRtcp[];
extern const char kEnableBundleFailed[];
// Maximum number of received video streams that will be processed by webrtc
@ -158,13 +161,15 @@ class WebRtcSession :
ERROR_TRANSPORT = 2, // transport error of some kind
};
// |sctp_factory| may be null, in which case SCTP is treated as unsupported.
WebRtcSession(
webrtc::MediaControllerInterface* media_controller,
rtc::Thread* network_thread,
rtc::Thread* worker_thread,
rtc::Thread* signaling_thread,
cricket::PortAllocator* port_allocator,
std::unique_ptr<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 @@ class WebRtcSession :
ice_observer_ = observer;
}
// Exposed for stats collecting.
virtual cricket::VoiceChannel* voice_channel() {
return voice_channel_.get();
}
virtual cricket::VideoChannel* video_channel() {
return video_channel_.get();
}
virtual cricket::DataChannel* data_channel() {
return data_channel_.get();
// Only valid when using deprecated RTP data channels.
virtual cricket::RtpDataChannel* rtp_data_channel() {
return rtp_data_channel_.get();
}
virtual rtc::Optional<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 @@ class WebRtcSession :
// The ownership of |desc| will be transferred after this call.
bool SetRemoteDescription(SessionDescriptionInterface* desc,
std::string* err_desc);
bool ProcessIceMessage(const IceCandidateInterface* ice_candidate);
bool RemoveRemoteIceCandidates(
@ -326,7 +340,7 @@ class WebRtcSession :
// WebRTCSessionDescriptionFactory. Should happen before setLocalDescription.
void OnCertificateReady(
const rtc::scoped_refptr<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 @@ class WebRtcSession :
transport_controller_->SetMetricsObserver(metrics_observer);
}
// Called when voice_channel_, video_channel_ and data_channel_ are created
// and destroyed. As a result of, for example, setting a new description.
// Called when voice_channel_, video_channel_ and
// rtp_data_channel_/sctp_transport_ are created and destroyed. As a result
// of, for example, setting a new description.
sigslot::signal0<> SignalVoiceChannelCreated;
sigslot::signal0<> SignalVoiceChannelDestroyed;
sigslot::signal0<> SignalVideoChannelCreated;
@ -397,6 +412,7 @@ class WebRtcSession :
bool PushdownMediaDescription(cricket::ContentAction action,
cricket::ContentSource source,
std::string* error_desc);
bool PushdownSctpParameters_n(cricket::ContentSource source);
bool PushdownTransportDescription(cricket::ContentSource source,
cricket::ContentAction action,
@ -461,11 +477,24 @@ class WebRtcSession :
std::unique_ptr<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 @@ class WebRtcSession :
// this session.
bool SrtpRequired() const;
// TransportController signal handlers.
void OnTransportControllerConnectionState(cricket::IceConnectionState state);
void OnTransportControllerReceiving(bool receiving);
void OnTransportControllerGatheringState(cricket::IceGatheringState state);
@ -506,6 +536,7 @@ class WebRtcSession :
const std::vector<cricket::Candidate>& candidates);
void OnTransportControllerCandidatesRemoved(
const std::vector<cricket::Candidate>& candidates);
void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error);
std::string GetSessionErrorMsg();
@ -522,8 +553,6 @@ class WebRtcSession :
const std::string GetTransportName(const std::string& content_name);
void OnDtlsHandshakeError(rtc::SSLHandshakeError error);
rtc::Thread* const network_thread_;
rtc::Thread* const worker_thread_;
rtc::Thread* const signaling_thread_;
@ -536,10 +565,39 @@ class WebRtcSession :
bool initial_offerer_ = false;
const std::unique_ptr<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_;

View File

@ -40,6 +40,7 @@
#include "webrtc/media/base/fakevideorenderer.h"
#include "webrtc/media/base/mediachannel.h"
#include "webrtc/media/engine/fakewebrtccall.h"
#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/p2p/base/packettransportinterface.h"
#include "webrtc/p2p/base/stunserver.h"
#include "webrtc/p2p/base/teststunserver.h"
@ -109,6 +110,7 @@ static const char kMediaContentName0[] = "audio";
static const int kMediaContentIndex1 = 1;
static const char kMediaContentName1[] = "video";
static const int kDefaultTimeout = 10000; // 10 seconds.
static const int kIceCandidatesTimeout = 10000;
// STUN timeout with all retransmissions is a total of 9500ms.
static const int kStunTimeout = 9500;
@ -211,6 +213,52 @@ class MockIceObserver : public webrtc::IceObserver {
size_t num_candidates_removed_ = 0;
};
// Used for tests in this file to verify that WebRtcSession responds to signals
// from the SctpTransport correctly, and calls Start with the correct
// local/remote ports.
class FakeSctpTransport : public cricket::SctpTransportInternal {
public:
void SetTransportChannel(cricket::TransportChannel* channel) override {}
bool Start(int local_port, int remote_port) override {
local_port_ = local_port;
remote_port_ = remote_port;
return true;
}
bool OpenStream(int sid) override { return true; }
bool ResetStream(int sid) override { return true; }
bool SendData(const cricket::SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
cricket::SendDataResult* result = nullptr) override {
return true;
}
bool ReadyToSendData() override { return true; }
void set_debug_name_for_testing(const char* debug_name) override {}
int local_port() const { return local_port_; }
int remote_port() const { return remote_port_; }
private:
int local_port_ = -1;
int remote_port_ = -1;
};
class FakeSctpTransportFactory : public cricket::SctpTransportInternalFactory {
public:
std::unique_ptr<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 @@ class WebRtcSessionForTest : public webrtc::WebRtcSession {
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 @@ class WebRtcSessionForTest : public webrtc::WebRtcSession {
return rtcp_transport_channel(video_channel());
}
rtc::PacketTransportInterface* data_rtp_transport_channel() {
return rtp_transport_channel(data_channel());
}
rtc::PacketTransportInterface* data_rtcp_transport_channel() {
return rtcp_transport_channel(data_channel());
}
private:
rtc::PacketTransportInterface* rtp_transport_channel(
cricket::BaseChannel* ch) {
@ -386,13 +428,16 @@ class WebRtcSessionTest
std::unique_ptr<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 @@ class WebRtcSessionTest
webrtc::RtcEventLogNullImpl event_log_;
cricket::FakeMediaEngine* media_engine_;
cricket::FakeDataEngine* data_engine_;
// Actually owned by session_.
FakeSctpTransportFactory* fake_sctp_transport_factory_ = nullptr;
std::unique_ptr<cricket::ChannelManager> channel_manager_;
cricket::FakeCall fake_call_;
std::unique_ptr<webrtc::MediaControllerInterface> media_controller_;
@ -3875,7 +3922,7 @@ TEST_F(WebRtcSessionTest, TestRtpDataChannel) {
Init();
SetLocalDescriptionWithDataChannel();
ASSERT_TRUE(data_engine_);
EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type());
EXPECT_NE(nullptr, data_engine_->GetChannel(0));
}
TEST_P(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) {
@ -3887,7 +3934,43 @@ TEST_P(WebRtcSessionTest, TestRtpDataChannelConstraintTakesPrecedence) {
InitWithDtls(GetParam());
SetLocalDescriptionWithDataChannel();
EXPECT_EQ(cricket::DCT_RTP, data_engine_->last_channel_type());
EXPECT_NE(nullptr, data_engine_->GetChannel(0));
}
// Test that sctp_content_name/sctp_transport_name (used for stats) are correct
// before and after BUNDLE is negotiated.
TEST_P(WebRtcSessionTest, SctpContentAndTransportName) {
MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
SetFactoryDtlsSrtp();
InitWithDtls(GetParam());
// Initially these fields should be empty.
EXPECT_FALSE(session_->sctp_content_name());
EXPECT_FALSE(session_->sctp_transport_name());
// Create offer with audio/video/data.
// Default bundle policy is "balanced", so data should be using its own
// transport.
SendAudioVideoStream1();
CreateDataChannel();
InitiateCall();
ASSERT_TRUE(session_->sctp_content_name());
ASSERT_TRUE(session_->sctp_transport_name());
EXPECT_EQ("data", *session_->sctp_content_name());
EXPECT_EQ("data", *session_->sctp_transport_name());
// Create answer that finishes BUNDLE negotiation, which means everything
// should be bundled on the first transport (audio).
cricket::MediaSessionOptions answer_options;
answer_options.recv_video = true;
answer_options.bundle_enabled = true;
answer_options.data_channel_type = cricket::DCT_SCTP;
SetRemoteDescriptionWithoutError(CreateRemoteAnswer(
session_->local_description(), answer_options, cricket::SEC_DISABLED));
ASSERT_TRUE(session_->sctp_content_name());
ASSERT_TRUE(session_->sctp_transport_name());
EXPECT_EQ("data", *session_->sctp_content_name());
EXPECT_EQ("audio", *session_->sctp_transport_name());
}
TEST_P(WebRtcSessionTest, TestCreateOfferWithSctpEnabledWithoutStreams) {
@ -3919,30 +4002,39 @@ TEST_P(WebRtcSessionTest, TestCreateAnswerWithSctpInOfferAndNoStreams) {
EXPECT_TRUE(answer->description()->GetTransportInfoByName("data") != NULL);
}
// Test that if DTLS is disabled, we don't end up with an SctpTransport
// created (or an RtpDataChannel).
TEST_P(WebRtcSessionTest, TestSctpDataChannelWithoutDtls) {
configuration_.enable_dtls_srtp = rtc::Optional<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_P(WebRtcSessionTest, TestSctpDataChannelSendPortParsing) {
// TEST PLAN: Set the port number to something new, set it in the SDP,
// and pass it all the way down.
EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
CreateDataChannel();
cricket::FakeDataMediaChannel* ch = data_engine_->GetChannel(0);
int portnum = -1;
ASSERT_TRUE(ch != NULL);
ASSERT_EQ(1UL, ch->send_codecs().size());
EXPECT_EQ(cricket::kGoogleSctpDataCodecPlType, ch->send_codecs()[0].id);
EXPECT_EQ(0, strcmp(cricket::kGoogleSctpDataCodecName,
ch->send_codecs()[0].name.c_str()));
EXPECT_TRUE(ch->send_codecs()[0].GetParam(cricket::kCodecParamPort,
&portnum));
EXPECT_EQ(new_send_port, portnum);
ASSERT_EQ(1UL, ch->recv_codecs().size());
EXPECT_EQ(cricket::kGoogleSctpDataCodecPlType, ch->recv_codecs()[0].id);
EXPECT_EQ(0, strcmp(cricket::kGoogleSctpDataCodecName,
ch->recv_codecs()[0].name.c_str()));
EXPECT_TRUE(ch->recv_codecs()[0].GetParam(cricket::kCodecParamPort,
&portnum));
EXPECT_EQ(new_recv_port, portnum);
ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
EXPECT_EQ(
new_recv_port,
fake_sctp_transport_factory_->last_fake_sctp_transport()->local_port());
EXPECT_EQ(
new_send_port,
fake_sctp_transport_factory_->last_fake_sctp_transport()->remote_port());
}
// Verifies that when a session's DataChannel receives an OPEN message,
// WebRtcSession signals the DataChannel creation request with the expected
// Verifies that when a session's SctpTransport receives an OPEN message,
// WebRtcSession signals the SctpTransport creation request with the expected
// config.
TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) {
MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
@ -4005,8 +4085,10 @@ TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) {
InitWithDtls(GetParam());
SetLocalDescriptionWithDataChannel();
EXPECT_EQ(cricket::DCT_SCTP, data_engine_->last_channel_type());
EXPECT_EQ(nullptr, data_engine_->GetChannel(0));
ASSERT_NE(nullptr, fake_sctp_transport_factory_->last_fake_sctp_transport());
// Make the fake SCTP transport pretend it received an OPEN message.
webrtc::DataChannelInit config;
config.id = 1;
rtc::CopyOnWriteBuffer payload;
@ -4014,11 +4096,10 @@ TEST_P(WebRtcSessionTest, TestSctpDataChannelOpenMessage) {
cricket::ReceiveDataParams params;
params.ssrc = config.id;
params.type = cricket::DMT_CONTROL;
fake_sctp_transport_factory_->last_fake_sctp_transport()->SignalDataReceived(
params, payload);
cricket::DataChannel* data_channel = session_->data_channel();
data_channel->SignalDataReceived(data_channel, params, payload);
EXPECT_EQ("a", last_data_channel_label_);
EXPECT_EQ_WAIT("a", last_data_channel_label_, kDefaultTimeout);
EXPECT_EQ(config.id, last_data_channel_config_.id);
EXPECT_FALSE(last_data_channel_config_.negotiated);
EXPECT_EQ(webrtc::InternalDataChannelInit::kAcker,

View File

@ -400,7 +400,7 @@ void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
// We should pass the current SSL role to the transport description
// factory, if there is already an existing ongoing session.
rtc::SSLRole ssl_role;
if (session_->GetSslRole(session_->GetChannel(content.name), &ssl_role)) {
if (session_->GetSslRole(content.name, &ssl_role)) {
request.options.transport_options[content.name].prefer_passive_role =
(rtc::SSL_SERVER == ssl_role);
}