Separating SCTP code from BaseChannel/MediaChannel.

The BaseChannel code is geared around RTP; the presence of media engines,
send and receive streams, SRTP, SDP directional attribute negotiation, etc.
It doesn't make sense to use it for SCTP as well. This separation should make
future work both on BaseChannel and the SCTP code paths easier.

SctpDataEngine now becomes SctpTransport, and is used by WebRtcSession
directly. cricket::DataChannel is also renamed, to RtpDataChannel, so it
doesn't get confused with webrtc::DataChannel any more.

Beyond just moving code around, some consequences of this CL:
- We'll now stop using the worker thread for SCTP. Packets will be
  processed right on the network thread instead.
- The SDP directional attribute is ignored, as it's supposed to be.

BUG=None

Review-Url: https://codereview.webrtc.org/2564333002
Cr-Commit-Position: refs/heads/master@{#15906}
This commit is contained in:
deadbeef 2017-01-04 18:38:02 -08:00 committed by Commit bot
parent d9762e7938
commit 67b3bbe639
34 changed files with 2826 additions and 2481 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"
@ -652,7 +652,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);
@ -1119,7 +1126,7 @@ void PeerConnection::SetLocalDescription(
// SCTP sids.
rtc::SSLRole role;
if (session_->data_channel_type() == cricket::DCT_SCTP &&
session_->GetSslRole(session_->data_channel(), &role)) {
session_->GetSctpSslRole(&role)) {
AllocateSctpSids(role);
}
@ -1201,7 +1208,7 @@ void PeerConnection::SetRemoteDescription(
// SCTP sids.
rtc::SSLRole role;
if (session_->data_channel_type() == cricket::DCT_SCTP &&
session_->GetSslRole(session_->data_channel(), &role)) {
session_->GetSctpSslRole(&role)) {
AllocateSctpSids(role);
}
@ -2143,7 +2150,7 @@ rtc::scoped_refptr<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,27 @@ bool WebRtcSession::PushdownMediaDescription(
}
};
return (set_content(voice_channel()) &&
set_content(video_channel()) &&
set_content(data_channel()));
bool ret = (set_content(voice_channel()) && set_content(video_channel()) &&
set_content(rtp_data_channel()));
// Need complete offer/answer before starting SCTP, according to
// https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19
if (sctp_transport_ && local_description() && remote_description()) {
ret &= network_thread_->Invoke<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 +1056,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 +1063,6 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
return false;
}
const std::string& transport_name = *first_content_name;
cricket::BaseChannel* first_channel = GetChannel(transport_name);
#ifdef HAVE_QUIC
if (quic_data_transport_ &&
@ -1050,8 +1073,8 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
}
#endif
auto maybe_set_transport = [this, bundle, transport_name,
first_channel](cricket::BaseChannel* ch) {
auto maybe_set_transport = [this, bundle,
transport_name](cricket::BaseChannel* ch) {
if (!ch || !bundle.HasContentName(ch->content_name())) {
return true;
}
@ -1073,9 +1096,21 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
if (!maybe_set_transport(voice_channel()) ||
!maybe_set_transport(video_channel()) ||
!maybe_set_transport(data_channel())) {
!maybe_set_transport(rtp_data_channel())) {
return false;
}
// For SCTP, transport creation/deletion happens here instead of in the
// object itself.
if (sctp_transport_) {
RTC_DCHECK(sctp_transport_name_);
RTC_DCHECK(sctp_content_name_);
if (transport_name != *sctp_transport_name_ &&
bundle.HasContentName(*sctp_content_name_)) {
network_thread_->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&WebRtcSession::ChangeSctpTransport_n, this,
transport_name));
}
}
return true;
}
@ -1248,60 +1283,129 @@ sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
bool WebRtcSession::SendData(const cricket::SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
cricket::SendDataResult* result) {
if (!data_channel_) {
LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
if (!rtp_data_channel_ && !sctp_transport_) {
LOG(LS_ERROR) << "SendData called when rtp_data_channel_ "
<< "and sctp_transport_ are NULL.";
return false;
}
return data_channel_->SendData(params, payload, result);
return rtp_data_channel_
? rtp_data_channel_->SendData(params, payload, result)
: network_thread_->Invoke<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 +1430,11 @@ void WebRtcSession::OnCertificateReady(
transport_controller_->SetLocalCertificate(certificate);
}
void WebRtcSession::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) {
SetError(ERROR_TRANSPORT,
rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp);
}
bool WebRtcSession::waiting_for_certificate_for_testing() const {
return webrtc_session_desc_factory_->waiting_for_certificate_for_testing();
}
@ -1455,7 +1564,16 @@ void WebRtcSession::OnTransportControllerCandidatesRemoved(
}
}
// Enabling voice and video channel.
void WebRtcSession::OnTransportControllerDtlsHandshakeError(
rtc::SSLHandshakeError error) {
if (metrics_observer_) {
metrics_observer_->IncrementEnumCounter(
webrtc::kEnumCounterDtlsHandshakeError, static_cast<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 +1581,8 @@ void WebRtcSession::EnableChannels() {
if (video_channel_ && !video_channel_->enabled())
video_channel_->Enable(true);
if (data_channel_ && !data_channel_->enabled())
data_channel_->Enable(true);
if (rtp_data_channel_ && !rtp_data_channel_->enabled())
rtp_data_channel_->Enable(true);
}
// Returns the media index for a local ice candidate given the content name.
@ -1574,9 +1692,15 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) {
const cricket::ContentInfo* data_info =
cricket::GetFirstDataContent(desc);
if (!data_info || data_info->rejected) {
if (data_channel_) {
if (rtp_data_channel_) {
SignalDataChannelDestroyed();
channel_manager_->DestroyDataChannel(data_channel_.release());
channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release());
}
if (sctp_transport_) {
SignalDataChannelDestroyed();
network_thread_->Invoke<void>(
RTC_FROM_HERE,
rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this));
}
#ifdef HAVE_QUIC
// Clean up the existing QuicDataTransport and its QuicTransportChannels.
@ -1637,8 +1761,8 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
}
const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
if (data_channel_type_ != cricket::DCT_NONE &&
data && !data->rejected && !data_channel_) {
if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected &&
!rtp_data_channel_ && !sctp_transport_) {
if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) {
LOG(LS_ERROR) << "Failed to create data channel.";
return false;
@ -1664,8 +1788,8 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content,
voice_channel_->ActivateRtcpMux();
}
voice_channel_->SignalDtlsSetupFailure.connect(
this, &WebRtcSession::OnDtlsSetupFailure);
voice_channel_->SignalDtlsSrtpSetupFailure.connect(
this, &WebRtcSession::OnDtlsSrtpSetupFailure);
SignalVoiceChannelCreated();
voice_channel_->SignalSentPacket.connect(this,
@ -1688,8 +1812,8 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content,
if (require_rtcp_mux) {
video_channel_->ActivateRtcpMux();
}
video_channel_->SignalDtlsSetupFailure.connect(
this, &WebRtcSession::OnDtlsSetupFailure);
video_channel_->SignalDtlsSrtpSetupFailure.connect(
this, &WebRtcSession::OnDtlsSrtpSetupFailure);
SignalVideoChannelCreated();
video_channel_->SignalSentPacket.connect(this,
@ -1699,40 +1823,48 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content,
bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport) {
const std::string transport_name =
bundle_transport ? *bundle_transport : content->name;
#ifdef HAVE_QUIC
if (data_channel_type_ == cricket::DCT_QUIC) {
RTC_DCHECK(transport_controller_->quic());
const std::string transport_name =
bundle_transport ? *bundle_transport : content->name;
quic_data_transport_->SetTransport(transport_name);
return true;
}
#endif // HAVE_QUIC
bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
bool require_rtcp_mux =
rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire;
bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux;
data_channel_.reset(channel_manager_->CreateDataChannel(
media_controller_, transport_controller_.get(), content->name,
bundle_transport, create_rtcp_transport_channel, SrtpRequired(),
data_channel_type_));
if (!data_channel_) {
return false;
}
if (require_rtcp_mux) {
data_channel_->ActivateRtcpMux();
}
if (sctp) {
data_channel_->SignalDataReceived.connect(
this, &WebRtcSession::OnDataChannelMessageReceived);
if (!sctp_factory_) {
LOG(LS_ERROR)
<< "Trying to create SCTP transport, but didn't compile with "
"SCTP support (HAVE_SCTP)";
return false;
}
if (!network_thread_->Invoke<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 +1890,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 +1976,19 @@ void WebRtcSession::OnDataChannelMessageReceived(
}
config.open_handshake_role = InternalDataChannelInit::kAcker;
SignalDataChannelOpenMessage(label, config);
} else {
// Otherwise just forward the signal.
SignalSctpDataReceived(params, payload);
}
// Otherwise ignore the message.
}
void WebRtcSession::OnSctpStreamClosedRemotely_n(int sid) {
RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP);
RTC_DCHECK(network_thread_->IsCurrent());
sctp_invoker_->AsyncInvoke<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 +2182,11 @@ void WebRtcSession::ReportTransportStats() {
if (video_channel()) {
transport_names.insert(video_channel()->transport_name());
}
if (data_channel()) {
transport_names.insert(data_channel()->transport_name());
if (rtp_data_channel()) {
transport_names.insert(rtp_data_channel()->transport_name());
}
if (sctp_transport_name_) {
transport_names.insert(*sctp_transport_name_);
}
for (const auto& name : transport_names) {
cricket::TransportStats stats;
@ -2094,17 +2303,17 @@ const std::string WebRtcSession::GetTransportName(
return quic_data_transport_->transport_name();
}
#endif
if (sctp_transport_) {
RTC_DCHECK(sctp_content_name_);
RTC_DCHECK(sctp_transport_name_);
if (content_name == *sctp_content_name_) {
return *sctp_transport_name_;
}
}
// Return an empty string if failed to retrieve the transport name.
return "";
}
return channel->transport_name();
}
void WebRtcSession::OnDtlsHandshakeError(rtc::SSLHandshakeError error) {
if (metrics_observer_) {
metrics_observer_->IncrementEnumCounter(
webrtc::kEnumCounterDtlsHandshakeError, static_cast<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);
}

View File

@ -143,12 +143,13 @@ rtc_static_library("rtc_media") {
"engine/webrtcvoe.h",
"engine/webrtcvoiceengine.cc",
"engine/webrtcvoiceengine.h",
"sctp/sctptransportinternal.h",
]
if (rtc_enable_sctp) {
sources += [
"sctp/sctpdataengine.cc",
"sctp/sctpdataengine.h",
"sctp/sctptransport.cc",
"sctp/sctptransport.h",
]
}
@ -346,7 +347,7 @@ if (rtc_include_tests) {
]
if (rtc_enable_sctp) {
sources += [ "sctp/sctpdataengine_unittest.cc" ]
sources += [ "sctp/sctptransport_unittest.cc" ]
}
configs += [ ":rtc_media_unittests_config" ]

View File

@ -942,11 +942,9 @@ inline FakeVideoMediaChannel::~FakeVideoMediaChannel() {
class FakeDataEngine : public DataEngineInterface {
public:
FakeDataEngine() : last_channel_type_(DCT_NONE) {}
FakeDataEngine(){};
virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type,
const MediaConfig& config) {
last_channel_type_ = data_channel_type;
virtual DataMediaChannel* CreateChannel(const MediaConfig& config) {
FakeDataMediaChannel* ch = new FakeDataMediaChannel(this, DataOptions());
channels_.push_back(ch);
return ch;
@ -966,12 +964,9 @@ class FakeDataEngine : public DataEngineInterface {
virtual const std::vector<DataCodec>& data_codecs() { return data_codecs_; }
DataChannelType last_channel_type() const { return last_channel_type_; }
private:
std::vector<FakeDataMediaChannel*> channels_;
std::vector<DataCodec> data_codecs_;
DataChannelType last_channel_type_;
};
} // namespace cricket

View File

@ -1,60 +0,0 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MEDIA_BASE_HYBRIDDATAENGINE_H_
#define WEBRTC_MEDIA_BASE_HYBRIDDATAENGINE_H_
#include <memory>
#include <string>
#include <vector>
#include "webrtc/media/base/codec.h"
#include "webrtc/media/base/mediachannel.h"
#include "webrtc/media/base/mediaengine.h"
namespace cricket {
class HybridDataEngine : public DataEngineInterface {
public:
// Takes ownership.
HybridDataEngine(DataEngineInterface* first,
DataEngineInterface* second)
: first_(first),
second_(second) {
codecs_ = first_->data_codecs();
codecs_.insert(
codecs_.end(),
second_->data_codecs().begin(),
second_->data_codecs().end());
}
virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type,
const MediaConfig& config) {
DataMediaChannel* channel = NULL;
if (first_) {
channel = first_->CreateChannel(data_channel_type, config);
}
if (!channel && second_) {
channel = second_->CreateChannel(data_channel_type, config);
}
return channel;
}
virtual const std::vector<DataCodec>& data_codecs() { return codecs_; }
private:
std::unique_ptr<DataEngineInterface> first_;
std::unique_ptr<DataEngineInterface> second_;
std::vector<DataCodec> codecs_;
};
} // namespace cricket
#endif // WEBRTC_MEDIA_BASE_HYBRIDDATAENGINE_H_

View File

@ -1077,8 +1077,11 @@ enum DataMessageType {
// signal fires, on up the chain.
struct ReceiveDataParams {
// The in-packet stream indentifier.
// For SCTP, this is really SID, not SSRC.
uint32_t ssrc;
// RTP data channels use SSRCs, SCTP data channels use SIDs.
union {
uint32_t ssrc;
int sid;
};
// The type of message (binary, text, or control).
DataMessageType type;
// A per-stream value incremented per packet in the stream.
@ -1086,18 +1089,16 @@ struct ReceiveDataParams {
// A per-stream value monotonically increasing with time.
int timestamp;
ReceiveDataParams() :
ssrc(0),
type(DMT_TEXT),
seq_num(0),
timestamp(0) {
}
ReceiveDataParams() : sid(0), type(DMT_TEXT), seq_num(0), timestamp(0) {}
};
struct SendDataParams {
// The in-packet stream indentifier.
// For SCTP, this is really SID, not SSRC.
uint32_t ssrc;
// RTP data channels use SSRCs, SCTP data channels use SIDs.
union {
uint32_t ssrc;
int sid;
};
// The type of message (binary, text, or control).
DataMessageType type;
@ -1116,15 +1117,14 @@ struct SendDataParams {
// is supported, not both at the same time.
int max_rtx_ms;
SendDataParams() :
ssrc(0),
type(DMT_TEXT),
// TODO(pthatcher): Make these true by default?
ordered(false),
reliable(false),
max_rtx_count(0),
max_rtx_ms(0) {
}
SendDataParams()
: sid(0),
type(DMT_TEXT),
// TODO(pthatcher): Make these true by default?
ordered(false),
reliable(false),
max_rtx_count(0),
max_rtx_ms(0) {}
};
enum SendDataResult { SDR_SUCCESS, SDR_ERROR, SDR_BLOCK };
@ -1183,8 +1183,6 @@ class DataMediaChannel : public MediaChannel {
// Signal when the media channel is ready to send the stream. Arguments are:
// writable(bool)
sigslot::signal1<bool> SignalReadyToSend;
// Signal for notifying that the remote side has closed the DataChannel.
sigslot::signal1<uint32_t> SignalStreamClosedRemotely;
};
} // namespace cricket

View File

@ -106,9 +106,9 @@ extern const char kCodecParamStartBitrate[];
extern const char kCodecParamMaxQuantization[];
extern const char kCodecParamPort[];
// We put the data codec names here so callers of
// DataEngine::CreateChannel don't have to import rtpdataengine.h or
// sctpdataengine.h to get the codec names they want to pass in.
// We put the data codec names here so callers of DataEngine::CreateChannel
// don't have to import rtpdataengine.h to get the codec names they want to
// pass in.
extern const int kGoogleRtpDataCodecPlType;
extern const char kGoogleRtpDataCodecName[];

View File

@ -171,8 +171,7 @@ enum DataChannelType { DCT_NONE = 0, DCT_RTP = 1, DCT_SCTP = 2, DCT_QUIC = 3 };
class DataEngineInterface {
public:
virtual ~DataEngineInterface() {}
virtual DataMediaChannel* CreateChannel(DataChannelType type,
const MediaConfig& config) = 0;
virtual DataMediaChannel* CreateChannel(const MediaConfig& config) = 0;
virtual const std::vector<DataCodec>& data_codecs() = 0;
};

View File

@ -40,11 +40,7 @@ RtpDataEngine::RtpDataEngine() {
}
DataMediaChannel* RtpDataEngine::CreateChannel(
DataChannelType data_channel_type,
const MediaConfig& config) {
if (data_channel_type != DCT_RTP) {
return NULL;
}
return new RtpDataMediaChannel(config);
}

View File

@ -27,8 +27,7 @@ class RtpDataEngine : public DataEngineInterface {
public:
RtpDataEngine();
virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type,
const MediaConfig& config);
virtual DataMediaChannel* CreateChannel(const MediaConfig& config);
virtual const std::vector<DataCodec>& data_codecs() {
return data_codecs_;

View File

@ -72,8 +72,7 @@ class RtpDataMediaChannelTest : public testing::Test {
cricket::RtpDataMediaChannel* CreateChannel(cricket::RtpDataEngine* dme) {
cricket::MediaConfig config;
cricket::RtpDataMediaChannel* channel =
static_cast<cricket::RtpDataMediaChannel*>(
dme->CreateChannel(cricket::DCT_RTP, config));
static_cast<cricket::RtpDataMediaChannel*>(dme->CreateChannel(config));
channel->SetInterface(iface_.get());
channel->SignalDataReceived.connect(
receiver_.get(), &FakeDataReceiver::OnDataReceived);

File diff suppressed because it is too large Load Diff

View File

@ -1,250 +0,0 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MEDIA_SCTP_SCTPDATAENGINE_H_
#define WEBRTC_MEDIA_SCTP_SCTPDATAENGINE_H_
#include <errno.h>
#include <string>
#include <vector>
namespace cricket {
// Some ERRNO values get re-#defined to WSA* equivalents in some talk/
// headers. We save the original ones in an enum.
enum PreservedErrno {
SCTP_EINPROGRESS = EINPROGRESS,
SCTP_EWOULDBLOCK = EWOULDBLOCK
};
} // namespace cricket
#include "webrtc/base/copyonwritebuffer.h"
#include "webrtc/base/gtest_prod_util.h"
#include "webrtc/media/base/codec.h"
#include "webrtc/media/base/mediachannel.h"
#include "webrtc/media/base/mediaengine.h"
// Defined by "usrsctplib/usrsctp.h"
struct sockaddr_conn;
struct sctp_assoc_change;
struct sctp_stream_reset_event;
// Defined by <sys/socket.h>
struct socket;
namespace cricket {
// The number of outgoing streams that we'll negotiate. Since stream IDs (SIDs)
// are 0-based, the highest usable SID is 1023.
//
// It's recommended to use the maximum of 65535 in:
// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.2
// However, we use 1024 in order to save memory. usrsctp allocates 104 bytes
// for each pair of incoming/outgoing streams (on a 64-bit system), so 65535
// streams would waste ~6MB.
//
// Note: "max" and "min" here are inclusive.
constexpr uint16_t kMaxSctpStreams = 1024;
constexpr uint16_t kMaxSctpSid = kMaxSctpStreams - 1;
constexpr uint16_t kMinSctpSid = 0;
// This is the default SCTP port to use. It is passed along the wire and the
// connectee and connector must be using the same port. It is not related to the
// ports at the IP level. (Corresponds to: sockaddr_conn.sconn_port in
// usrsctp.h)
const int kSctpDefaultPort = 5000;
class SctpDataMediaChannel;
// A DataEngine that interacts with usrsctp.
//
// From channel calls, data flows like this:
// [worker thread (although it can in princple be another thread)]
// 1. SctpDataMediaChannel::SendData(data)
// 2. usrsctp_sendv(data)
// [worker thread returns; sctp thread then calls the following]
// 3. OnSctpOutboundPacket(wrapped_data)
// [sctp thread returns having posted a message for the worker thread]
// 4. SctpDataMediaChannel::OnMessage(wrapped_data)
// 5. SctpDataMediaChannel::OnPacketFromSctpToNetwork(wrapped_data)
// 6. NetworkInterface::SendPacket(wrapped_data)
// 7. ... across network ... a packet is sent back ...
// 8. SctpDataMediaChannel::OnPacketReceived(wrapped_data)
// 9. usrsctp_conninput(wrapped_data)
// [worker thread returns; sctp thread then calls the following]
// 10. OnSctpInboundData(data)
// [sctp thread returns having posted a message fot the worker thread]
// 11. SctpDataMediaChannel::OnMessage(inboundpacket)
// 12. SctpDataMediaChannel::OnInboundPacketFromSctpToChannel(inboundpacket)
// 13. SctpDataMediaChannel::OnDataFromSctpToChannel(data)
// 14. SctpDataMediaChannel::SignalDataReceived(data)
// [from the same thread, methods registered/connected to
// SctpDataMediaChannel are called with the recieved data]
class SctpDataEngine : public DataEngineInterface, public sigslot::has_slots<> {
public:
SctpDataEngine();
~SctpDataEngine() override;
DataMediaChannel* CreateChannel(DataChannelType data_channel_type,
const MediaConfig& config) override;
const std::vector<DataCodec>& data_codecs() override { return codecs_; }
private:
const std::vector<DataCodec> codecs_;
};
// TODO(ldixon): Make into a special type of TypedMessageData.
// Holds data to be passed on to a channel.
struct SctpInboundPacket;
class SctpDataMediaChannel : public DataMediaChannel,
public rtc::MessageHandler {
public:
// DataMessageType is used for the SCTP "Payload Protocol Identifier", as
// defined in http://tools.ietf.org/html/rfc4960#section-14.4
//
// For the list of IANA approved values see:
// http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xml
// The value is not used by SCTP itself. It indicates the protocol running
// on top of SCTP.
enum PayloadProtocolIdentifier {
PPID_NONE = 0, // No protocol is specified.
// Matches the PPIDs in mozilla source and
// https://datatracker.ietf.org/doc/draft-ietf-rtcweb-data-protocol Sec. 9
// They're not yet assigned by IANA.
PPID_CONTROL = 50,
PPID_BINARY_PARTIAL = 52,
PPID_BINARY_LAST = 53,
PPID_TEXT_PARTIAL = 54,
PPID_TEXT_LAST = 51
};
typedef std::set<uint32_t> StreamSet;
// Given a thread which will be used to post messages (received data) to this
// SctpDataMediaChannel instance.
explicit SctpDataMediaChannel(rtc::Thread* thread, const MediaConfig& config);
virtual ~SctpDataMediaChannel();
// When SetSend is set to true, connects. When set to false, disconnects.
// Calling: "SetSend(true); SetSend(false); SetSend(true);" will connect,
// disconnect, and reconnect.
virtual bool SetSend(bool send);
// Unless SetReceive(true) is called, received packets will be discarded.
virtual bool SetReceive(bool receive);
virtual bool SetSendParameters(const DataSendParameters& params);
virtual bool SetRecvParameters(const DataRecvParameters& params);
virtual bool AddSendStream(const StreamParams& sp);
virtual bool RemoveSendStream(uint32_t ssrc);
virtual bool AddRecvStream(const StreamParams& sp);
virtual bool RemoveRecvStream(uint32_t ssrc);
// Called when Sctp gets data. The data may be a notification or data for
// OnSctpInboundData. Called from the worker thread.
virtual void OnMessage(rtc::Message* msg);
// Send data down this channel (will be wrapped as SCTP packets then given to
// sctp that will then post the network interface by OnMessage).
// Returns true iff successful data somewhere on the send-queue/network.
virtual bool SendData(const SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
SendDataResult* result = NULL);
// A packet is received from the network interface. Posted to OnMessage.
virtual void OnPacketReceived(rtc::CopyOnWriteBuffer* packet,
const rtc::PacketTime& packet_time);
// Exposed to allow Post call from c-callbacks.
rtc::Thread* worker_thread() const { return worker_thread_; }
// Many of these things are unused by SCTP, but are needed to fulfill
// the MediaChannel interface.
virtual void OnRtcpReceived(rtc::CopyOnWriteBuffer* packet,
const rtc::PacketTime& packet_time) {}
virtual void OnReadyToSend(bool ready) {}
virtual void OnTransportOverheadChanged(int transport_overhead_per_packet) {}
void OnSendThresholdCallback();
// Helper for debugging.
void set_debug_name_for_testing(const char* debug_name) {
debug_name_ = debug_name;
}
const struct socket* socket() const { return sock_; }
private:
FRIEND_TEST_ALL_PREFIXES(SctpDataMediaChannelTest, EngineSignalsRightChannel);
static int SendThresholdCallback(struct socket* sock, uint32_t sb_free);
static SctpDataMediaChannel* GetChannelFromSocket(struct socket* sock);
private:
sockaddr_conn GetSctpSockAddr(int port);
bool SetSendCodecs(const std::vector<DataCodec>& codecs);
bool SetRecvCodecs(const std::vector<DataCodec>& codecs);
// Creates the socket and connects. Sets sending_ to true.
bool Connect();
// Closes the socket. Sets sending_ to false.
void Disconnect();
// Returns false when openning the socket failed; when successfull sets
// sending_ to true
bool OpenSctpSocket();
// Sets sending_ to false and sock_ to NULL.
void CloseSctpSocket();
// Sends a SCTP_RESET_STREAM for all streams in closing_ssids_.
bool SendQueuedStreamResets();
// Adds a stream.
bool AddStream(const StreamParams &sp);
// Queues a stream for reset.
bool ResetStream(uint32_t ssrc);
// Called by OnMessage to send packet on the network.
void OnPacketFromSctpToNetwork(rtc::CopyOnWriteBuffer* buffer);
// Called by OnMessage to decide what to do with the packet.
void OnInboundPacketFromSctpToChannel(SctpInboundPacket* packet);
void OnDataFromSctpToChannel(const ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& buffer);
void OnNotificationFromSctp(const rtc::CopyOnWriteBuffer& buffer);
void OnNotificationAssocChange(const sctp_assoc_change& change);
void OnStreamResetEvent(const struct sctp_stream_reset_event* evt);
// Responsible for marshalling incoming data to the channels listeners, and
// outgoing data to the network interface.
rtc::Thread* worker_thread_;
// The local and remote SCTP port to use. These are passed along the wire
// and the listener and connector must be using the same port. It is not
// related to the ports at the IP level. If set to -1, we default to
// kSctpDefaultPort.
int local_port_;
int remote_port_;
struct socket* sock_; // The socket created by usrsctp_socket(...).
// sending_ is true iff there is a connected socket.
bool sending_;
// receiving_ controls whether inbound packets are thrown away.
bool receiving_;
// When a data channel opens a stream, it goes into open_streams_. When we
// want to close it, the stream's ID goes into queued_reset_streams_. When
// we actually transmit a RE-CONFIG chunk with that stream ID, the ID goes
// into sent_reset_streams_. When we get a response RE-CONFIG chunk back
// acknowledging the reset, we remove the stream ID from
// sent_reset_streams_. We use sent_reset_streams_ to differentiate
// between acknowledgment RE-CONFIG and peer-initiated RE-CONFIGs.
StreamSet open_streams_;
StreamSet queued_reset_streams_;
StreamSet sent_reset_streams_;
// A static human-readable name for debugging messages.
const char* debug_name_;
};
} // namespace cricket
#endif // WEBRTC_MEDIA_SCTP_SCTPDATAENGINE_H_

View File

@ -1,523 +0,0 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <memory>
#include <string>
#include <vector>
#include "webrtc/base/bind.h"
#include "webrtc/base/copyonwritebuffer.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/messagehandler.h"
#include "webrtc/base/messagequeue.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/thread.h"
#include "webrtc/media/base/mediachannel.h"
#include "webrtc/media/base/mediaconstants.h"
#include "webrtc/media/sctp/sctpdataengine.h"
namespace cricket {
enum {
MSG_PACKET = 1,
};
// Fake NetworkInterface that sends/receives sctp packets. The one in
// webrtc/media/base/fakenetworkinterface.h only works with rtp/rtcp.
class SctpFakeNetworkInterface : public MediaChannel::NetworkInterface,
public rtc::MessageHandler {
public:
explicit SctpFakeNetworkInterface(rtc::Thread* thread)
: thread_(thread),
dest_(NULL) {
}
void SetDestination(DataMediaChannel* dest) { dest_ = dest; }
protected:
// Called to send raw packet down the wire (e.g. SCTP an packet).
virtual bool SendPacket(rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options) {
LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::SendPacket";
rtc::CopyOnWriteBuffer* buffer = new rtc::CopyOnWriteBuffer(*packet);
thread_->Post(RTC_FROM_HERE, this, MSG_PACKET,
rtc::WrapMessageData(buffer));
LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::SendPacket, Posted message.";
return true;
}
// Called when a raw packet has been recieved. This passes the data to the
// code that will interpret the packet. e.g. to get the content payload from
// an SCTP packet.
virtual void OnMessage(rtc::Message* msg) {
LOG(LS_VERBOSE) << "SctpFakeNetworkInterface::OnMessage";
std::unique_ptr<rtc::CopyOnWriteBuffer> buffer(
static_cast<rtc::TypedMessageData<rtc::CopyOnWriteBuffer*>*>(
msg->pdata)->data());
if (dest_) {
dest_->OnPacketReceived(buffer.get(), rtc::PacketTime());
}
delete msg->pdata;
}
// Unsupported functions required to exist by NetworkInterface.
// TODO(ldixon): Refactor parent NetworkInterface class so these are not
// required. They are RTC specific and should be in an appropriate subclass.
virtual bool SendRtcp(rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options) {
LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SendRtcp.";
return false;
}
virtual int SetOption(SocketType type, rtc::Socket::Option opt,
int option) {
LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SetOption.";
return 0;
}
virtual void SetDefaultDSCPCode(rtc::DiffServCodePoint dscp) {
LOG(LS_WARNING) << "Unsupported: SctpFakeNetworkInterface::SetOption.";
}
private:
// Not owned by this class.
rtc::Thread* thread_;
DataMediaChannel* dest_;
};
// This is essentially a buffer to hold recieved data. It stores only the last
// received data. Calling OnDataReceived twice overwrites old data with the
// newer one.
// TODO(ldixon): Implement constraints, and allow new data to be added to old
// instead of replacing it.
class SctpFakeDataReceiver : public sigslot::has_slots<> {
public:
SctpFakeDataReceiver() : received_(false) {}
void Clear() {
received_ = false;
last_data_ = "";
last_params_ = ReceiveDataParams();
}
virtual void OnDataReceived(const ReceiveDataParams& params,
const char* data,
size_t length) {
received_ = true;
last_data_ = std::string(data, length);
last_params_ = params;
}
bool received() const { return received_; }
std::string last_data() const { return last_data_; }
ReceiveDataParams last_params() const { return last_params_; }
private:
bool received_;
std::string last_data_;
ReceiveDataParams last_params_;
};
class SignalReadyToSendObserver : public sigslot::has_slots<> {
public:
SignalReadyToSendObserver() : signaled_(false), writable_(false) {}
void OnSignaled(bool writable) {
signaled_ = true;
writable_ = writable;
}
bool IsSignaled(bool writable) {
return signaled_ && (writable_ == writable);
}
private:
bool signaled_;
bool writable_;
};
class SignalChannelClosedObserver : public sigslot::has_slots<> {
public:
SignalChannelClosedObserver() {}
void BindSelf(SctpDataMediaChannel* channel) {
channel->SignalStreamClosedRemotely.connect(
this, &SignalChannelClosedObserver::OnStreamClosed);
}
void OnStreamClosed(uint32_t stream) { streams_.push_back(stream); }
int StreamCloseCount(uint32_t stream) {
return std::count(streams_.begin(), streams_.end(), stream);
}
bool WasStreamClosed(uint32_t stream) {
return std::find(streams_.begin(), streams_.end(), stream)
!= streams_.end();
}
private:
std::vector<uint32_t> streams_;
};
class SignalChannelClosedReopener : public sigslot::has_slots<> {
public:
SignalChannelClosedReopener(SctpDataMediaChannel* channel,
SctpDataMediaChannel* peer)
: channel_(channel), peer_(peer) {}
void OnStreamClosed(int stream) {
StreamParams p(StreamParams::CreateLegacy(stream));
channel_->AddSendStream(p);
channel_->AddRecvStream(p);
peer_->AddSendStream(p);
peer_->AddRecvStream(p);
streams_.push_back(stream);
}
int StreamCloseCount(int stream) {
return std::count(streams_.begin(), streams_.end(), stream);
}
private:
SctpDataMediaChannel* channel_;
SctpDataMediaChannel* peer_;
std::vector<int> streams_;
};
// SCTP Data Engine testing framework.
class SctpDataMediaChannelTest : public testing::Test,
public sigslot::has_slots<> {
protected:
// usrsctp uses the NSS random number generator on non-Android platforms,
// so we need to initialize SSL.
static void SetUpTestCase() {
}
virtual void SetUp() { engine_.reset(new SctpDataEngine()); }
void SetupConnectedChannels() {
net1_.reset(new SctpFakeNetworkInterface(rtc::Thread::Current()));
net2_.reset(new SctpFakeNetworkInterface(rtc::Thread::Current()));
recv1_.reset(new SctpFakeDataReceiver());
recv2_.reset(new SctpFakeDataReceiver());
chan1_ready_to_send_count_ = 0;
chan2_ready_to_send_count_ = 0;
chan1_.reset(CreateChannel(net1_.get(), recv1_.get()));
chan1_->set_debug_name_for_testing("chan1/connector");
chan1_->SignalReadyToSend.connect(
this, &SctpDataMediaChannelTest::OnChan1ReadyToSend);
chan2_.reset(CreateChannel(net2_.get(), recv2_.get()));
chan2_->set_debug_name_for_testing("chan2/listener");
chan2_->SignalReadyToSend.connect(
this, &SctpDataMediaChannelTest::OnChan2ReadyToSend);
// Setup two connected channels ready to send and receive.
net1_->SetDestination(chan2_.get());
net2_->SetDestination(chan1_.get());
LOG(LS_VERBOSE) << "Channel setup ----------------------------- ";
AddStream(1);
AddStream(2);
LOG(LS_VERBOSE) << "Connect the channels -----------------------------";
// chan1 wants to setup a data connection.
chan1_->SetReceive(true);
// chan1 will have sent chan2 a request to setup a data connection. After
// chan2 accepts the offer, chan2 connects to chan1 with the following.
chan2_->SetReceive(true);
chan2_->SetSend(true);
// Makes sure that network packets are delivered and simulates a
// deterministic and realistic small timing delay between the SetSend calls.
ProcessMessagesUntilIdle();
// chan1 and chan2 are now connected so chan1 enables sending to complete
// the creation of the connection.
chan1_->SetSend(true);
}
virtual void TearDown() {
channel1()->SetSend(false);
channel2()->SetSend(false);
// Process messages until idle to prevent a sent packet from being dropped
// and causing memory leaks (not being deleted by the receiver).
ProcessMessagesUntilIdle();
}
bool AddStream(int ssrc) {
bool ret = true;
StreamParams p(StreamParams::CreateLegacy(ssrc));
ret = ret && chan1_->AddSendStream(p);
ret = ret && chan1_->AddRecvStream(p);
ret = ret && chan2_->AddSendStream(p);
ret = ret && chan2_->AddRecvStream(p);
return ret;
}
SctpDataMediaChannel* CreateChannel(SctpFakeNetworkInterface* net,
SctpFakeDataReceiver* recv) {
cricket::MediaConfig config;
SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(
engine_->CreateChannel(DCT_SCTP, config));
channel->SetInterface(net);
// When data is received, pass it to the SctpFakeDataReceiver.
channel->SignalDataReceived.connect(
recv, &SctpFakeDataReceiver::OnDataReceived);
return channel;
}
bool SendData(SctpDataMediaChannel* chan,
uint32_t ssrc,
const std::string& msg,
SendDataResult* result) {
SendDataParams params;
params.ssrc = ssrc;
return chan->SendData(params, rtc::CopyOnWriteBuffer(
&msg[0], msg.length()), result);
}
bool ReceivedData(const SctpFakeDataReceiver* recv,
uint32_t ssrc,
const std::string& msg) {
return (recv->received() &&
recv->last_params().ssrc == ssrc &&
recv->last_data() == msg);
}
bool ProcessMessagesUntilIdle() {
rtc::Thread* thread = rtc::Thread::Current();
while (!thread->empty()) {
rtc::Message msg;
if (thread->Get(&msg, rtc::Thread::kForever)) {
thread->Dispatch(&msg);
}
}
return !thread->IsQuitting();
}
SctpDataMediaChannel* channel1() { return chan1_.get(); }
SctpDataMediaChannel* channel2() { return chan2_.get(); }
SctpFakeDataReceiver* receiver1() { return recv1_.get(); }
SctpFakeDataReceiver* receiver2() { return recv2_.get(); }
int channel1_ready_to_send_count() { return chan1_ready_to_send_count_; }
int channel2_ready_to_send_count() { return chan2_ready_to_send_count_; }
private:
std::unique_ptr<SctpDataEngine> engine_;
std::unique_ptr<SctpFakeNetworkInterface> net1_;
std::unique_ptr<SctpFakeNetworkInterface> net2_;
std::unique_ptr<SctpFakeDataReceiver> recv1_;
std::unique_ptr<SctpFakeDataReceiver> recv2_;
std::unique_ptr<SctpDataMediaChannel> chan1_;
std::unique_ptr<SctpDataMediaChannel> chan2_;
int chan1_ready_to_send_count_;
int chan2_ready_to_send_count_;
void OnChan1ReadyToSend(bool send) {
if (send)
++chan1_ready_to_send_count_;
}
void OnChan2ReadyToSend(bool send) {
if (send)
++chan2_ready_to_send_count_;
}
};
// Verifies that SignalReadyToSend is fired.
TEST_F(SctpDataMediaChannelTest, SignalReadyToSend) {
SetupConnectedChannels();
SignalReadyToSendObserver signal_observer_1;
SignalReadyToSendObserver signal_observer_2;
channel1()->SignalReadyToSend.connect(&signal_observer_1,
&SignalReadyToSendObserver::OnSignaled);
channel2()->SignalReadyToSend.connect(&signal_observer_2,
&SignalReadyToSendObserver::OnSignaled);
SendDataResult result;
ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
EXPECT_TRUE_WAIT(signal_observer_1.IsSignaled(true), 1000);
EXPECT_TRUE_WAIT(signal_observer_2.IsSignaled(true), 1000);
}
TEST_F(SctpDataMediaChannelTest, SendData) {
SetupConnectedChannels();
SendDataResult result;
LOG(LS_VERBOSE) << "chan1 sending: 'hello?' -----------------------------";
ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received()
<< ", recv2.last_params.ssrc="
<< receiver2()->last_params().ssrc
<< ", recv2.last_params.timestamp="
<< receiver2()->last_params().ssrc
<< ", recv2.last_params.seq_num="
<< receiver2()->last_params().seq_num
<< ", recv2.last_data=" << receiver2()->last_data();
LOG(LS_VERBOSE) << "chan2 sending: 'hi chan1' -----------------------------";
ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received()
<< ", recv1.last_params.ssrc="
<< receiver1()->last_params().ssrc
<< ", recv1.last_params.timestamp="
<< receiver1()->last_params().ssrc
<< ", recv1.last_params.seq_num="
<< receiver1()->last_params().seq_num
<< ", recv1.last_data=" << receiver1()->last_data();
}
// Sends a lot of large messages at once and verifies SDR_BLOCK is returned.
TEST_F(SctpDataMediaChannelTest, SendDataBlocked) {
SetupConnectedChannels();
SendDataResult result;
SendDataParams params;
params.ssrc = 1;
std::vector<char> buffer(1024 * 64, 0);
for (size_t i = 0; i < 100; ++i) {
channel1()->SendData(
params, rtc::CopyOnWriteBuffer(&buffer[0], buffer.size()), &result);
if (result == SDR_BLOCK)
break;
}
EXPECT_EQ(SDR_BLOCK, result);
}
TEST_F(SctpDataMediaChannelTest, ClosesRemoteStream) {
SetupConnectedChannels();
SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver;
chan_1_sig_receiver.BindSelf(channel1());
chan_2_sig_receiver.BindSelf(channel2());
SendDataResult result;
ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
// Close channel 1. Channel 2 should notify us.
channel1()->RemoveSendStream(1);
EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000);
}
TEST_F(SctpDataMediaChannelTest, ClosesTwoRemoteStreams) {
SetupConnectedChannels();
AddStream(3);
SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver;
chan_1_sig_receiver.BindSelf(channel1());
chan_2_sig_receiver.BindSelf(channel2());
SendDataResult result;
ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
// Close two streams on one side.
channel2()->RemoveSendStream(2);
channel2()->RemoveSendStream(3);
EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(2), 1000);
EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(3), 1000);
}
TEST_F(SctpDataMediaChannelTest, ClosesStreamsOnBothSides) {
SetupConnectedChannels();
AddStream(3);
AddStream(4);
SignalChannelClosedObserver chan_1_sig_receiver, chan_2_sig_receiver;
chan_1_sig_receiver.BindSelf(channel1());
chan_2_sig_receiver.BindSelf(channel2());
SendDataResult result;
ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
ASSERT_TRUE(SendData(channel2(), 2, "hi chan1", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi chan1"), 1000);
// Close one stream on channel1(), while closing three streams on
// channel2(). They will conflict (only one side can close anything at a
// time, apparently). Test the resolution of the conflict.
channel1()->RemoveSendStream(1);
channel2()->RemoveSendStream(2);
channel2()->RemoveSendStream(3);
channel2()->RemoveSendStream(4);
EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000);
EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(2), 1000);
EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(3), 1000);
EXPECT_TRUE_WAIT(chan_1_sig_receiver.WasStreamClosed(4), 1000);
}
TEST_F(SctpDataMediaChannelTest, EngineSignalsRightChannel) {
SetupConnectedChannels();
EXPECT_TRUE_WAIT(channel1()->socket() != NULL, 1000);
struct socket *sock = const_cast<struct socket*>(channel1()->socket());
int prior_count = channel1_ready_to_send_count();
SctpDataMediaChannel::SendThresholdCallback(sock, 0);
EXPECT_GT(channel1_ready_to_send_count(), prior_count);
}
TEST_F(SctpDataMediaChannelTest, RefusesHighNumberedChannels) {
SetupConnectedChannels();
EXPECT_TRUE(AddStream(kMaxSctpSid));
EXPECT_FALSE(AddStream(kMaxSctpSid + 1));
}
// Flaky, see webrtc:4453.
TEST_F(SctpDataMediaChannelTest, DISABLED_ReusesAStream) {
// Shut down channel 1, then open it up again for reuse.
SetupConnectedChannels();
SendDataResult result;
SignalChannelClosedObserver chan_2_sig_receiver;
chan_2_sig_receiver.BindSelf(channel2());
ASSERT_TRUE(SendData(channel1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), 1000);
channel1()->RemoveSendStream(1);
EXPECT_TRUE_WAIT(chan_2_sig_receiver.WasStreamClosed(1), 1000);
// Channel 1 is gone now.
// Create a new channel 1.
AddStream(1);
ASSERT_TRUE(SendData(channel1(), 1, "hi?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hi?"), 1000);
channel1()->RemoveSendStream(1);
EXPECT_TRUE_WAIT(chan_2_sig_receiver.StreamCloseCount(1) == 2, 1000);
}
} // namespace cricket

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,193 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MEDIA_SCTP_SCTPTRANSPORT_H_
#define WEBRTC_MEDIA_SCTP_SCTPTRANSPORT_H_
#include <errno.h>
#include <memory> // for unique_ptr.
#include <set>
#include <string>
#include <vector>
#include "webrtc/base/asyncinvoker.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/copyonwritebuffer.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/thread.h"
// For SendDataParams/ReceiveDataParams.
#include "webrtc/media/base/mediachannel.h"
#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/p2p/base/transportchannel.h"
// Defined by "usrsctplib/usrsctp.h"
struct sockaddr_conn;
struct sctp_assoc_change;
struct sctp_stream_reset_event;
// Defined by <sys/socket.h>
struct socket;
namespace cricket {
// Holds data to be passed on to a channel.
struct SctpInboundPacket;
// From channel calls, data flows like this:
// [network thread (although it can in princple be another thread)]
// 1. SctpTransport::SendData(data)
// 2. usrsctp_sendv(data)
// [network thread returns; sctp thread then calls the following]
// 3. OnSctpOutboundPacket(wrapped_data)
// [sctp thread returns having async invoked on the network thread]
// 4. SctpTransport::OnPacketFromSctpToNetwork(wrapped_data)
// 5. TransportChannel::SendPacket(wrapped_data)
// 6. ... across network ... a packet is sent back ...
// 7. SctpTransport::OnPacketReceived(wrapped_data)
// 8. usrsctp_conninput(wrapped_data)
// [network thread returns; sctp thread then calls the following]
// 9. OnSctpInboundData(data)
// [sctp thread returns having async invoked on the network thread]
// 10. SctpTransport::OnInboundPacketFromSctpToChannel(inboundpacket)
// 11. SctpTransport::OnDataFromSctpToChannel(data)
// 12. SctpTransport::SignalDataReceived(data)
// [from the same thread, methods registered/connected to
// SctpTransport are called with the recieved data]
class SctpTransport : public SctpTransportInternal,
public sigslot::has_slots<> {
public:
// |network_thread| is where packets will be processed and callbacks from
// this transport will be posted, and is the only thread on which public
// methods can be called.
// |channel| is required (must not be null).
SctpTransport(rtc::Thread* network_thread,
cricket::TransportChannel* channel);
~SctpTransport() override;
// SctpTransportInternal overrides (see sctptransportinternal.h for comments).
void SetTransportChannel(cricket::TransportChannel* channel) override;
bool Start(int local_port, int remote_port) override;
bool OpenStream(int sid) override;
bool ResetStream(int sid) override;
bool SendData(const SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
SendDataResult* result = nullptr) override;
bool ReadyToSendData() override;
void set_debug_name_for_testing(const char* debug_name) override {
debug_name_ = debug_name;
}
// Exposed to allow Post call from c-callbacks.
// TODO(deadbeef): Remove this or at least make it return a const pointer.
rtc::Thread* network_thread() const { return network_thread_; }
private:
void ConnectTransportChannelSignals();
void DisconnectTransportChannelSignals();
// Creates the socket and connects.
bool Connect();
// Returns false when opening the socket failed.
bool OpenSctpSocket();
// Helpet method to set socket options.
bool ConfigureSctpSocket();
// Sets |sock_ |to nullptr.
void CloseSctpSocket();
// Sends a SCTP_RESET_STREAM for all streams in closing_ssids_.
bool SendQueuedStreamResets();
// Sets the "ready to send" flag and fires signal if needed.
void SetReadyToSendData();
// Callbacks from DTLS channel.
void OnWritableState(rtc::PacketTransportInterface* transport);
virtual void OnPacketRead(rtc::PacketTransportInterface* transport,
const char* data,
size_t len,
const rtc::PacketTime& packet_time,
int flags);
// Methods related to usrsctp callbacks.
void OnSendThresholdCallback();
sockaddr_conn GetSctpSockAddr(int port);
// Called using |invoker_| to send packet on the network.
void OnPacketFromSctpToNetwork(const rtc::CopyOnWriteBuffer& buffer);
// Called using |invoker_| to decide what to do with the packet.
// The |flags| parameter is used by SCTP to distinguish notification packets
// from other types of packets.
void OnInboundPacketFromSctpToChannel(const rtc::CopyOnWriteBuffer& buffer,
ReceiveDataParams params,
int flags);
void OnDataFromSctpToChannel(const ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& buffer);
void OnNotificationFromSctp(const rtc::CopyOnWriteBuffer& buffer);
void OnNotificationAssocChange(const sctp_assoc_change& change);
void OnStreamResetEvent(const struct sctp_stream_reset_event* evt);
// Responsible for marshalling incoming data to the channels listeners, and
// outgoing data to the network interface.
rtc::Thread* network_thread_;
// Helps pass inbound/outbound packets asynchronously to the network thread.
rtc::AsyncInvoker invoker_;
// Underlying DTLS channel.
TransportChannel* transport_channel_;
bool was_ever_writable_ = false;
int local_port_ = kSctpDefaultPort;
int remote_port_ = kSctpDefaultPort;
struct socket* sock_ = nullptr; // The socket created by usrsctp_socket(...).
// Has Start been called? Don't create SCTP socket until it has.
bool started_ = false;
// Are we ready to queue data (SCTP socket created, and not blocked due to
// congestion control)? Different than |transport_channel_|'s "ready to
// send".
bool ready_to_send_data_ = false;
typedef std::set<uint32_t> StreamSet;
// When a data channel opens a stream, it goes into open_streams_. When we
// want to close it, the stream's ID goes into queued_reset_streams_. When
// we actually transmit a RE-CONFIG chunk with that stream ID, the ID goes
// into sent_reset_streams_. When we get a response RE-CONFIG chunk back
// acknowledging the reset, we remove the stream ID from
// sent_reset_streams_. We use sent_reset_streams_ to differentiate
// between acknowledgment RE-CONFIG and peer-initiated RE-CONFIGs.
StreamSet open_streams_;
StreamSet queued_reset_streams_;
StreamSet sent_reset_streams_;
// A static human-readable name for debugging messages.
const char* debug_name_ = "SctpTransport";
// Hides usrsctp interactions from this header file.
class UsrSctpWrapper;
RTC_DISALLOW_COPY_AND_ASSIGN(SctpTransport);
};
class SctpTransportFactory : public SctpTransportInternalFactory {
public:
explicit SctpTransportFactory(rtc::Thread* network_thread)
: network_thread_(network_thread) {}
std::unique_ptr<SctpTransportInternal> CreateSctpTransport(
TransportChannel* channel) override {
return std::unique_ptr<SctpTransportInternal>(
new SctpTransport(network_thread_, channel));
}
private:
rtc::Thread* network_thread_;
};
} // namespace cricket
#endif // WEBRTC_MEDIA_SCTP_SCTPTRANSPORT_H_

View File

@ -0,0 +1,563 @@
/*
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <memory>
#include <string>
#include <vector>
#include "webrtc/base/bind.h"
#include "webrtc/base/copyonwritebuffer.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/helpers.h"
#include "webrtc/base/ssladapter.h"
#include "webrtc/base/thread.h"
#include "webrtc/media/sctp/sctptransport.h"
#include "webrtc/p2p/base/faketransportcontroller.h"
namespace {
static const int kDefaultTimeout = 10000; // 10 seconds.
// Use ports other than the default 5000 for testing.
static const int kTransport1Port = 5001;
static const int kTransport2Port = 5002;
}
namespace cricket {
// This is essentially a buffer to hold recieved data. It stores only the last
// received data. Calling OnDataReceived twice overwrites old data with the
// newer one.
// TODO(ldixon): Implement constraints, and allow new data to be added to old
// instead of replacing it.
class SctpFakeDataReceiver : public sigslot::has_slots<> {
public:
SctpFakeDataReceiver() : received_(false) {}
void Clear() {
received_ = false;
last_data_ = "";
last_params_ = ReceiveDataParams();
}
virtual void OnDataReceived(const ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& data) {
received_ = true;
last_data_ = std::string(data.data<char>(), data.size());
last_params_ = params;
}
bool received() const { return received_; }
std::string last_data() const { return last_data_; }
ReceiveDataParams last_params() const { return last_params_; }
private:
bool received_;
std::string last_data_;
ReceiveDataParams last_params_;
};
class SignalReadyToSendObserver : public sigslot::has_slots<> {
public:
SignalReadyToSendObserver() : signaled_(false) {}
void OnSignaled() { signaled_ = true; }
bool IsSignaled() { return signaled_; }
private:
bool signaled_;
};
class SignalTransportClosedObserver : public sigslot::has_slots<> {
public:
SignalTransportClosedObserver() {}
void BindSelf(SctpTransport* transport) {
transport->SignalStreamClosedRemotely.connect(
this, &SignalTransportClosedObserver::OnStreamClosed);
}
void OnStreamClosed(int stream) { streams_.push_back(stream); }
int StreamCloseCount(int stream) {
return std::count(streams_.begin(), streams_.end(), stream);
}
bool WasStreamClosed(int stream) {
return std::find(streams_.begin(), streams_.end(), stream) !=
streams_.end();
}
private:
std::vector<int> streams_;
};
class SignalTransportClosedReopener : public sigslot::has_slots<> {
public:
SignalTransportClosedReopener(SctpTransport* transport, SctpTransport* peer)
: transport_(transport), peer_(peer) {}
void OnStreamClosed(int stream) {
transport_->OpenStream(stream);
peer_->OpenStream(stream);
streams_.push_back(stream);
}
int StreamCloseCount(int stream) {
return std::count(streams_.begin(), streams_.end(), stream);
}
private:
SctpTransport* transport_;
SctpTransport* peer_;
std::vector<int> streams_;
};
// SCTP Data Engine testing framework.
class SctpTransportTest : public testing::Test, public sigslot::has_slots<> {
protected:
// usrsctp uses the NSS random number generator on non-Android platforms,
// so we need to initialize SSL.
static void SetUpTestCase() {}
void SetupConnectedTransportsWithTwoStreams() {
fake_dtls1_.reset(new FakeTransportChannel("fake dtls 1", 0));
fake_dtls2_.reset(new FakeTransportChannel("fake dtls 2", 0));
recv1_.reset(new SctpFakeDataReceiver());
recv2_.reset(new SctpFakeDataReceiver());
transport1_.reset(CreateTransport(fake_dtls1_.get(), recv1_.get()));
transport1_->set_debug_name_for_testing("transport1");
transport1_->SignalReadyToSendData.connect(
this, &SctpTransportTest::OnChan1ReadyToSend);
transport2_.reset(CreateTransport(fake_dtls2_.get(), recv2_.get()));
transport2_->set_debug_name_for_testing("transport2");
transport2_->SignalReadyToSendData.connect(
this, &SctpTransportTest::OnChan2ReadyToSend);
// Setup two connected transports ready to send and receive.
bool asymmetric = false;
fake_dtls1_->SetDestination(fake_dtls2_.get(), asymmetric);
LOG(LS_VERBOSE) << "Transport setup ----------------------------- ";
AddStream(1);
AddStream(2);
LOG(LS_VERBOSE) << "Connect the transports -----------------------------";
// Both transports need to have started (with matching ports) for an
// association to be formed.
transport1_->Start(kTransport1Port, kTransport2Port);
transport2_->Start(kTransport2Port, kTransport1Port);
}
bool AddStream(int sid) {
bool ret = true;
ret = ret && transport1_->OpenStream(sid);
ret = ret && transport2_->OpenStream(sid);
return ret;
}
SctpTransport* CreateTransport(FakeTransportChannel* fake_dtls,
SctpFakeDataReceiver* recv) {
SctpTransport* transport =
new SctpTransport(rtc::Thread::Current(), fake_dtls);
// When data is received, pass it to the SctpFakeDataReceiver.
transport->SignalDataReceived.connect(
recv, &SctpFakeDataReceiver::OnDataReceived);
return transport;
}
bool SendData(SctpTransport* chan,
int sid,
const std::string& msg,
SendDataResult* result) {
SendDataParams params;
params.sid = sid;
return chan->SendData(params, rtc::CopyOnWriteBuffer(&msg[0], msg.length()),
result);
}
bool ReceivedData(const SctpFakeDataReceiver* recv,
int sid,
const std::string& msg) {
return (recv->received() && recv->last_params().sid == sid &&
recv->last_data() == msg);
}
bool ProcessMessagesUntilIdle() {
rtc::Thread* thread = rtc::Thread::Current();
while (!thread->empty()) {
rtc::Message msg;
if (thread->Get(&msg, rtc::Thread::kForever)) {
thread->Dispatch(&msg);
}
}
return !thread->IsQuitting();
}
SctpTransport* transport1() { return transport1_.get(); }
SctpTransport* transport2() { return transport2_.get(); }
SctpFakeDataReceiver* receiver1() { return recv1_.get(); }
SctpFakeDataReceiver* receiver2() { return recv2_.get(); }
FakeTransportChannel* fake_dtls1() { return fake_dtls1_.get(); }
FakeTransportChannel* fake_dtls2() { return fake_dtls2_.get(); }
int transport1_ready_to_send_count() {
return transport1_ready_to_send_count_;
}
int transport2_ready_to_send_count() {
return transport2_ready_to_send_count_;
}
private:
std::unique_ptr<FakeTransportChannel> fake_dtls1_;
std::unique_ptr<FakeTransportChannel> fake_dtls2_;
std::unique_ptr<SctpFakeDataReceiver> recv1_;
std::unique_ptr<SctpFakeDataReceiver> recv2_;
std::unique_ptr<SctpTransport> transport1_;
std::unique_ptr<SctpTransport> transport2_;
int transport1_ready_to_send_count_ = 0;
int transport2_ready_to_send_count_ = 0;
void OnChan1ReadyToSend() { ++transport1_ready_to_send_count_; }
void OnChan2ReadyToSend() { ++transport2_ready_to_send_count_; }
};
// Test that data can be sent end-to-end when an SCTP transport starts with one
// transport channel (which is unwritable), and then switches to another
// channel. A common scenario due to how BUNDLE works.
TEST_F(SctpTransportTest, SwitchTransportChannel) {
FakeTransportChannel black_hole("black hole", 0);
FakeTransportChannel fake_dtls1("fake dtls 1", 0);
FakeTransportChannel fake_dtls2("fake dtls 2", 0);
SctpFakeDataReceiver recv1;
SctpFakeDataReceiver recv2;
// Construct transport1 with the "black hole" channel.
std::unique_ptr<SctpTransport> transport1(
CreateTransport(&black_hole, &recv1));
std::unique_ptr<SctpTransport> transport2(
CreateTransport(&fake_dtls2, &recv2));
// Add a stream.
transport1->OpenStream(1);
transport2->OpenStream(1);
// Tell them both to start (though transport1_ is connected to black_hole).
transport1->Start(kTransport1Port, kTransport2Port);
transport2->Start(kTransport2Port, kTransport1Port);
// Switch transport1_ to the normal fake_dtls1_ channel.
transport1->SetTransportChannel(&fake_dtls1);
// Connect the two fake DTLS channels.
bool asymmetric = false;
fake_dtls1.SetDestination(&fake_dtls2, asymmetric);
// Make sure we end up able to send data.
SendDataResult result;
ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result));
ASSERT_TRUE(SendData(transport2.get(), 1, "bar", &result));
EXPECT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout);
EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "bar"), kDefaultTimeout);
}
// Calling Start twice shouldn't do anything bad, if with the same parameters.
TEST_F(SctpTransportTest, DuplicateStartCallsIgnored) {
SetupConnectedTransportsWithTwoStreams();
EXPECT_TRUE(transport1()->Start(kTransport1Port, kTransport2Port));
// Make sure we can still send/recv data.
SendDataResult result;
ASSERT_TRUE(SendData(transport1(), 1, "foo", &result));
ASSERT_TRUE(SendData(transport2(), 1, "bar", &result));
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "foo"), kDefaultTimeout);
EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 1, "bar"), kDefaultTimeout);
}
// Calling Start a second time with a different port should fail.
TEST_F(SctpTransportTest, CallingStartWithDifferentPortFails) {
SetupConnectedTransportsWithTwoStreams();
EXPECT_FALSE(transport1()->Start(kTransport1Port, 1234));
EXPECT_FALSE(transport1()->Start(1234, kTransport2Port));
}
// A value of -1 for the local/remote port should be treated as the default
// (5000).
TEST_F(SctpTransportTest, NegativeOnePortTreatedAsDefault) {
FakeTransportChannel fake_dtls1("fake dtls 1", 0);
FakeTransportChannel fake_dtls2("fake dtls 2", 0);
SctpFakeDataReceiver recv1;
SctpFakeDataReceiver recv2;
std::unique_ptr<SctpTransport> transport1(
CreateTransport(&fake_dtls1, &recv1));
std::unique_ptr<SctpTransport> transport2(
CreateTransport(&fake_dtls2, &recv2));
// Add a stream.
transport1->OpenStream(1);
transport2->OpenStream(1);
// Tell them both to start, giving one transport the default port and the
// other transport -1.
transport1->Start(kSctpDefaultPort, kSctpDefaultPort);
transport2->Start(-1, -1);
// Connect the two fake DTLS channels.
bool asymmetric = false;
fake_dtls1.SetDestination(&fake_dtls2, asymmetric);
// Make sure we end up able to send data.
SendDataResult result;
ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result));
ASSERT_TRUE(SendData(transport2.get(), 1, "bar", &result));
EXPECT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout);
EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "bar"), kDefaultTimeout);
}
TEST_F(SctpTransportTest, OpenStreamWithAlreadyOpenedStreamFails) {
FakeTransportChannel fake_dtls("fake dtls", 0);
SctpFakeDataReceiver recv;
std::unique_ptr<SctpTransport> transport(CreateTransport(&fake_dtls, &recv));
EXPECT_TRUE(transport->OpenStream(1));
EXPECT_FALSE(transport->OpenStream(1));
}
TEST_F(SctpTransportTest, ResetStreamWithAlreadyResetStreamFails) {
FakeTransportChannel fake_dtls("fake dtls", 0);
SctpFakeDataReceiver recv;
std::unique_ptr<SctpTransport> transport(CreateTransport(&fake_dtls, &recv));
EXPECT_TRUE(transport->OpenStream(1));
EXPECT_TRUE(transport->ResetStream(1));
EXPECT_FALSE(transport->ResetStream(1));
}
// Test that SignalReadyToSendData is fired after Start has been called and the
// DTLS channel is writable.
TEST_F(SctpTransportTest, SignalReadyToSendDataAfterDtlsWritable) {
FakeTransportChannel fake_dtls("fake dtls", 0);
SctpFakeDataReceiver recv;
std::unique_ptr<SctpTransport> transport(CreateTransport(&fake_dtls, &recv));
SignalReadyToSendObserver signal_observer;
transport->SignalReadyToSendData.connect(
&signal_observer, &SignalReadyToSendObserver::OnSignaled);
transport->Start(kSctpDefaultPort, kSctpDefaultPort);
fake_dtls.SetWritable(true);
EXPECT_TRUE_WAIT(signal_observer.IsSignaled(), kDefaultTimeout);
}
// Test that after an SCTP socket's buffer is filled, SignalReadyToSendData
// is fired after it begins to be drained.
TEST_F(SctpTransportTest, SignalReadyToSendDataAfterBlocked) {
SetupConnectedTransportsWithTwoStreams();
// Wait for initial SCTP association to be formed.
EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout);
// Make the fake transport unwritable so that messages pile up for the SCTP
// socket.
fake_dtls1()->SetWritable(false);
// Send messages until we get EWOULDBLOCK.
static const int kMaxMessages = 1024;
SendDataParams params;
params.sid = 1;
rtc::CopyOnWriteBuffer buf(1024);
memset(buf.data<uint8_t>(), 0, 1024);
SendDataResult result;
int message_count;
for (message_count = 0; message_count < kMaxMessages; ++message_count) {
if (!transport1()->SendData(params, buf, &result) && result == SDR_BLOCK) {
break;
}
}
ASSERT_NE(kMaxMessages, message_count)
<< "Sent max number of messages without getting SDR_BLOCK?";
// Make sure the ready-to-send count hasn't changed.
EXPECT_EQ(1, transport1_ready_to_send_count());
// Make the transport writable again and expect a "SignalReadyToSendData" at
// some point.
fake_dtls1()->SetWritable(true);
EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout);
}
TEST_F(SctpTransportTest, SendData) {
SetupConnectedTransportsWithTwoStreams();
SendDataResult result;
LOG(LS_VERBOSE)
<< "transport1 sending: 'hello?' -----------------------------";
ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received()
<< ", recv2.last_params.sid="
<< receiver2()->last_params().sid
<< ", recv2.last_params.timestamp="
<< receiver2()->last_params().timestamp
<< ", recv2.last_params.seq_num="
<< receiver2()->last_params().seq_num
<< ", recv2.last_data=" << receiver2()->last_data();
LOG(LS_VERBOSE)
<< "transport2 sending: 'hi transport1' -----------------------------";
ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
kDefaultTimeout);
LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received()
<< ", recv1.last_params.sid="
<< receiver1()->last_params().sid
<< ", recv1.last_params.timestamp="
<< receiver1()->last_params().timestamp
<< ", recv1.last_params.seq_num="
<< receiver1()->last_params().seq_num
<< ", recv1.last_data=" << receiver1()->last_data();
}
// Sends a lot of large messages at once and verifies SDR_BLOCK is returned.
TEST_F(SctpTransportTest, SendDataBlocked) {
SetupConnectedTransportsWithTwoStreams();
SendDataResult result;
SendDataParams params;
params.sid = 1;
std::vector<char> buffer(1024 * 64, 0);
for (size_t i = 0; i < 100; ++i) {
transport1()->SendData(
params, rtc::CopyOnWriteBuffer(&buffer[0], buffer.size()), &result);
if (result == SDR_BLOCK)
break;
}
EXPECT_EQ(SDR_BLOCK, result);
}
// Trying to send data for a nonexistent stream should fail.
TEST_F(SctpTransportTest, SendDataWithNonexistentStreamFails) {
SetupConnectedTransportsWithTwoStreams();
SendDataResult result;
EXPECT_FALSE(SendData(transport2(), 123, "some data", &result));
EXPECT_EQ(SDR_ERROR, result);
}
TEST_F(SctpTransportTest, ClosesRemoteStream) {
SetupConnectedTransportsWithTwoStreams();
SignalTransportClosedObserver transport1_sig_receiver,
transport2_sig_receiver;
transport1_sig_receiver.BindSelf(transport1());
transport2_sig_receiver.BindSelf(transport2());
SendDataResult result;
ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
kDefaultTimeout);
// Close transport 1. Transport 2 should notify us.
transport1()->ResetStream(1);
EXPECT_TRUE_WAIT(transport2_sig_receiver.WasStreamClosed(1), kDefaultTimeout);
}
TEST_F(SctpTransportTest, ClosesTwoRemoteStreams) {
SetupConnectedTransportsWithTwoStreams();
AddStream(3);
SignalTransportClosedObserver transport1_sig_receiver,
transport2_sig_receiver;
transport1_sig_receiver.BindSelf(transport1());
transport2_sig_receiver.BindSelf(transport2());
SendDataResult result;
ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
kDefaultTimeout);
// Close two streams on one side.
transport2()->ResetStream(2);
transport2()->ResetStream(3);
EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(2), kDefaultTimeout);
EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(3), kDefaultTimeout);
}
TEST_F(SctpTransportTest, ClosesStreamsOnBothSides) {
SetupConnectedTransportsWithTwoStreams();
AddStream(3);
AddStream(4);
SignalTransportClosedObserver transport1_sig_receiver,
transport2_sig_receiver;
transport1_sig_receiver.BindSelf(transport1());
transport2_sig_receiver.BindSelf(transport2());
SendDataResult result;
ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"),
kDefaultTimeout);
// Close one stream on transport1(), while closing three streams on
// transport2(). They will conflict (only one side can close anything at a
// time, apparently). Test the resolution of the conflict.
transport1()->ResetStream(1);
transport2()->ResetStream(2);
transport2()->ResetStream(3);
transport2()->ResetStream(4);
EXPECT_TRUE_WAIT(transport2_sig_receiver.WasStreamClosed(1), kDefaultTimeout);
EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(2), kDefaultTimeout);
EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(3), kDefaultTimeout);
EXPECT_TRUE_WAIT(transport1_sig_receiver.WasStreamClosed(4), kDefaultTimeout);
}
TEST_F(SctpTransportTest, RefusesHighNumberedTransports) {
SetupConnectedTransportsWithTwoStreams();
EXPECT_TRUE(AddStream(kMaxSctpSid));
EXPECT_FALSE(AddStream(kMaxSctpSid + 1));
}
// Flaky, see webrtc:4453.
TEST_F(SctpTransportTest, DISABLED_ReusesAStream) {
// Shut down transport 1, then open it up again for reuse.
SetupConnectedTransportsWithTwoStreams();
SendDataResult result;
SignalTransportClosedObserver transport2_sig_receiver;
transport2_sig_receiver.BindSelf(transport2());
ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout);
transport1()->ResetStream(1);
EXPECT_TRUE_WAIT(transport2_sig_receiver.WasStreamClosed(1), kDefaultTimeout);
// Transport 1 is gone now.
// Create a new transport 1.
AddStream(1);
ASSERT_TRUE(SendData(transport1(), 1, "hi?", &result));
EXPECT_EQ(SDR_SUCCESS, result);
EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hi?"), kDefaultTimeout);
transport1()->ResetStream(1);
EXPECT_TRUE_WAIT(transport2_sig_receiver.StreamCloseCount(1) == 2,
kDefaultTimeout);
}
} // namespace cricket

View File

@ -0,0 +1,137 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_
#define WEBRTC_MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_
// TODO(deadbeef): Move SCTP code out of media/, and make it not depend on
// anything in media/.
#include <memory> // for unique_ptr
#include <string>
#include <vector>
#include "webrtc/base/copyonwritebuffer.h"
#include "webrtc/base/thread.h"
// For SendDataParams/ReceiveDataParams.
// TODO(deadbeef): Use something else for SCTP. It's confusing that we use an
// SSRC field for SID.
#include "webrtc/media/base/mediachannel.h"
#include "webrtc/p2p/base/transportchannel.h"
namespace cricket {
// The number of outgoing streams that we'll negotiate. Since stream IDs (SIDs)
// are 0-based, the highest usable SID is 1023.
//
// It's recommended to use the maximum of 65535 in:
// https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.2
// However, we use 1024 in order to save memory. usrsctp allocates 104 bytes
// for each pair of incoming/outgoing streams (on a 64-bit system), so 65535
// streams would waste ~6MB.
//
// Note: "max" and "min" here are inclusive.
constexpr uint16_t kMaxSctpStreams = 1024;
constexpr uint16_t kMaxSctpSid = kMaxSctpStreams - 1;
constexpr uint16_t kMinSctpSid = 0;
// This is the default SCTP port to use. It is passed along the wire and the
// connectee and connector must be using the same port. It is not related to the
// ports at the IP level. (Corresponds to: sockaddr_conn.sconn_port in
// usrsctp.h)
const int kSctpDefaultPort = 5000;
// Abstract SctpTransport interface for use internally (by
// PeerConnection/WebRtcSession/etc.). Exists to allow mock/fake SctpTransports
// to be created.
class SctpTransportInternal {
public:
virtual ~SctpTransportInternal() {}
// Changes what underlying DTLS channel is uses. Used when switching which
// bundled transport the SctpTransport uses.
// Assumes |channel| is non-null.
virtual void SetTransportChannel(TransportChannel* channel) = 0;
// When Start is called, connects as soon as possible; this can be called
// before DTLS completes, in which case the connection will begin when DTLS
// completes. This method can be called multiple times, though not if either
// of the ports are changed.
//
// |local_sctp_port| and |remote_sctp_port| are passed along the wire and the
// listener and connector must be using the same port. They are not related
// to the ports at the IP level. If set to -1, we default to
// kSctpDefaultPort.
//
// TODO(deadbeef): Add remote max message size as parameter to Start, once we
// start supporting it.
// TODO(deadbeef): Support calling Start with different local/remote ports
// and create a new association? Not clear if this is something we need to
// support though. See: https://github.com/w3c/webrtc-pc/issues/979
virtual bool Start(int local_sctp_port, int remote_sctp_port) = 0;
// NOTE: Initially there was a "Stop" method here, but it was never used, so
// it was removed.
// Informs SctpTransport that |sid| will start being used. Returns false if
// it is impossible to use |sid|, or if it's already in use.
// Until calling this, can't send data using |sid|.
// TODO(deadbeef): Actually implement the "returns false if |sid| can't be
// used" part. See:
// https://bugs.chromium.org/p/chromium/issues/detail?id=619849
virtual bool OpenStream(int sid) = 0;
// The inverse of OpenStream. When this method returns, the reset process may
// have not finished but it will have begun.
// TODO(deadbeef): We need a way to tell when it's done. See:
// https://bugs.chromium.org/p/webrtc/issues/detail?id=4453
virtual bool ResetStream(int sid) = 0;
// Send data down this channel (will be wrapped as SCTP packets then given to
// usrsctp that will then post the network interface).
// Returns true iff successful data somewhere on the send-queue/network.
// Uses |params.ssrc| as the SCTP sid.
virtual bool SendData(const SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
SendDataResult* result = nullptr) = 0;
// Indicates when the SCTP socket is created and not blocked by congestion
// control. This changes to false when SDR_BLOCK is returned from SendData,
// and
// changes to true when SignalReadyToSendData is fired. The underlying DTLS/
// ICE channels may be unwritable while ReadyToSendData is true, because data
// can still be queued in usrsctp.
virtual bool ReadyToSendData() = 0;
sigslot::signal0<> SignalReadyToSendData;
// ReceiveDataParams includes SID, seq num, timestamp, etc. CopyOnWriteBuffer
// contains message payload.
sigslot::signal2<const ReceiveDataParams&, const rtc::CopyOnWriteBuffer&>
SignalDataReceived;
// Parameter is SID of closed stream.
sigslot::signal1<int> SignalStreamClosedRemotely;
// Helper for debugging.
virtual void set_debug_name_for_testing(const char* debug_name) = 0;
};
// Factory class which can be used to allow fake SctpTransports to be injected
// for testing. Or, theoretically, SctpTransportInternal implementations that
// use something other than usrsctp.
class SctpTransportInternalFactory {
public:
virtual ~SctpTransportInternalFactory() {}
// Create an SCTP transport using |channel| for the underlying transport.
virtual std::unique_ptr<SctpTransportInternal> CreateSctpTransport(
TransportChannel* channel) = 0;
};
} // namespace cricket
#endif // WEBRTC_MEDIA_SCTP_SCTPTRANSPORTINTERNAL_H_

View File

@ -69,7 +69,6 @@ enum {
MSG_READYTOSENDDATA,
MSG_DATARECEIVED,
MSG_FIRSTPACKETRECEIVED,
MSG_STREAMCLOSEDREMOTELY,
};
// Value specified in RFC 5764.
@ -931,16 +930,16 @@ void BaseChannel::ChannelWritable_n() {
UpdateMediaSendRecvState();
}
void BaseChannel::SignalDtlsSetupFailure_n(bool rtcp) {
void BaseChannel::SignalDtlsSrtpSetupFailure_n(bool rtcp) {
RTC_DCHECK(network_thread_->IsCurrent());
invoker_.AsyncInvoke<void>(
RTC_FROM_HERE, signaling_thread(),
Bind(&BaseChannel::SignalDtlsSetupFailure_s, this, rtcp));
Bind(&BaseChannel::SignalDtlsSrtpSetupFailure_s, this, rtcp));
}
void BaseChannel::SignalDtlsSetupFailure_s(bool rtcp) {
void BaseChannel::SignalDtlsSrtpSetupFailure_s(bool rtcp) {
RTC_DCHECK(signaling_thread() == rtc::Thread::Current());
SignalDtlsSetupFailure(this, rtcp);
SignalDtlsSrtpSetupFailure(this, rtcp);
}
bool BaseChannel::SetDtlsSrtpCryptoSuites_n(TransportChannel* tc, bool rtcp) {
@ -1061,13 +1060,13 @@ void BaseChannel::MaybeSetupDtlsSrtp_n() {
}
if (!SetupDtlsSrtp_n(false)) {
SignalDtlsSetupFailure_n(false);
SignalDtlsSrtpSetupFailure_n(false);
return;
}
if (rtcp_transport_channel_) {
if (!SetupDtlsSrtp_n(true)) {
SignalDtlsSetupFailure_n(true);
SignalDtlsSrtpSetupFailure_n(true);
return;
}
}
@ -2147,25 +2146,23 @@ void VideoChannel::GetSrtpCryptoSuites_n(
GetSupportedVideoCryptoSuites(crypto_options(), crypto_suites);
}
DataChannel::DataChannel(rtc::Thread* worker_thread,
rtc::Thread* network_thread,
DataMediaChannel* media_channel,
TransportController* transport_controller,
const std::string& content_name,
bool rtcp,
bool srtp_required)
RtpDataChannel::RtpDataChannel(rtc::Thread* worker_thread,
rtc::Thread* network_thread,
DataMediaChannel* media_channel,
TransportController* transport_controller,
const std::string& content_name,
bool rtcp,
bool srtp_required)
: BaseChannel(worker_thread,
network_thread,
media_channel,
transport_controller,
content_name,
rtcp,
srtp_required),
data_channel_type_(cricket::DCT_NONE),
ready_to_send_data_(false) {}
srtp_required) {}
DataChannel::~DataChannel() {
TRACE_EVENT0("webrtc", "DataChannel::~DataChannel");
RtpDataChannel::~RtpDataChannel() {
TRACE_EVENT0("webrtc", "RtpDataChannel::~RtpDataChannel");
StopMediaMonitor();
// this can't be done in the base class, since it calls a virtual
DisableMedia_w();
@ -2173,78 +2170,48 @@ DataChannel::~DataChannel() {
Deinit();
}
bool DataChannel::Init_w(const std::string* bundle_transport_name) {
bool RtpDataChannel::Init_w(const std::string* bundle_transport_name) {
if (!BaseChannel::Init_w(bundle_transport_name)) {
return false;
}
media_channel()->SignalDataReceived.connect(
this, &DataChannel::OnDataReceived);
media_channel()->SignalDataReceived.connect(this,
&RtpDataChannel::OnDataReceived);
media_channel()->SignalReadyToSend.connect(
this, &DataChannel::OnDataChannelReadyToSend);
media_channel()->SignalStreamClosedRemotely.connect(
this, &DataChannel::OnStreamClosedRemotely);
this, &RtpDataChannel::OnDataChannelReadyToSend);
return true;
}
bool DataChannel::SendData(const SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
SendDataResult* result) {
bool RtpDataChannel::SendData(const SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
SendDataResult* result) {
return InvokeOnWorker(
RTC_FROM_HERE, Bind(&DataMediaChannel::SendData, media_channel(), params,
payload, result));
}
const ContentInfo* DataChannel::GetFirstContent(
const ContentInfo* RtpDataChannel::GetFirstContent(
const SessionDescription* sdesc) {
return GetFirstDataContent(sdesc);
}
bool DataChannel::WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet) {
if (data_channel_type_ == DCT_SCTP) {
// TODO(pthatcher): Do this in a more robust way by checking for
// SCTP or DTLS.
return !IsRtpPacket(packet->data(), packet->size());
} else if (data_channel_type_ == DCT_RTP) {
return BaseChannel::WantsPacket(rtcp, packet);
}
return false;
}
bool DataChannel::SetDataChannelType(DataChannelType new_data_channel_type,
std::string* error_desc) {
// It hasn't been set before, so set it now.
if (data_channel_type_ == DCT_NONE) {
data_channel_type_ = new_data_channel_type;
return true;
}
// It's been set before, but doesn't match. That's bad.
if (data_channel_type_ != new_data_channel_type) {
std::ostringstream desc;
desc << "Data channel type mismatch."
<< " Expected " << data_channel_type_
<< " Got " << new_data_channel_type;
SafeSetError(desc.str(), error_desc);
return false;
}
// It's hasn't changed. Nothing to do.
return true;
}
bool DataChannel::SetDataChannelTypeFromContent(
bool RtpDataChannel::CheckDataChannelTypeFromContent(
const DataContentDescription* content,
std::string* error_desc) {
bool is_sctp = ((content->protocol() == kMediaProtocolSctp) ||
(content->protocol() == kMediaProtocolDtlsSctp));
DataChannelType data_channel_type = is_sctp ? DCT_SCTP : DCT_RTP;
return SetDataChannelType(data_channel_type, error_desc);
// It's been set before, but doesn't match. That's bad.
if (is_sctp) {
SafeSetError("Data channel type mismatch. Expected RTP, got SCTP.",
error_desc);
return false;
}
return true;
}
bool DataChannel::SetLocalContent_w(const MediaContentDescription* content,
ContentAction action,
std::string* error_desc) {
TRACE_EVENT0("webrtc", "DataChannel::SetLocalContent_w");
bool RtpDataChannel::SetLocalContent_w(const MediaContentDescription* content,
ContentAction action,
std::string* error_desc) {
TRACE_EVENT0("webrtc", "RtpDataChannel::SetLocalContent_w");
RTC_DCHECK(worker_thread() == rtc::Thread::Current());
LOG(LS_INFO) << "Setting local data description";
@ -2256,19 +2223,14 @@ bool DataChannel::SetLocalContent_w(const MediaContentDescription* content,
return false;
}
if (!SetDataChannelTypeFromContent(data, error_desc)) {
if (!CheckDataChannelTypeFromContent(data, error_desc)) {
return false;
}
if (data_channel_type_ == DCT_RTP) {
if (!SetRtpTransportParameters(content, action, CS_LOCAL, error_desc)) {
return false;
}
if (!SetRtpTransportParameters(content, action, CS_LOCAL, error_desc)) {
return false;
}
// FYI: We send the SCTP port number (not to be confused with the
// underlying UDP port number) as a codec parameter. So even SCTP
// data channels need codecs.
DataRecvParameters recv_params = last_recv_params_;
RtpParametersFromMediaDescription(data, &recv_params);
if (!media_channel()->SetRecvParameters(recv_params)) {
@ -2276,10 +2238,8 @@ bool DataChannel::SetLocalContent_w(const MediaContentDescription* content,
error_desc);
return false;
}
if (data_channel_type_ == DCT_RTP) {
for (const DataCodec& codec : data->codecs()) {
bundle_filter()->AddPayloadType(codec.id);
}
for (const DataCodec& codec : data->codecs()) {
bundle_filter()->AddPayloadType(codec.id);
}
last_recv_params_ = recv_params;
@ -2297,10 +2257,10 @@ bool DataChannel::SetLocalContent_w(const MediaContentDescription* content,
return true;
}
bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content,
ContentAction action,
std::string* error_desc) {
TRACE_EVENT0("webrtc", "DataChannel::SetRemoteContent_w");
bool RtpDataChannel::SetRemoteContent_w(const MediaContentDescription* content,
ContentAction action,
std::string* error_desc) {
TRACE_EVENT0("webrtc", "RtpDataChannel::SetRemoteContent_w");
RTC_DCHECK(worker_thread() == rtc::Thread::Current());
const DataContentDescription* data =
@ -2317,17 +2277,15 @@ bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content,
return true;
}
if (!SetDataChannelTypeFromContent(data, error_desc)) {
if (!CheckDataChannelTypeFromContent(data, error_desc)) {
return false;
}
LOG(LS_INFO) << "Setting remote data description";
if (data_channel_type_ == DCT_RTP &&
!SetRtpTransportParameters(content, action, CS_REMOTE, error_desc)) {
if (!SetRtpTransportParameters(content, action, CS_REMOTE, error_desc)) {
return false;
}
DataSendParameters send_params = last_send_params_;
RtpSendParametersFromMediaDescription<DataCodec>(data, &send_params);
if (!media_channel()->SetSendParameters(send_params)) {
@ -2352,7 +2310,7 @@ bool DataChannel::SetRemoteContent_w(const MediaContentDescription* content,
return true;
}
void DataChannel::UpdateMediaSendRecvState_w() {
void RtpDataChannel::UpdateMediaSendRecvState_w() {
// Render incoming data if we're the active call, and we have the local
// content. We receive data on the default channel and multiplexed streams.
bool recv = IsReadyToReceiveMedia_w();
@ -2373,7 +2331,7 @@ void DataChannel::UpdateMediaSendRecvState_w() {
LOG(LS_INFO) << "Changing data state, recv=" << recv << " send=" << send;
}
void DataChannel::OnMessage(rtc::Message *pmsg) {
void RtpDataChannel::OnMessage(rtc::Message* pmsg) {
switch (pmsg->message_id) {
case MSG_READYTOSENDDATA: {
DataChannelReadyToSendMessageData* data =
@ -2386,7 +2344,7 @@ void DataChannel::OnMessage(rtc::Message *pmsg) {
case MSG_DATARECEIVED: {
DataReceivedMessageData* data =
static_cast<DataReceivedMessageData*>(pmsg->pdata);
SignalDataReceived(this, data->params, data->payload);
SignalDataReceived(data->params, data->payload);
delete data;
break;
}
@ -2396,33 +2354,27 @@ void DataChannel::OnMessage(rtc::Message *pmsg) {
delete data;
break;
}
case MSG_STREAMCLOSEDREMOTELY: {
rtc::TypedMessageData<uint32_t>* data =
static_cast<rtc::TypedMessageData<uint32_t>*>(pmsg->pdata);
SignalStreamClosedRemotely(data->data());
delete data;
break;
}
default:
BaseChannel::OnMessage(pmsg);
break;
}
}
void DataChannel::OnConnectionMonitorUpdate(
ConnectionMonitor* monitor, const std::vector<ConnectionInfo>& infos) {
void RtpDataChannel::OnConnectionMonitorUpdate(
ConnectionMonitor* monitor,
const std::vector<ConnectionInfo>& infos) {
SignalConnectionMonitor(this, infos);
}
void DataChannel::StartMediaMonitor(int cms) {
void RtpDataChannel::StartMediaMonitor(int cms) {
media_monitor_.reset(new DataMediaMonitor(media_channel(), worker_thread(),
rtc::Thread::Current()));
media_monitor_->SignalUpdate.connect(
this, &DataChannel::OnMediaMonitorUpdate);
media_monitor_->SignalUpdate.connect(this,
&RtpDataChannel::OnMediaMonitorUpdate);
media_monitor_->Start(cms);
}
void DataChannel::StopMediaMonitor() {
void RtpDataChannel::StopMediaMonitor() {
if (media_monitor_) {
media_monitor_->Stop();
media_monitor_->SignalUpdate.disconnect(this);
@ -2430,27 +2382,28 @@ void DataChannel::StopMediaMonitor() {
}
}
void DataChannel::OnMediaMonitorUpdate(
DataMediaChannel* media_channel, const DataMediaInfo& info) {
void RtpDataChannel::OnMediaMonitorUpdate(DataMediaChannel* media_channel,
const DataMediaInfo& info) {
RTC_DCHECK(media_channel == this->media_channel());
SignalMediaMonitor(this, info);
}
void DataChannel::OnDataReceived(
const ReceiveDataParams& params, const char* data, size_t len) {
void RtpDataChannel::OnDataReceived(const ReceiveDataParams& params,
const char* data,
size_t len) {
DataReceivedMessageData* msg = new DataReceivedMessageData(
params, data, len);
signaling_thread()->Post(RTC_FROM_HERE, this, MSG_DATARECEIVED, msg);
}
void DataChannel::OnDataChannelError(uint32_t ssrc,
DataMediaChannel::Error err) {
void RtpDataChannel::OnDataChannelError(uint32_t ssrc,
DataMediaChannel::Error err) {
DataChannelErrorMessageData* data = new DataChannelErrorMessageData(
ssrc, err);
signaling_thread()->Post(RTC_FROM_HERE, this, MSG_CHANNEL_ERROR, data);
}
void DataChannel::OnDataChannelReadyToSend(bool writable) {
void RtpDataChannel::OnDataChannelReadyToSend(bool writable) {
// This is usded for congestion control to indicate that the stream is ready
// to send by the MediaChannel, as opposed to OnReadyToSend, which indicates
// that the transport channel is ready.
@ -2458,19 +2411,9 @@ void DataChannel::OnDataChannelReadyToSend(bool writable) {
new DataChannelReadyToSendMessageData(writable));
}
void DataChannel::GetSrtpCryptoSuites_n(std::vector<int>* crypto_suites) const {
void RtpDataChannel::GetSrtpCryptoSuites_n(
std::vector<int>* crypto_suites) const {
GetSupportedDataCryptoSuites(crypto_options(), crypto_suites);
}
bool DataChannel::ShouldSetupDtlsSrtp_n() const {
return data_channel_type_ == DCT_RTP && BaseChannel::ShouldSetupDtlsSrtp_n();
}
void DataChannel::OnStreamClosedRemotely(uint32_t sid) {
rtc::TypedMessageData<uint32_t>* message =
new rtc::TypedMessageData<uint32_t>(sid);
signaling_thread()->Post(RTC_FROM_HERE, this, MSG_STREAMCLOSEDREMOTELY,
message);
}
} // namespace cricket

View File

@ -149,9 +149,9 @@ class BaseChannel
return remote_streams_;
}
sigslot::signal2<BaseChannel*, bool> SignalDtlsSetupFailure;
void SignalDtlsSetupFailure_n(bool rtcp);
void SignalDtlsSetupFailure_s(bool rtcp);
sigslot::signal2<BaseChannel*, bool> SignalDtlsSrtpSetupFailure;
void SignalDtlsSrtpSetupFailure_n(bool rtcp);
void SignalDtlsSrtpSetupFailure_s(bool rtcp);
// Used for latency measurements.
sigslot::signal1<BaseChannel*> SignalFirstPacketReceived;
@ -261,7 +261,7 @@ class BaseChannel
rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options);
virtual bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet);
bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet);
void HandlePacket(bool rtcp, rtc::CopyOnWriteBuffer* packet,
const rtc::PacketTime& packet_time);
void OnPacketReceived(bool rtcp,
@ -282,7 +282,7 @@ class BaseChannel
bool RemoveRecvStream_w(uint32_t ssrc);
bool AddSendStream_w(const StreamParams& sp);
bool RemoveSendStream_w(uint32_t ssrc);
virtual bool ShouldSetupDtlsSrtp_n() const;
bool ShouldSetupDtlsSrtp_n() const;
// Do the DTLS key expansion and impose it on the SRTP/SRTCP filters.
// |rtcp_channel| indicates whether to set up the RTP or RTCP filter.
bool SetupDtlsSrtp_n(bool rtcp_channel);
@ -615,17 +615,17 @@ class VideoChannel : public BaseChannel {
VideoRecvParameters last_recv_params_;
};
// DataChannel is a specialization for data.
class DataChannel : public BaseChannel {
// RtpDataChannel is a specialization for data.
class RtpDataChannel : public BaseChannel {
public:
DataChannel(rtc::Thread* worker_thread,
rtc::Thread* network_thread,
DataMediaChannel* media_channel,
TransportController* transport_controller,
const std::string& content_name,
bool rtcp,
bool srtp_required);
~DataChannel();
RtpDataChannel(rtc::Thread* worker_thread,
rtc::Thread* network_thread,
DataMediaChannel* media_channel,
TransportController* transport_controller,
const std::string& content_name,
bool rtcp,
bool srtp_required);
~RtpDataChannel();
bool Init_w(const std::string* bundle_transport_name);
virtual bool SendData(const SendDataParams& params,
@ -640,17 +640,16 @@ class DataChannel : public BaseChannel {
return ready_to_send_data_;
}
sigslot::signal2<DataChannel*, const DataMediaInfo&> SignalMediaMonitor;
sigslot::signal2<DataChannel*, const std::vector<ConnectionInfo>&>
sigslot::signal2<RtpDataChannel*, const DataMediaInfo&> SignalMediaMonitor;
sigslot::signal2<RtpDataChannel*, const std::vector<ConnectionInfo>&>
SignalConnectionMonitor;
sigslot::signal3<DataChannel*, const ReceiveDataParams&,
const rtc::CopyOnWriteBuffer&> SignalDataReceived;
sigslot::signal2<const ReceiveDataParams&, const rtc::CopyOnWriteBuffer&>
SignalDataReceived;
// Signal for notifying when the channel becomes ready to send data.
// That occurs when the channel is enabled, the transport is writable,
// both local and remote descriptions are set, and the channel is unblocked.
sigslot::signal1<bool> SignalReadyToSendData;
// Signal for notifying that the remote side has closed the DataChannel.
sigslot::signal1<uint32_t> SignalStreamClosedRemotely;
cricket::MediaType media_type() override { return cricket::MEDIA_TYPE_DATA; }
protected:
@ -693,15 +692,9 @@ class DataChannel : public BaseChannel {
// overrides from BaseChannel
const ContentInfo* GetFirstContent(const SessionDescription* sdesc) override;
// If data_channel_type_ is DCT_NONE, set it. Otherwise, check that
// it's the same as what was set previously. Returns false if it's
// set to one type one type and changed to another type later.
bool SetDataChannelType(DataChannelType new_data_channel_type,
std::string* error_desc);
// Same as SetDataChannelType, but extracts the type from the
// DataContentDescription.
bool SetDataChannelTypeFromContent(const DataContentDescription* content,
std::string* error_desc);
// Checks that data channel type is RTP.
bool CheckDataChannelTypeFromContent(const DataContentDescription* content,
std::string* error_desc);
bool SetLocalContent_w(const MediaContentDescription* content,
ContentAction action,
std::string* error_desc) override;
@ -709,7 +702,6 @@ class DataChannel : public BaseChannel {
ContentAction action,
std::string* error_desc) override;
void UpdateMediaSendRecvState_w() override;
bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet) override;
void OnMessage(rtc::Message* pmsg) override;
void GetSrtpCryptoSuites_n(std::vector<int>* crypto_suites) const override;
@ -718,18 +710,13 @@ class DataChannel : public BaseChannel {
const std::vector<ConnectionInfo>& infos) override;
void OnMediaMonitorUpdate(DataMediaChannel* media_channel,
const DataMediaInfo& info);
bool ShouldSetupDtlsSrtp_n() const override;
void OnDataReceived(
const ReceiveDataParams& params, const char* data, size_t len);
void OnDataChannelError(uint32_t ssrc, DataMediaChannel::Error error);
void OnDataChannelReadyToSend(bool writable);
void OnStreamClosedRemotely(uint32_t sid);
std::unique_ptr<DataMediaMonitor> media_monitor_;
// TODO(pthatcher): Make a separate SctpDataChannel and
// RtpDataChannel instead of using this.
DataChannelType data_channel_type_;
bool ready_to_send_data_;
bool ready_to_send_data_ = false;
// Last DataSendParameters sent down to the media_channel() via
// SetSendParameters.

View File

@ -84,14 +84,14 @@ class VideoTraits : public Traits<cricket::VideoChannel,
cricket::VideoMediaInfo,
cricket::VideoOptions> {};
class DataTraits : public Traits<cricket::DataChannel,
class DataTraits : public Traits<cricket::RtpDataChannel,
cricket::FakeDataMediaChannel,
cricket::DataContentDescription,
cricket::DataCodec,
cricket::DataMediaInfo,
cricket::DataOptions> {};
// Base class for Voice/Video/DataChannel tests
// Base class for Voice/Video/RtpDataChannel tests
template<class T>
class ChannelTest : public testing::Test, public sigslot::has_slots<> {
public:
@ -3288,32 +3288,32 @@ TEST_F(VideoChannelDoubleThreadTest, CanChangeMaxBitrate) {
Base::CanChangeMaxBitrate();
}
// DataChannelSingleThreadTest
class DataChannelSingleThreadTest : public ChannelTest<DataTraits> {
// RtpDataChannelSingleThreadTest
class RtpDataChannelSingleThreadTest : public ChannelTest<DataTraits> {
public:
typedef ChannelTest<DataTraits> Base;
DataChannelSingleThreadTest()
RtpDataChannelSingleThreadTest()
: Base(true, kDataPacket, kRtcpReport, NetworkIsWorker::Yes) {}
};
// DataChannelDoubleThreadTest
class DataChannelDoubleThreadTest : public ChannelTest<DataTraits> {
// RtpDataChannelDoubleThreadTest
class RtpDataChannelDoubleThreadTest : public ChannelTest<DataTraits> {
public:
typedef ChannelTest<DataTraits> Base;
DataChannelDoubleThreadTest()
RtpDataChannelDoubleThreadTest()
: Base(true, kDataPacket, kRtcpReport, NetworkIsWorker::No) {}
};
// Override to avoid engine channel parameter.
template <>
cricket::DataChannel* ChannelTest<DataTraits>::CreateChannel(
cricket::RtpDataChannel* ChannelTest<DataTraits>::CreateChannel(
rtc::Thread* worker_thread,
rtc::Thread* network_thread,
cricket::MediaEngineInterface* engine,
cricket::FakeDataMediaChannel* ch,
cricket::TransportController* transport_controller,
int flags) {
cricket::DataChannel* channel = new cricket::DataChannel(
cricket::RtpDataChannel* channel = new cricket::RtpDataChannel(
worker_thread, network_thread, ch, transport_controller, cricket::CN_DATA,
(flags & RTCP) != 0, (flags & SECURE) != 0);
rtc::CryptoOptions crypto_options;
@ -3362,136 +3362,136 @@ void ChannelTest<DataTraits>::AddLegacyStreamInContent(
data->AddLegacyStream(ssrc);
}
TEST_F(DataChannelSingleThreadTest, TestInit) {
TEST_F(RtpDataChannelSingleThreadTest, TestInit) {
Base::TestInit();
EXPECT_FALSE(media_channel1_->IsStreamMuted(0));
}
TEST_F(DataChannelSingleThreadTest, TestDeinit) {
TEST_F(RtpDataChannelSingleThreadTest, TestDeinit) {
Base::TestDeinit();
}
TEST_F(DataChannelSingleThreadTest, TestSetContents) {
TEST_F(RtpDataChannelSingleThreadTest, TestSetContents) {
Base::TestSetContents();
}
TEST_F(DataChannelSingleThreadTest, TestSetContentsNullOffer) {
TEST_F(RtpDataChannelSingleThreadTest, TestSetContentsNullOffer) {
Base::TestSetContentsNullOffer();
}
TEST_F(DataChannelSingleThreadTest, TestSetContentsRtcpMux) {
TEST_F(RtpDataChannelSingleThreadTest, TestSetContentsRtcpMux) {
Base::TestSetContentsRtcpMux();
}
TEST_F(DataChannelSingleThreadTest, TestSetRemoteContentUpdate) {
TEST_F(RtpDataChannelSingleThreadTest, TestSetRemoteContentUpdate) {
Base::TestSetRemoteContentUpdate();
}
TEST_F(DataChannelSingleThreadTest, TestStreams) {
TEST_F(RtpDataChannelSingleThreadTest, TestStreams) {
Base::TestStreams();
}
TEST_F(DataChannelSingleThreadTest, TestUpdateStreamsInLocalContent) {
TEST_F(RtpDataChannelSingleThreadTest, TestUpdateStreamsInLocalContent) {
Base::TestUpdateStreamsInLocalContent();
}
TEST_F(DataChannelSingleThreadTest, TestUpdateRemoteStreamsInContent) {
TEST_F(RtpDataChannelSingleThreadTest, TestUpdateRemoteStreamsInContent) {
Base::TestUpdateStreamsInRemoteContent();
}
TEST_F(DataChannelSingleThreadTest, TestChangeStreamParamsInContent) {
TEST_F(RtpDataChannelSingleThreadTest, TestChangeStreamParamsInContent) {
Base::TestChangeStreamParamsInContent();
}
TEST_F(DataChannelSingleThreadTest, TestPlayoutAndSendingStates) {
TEST_F(RtpDataChannelSingleThreadTest, TestPlayoutAndSendingStates) {
Base::TestPlayoutAndSendingStates();
}
TEST_F(DataChannelSingleThreadTest, TestMediaContentDirection) {
TEST_F(RtpDataChannelSingleThreadTest, TestMediaContentDirection) {
Base::TestMediaContentDirection();
}
TEST_F(DataChannelSingleThreadTest, TestCallSetup) {
TEST_F(RtpDataChannelSingleThreadTest, TestCallSetup) {
Base::TestCallSetup();
}
TEST_F(DataChannelSingleThreadTest, TestCallTeardownRtcpMux) {
TEST_F(RtpDataChannelSingleThreadTest, TestCallTeardownRtcpMux) {
Base::TestCallTeardownRtcpMux();
}
TEST_F(DataChannelSingleThreadTest, TestOnReadyToSend) {
TEST_F(RtpDataChannelSingleThreadTest, TestOnReadyToSend) {
Base::TestOnReadyToSend();
}
TEST_F(DataChannelSingleThreadTest, TestOnReadyToSendWithRtcpMux) {
TEST_F(RtpDataChannelSingleThreadTest, TestOnReadyToSendWithRtcpMux) {
Base::TestOnReadyToSendWithRtcpMux();
}
TEST_F(DataChannelSingleThreadTest, SendRtpToRtp) {
TEST_F(RtpDataChannelSingleThreadTest, SendRtpToRtp) {
Base::SendRtpToRtp();
}
TEST_F(DataChannelSingleThreadTest, SendNoRtcpToNoRtcp) {
TEST_F(RtpDataChannelSingleThreadTest, SendNoRtcpToNoRtcp) {
Base::SendNoRtcpToNoRtcp();
}
TEST_F(DataChannelSingleThreadTest, SendNoRtcpToRtcp) {
TEST_F(RtpDataChannelSingleThreadTest, SendNoRtcpToRtcp) {
Base::SendNoRtcpToRtcp();
}
TEST_F(DataChannelSingleThreadTest, SendRtcpToNoRtcp) {
TEST_F(RtpDataChannelSingleThreadTest, SendRtcpToNoRtcp) {
Base::SendRtcpToNoRtcp();
}
TEST_F(DataChannelSingleThreadTest, SendRtcpToRtcp) {
TEST_F(RtpDataChannelSingleThreadTest, SendRtcpToRtcp) {
Base::SendRtcpToRtcp();
}
TEST_F(DataChannelSingleThreadTest, SendRtcpMuxToRtcp) {
TEST_F(RtpDataChannelSingleThreadTest, SendRtcpMuxToRtcp) {
Base::SendRtcpMuxToRtcp();
}
TEST_F(DataChannelSingleThreadTest, SendRtcpMuxToRtcpMux) {
TEST_F(RtpDataChannelSingleThreadTest, SendRtcpMuxToRtcpMux) {
Base::SendRtcpMuxToRtcpMux();
}
TEST_F(DataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcp) {
TEST_F(RtpDataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcp) {
Base::SendEarlyRtcpMuxToRtcp();
}
TEST_F(DataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcpMux) {
TEST_F(RtpDataChannelSingleThreadTest, SendEarlyRtcpMuxToRtcpMux) {
Base::SendEarlyRtcpMuxToRtcpMux();
}
TEST_F(DataChannelSingleThreadTest, SendSrtpToSrtp) {
TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToSrtp) {
Base::SendSrtpToSrtp();
}
TEST_F(DataChannelSingleThreadTest, SendSrtpToRtp) {
TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToRtp) {
Base::SendSrtpToSrtp();
}
TEST_F(DataChannelSingleThreadTest, SendSrtcpMux) {
TEST_F(RtpDataChannelSingleThreadTest, SendSrtcpMux) {
Base::SendSrtpToSrtp(RTCP_MUX, RTCP_MUX);
}
TEST_F(DataChannelSingleThreadTest, SendRtpToRtpOnThread) {
TEST_F(RtpDataChannelSingleThreadTest, SendRtpToRtpOnThread) {
Base::SendRtpToRtpOnThread();
}
TEST_F(DataChannelSingleThreadTest, SendSrtpToSrtpOnThread) {
TEST_F(RtpDataChannelSingleThreadTest, SendSrtpToSrtpOnThread) {
Base::SendSrtpToSrtpOnThread();
}
TEST_F(DataChannelSingleThreadTest, SendWithWritabilityLoss) {
TEST_F(RtpDataChannelSingleThreadTest, SendWithWritabilityLoss) {
Base::SendWithWritabilityLoss();
}
TEST_F(DataChannelSingleThreadTest, TestMediaMonitor) {
TEST_F(RtpDataChannelSingleThreadTest, TestMediaMonitor) {
Base::TestMediaMonitor();
}
TEST_F(DataChannelSingleThreadTest, TestSendData) {
TEST_F(RtpDataChannelSingleThreadTest, TestSendData) {
CreateChannels(0, 0);
EXPECT_TRUE(SendInitiate());
EXPECT_TRUE(SendAccept());
@ -3506,136 +3506,136 @@ TEST_F(DataChannelSingleThreadTest, TestSendData) {
EXPECT_EQ("foo", media_channel1_->last_sent_data());
}
TEST_F(DataChannelDoubleThreadTest, TestInit) {
TEST_F(RtpDataChannelDoubleThreadTest, TestInit) {
Base::TestInit();
EXPECT_FALSE(media_channel1_->IsStreamMuted(0));
}
TEST_F(DataChannelDoubleThreadTest, TestDeinit) {
TEST_F(RtpDataChannelDoubleThreadTest, TestDeinit) {
Base::TestDeinit();
}
TEST_F(DataChannelDoubleThreadTest, TestSetContents) {
TEST_F(RtpDataChannelDoubleThreadTest, TestSetContents) {
Base::TestSetContents();
}
TEST_F(DataChannelDoubleThreadTest, TestSetContentsNullOffer) {
TEST_F(RtpDataChannelDoubleThreadTest, TestSetContentsNullOffer) {
Base::TestSetContentsNullOffer();
}
TEST_F(DataChannelDoubleThreadTest, TestSetContentsRtcpMux) {
TEST_F(RtpDataChannelDoubleThreadTest, TestSetContentsRtcpMux) {
Base::TestSetContentsRtcpMux();
}
TEST_F(DataChannelDoubleThreadTest, TestSetRemoteContentUpdate) {
TEST_F(RtpDataChannelDoubleThreadTest, TestSetRemoteContentUpdate) {
Base::TestSetRemoteContentUpdate();
}
TEST_F(DataChannelDoubleThreadTest, TestStreams) {
TEST_F(RtpDataChannelDoubleThreadTest, TestStreams) {
Base::TestStreams();
}
TEST_F(DataChannelDoubleThreadTest, TestUpdateStreamsInLocalContent) {
TEST_F(RtpDataChannelDoubleThreadTest, TestUpdateStreamsInLocalContent) {
Base::TestUpdateStreamsInLocalContent();
}
TEST_F(DataChannelDoubleThreadTest, TestUpdateRemoteStreamsInContent) {
TEST_F(RtpDataChannelDoubleThreadTest, TestUpdateRemoteStreamsInContent) {
Base::TestUpdateStreamsInRemoteContent();
}
TEST_F(DataChannelDoubleThreadTest, TestChangeStreamParamsInContent) {
TEST_F(RtpDataChannelDoubleThreadTest, TestChangeStreamParamsInContent) {
Base::TestChangeStreamParamsInContent();
}
TEST_F(DataChannelDoubleThreadTest, TestPlayoutAndSendingStates) {
TEST_F(RtpDataChannelDoubleThreadTest, TestPlayoutAndSendingStates) {
Base::TestPlayoutAndSendingStates();
}
TEST_F(DataChannelDoubleThreadTest, TestMediaContentDirection) {
TEST_F(RtpDataChannelDoubleThreadTest, TestMediaContentDirection) {
Base::TestMediaContentDirection();
}
TEST_F(DataChannelDoubleThreadTest, TestCallSetup) {
TEST_F(RtpDataChannelDoubleThreadTest, TestCallSetup) {
Base::TestCallSetup();
}
TEST_F(DataChannelDoubleThreadTest, TestCallTeardownRtcpMux) {
TEST_F(RtpDataChannelDoubleThreadTest, TestCallTeardownRtcpMux) {
Base::TestCallTeardownRtcpMux();
}
TEST_F(DataChannelDoubleThreadTest, TestOnReadyToSend) {
TEST_F(RtpDataChannelDoubleThreadTest, TestOnReadyToSend) {
Base::TestOnReadyToSend();
}
TEST_F(DataChannelDoubleThreadTest, TestOnReadyToSendWithRtcpMux) {
TEST_F(RtpDataChannelDoubleThreadTest, TestOnReadyToSendWithRtcpMux) {
Base::TestOnReadyToSendWithRtcpMux();
}
TEST_F(DataChannelDoubleThreadTest, SendRtpToRtp) {
TEST_F(RtpDataChannelDoubleThreadTest, SendRtpToRtp) {
Base::SendRtpToRtp();
}
TEST_F(DataChannelDoubleThreadTest, SendNoRtcpToNoRtcp) {
TEST_F(RtpDataChannelDoubleThreadTest, SendNoRtcpToNoRtcp) {
Base::SendNoRtcpToNoRtcp();
}
TEST_F(DataChannelDoubleThreadTest, SendNoRtcpToRtcp) {
TEST_F(RtpDataChannelDoubleThreadTest, SendNoRtcpToRtcp) {
Base::SendNoRtcpToRtcp();
}
TEST_F(DataChannelDoubleThreadTest, SendRtcpToNoRtcp) {
TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpToNoRtcp) {
Base::SendRtcpToNoRtcp();
}
TEST_F(DataChannelDoubleThreadTest, SendRtcpToRtcp) {
TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpToRtcp) {
Base::SendRtcpToRtcp();
}
TEST_F(DataChannelDoubleThreadTest, SendRtcpMuxToRtcp) {
TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpMuxToRtcp) {
Base::SendRtcpMuxToRtcp();
}
TEST_F(DataChannelDoubleThreadTest, SendRtcpMuxToRtcpMux) {
TEST_F(RtpDataChannelDoubleThreadTest, SendRtcpMuxToRtcpMux) {
Base::SendRtcpMuxToRtcpMux();
}
TEST_F(DataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcp) {
TEST_F(RtpDataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcp) {
Base::SendEarlyRtcpMuxToRtcp();
}
TEST_F(DataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcpMux) {
TEST_F(RtpDataChannelDoubleThreadTest, SendEarlyRtcpMuxToRtcpMux) {
Base::SendEarlyRtcpMuxToRtcpMux();
}
TEST_F(DataChannelDoubleThreadTest, SendSrtpToSrtp) {
TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToSrtp) {
Base::SendSrtpToSrtp();
}
TEST_F(DataChannelDoubleThreadTest, SendSrtpToRtp) {
TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToRtp) {
Base::SendSrtpToSrtp();
}
TEST_F(DataChannelDoubleThreadTest, SendSrtcpMux) {
TEST_F(RtpDataChannelDoubleThreadTest, SendSrtcpMux) {
Base::SendSrtpToSrtp(RTCP_MUX, RTCP_MUX);
}
TEST_F(DataChannelDoubleThreadTest, SendRtpToRtpOnThread) {
TEST_F(RtpDataChannelDoubleThreadTest, SendRtpToRtpOnThread) {
Base::SendRtpToRtpOnThread();
}
TEST_F(DataChannelDoubleThreadTest, SendSrtpToSrtpOnThread) {
TEST_F(RtpDataChannelDoubleThreadTest, SendSrtpToSrtpOnThread) {
Base::SendSrtpToSrtpOnThread();
}
TEST_F(DataChannelDoubleThreadTest, SendWithWritabilityLoss) {
TEST_F(RtpDataChannelDoubleThreadTest, SendWithWritabilityLoss) {
Base::SendWithWritabilityLoss();
}
TEST_F(DataChannelDoubleThreadTest, TestMediaMonitor) {
TEST_F(RtpDataChannelDoubleThreadTest, TestMediaMonitor) {
Base::TestMediaMonitor();
}
TEST_F(DataChannelDoubleThreadTest, TestSendData) {
TEST_F(RtpDataChannelDoubleThreadTest, TestSendData) {
CreateChannels(0, 0);
EXPECT_TRUE(SendInitiate());
EXPECT_TRUE(SendAccept());

View File

@ -20,11 +20,7 @@
#include "webrtc/base/stringutils.h"
#include "webrtc/base/trace_event.h"
#include "webrtc/media/base/device.h"
#include "webrtc/media/base/hybriddataengine.h"
#include "webrtc/media/base/rtpdataengine.h"
#ifdef HAVE_SCTP
#include "webrtc/media/sctp/sctpdataengine.h"
#endif
#include "webrtc/pc/srtpfilter.h"
namespace cricket {
@ -33,11 +29,7 @@ namespace cricket {
using rtc::Bind;
static DataEngineInterface* ConstructDataEngine() {
#ifdef HAVE_SCTP
return new HybridDataEngine(new RtpDataEngine(), new SctpDataEngine());
#else
return new RtpDataEngine();
#endif
}
ChannelManager::ChannelManager(MediaEngineInterface* me,
@ -344,73 +336,66 @@ void ChannelManager::DestroyVideoChannel_w(VideoChannel* video_channel) {
delete video_channel;
}
DataChannel* ChannelManager::CreateDataChannel(
RtpDataChannel* ChannelManager::CreateRtpDataChannel(
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
bool srtp_required,
DataChannelType channel_type) {
return worker_thread_->Invoke<DataChannel*>(
RTC_FROM_HERE,
Bind(&ChannelManager::CreateDataChannel_w, this, media_controller,
transport_controller, content_name, bundle_transport_name, rtcp,
srtp_required, channel_type));
bool srtp_required) {
return worker_thread_->Invoke<RtpDataChannel*>(
RTC_FROM_HERE, Bind(&ChannelManager::CreateRtpDataChannel_w, this,
media_controller, transport_controller, content_name,
bundle_transport_name, rtcp, srtp_required));
}
DataChannel* ChannelManager::CreateDataChannel_w(
RtpDataChannel* ChannelManager::CreateRtpDataChannel_w(
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
bool srtp_required,
DataChannelType data_channel_type) {
bool srtp_required) {
// This is ok to alloc from a thread other than the worker thread.
ASSERT(initialized_);
MediaConfig config;
if (media_controller) {
config = media_controller->config();
}
DataMediaChannel* media_channel =
data_media_engine_->CreateChannel(data_channel_type, config);
DataMediaChannel* media_channel = data_media_engine_->CreateChannel(config);
if (!media_channel) {
LOG(LS_WARNING) << "Failed to create data channel of type "
<< data_channel_type;
return NULL;
LOG(LS_WARNING) << "Failed to create RTP data channel.";
return nullptr;
}
// Only RTP data channels need SRTP.
srtp_required = srtp_required && data_channel_type == DCT_RTP;
DataChannel* data_channel =
new DataChannel(worker_thread_, network_thread_, media_channel,
transport_controller, content_name, rtcp, srtp_required);
RtpDataChannel* data_channel = new RtpDataChannel(
worker_thread_, network_thread_, media_channel, transport_controller,
content_name, rtcp, srtp_required);
data_channel->SetCryptoOptions(crypto_options_);
if (!data_channel->Init_w(bundle_transport_name)) {
LOG(LS_WARNING) << "Failed to init data channel.";
delete data_channel;
return NULL;
return nullptr;
}
data_channels_.push_back(data_channel);
return data_channel;
}
void ChannelManager::DestroyDataChannel(DataChannel* data_channel) {
TRACE_EVENT0("webrtc", "ChannelManager::DestroyDataChannel");
void ChannelManager::DestroyRtpDataChannel(RtpDataChannel* data_channel) {
TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel");
if (data_channel) {
worker_thread_->Invoke<void>(
RTC_FROM_HERE,
Bind(&ChannelManager::DestroyDataChannel_w, this, data_channel));
Bind(&ChannelManager::DestroyRtpDataChannel_w, this, data_channel));
}
}
void ChannelManager::DestroyDataChannel_w(DataChannel* data_channel) {
TRACE_EVENT0("webrtc", "ChannelManager::DestroyDataChannel_w");
void ChannelManager::DestroyRtpDataChannel_w(RtpDataChannel* data_channel) {
TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel_w");
// Destroy data channel.
ASSERT(initialized_);
DataChannels::iterator it = std::find(data_channels_.begin(),
data_channels_.end(), data_channel);
RtpDataChannels::iterator it =
std::find(data_channels_.begin(), data_channels_.end(), data_channel);
ASSERT(it != data_channels_.end());
if (it == data_channels_.end())
return;

View File

@ -110,16 +110,15 @@ class ChannelManager {
const VideoOptions& options);
// Destroys a video channel created with the Create API.
void DestroyVideoChannel(VideoChannel* video_channel);
DataChannel* CreateDataChannel(
RtpDataChannel* CreateRtpDataChannel(
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
bool srtp_required,
DataChannelType data_channel_type);
bool srtp_required);
// Destroys a data channel created with the Create API.
void DestroyDataChannel(DataChannel* data_channel);
void DestroyRtpDataChannel(RtpDataChannel* data_channel);
// Indicates whether any channels exist.
bool has_channels() const {
@ -150,7 +149,7 @@ class ChannelManager {
private:
typedef std::vector<VoiceChannel*> VoiceChannels;
typedef std::vector<VideoChannel*> VideoChannels;
typedef std::vector<DataChannel*> DataChannels;
typedef std::vector<RtpDataChannel*> RtpDataChannels;
void Construct(MediaEngineInterface* me,
DataEngineInterface* dme,
@ -178,15 +177,14 @@ class ChannelManager {
bool srtp_required,
const VideoOptions& options);
void DestroyVideoChannel_w(VideoChannel* video_channel);
DataChannel* CreateDataChannel_w(
RtpDataChannel* CreateRtpDataChannel_w(
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
bool srtp_required,
DataChannelType data_channel_type);
void DestroyDataChannel_w(DataChannel* data_channel);
bool srtp_required);
void DestroyRtpDataChannel_w(RtpDataChannel* data_channel);
std::unique_ptr<MediaEngineInterface> media_engine_;
std::unique_ptr<DataEngineInterface> data_media_engine_;
@ -197,7 +195,7 @@ class ChannelManager {
VoiceChannels voice_channels_;
VideoChannels video_channels_;
DataChannels data_channels_;
RtpDataChannels data_channels_;
bool enable_rtx_;
rtc::CryptoOptions crypto_options_;

View File

@ -110,13 +110,13 @@ TEST_F(ChannelManagerTest, CreateDestroyChannels) {
&fake_mc_, transport_controller_, cricket::CN_VIDEO, nullptr,
kDefaultRtcpEnabled, kDefaultSrtpRequired, VideoOptions());
EXPECT_TRUE(video_channel != nullptr);
cricket::DataChannel* data_channel = cm_->CreateDataChannel(
cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
&fake_mc_, transport_controller_, cricket::CN_DATA, nullptr,
kDefaultRtcpEnabled, kDefaultSrtpRequired, cricket::DCT_RTP);
EXPECT_TRUE(data_channel != nullptr);
kDefaultRtcpEnabled, kDefaultSrtpRequired);
EXPECT_TRUE(rtp_data_channel != nullptr);
cm_->DestroyVideoChannel(video_channel);
cm_->DestroyVoiceChannel(voice_channel);
cm_->DestroyDataChannel(data_channel);
cm_->DestroyRtpDataChannel(rtp_data_channel);
cm_->Terminate();
}
@ -138,13 +138,13 @@ TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) {
&fake_mc_, transport_controller_, cricket::CN_VIDEO, nullptr,
kDefaultRtcpEnabled, kDefaultSrtpRequired, VideoOptions());
EXPECT_TRUE(video_channel != nullptr);
cricket::DataChannel* data_channel = cm_->CreateDataChannel(
cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
&fake_mc_, transport_controller_, cricket::CN_DATA, nullptr,
kDefaultRtcpEnabled, kDefaultSrtpRequired, cricket::DCT_RTP);
EXPECT_TRUE(data_channel != nullptr);
kDefaultRtcpEnabled, kDefaultSrtpRequired);
EXPECT_TRUE(rtp_data_channel != nullptr);
cm_->DestroyVideoChannel(video_channel);
cm_->DestroyVoiceChannel(voice_channel);
cm_->DestroyDataChannel(data_channel);
cm_->DestroyRtpDataChannel(rtp_data_channel);
cm_->Terminate();
}