Revert of Separating SCTP code from BaseChannel/MediaChannel. (patchset #14 id:240001 of https://codereview.webrtc.org/2564333002/ )

Reason for revert:
Hitting DCHECK in chromium's WebrtcTransportTest.TerminateDataChannel and WebrtcTransportTest.DataStreamLate. Will investigate and reland.

Original issue's description:
> Separating SCTP code from BaseChannel/MediaChannel.
>
> The BaseChannel code is geared around RTP; the presence of media engines,
> send and receive streams, SRTP, SDP directional attribute negotiation, etc.
> It doesn't make sense to use it for SCTP as well. This separation should make
> future work both on BaseChannel and the SCTP code paths easier.
>
> SctpDataEngine now becomes SctpTransport, and is used by WebRtcSession
> directly. cricket::DataChannel is also renamed, to RtpDataChannel, so it
> doesn't get confused with webrtc::DataChannel any more.
>
> Beyond just moving code around, some consequences of this CL:
> - We'll now stop using the worker thread for SCTP. Packets will be
>   processed right on the network thread instead.
> - The SDP directional attribute is ignored, as it's supposed to be.
>
> BUG=None
>
> Review-Url: https://codereview.webrtc.org/2564333002
> Cr-Commit-Position: refs/heads/master@{#15906}
> Committed: 67b3bbe639

TBR=pthatcher@webrtc.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=None

Review-Url: https://codereview.webrtc.org/2614813003
Cr-Commit-Position: refs/heads/master@{#15908}
This commit is contained in:
deadbeef 2017-01-04 20:28:21 -08:00 committed by Commit bot
parent e9bbde5830
commit c0dad89bed
34 changed files with 2483 additions and 2827 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/sctptransportinternal.h"
#include "webrtc/media/sctp/sctpdataengine.h"
namespace webrtc {
@ -328,12 +328,12 @@ void DataChannel::OnMessage(rtc::Message* msg) {
}
}
void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params,
void DataChannel::OnDataReceived(cricket::DataChannel* channel,
const cricket::ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& payload) {
if (data_channel_type_ == cricket::DCT_RTP && params.ssrc != receive_ssrc_) {
return;
}
if (data_channel_type_ == cricket::DCT_SCTP && params.sid != config_.id) {
uint32_t expected_ssrc =
(data_channel_type_ == cricket::DCT_RTP) ? receive_ssrc_ : config_.id;
if (params.ssrc != expected_ssrc) {
return;
}
@ -342,17 +342,17 @@ void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params,
if (handshake_state_ != kHandshakeWaitingForAck) {
// Ignore it if we are not expecting an ACK message.
LOG(LS_WARNING) << "DataChannel received unexpected CONTROL message, "
<< "sid = " << params.sid;
<< "sid = " << params.ssrc;
return;
}
if (ParseDataChannelOpenAckMessage(payload)) {
// We can send unordered as soon as we receive the ACK message.
handshake_state_ = kHandshakeReady;
LOG(LS_INFO) << "DataChannel received OPEN_ACK message, sid = "
<< params.sid;
<< params.ssrc;
} else {
LOG(LS_WARNING) << "DataChannel failed to parse OPEN_ACK message, sid = "
<< params.sid;
<< params.ssrc;
}
return;
}
@ -360,7 +360,7 @@ void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params,
ASSERT(params.type == cricket::DMT_BINARY ||
params.type == cricket::DMT_TEXT);
LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.sid;
LOG(LS_VERBOSE) << "DataChannel received DATA message, sid = " << params.ssrc;
// We can send unordered as soon as we receive any DATA message since the
// remote side must have received the OPEN (and old clients do not send
// OPEN_ACK).
@ -390,8 +390,9 @@ void DataChannel::OnDataReceived(const cricket::ReceiveDataParams& params,
}
}
void DataChannel::OnStreamClosedRemotely(int sid) {
if (data_channel_type_ == cricket::DCT_SCTP && sid == config_.id) {
void DataChannel::OnStreamClosedRemotely(uint32_t sid) {
if (data_channel_type_ == cricket::DCT_SCTP &&
sid == static_cast<uint32_t>(config_.id)) {
Close();
}
}
@ -550,7 +551,7 @@ bool DataChannel::SendDataMessage(const DataBuffer& buffer,
send_params.max_rtx_count = config_.maxRetransmits;
send_params.max_rtx_ms = config_.maxRetransmitTime;
send_params.sid = config_.id;
send_params.ssrc = config_.id;
} else {
send_params.ssrc = send_ssrc_;
}
@ -622,7 +623,7 @@ bool DataChannel::SendControlMessage(const rtc::CopyOnWriteBuffer& buffer) {
(!is_open_message || !config_.negotiated));
cricket::SendDataParams send_params;
send_params.sid = config_.id;
send_params.ssrc = config_.id;
// Send data as ordered before we receive any message from the remote peer to
// make sure the remote peer will not receive any data before it receives the
// OPEN message.

View File

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

View File

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

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/sctptransport.h"
#include "webrtc/media/sctp/sctpdataengine.h"
#include "webrtc/pc/channelmanager.h"
#include "webrtc/system_wrappers/include/field_trial.h"
@ -652,14 +652,7 @@ bool PeerConnection::Initialize(
std::unique_ptr<cricket::TransportController>(
factory_->CreateTransportController(
port_allocator_.get(),
configuration.redetermine_role_on_ice_restart)),
#ifdef HAVE_SCTP
std::unique_ptr<cricket::SctpTransportInternalFactory>(
new cricket::SctpTransportFactory(factory_->network_thread()))
#else
nullptr
#endif
));
configuration.redetermine_role_on_ice_restart))));
stats_.reset(new StatsCollector(this));
stats_collector_ = RTCStatsCollector::Create(this);
@ -1126,7 +1119,7 @@ void PeerConnection::SetLocalDescription(
// SCTP sids.
rtc::SSLRole role;
if (session_->data_channel_type() == cricket::DCT_SCTP &&
session_->GetSctpSslRole(&role)) {
session_->GetSslRole(session_->data_channel(), &role)) {
AllocateSctpSids(role);
}
@ -1208,7 +1201,7 @@ void PeerConnection::SetRemoteDescription(
// SCTP sids.
rtc::SSLRole role;
if (session_->data_channel_type() == cricket::DCT_SCTP &&
session_->GetSctpSslRole(&role)) {
session_->GetSslRole(session_->data_channel(), &role)) {
AllocateSctpSids(role);
}
@ -2150,7 +2143,7 @@ rtc::scoped_refptr<DataChannel> PeerConnection::InternalCreateDataChannel(
if (session_->data_channel_type() == cricket::DCT_SCTP) {
if (new_config.id < 0) {
rtc::SSLRole role;
if ((session_->GetSctpSslRole(&role)) &&
if ((session_->GetSslRole(session_->data_channel(), &role)) &&
!sid_allocator_.AllocateSid(role, &new_config.id)) {
LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
return nullptr;

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/sctptransportinternal.h"
#include "webrtc/media/sctp/sctpdataengine.h"
#include "webrtc/p2p/base/fakeportallocator.h"
#include "webrtc/p2p/base/faketransportcontroller.h"
#include "webrtc/pc/mediasession.h"

View File

@ -463,16 +463,10 @@ void RTCStatsCollector::GetStatsReport(
ChannelNamePair(pc_->session()->video_channel()->content_name(),
pc_->session()->video_channel()->transport_name()));
}
if (pc_->session()->rtp_data_channel()) {
channel_name_pairs_->data =
rtc::Optional<ChannelNamePair>(ChannelNamePair(
pc_->session()->rtp_data_channel()->content_name(),
pc_->session()->rtp_data_channel()->transport_name()));
}
if (pc_->session()->sctp_content_name()) {
if (pc_->session()->data_channel()) {
channel_name_pairs_->data = rtc::Optional<ChannelNamePair>(
ChannelNamePair(*pc_->session()->sctp_content_name(),
*pc_->session()->sctp_transport_name()));
ChannelNamePair(pc_->session()->data_channel()->content_name(),
pc_->session()->data_channel()->transport_name()));
}
media_info_.reset(PrepareMediaInfo_s().release());

View File

@ -15,7 +15,6 @@
#include <string>
#include "webrtc/api/webrtcsession.h"
#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/test/gmock.h"
namespace webrtc {
@ -36,8 +35,7 @@ class MockWebRtcSession : public webrtc::WebRtcSession {
std::unique_ptr<cricket::TransportController>(
new cricket::TransportController(rtc::Thread::Current(),
rtc::Thread::Current(),
nullptr)),
std::unique_ptr<cricket::SctpTransportInternalFactory>()) {}
nullptr))) {}
MOCK_METHOD0(voice_channel, cricket::VoiceChannel*());
MOCK_METHOD0(video_channel, cricket::VideoChannel*());
// Libjingle uses "local" for a outgoing track, and "remote" for a incoming

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/sctptransportinternal.h"
#include "webrtc/media/sctp/sctpdataengine.h"
#include "webrtc/p2p/base/candidate.h"
#include "webrtc/p2p/base/p2pconstants.h"
#include "webrtc/p2p/base/port.h"

View File

@ -33,7 +33,6 @@
#include "webrtc/call/call.h"
#include "webrtc/media/base/mediaconstants.h"
#include "webrtc/media/base/videocapturer.h"
#include "webrtc/media/sctp/sctptransportinternal.h"
#include "webrtc/p2p/base/portallocator.h"
#include "webrtc/p2p/base/transportchannel.h"
#include "webrtc/pc/channel.h"
@ -75,9 +74,9 @@ const char kSdpWithoutIceUfragPwd[] =
"Called with SDP without ice-ufrag and ice-pwd.";
const char kSessionError[] = "Session error code: ";
const char kSessionErrorDesc[] = "Session error description: ";
const char kDtlsSrtpSetupFailureRtp[] =
const char kDtlsSetupFailureRtp[] =
"Couldn't set up DTLS-SRTP on RTP channel.";
const char kDtlsSrtpSetupFailureRtcp[] =
const char kDtlsSetupFailureRtcp[] =
"Couldn't set up DTLS-SRTP on RTCP channel.";
const char kEnableBundleFailed[] = "Failed to enable BUNDLE.";
@ -292,31 +291,6 @@ static bool GetTrackIdBySsrc(const SessionDescription* session_description,
return false;
}
// Get the SCTP port out of a SessionDescription.
// Return -1 if not found.
static int GetSctpPort(const SessionDescription* session_description) {
const ContentInfo* content_info = GetFirstDataContent(session_description);
RTC_DCHECK(content_info);
if (!content_info) {
return -1;
}
const cricket::DataContentDescription* data =
static_cast<const cricket::DataContentDescription*>(
(content_info->description));
std::string value;
cricket::DataCodec match_pattern(cricket::kGoogleSctpDataCodecPlType,
cricket::kGoogleSctpDataCodecName);
for (const cricket::DataCodec& codec : data->codecs()) {
if (!codec.Matches(match_pattern)) {
continue;
}
if (codec.GetParam(cricket::kCodecParamPort, &value)) {
return rtc::FromString<int>(value);
}
}
return -1;
}
static bool BadSdp(const std::string& source,
const std::string& type,
const std::string& reason,
@ -466,8 +440,7 @@ 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::SctpTransportInternalFactory> sctp_factory)
std::unique_ptr<cricket::TransportController> transport_controller)
: network_thread_(network_thread),
worker_thread_(worker_thread),
signaling_thread_(signaling_thread),
@ -476,7 +449,6 @@ WebRtcSession::WebRtcSession(
// Due to this constraint session id |sid_| is max limited to LLONG_MAX.
sid_(rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX)),
transport_controller_(std::move(transport_controller)),
sctp_factory_(std::move(sctp_factory)),
media_controller_(media_controller),
channel_manager_(media_controller_->channel_manager()),
ice_observer_(NULL),
@ -498,7 +470,7 @@ WebRtcSession::WebRtcSession(
transport_controller_->SignalCandidatesRemoved.connect(
this, &WebRtcSession::OnTransportControllerCandidatesRemoved);
transport_controller_->SignalDtlsHandshakeError.connect(
this, &WebRtcSession::OnTransportControllerDtlsHandshakeError);
this, &WebRtcSession::OnDtlsHandshakeError);
}
WebRtcSession::~WebRtcSession() {
@ -513,14 +485,9 @@ WebRtcSession::~WebRtcSession() {
SignalVoiceChannelDestroyed();
channel_manager_->DestroyVoiceChannel(voice_channel_.release());
}
if (rtp_data_channel_) {
if (data_channel_) {
SignalDataChannelDestroyed();
channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release());
}
if (sctp_transport_) {
SignalDataChannelDestroyed();
network_thread_->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this));
channel_manager_->DestroyDataChannel(data_channel_.release());
}
#ifdef HAVE_QUIC
if (quic_data_transport_) {
@ -630,10 +597,9 @@ bool WebRtcSession::Initialize(
void WebRtcSession::Close() {
SetState(STATE_CLOSED);
RemoveUnusedChannels(nullptr);
RTC_DCHECK(!voice_channel_);
RTC_DCHECK(!video_channel_);
RTC_DCHECK(!rtp_data_channel_);
RTC_DCHECK(!sctp_transport_);
ASSERT(!voice_channel_);
ASSERT(!video_channel_);
ASSERT(!data_channel_);
media_controller_->Close();
}
@ -645,9 +611,8 @@ cricket::BaseChannel* WebRtcSession::GetChannel(
if (video_channel() && video_channel()->content_name() == content_name) {
return video_channel();
}
if (rtp_data_channel() &&
rtp_data_channel()->content_name() == content_name) {
return rtp_data_channel();
if (data_channel() && data_channel()->content_name() == content_name) {
return data_channel();
}
return nullptr;
}
@ -656,31 +621,20 @@ cricket::SecurePolicy WebRtcSession::SdesPolicy() const {
return webrtc_session_desc_factory_->SdesPolicy();
}
bool WebRtcSession::GetSctpSslRole(rtc::SSLRole* role) {
if (!local_description() || !remote_description()) {
LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the "
<< "SSL Role of the SCTP transport.";
return false;
}
if (!sctp_transport_) {
LOG(LS_INFO) << "Non-rejected SCTP m= section is needed to get the "
<< "SSL Role of the SCTP transport.";
return false;
}
return transport_controller_->GetSslRole(*sctp_transport_name_, role);
}
bool WebRtcSession::GetSslRole(const std::string& content_name,
bool WebRtcSession::GetSslRole(const std::string& transport_name,
rtc::SSLRole* role) {
if (!local_description() || !remote_description()) {
LOG(LS_INFO) << "Local and Remote descriptions must be applied to get the "
LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
<< "SSL Role of the session.";
return false;
}
return transport_controller_->GetSslRole(GetTransportName(content_name),
role);
return transport_controller_->GetSslRole(transport_name, role);
}
bool WebRtcSession::GetSslRole(const cricket::BaseChannel* channel,
rtc::SSLRole* role) {
return channel && GetSslRole(channel->transport_name(), role);
}
void WebRtcSession::CreateOffer(
@ -964,27 +918,9 @@ bool WebRtcSession::PushdownMediaDescription(
}
};
bool ret = (set_content(voice_channel()) && set_content(video_channel()) &&
set_content(rtp_data_channel()));
// Need complete offer/answer before starting SCTP, according to
// https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19
if (sctp_transport_ && local_description() && remote_description()) {
ret &= network_thread_->Invoke<bool>(
RTC_FROM_HERE,
rtc::Bind(&WebRtcSession::PushdownSctpParameters_n, this, source));
}
return ret;
}
bool WebRtcSession::PushdownSctpParameters_n(cricket::ContentSource source) {
RTC_DCHECK(network_thread_->IsCurrent());
RTC_DCHECK(local_description());
RTC_DCHECK(remote_description());
// Apply the SCTP port (which is hidden inside a DataCodec structure...)
// When we support "max-message-size", that would also be pushed down here.
return sctp_transport_->Start(
GetSctpPort(local_description()->description()),
GetSctpPort(remote_description()->description()));
return (set_content(voice_channel()) &&
set_content(video_channel()) &&
set_content(data_channel()));
}
bool WebRtcSession::PushdownTransportDescription(cricket::ContentSource source,
@ -1056,6 +992,46 @@ 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) {
@ -1063,6 +1039,7 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
return false;
}
const std::string& transport_name = *first_content_name;
cricket::BaseChannel* first_channel = GetChannel(transport_name);
#ifdef HAVE_QUIC
if (quic_data_transport_ &&
@ -1073,8 +1050,8 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
}
#endif
auto maybe_set_transport = [this, bundle,
transport_name](cricket::BaseChannel* ch) {
auto maybe_set_transport = [this, bundle, transport_name,
first_channel](cricket::BaseChannel* ch) {
if (!ch || !bundle.HasContentName(ch->content_name())) {
return true;
}
@ -1096,21 +1073,9 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
if (!maybe_set_transport(voice_channel()) ||
!maybe_set_transport(video_channel()) ||
!maybe_set_transport(rtp_data_channel())) {
!maybe_set_transport(data_channel())) {
return false;
}
// For SCTP, transport creation/deletion happens here instead of in the
// object itself.
if (sctp_transport_) {
RTC_DCHECK(sctp_transport_name_);
RTC_DCHECK(sctp_content_name_);
if (transport_name != *sctp_transport_name_ &&
bundle.HasContentName(*sctp_content_name_)) {
network_thread_->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&WebRtcSession::ChangeSctpTransport_n, this,
transport_name));
}
}
return true;
}
@ -1283,129 +1248,60 @@ sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
bool WebRtcSession::SendData(const cricket::SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,
cricket::SendDataResult* result) {
if (!rtp_data_channel_ && !sctp_transport_) {
LOG(LS_ERROR) << "SendData called when rtp_data_channel_ "
<< "and sctp_transport_ are NULL.";
if (!data_channel_) {
LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
return false;
}
return rtp_data_channel_
? rtp_data_channel_->SendData(params, payload, result)
: network_thread_->Invoke<bool>(
RTC_FROM_HERE,
Bind(&cricket::SctpTransportInternal::SendData,
sctp_transport_.get(), params, payload, result));
return data_channel_->SendData(params, payload, result);
}
bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
if (!rtp_data_channel_ && !sctp_transport_) {
if (!data_channel_) {
// Don't log an error here, because DataChannels are expected to call
// ConnectDataChannel in this state. It's the only way to initially tell
// whether or not the underlying transport is ready.
return false;
}
if (rtp_data_channel_) {
rtp_data_channel_->SignalReadyToSendData.connect(
webrtc_data_channel, &DataChannel::OnChannelReady);
rtp_data_channel_->SignalDataReceived.connect(webrtc_data_channel,
&DataChannel::OnDataReceived);
} else {
SignalSctpReadyToSendData.connect(webrtc_data_channel,
&DataChannel::OnChannelReady);
SignalSctpDataReceived.connect(webrtc_data_channel,
&DataChannel::OnDataReceived);
SignalSctpStreamClosedRemotely.connect(
webrtc_data_channel, &DataChannel::OnStreamClosedRemotely);
}
data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
&DataChannel::OnChannelReady);
data_channel_->SignalDataReceived.connect(webrtc_data_channel,
&DataChannel::OnDataReceived);
data_channel_->SignalStreamClosedRemotely.connect(
webrtc_data_channel, &DataChannel::OnStreamClosedRemotely);
return true;
}
void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
if (!rtp_data_channel_ && !sctp_transport_) {
LOG(LS_ERROR) << "DisconnectDataChannel called when rtp_data_channel_ and "
"sctp_transport_ are NULL.";
if (!data_channel_) {
LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL.";
return;
}
if (rtp_data_channel_) {
rtp_data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
rtp_data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
} else {
SignalSctpReadyToSendData.disconnect(webrtc_data_channel);
SignalSctpDataReceived.disconnect(webrtc_data_channel);
SignalSctpStreamClosedRemotely.disconnect(webrtc_data_channel);
}
data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
data_channel_->SignalStreamClosedRemotely.disconnect(webrtc_data_channel);
}
void WebRtcSession::AddSctpDataStream(int sid) {
if (!sctp_transport_) {
LOG(LS_ERROR) << "AddSctpDataStream called when sctp_transport_ is NULL.";
if (!data_channel_) {
LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL.";
return;
}
network_thread_->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream,
sctp_transport_.get(), sid));
data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(sid));
data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(sid));
}
void WebRtcSession::RemoveSctpDataStream(int sid) {
if (!sctp_transport_) {
LOG(LS_ERROR) << "RemoveSctpDataStream called when sctp_transport_ is "
if (!data_channel_) {
LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is "
<< "NULL.";
return;
}
network_thread_->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream,
sctp_transport_.get(), sid));
data_channel_->RemoveRecvStream(sid);
data_channel_->RemoveSendStream(sid);
}
bool WebRtcSession::ReadyToSendData() const {
return (rtp_data_channel_ && rtp_data_channel_->ready_to_send_data()) ||
sctp_ready_to_send_data_;
}
std::unique_ptr<SessionStats> WebRtcSession::GetStats_s() {
ASSERT(signaling_thread()->IsCurrent());
ChannelNamePairs channel_name_pairs;
if (voice_channel()) {
channel_name_pairs.voice = rtc::Optional<ChannelNamePair>(ChannelNamePair(
voice_channel()->content_name(), voice_channel()->transport_name()));
}
if (video_channel()) {
channel_name_pairs.video = rtc::Optional<ChannelNamePair>(ChannelNamePair(
video_channel()->content_name(), video_channel()->transport_name()));
}
if (rtp_data_channel()) {
channel_name_pairs.data = rtc::Optional<ChannelNamePair>(
ChannelNamePair(rtp_data_channel()->content_name(),
rtp_data_channel()->transport_name()));
}
if (sctp_transport_) {
RTC_DCHECK(sctp_content_name_);
RTC_DCHECK(sctp_transport_name_);
channel_name_pairs.data = rtc::Optional<ChannelNamePair>(
ChannelNamePair(*sctp_content_name_, *sctp_transport_name_));
}
return GetStats(channel_name_pairs);
}
std::unique_ptr<SessionStats> WebRtcSession::GetStats(
const ChannelNamePairs& channel_name_pairs) {
if (network_thread()->IsCurrent()) {
return GetStats_n(channel_name_pairs);
}
return network_thread()->Invoke<std::unique_ptr<SessionStats>>(
RTC_FROM_HERE,
rtc::Bind(&WebRtcSession::GetStats_n, this, channel_name_pairs));
}
bool WebRtcSession::GetLocalCertificate(
const std::string& transport_name,
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
return transport_controller_->GetLocalCertificate(transport_name,
certificate);
}
std::unique_ptr<rtc::SSLCertificate> WebRtcSession::GetRemoteSSLCertificate(
const std::string& transport_name) {
return transport_controller_->GetRemoteSSLCertificate(transport_name);
return data_channel_ && data_channel_->ready_to_send_data();
}
cricket::DataChannelType WebRtcSession::data_channel_type() const {
@ -1430,11 +1326,6 @@ void WebRtcSession::OnCertificateReady(
transport_controller_->SetLocalCertificate(certificate);
}
void WebRtcSession::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) {
SetError(ERROR_TRANSPORT,
rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp);
}
bool WebRtcSession::waiting_for_certificate_for_testing() const {
return webrtc_session_desc_factory_->waiting_for_certificate_for_testing();
}
@ -1564,16 +1455,7 @@ void WebRtcSession::OnTransportControllerCandidatesRemoved(
}
}
void WebRtcSession::OnTransportControllerDtlsHandshakeError(
rtc::SSLHandshakeError error) {
if (metrics_observer_) {
metrics_observer_->IncrementEnumCounter(
webrtc::kEnumCounterDtlsHandshakeError, static_cast<int>(error),
static_cast<int>(rtc::SSLHandshakeError::MAX_VALUE));
}
}
// Enabling voice and video (and RTP data) channel.
// Enabling voice and video channel.
void WebRtcSession::EnableChannels() {
if (voice_channel_ && !voice_channel_->enabled())
voice_channel_->Enable(true);
@ -1581,8 +1463,8 @@ void WebRtcSession::EnableChannels() {
if (video_channel_ && !video_channel_->enabled())
video_channel_->Enable(true);
if (rtp_data_channel_ && !rtp_data_channel_->enabled())
rtp_data_channel_->Enable(true);
if (data_channel_ && !data_channel_->enabled())
data_channel_->Enable(true);
}
// Returns the media index for a local ice candidate given the content name.
@ -1692,15 +1574,9 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) {
const cricket::ContentInfo* data_info =
cricket::GetFirstDataContent(desc);
if (!data_info || data_info->rejected) {
if (rtp_data_channel_) {
if (data_channel_) {
SignalDataChannelDestroyed();
channel_manager_->DestroyRtpDataChannel(rtp_data_channel_.release());
}
if (sctp_transport_) {
SignalDataChannelDestroyed();
network_thread_->Invoke<void>(
RTC_FROM_HERE,
rtc::Bind(&WebRtcSession::DestroySctpTransport_n, this));
channel_manager_->DestroyDataChannel(data_channel_.release());
}
#ifdef HAVE_QUIC
// Clean up the existing QuicDataTransport and its QuicTransportChannels.
@ -1761,8 +1637,8 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
}
const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected &&
!rtp_data_channel_ && !sctp_transport_) {
if (data_channel_type_ != cricket::DCT_NONE &&
data && !data->rejected && !data_channel_) {
if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) {
LOG(LS_ERROR) << "Failed to create data channel.";
return false;
@ -1788,8 +1664,8 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content,
voice_channel_->ActivateRtcpMux();
}
voice_channel_->SignalDtlsSrtpSetupFailure.connect(
this, &WebRtcSession::OnDtlsSrtpSetupFailure);
voice_channel_->SignalDtlsSetupFailure.connect(
this, &WebRtcSession::OnDtlsSetupFailure);
SignalVoiceChannelCreated();
voice_channel_->SignalSentPacket.connect(this,
@ -1812,8 +1688,8 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content,
if (require_rtcp_mux) {
video_channel_->ActivateRtcpMux();
}
video_channel_->SignalDtlsSrtpSetupFailure.connect(
this, &WebRtcSession::OnDtlsSrtpSetupFailure);
video_channel_->SignalDtlsSetupFailure.connect(
this, &WebRtcSession::OnDtlsSetupFailure);
SignalVideoChannelCreated();
video_channel_->SignalSentPacket.connect(this,
@ -1823,48 +1699,40 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content,
bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport) {
const std::string transport_name =
bundle_transport ? *bundle_transport : content->name;
#ifdef HAVE_QUIC
if (data_channel_type_ == cricket::DCT_QUIC) {
RTC_DCHECK(transport_controller_->quic());
const std::string transport_name =
bundle_transport ? *bundle_transport : content->name;
quic_data_transport_->SetTransport(transport_name);
return true;
}
#endif // HAVE_QUIC
bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
if (sctp) {
if (!sctp_factory_) {
LOG(LS_ERROR)
<< "Trying to create SCTP transport, but didn't compile with "
"SCTP support (HAVE_SCTP)";
return false;
}
if (!network_thread_->Invoke<bool>(
RTC_FROM_HERE, rtc::Bind(&WebRtcSession::CreateSctpTransport_n,
this, content->name, transport_name))) {
return false;
};
} else {
bool require_rtcp_mux =
rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire;
bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux;
rtp_data_channel_.reset(channel_manager_->CreateRtpDataChannel(
media_controller_, transport_controller_.get(), content->name,
bundle_transport, create_rtcp_transport_channel, SrtpRequired()));
if (!rtp_data_channel_) {
return false;
}
if (require_rtcp_mux) {
rtp_data_channel_->ActivateRtcpMux();
}
rtp_data_channel_->SignalDtlsSrtpSetupFailure.connect(
this, &WebRtcSession::OnDtlsSrtpSetupFailure);
rtp_data_channel_->SignalSentPacket.connect(this,
&WebRtcSession::OnSentPacket_w);
bool require_rtcp_mux =
rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire;
bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux;
data_channel_.reset(channel_manager_->CreateDataChannel(
media_controller_, transport_controller_.get(), content->name,
bundle_transport, create_rtcp_transport_channel, SrtpRequired(),
data_channel_type_));
if (!data_channel_) {
return false;
}
if (require_rtcp_mux) {
data_channel_->ActivateRtcpMux();
}
if (sctp) {
data_channel_->SignalDataReceived.connect(
this, &WebRtcSession::OnDataChannelMessageReceived);
}
data_channel_->SignalDtlsSetupFailure.connect(
this, &WebRtcSession::OnDtlsSetupFailure);
SignalDataChannelCreated();
data_channel_->SignalSentPacket.connect(this, &WebRtcSession::OnSentPacket_w);
return true;
}
@ -1890,79 +1758,16 @@ std::unique_ptr<SessionStats> WebRtcSession::GetStats_n(
return session_stats;
}
bool WebRtcSession::CreateSctpTransport_n(const std::string& content_name,
const std::string& transport_name) {
RTC_DCHECK(network_thread_->IsCurrent());
RTC_DCHECK(sctp_factory_);
cricket::TransportChannel* tc =
transport_controller_->CreateTransportChannel_n(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
sctp_transport_ = sctp_factory_->CreateSctpTransport(tc);
RTC_DCHECK(sctp_transport_);
sctp_invoker_.reset(new rtc::AsyncInvoker());
sctp_transport_->SignalReadyToSendData.connect(
this, &WebRtcSession::OnSctpTransportReadyToSendData_n);
sctp_transport_->SignalDataReceived.connect(
this, &WebRtcSession::OnSctpTransportDataReceived_n);
sctp_transport_->SignalStreamClosedRemotely.connect(
this, &WebRtcSession::OnSctpStreamClosedRemotely_n);
sctp_transport_name_ = rtc::Optional<std::string>(transport_name);
sctp_content_name_ = rtc::Optional<std::string>(content_name);
return true;
void WebRtcSession::OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp) {
SetError(ERROR_TRANSPORT,
rtcp ? kDtlsSetupFailureRtcp : kDtlsSetupFailureRtp);
}
void WebRtcSession::ChangeSctpTransport_n(const std::string& transport_name) {
RTC_DCHECK(network_thread_->IsCurrent());
RTC_DCHECK(sctp_transport_);
RTC_DCHECK(sctp_transport_name_);
std::string old_sctp_transport_name = *sctp_transport_name_;
sctp_transport_name_ = rtc::Optional<std::string>(transport_name);
cricket::TransportChannel* tc =
transport_controller_->CreateTransportChannel_n(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
sctp_transport_->SetTransportChannel(tc);
transport_controller_->DestroyTransportChannel_n(
old_sctp_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
}
void WebRtcSession::DestroySctpTransport_n() {
RTC_DCHECK(network_thread_->IsCurrent());
sctp_transport_.reset(nullptr);
sctp_content_name_.reset();
sctp_transport_name_.reset();
sctp_invoker_.reset(nullptr);
sctp_ready_to_send_data_ = false;
}
void WebRtcSession::OnSctpTransportReadyToSendData_n() {
RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP);
RTC_DCHECK(network_thread_->IsCurrent());
sctp_invoker_->AsyncInvoke<void>(
RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&WebRtcSession::OnSctpTransportReadyToSendData_s, this, true));
}
void WebRtcSession::OnSctpTransportReadyToSendData_s(bool ready) {
RTC_DCHECK(signaling_thread_->IsCurrent());
sctp_ready_to_send_data_ = ready;
SignalSctpReadyToSendData(ready);
}
void WebRtcSession::OnSctpTransportDataReceived_n(
void WebRtcSession::OnDataChannelMessageReceived(
cricket::DataChannel* channel,
const cricket::ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& payload) {
RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP);
RTC_DCHECK(network_thread_->IsCurrent());
sctp_invoker_->AsyncInvoke<void>(
RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&WebRtcSession::OnSctpTransportDataReceived_s, this, params,
payload));
}
void WebRtcSession::OnSctpTransportDataReceived_s(
const cricket::ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& payload) {
RTC_DCHECK(signaling_thread_->IsCurrent());
if (params.type == cricket::DMT_CONTROL && IsOpenMessage(payload)) {
// Received OPEN message; parse and signal that a new data channel should
// be created.
@ -1976,19 +1781,8 @@ void WebRtcSession::OnSctpTransportDataReceived_s(
}
config.open_handshake_role = InternalDataChannelInit::kAcker;
SignalDataChannelOpenMessage(label, config);
} else {
// Otherwise just forward the signal.
SignalSctpDataReceived(params, payload);
}
}
void WebRtcSession::OnSctpStreamClosedRemotely_n(int sid) {
RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP);
RTC_DCHECK(network_thread_->IsCurrent());
sctp_invoker_->AsyncInvoke<void>(
RTC_FROM_HERE, signaling_thread_,
rtc::Bind(&sigslot::signal1<int>::operator(),
&SignalSctpStreamClosedRemotely, sid));
// Otherwise ignore the message.
}
// Returns false if bundle is enabled and rtcp_mux is disabled.
@ -2182,11 +1976,8 @@ void WebRtcSession::ReportTransportStats() {
if (video_channel()) {
transport_names.insert(video_channel()->transport_name());
}
if (rtp_data_channel()) {
transport_names.insert(rtp_data_channel()->transport_name());
}
if (sctp_transport_name_) {
transport_names.insert(*sctp_transport_name_);
if (data_channel()) {
transport_names.insert(data_channel()->transport_name());
}
for (const auto& name : transport_names) {
cricket::TransportStats stats;
@ -2303,17 +2094,17 @@ const std::string WebRtcSession::GetTransportName(
return quic_data_transport_->transport_name();
}
#endif
if (sctp_transport_) {
RTC_DCHECK(sctp_content_name_);
RTC_DCHECK(sctp_transport_name_);
if (content_name == *sctp_content_name_) {
return *sctp_transport_name_;
}
}
// Return an empty string if failed to retrieve the transport name.
return "";
}
return channel->transport_name();
}
void WebRtcSession::OnDtlsHandshakeError(rtc::SSLHandshakeError error) {
if (metrics_observer_) {
metrics_observer_->IncrementEnumCounter(
webrtc::kEnumCounterDtlsHandshakeError, static_cast<int>(error),
static_cast<int>(rtc::SSLHandshakeError::MAX_VALUE));
}
}
} // namespace webrtc

View File

@ -22,7 +22,6 @@
#include "webrtc/api/peerconnectioninterface.h"
#include "webrtc/api/statstypes.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/optional.h"
#include "webrtc/base/sigslot.h"
#include "webrtc/base/sslidentity.h"
#include "webrtc/base/thread.h"
@ -38,9 +37,7 @@
namespace cricket {
class ChannelManager;
class RtpDataChannel;
class SctpTransportInternal;
class SctpTransportInternalFactory;
class DataChannel;
class StatsReport;
class VideoChannel;
class VoiceChannel;
@ -70,8 +67,8 @@ extern const char kSdpWithoutIceUfragPwd[];
extern const char kSdpWithoutSdesAndDtlsDisabled[];
extern const char kSessionError[];
extern const char kSessionErrorDesc[];
extern const char kDtlsSrtpSetupFailureRtp[];
extern const char kDtlsSrtpSetupFailureRtcp[];
extern const char kDtlsSetupFailureRtp[];
extern const char kDtlsSetupFailureRtcp[];
extern const char kEnableBundleFailed[];
// Maximum number of received video streams that will be processed by webrtc
@ -161,15 +158,13 @@ class WebRtcSession :
ERROR_TRANSPORT = 2, // transport error of some kind
};
// |sctp_factory| may be null, in which case SCTP is treated as unsupported.
WebRtcSession(
webrtc::MediaControllerInterface* media_controller,
rtc::Thread* network_thread,
rtc::Thread* worker_thread,
rtc::Thread* signaling_thread,
cricket::PortAllocator* port_allocator,
std::unique_ptr<cricket::TransportController> transport_controller,
std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory);
std::unique_ptr<cricket::TransportController> transport_controller);
virtual ~WebRtcSession();
// These are const to allow them to be called from const methods.
@ -204,34 +199,26 @@ class WebRtcSession :
ice_observer_ = observer;
}
// Exposed for stats collecting.
virtual cricket::VoiceChannel* voice_channel() {
return voice_channel_.get();
}
virtual cricket::VideoChannel* video_channel() {
return video_channel_.get();
}
// Only valid when using deprecated RTP data channels.
virtual cricket::RtpDataChannel* rtp_data_channel() {
return rtp_data_channel_.get();
}
virtual rtc::Optional<std::string> sctp_content_name() const {
return sctp_content_name_;
}
virtual rtc::Optional<std::string> sctp_transport_name() const {
return sctp_transport_name_;
virtual cricket::DataChannel* data_channel() {
return data_channel_.get();
}
cricket::BaseChannel* GetChannel(const std::string& content_name);
cricket::SecurePolicy SdesPolicy() const;
// Get current SSL role used by SCTP's underlying transport.
bool GetSctpSslRole(rtc::SSLRole* role);
// Get SSL role for an arbitrary m= section (handles bundling correctly).
// TODO(deadbeef): This is only used internally by the session description
// factory, it shouldn't really be public).
bool GetSslRole(const std::string& content_name, rtc::SSLRole* role);
// Get current ssl role from transport.
bool GetSslRole(const std::string& transport_name, rtc::SSLRole* role);
// Get current SSL role for this channel's transport.
// If |transport| is null, returns false.
bool GetSslRole(const cricket::BaseChannel* channel, rtc::SSLRole* role);
void CreateOffer(
CreateSessionDescriptionObserver* observer,
@ -245,7 +232,6 @@ class WebRtcSession :
// The ownership of |desc| will be transferred after this call.
bool SetRemoteDescription(SessionDescriptionInterface* desc,
std::string* err_desc);
bool ProcessIceMessage(const IceCandidateInterface* ice_candidate);
bool RemoveRemoteIceCandidates(
@ -340,7 +326,7 @@ class WebRtcSession :
// WebRTCSessionDescriptionFactory. Should happen before setLocalDescription.
void OnCertificateReady(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
void OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp);
void OnDtlsSetupFailure(cricket::BaseChannel*, bool rtcp);
// For unit test.
bool waiting_for_certificate_for_testing() const;
@ -352,9 +338,8 @@ class WebRtcSession :
transport_controller_->SetMetricsObserver(metrics_observer);
}
// Called when voice_channel_, video_channel_ and
// rtp_data_channel_/sctp_transport_ are created and destroyed. As a result
// of, for example, setting a new description.
// Called when voice_channel_, video_channel_ and data_channel_ are created
// and destroyed. As a result of, for example, setting a new description.
sigslot::signal0<> SignalVoiceChannelCreated;
sigslot::signal0<> SignalVoiceChannelDestroyed;
sigslot::signal0<> SignalVideoChannelCreated;
@ -412,7 +397,6 @@ class WebRtcSession :
bool PushdownMediaDescription(cricket::ContentAction action,
cricket::ContentSource source,
std::string* error_desc);
bool PushdownSctpParameters_n(cricket::ContentSource source);
bool PushdownTransportDescription(cricket::ContentSource source,
cricket::ContentAction action,
@ -477,24 +461,11 @@ class WebRtcSession :
std::unique_ptr<SessionStats> GetStats_n(
const ChannelNamePairs& channel_name_pairs);
bool CreateSctpTransport_n(const std::string& content_name,
const std::string& transport_name);
// For bundling.
void ChangeSctpTransport_n(const std::string& transport_name);
void DestroySctpTransport_n();
// SctpTransport signal handlers. Needed to marshal signals from the network
// to signaling thread.
void OnSctpTransportReadyToSendData_n();
// This may be called with "false" if the direction of the m= section causes
// us to tear down the SCTP connection.
void OnSctpTransportReadyToSendData_s(bool ready);
void OnSctpTransportDataReceived_n(const cricket::ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& payload);
// Beyond just firing the signal to the signaling thread, listens to SCTP
// CONTROL messages on unused SIDs and processes them as OPEN messages.
void OnSctpTransportDataReceived_s(const cricket::ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& payload);
void OnSctpStreamClosedRemotely_n(int sid);
// Listens to SCTP CONTROL messages on unused SIDs and process them as OPEN
// messages.
void OnDataChannelMessageReceived(cricket::DataChannel* channel,
const cricket::ReceiveDataParams& params,
const rtc::CopyOnWriteBuffer& payload);
std::string BadStateErrMsg(State state);
void SetIceConnectionState(PeerConnectionInterface::IceConnectionState state);
@ -527,7 +498,6 @@ class WebRtcSession :
// this session.
bool SrtpRequired() const;
// TransportController signal handlers.
void OnTransportControllerConnectionState(cricket::IceConnectionState state);
void OnTransportControllerReceiving(bool receiving);
void OnTransportControllerGatheringState(cricket::IceGatheringState state);
@ -536,7 +506,6 @@ class WebRtcSession :
const std::vector<cricket::Candidate>& candidates);
void OnTransportControllerCandidatesRemoved(
const std::vector<cricket::Candidate>& candidates);
void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error);
std::string GetSessionErrorMsg();
@ -553,6 +522,8 @@ class WebRtcSession :
const std::string GetTransportName(const std::string& content_name);
void OnDtlsHandshakeError(rtc::SSLHandshakeError error);
rtc::Thread* const network_thread_;
rtc::Thread* const worker_thread_;
rtc::Thread* const signaling_thread_;
@ -565,39 +536,10 @@ class WebRtcSession :
bool initial_offerer_ = false;
const std::unique_ptr<cricket::TransportController> transport_controller_;
const std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory_;
MediaControllerInterface* media_controller_;
std::unique_ptr<cricket::VoiceChannel> voice_channel_;
std::unique_ptr<cricket::VideoChannel> video_channel_;
// |rtp_data_channel_| is used if in RTP data channel mode, |sctp_transport_|
// when using SCTP.
std::unique_ptr<cricket::RtpDataChannel> rtp_data_channel_;
std::unique_ptr<cricket::SctpTransportInternal> sctp_transport_;
// |sctp_transport_name_| keeps track of what DTLS transport the SCTP
// transport is using (which can change due to bundling).
rtc::Optional<std::string> sctp_transport_name_;
// |sctp_content_name_| is the content name (MID) in SDP.
rtc::Optional<std::string> sctp_content_name_;
// Value cached on signaling thread. Only updated when SctpReadyToSendData
// fires on the signaling thread.
bool sctp_ready_to_send_data_ = false;
// Same as signals provided by SctpTransport, but these are guaranteed to
// fire on the signaling thread, whereas SctpTransport fires on the networking
// thread.
// |sctp_invoker_| is used so that any signals queued on the signaling thread
// from the network thread are immediately discarded if the SctpTransport is
// destroyed (due to m= section being rejected).
// TODO(deadbeef): Use a proxy object to ensure that method calls/signals
// are marshalled to the right thread. Could almost use proxy.h for this,
// but it doesn't have a mechanism for marshalling sigslot::signals
std::unique_ptr<rtc::AsyncInvoker> sctp_invoker_;
sigslot::signal1<bool> SignalSctpReadyToSendData;
sigslot::signal2<const cricket::ReceiveDataParams&,
const rtc::CopyOnWriteBuffer&>
SignalSctpDataReceived;
sigslot::signal1<int> SignalSctpStreamClosedRemotely;
std::unique_ptr<cricket::DataChannel> data_channel_;
cricket::ChannelManager* channel_manager_;
IceObserver* ice_observer_;
PeerConnectionInterface::IceConnectionState ice_connection_state_;

View File

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

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(content.name, &ssl_role)) {
if (session_->GetSslRole(session_->GetChannel(content.name), &ssl_role)) {
request.options.transport_options[content.name].prefer_passive_role =
(rtc::SSL_SERVER == ssl_role);
}

View File

@ -54,6 +54,7 @@ rtc_static_library("rtc_media_base") {
"base/codec.h",
"base/cryptoparams.h",
"base/device.h",
"base/hybriddataengine.h",
"base/mediachannel.h",
"base/mediaconstants.cc",
"base/mediaconstants.h",
@ -142,13 +143,12 @@ rtc_static_library("rtc_media") {
"engine/webrtcvoe.h",
"engine/webrtcvoiceengine.cc",
"engine/webrtcvoiceengine.h",
"sctp/sctptransportinternal.h",
]
if (rtc_enable_sctp) {
sources += [
"sctp/sctptransport.cc",
"sctp/sctptransport.h",
"sctp/sctpdataengine.cc",
"sctp/sctpdataengine.h",
]
}
@ -346,7 +346,7 @@ if (rtc_include_tests) {
]
if (rtc_enable_sctp) {
sources += [ "sctp/sctptransport_unittest.cc" ]
sources += [ "sctp/sctpdataengine_unittest.cc" ]
}
configs += [ ":rtc_media_unittests_config" ]

View File

@ -942,9 +942,11 @@ inline FakeVideoMediaChannel::~FakeVideoMediaChannel() {
class FakeDataEngine : public DataEngineInterface {
public:
FakeDataEngine(){};
FakeDataEngine() : last_channel_type_(DCT_NONE) {}
virtual DataMediaChannel* CreateChannel(const MediaConfig& config) {
virtual DataMediaChannel* CreateChannel(DataChannelType data_channel_type,
const MediaConfig& config) {
last_channel_type_ = data_channel_type;
FakeDataMediaChannel* ch = new FakeDataMediaChannel(this, DataOptions());
channels_.push_back(ch);
return ch;
@ -964,9 +966,12 @@ class FakeDataEngine : public DataEngineInterface {
virtual const std::vector<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

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

View File

@ -1077,11 +1077,8 @@ enum DataMessageType {
// signal fires, on up the chain.
struct ReceiveDataParams {
// The in-packet stream indentifier.
// RTP data channels use SSRCs, SCTP data channels use SIDs.
union {
uint32_t ssrc;
int sid;
};
// For SCTP, this is really SID, not SSRC.
uint32_t ssrc;
// The type of message (binary, text, or control).
DataMessageType type;
// A per-stream value incremented per packet in the stream.
@ -1089,16 +1086,18 @@ struct ReceiveDataParams {
// A per-stream value monotonically increasing with time.
int timestamp;
ReceiveDataParams() : sid(0), type(DMT_TEXT), seq_num(0), timestamp(0) {}
ReceiveDataParams() :
ssrc(0),
type(DMT_TEXT),
seq_num(0),
timestamp(0) {
}
};
struct SendDataParams {
// The in-packet stream indentifier.
// RTP data channels use SSRCs, SCTP data channels use SIDs.
union {
uint32_t ssrc;
int sid;
};
// For SCTP, this is really SID, not SSRC.
uint32_t ssrc;
// The type of message (binary, text, or control).
DataMessageType type;
@ -1117,14 +1116,15 @@ struct SendDataParams {
// is supported, not both at the same time.
int max_rtx_ms;
SendDataParams()
: sid(0),
type(DMT_TEXT),
// TODO(pthatcher): Make these true by default?
ordered(false),
reliable(false),
max_rtx_count(0),
max_rtx_ms(0) {}
SendDataParams() :
ssrc(0),
type(DMT_TEXT),
// TODO(pthatcher): Make these true by default?
ordered(false),
reliable(false),
max_rtx_count(0),
max_rtx_ms(0) {
}
};
enum SendDataResult { SDR_SUCCESS, SDR_ERROR, SDR_BLOCK };
@ -1183,6 +1183,8 @@ class DataMediaChannel : public MediaChannel {
// Signal when the media channel is ready to send the stream. Arguments are:
// writable(bool)
sigslot::signal1<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 to get the codec names they want to
// pass in.
// We put the data codec names here so callers of
// DataEngine::CreateChannel don't have to import rtpdataengine.h or
// sctpdataengine.h to get the codec names they want to pass in.
extern const int kGoogleRtpDataCodecPlType;
extern const char kGoogleRtpDataCodecName[];

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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