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:
parent
e9bbde5830
commit
c0dad89bed
@ -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.
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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());
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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" ]
|
||||
|
||||
@ -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
|
||||
|
||||
60
webrtc/media/base/hybriddataengine.h
Normal file
60
webrtc/media/base/hybriddataengine.h
Normal 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_
|
||||
@ -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
|
||||
|
||||
@ -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[];
|
||||
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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);
|
||||
|
||||
1066
webrtc/media/sctp/sctpdataengine.cc
Normal file
1066
webrtc/media/sctp/sctpdataengine.cc
Normal file
File diff suppressed because it is too large
Load Diff
250
webrtc/media/sctp/sctpdataengine.h
Normal file
250
webrtc/media/sctp/sctpdataengine.h
Normal 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_
|
||||
523
webrtc/media/sctp/sctpdataengine_unittest.cc
Normal file
523
webrtc/media/sctp/sctpdataengine_unittest.cc
Normal 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
@ -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_
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user