Add SCTP transport to the public API.

This involves inserting an extra layer between jsep_transport_controller
and the cricket::SctpTransportInternal layer. The objects at this layer
are reference counted.

Bug: chromium:818643
Change-Id: Ibed57c4a538de981cee63e0f7f1f319f029cab39
Reviewed-on: https://webrtc-review.googlesource.com/c/123884
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26889}
This commit is contained in:
Harald Alvestrand 2019-02-28 07:51:00 +01:00 committed by Commit Bot
parent 60fd73a9fc
commit c85328f2ca
17 changed files with 537 additions and 31 deletions

View File

@ -113,6 +113,8 @@ rtc_static_library("libjingle_peerconnection_api") {
"rtp_sender_interface.h",
"rtp_transceiver_interface.cc",
"rtp_transceiver_interface.h",
"sctp_transport_interface.cc",
"sctp_transport_interface.h",
"set_remote_description_observer_interface.h",
"stats_types.cc",
"stats_types.h",

View File

@ -166,6 +166,10 @@ specific_include_rules = {
"+rtc_base/ref_count.h",
],
"sctp_transport_interface\.h": [
"+rtc_base/ref_count.h",
],
"set_remote_description_observer_interface\.h": [
"+rtc_base/ref_count.h",
],

View File

@ -10,6 +10,7 @@
#include "api/peer_connection_interface.h"
#include "api/dtls_transport_interface.h"
#include "api/sctp_transport_interface.h"
namespace webrtc {
@ -187,6 +188,12 @@ PeerConnectionInterface::LookupDtlsTransportByMid(const std::string& mid) {
return nullptr;
}
rtc::scoped_refptr<SctpTransportInterface>
PeerConnectionInterface::GetSctpTransport() const {
RTC_NOTREACHED();
return nullptr;
}
PeerConnectionInterface::BitrateParameters::BitrateParameters() = default;
PeerConnectionInterface::BitrateParameters::~BitrateParameters() = default;

View File

@ -122,6 +122,7 @@ class AudioDeviceModule;
class AudioMixer;
class AudioProcessing;
class DtlsTransportInterface;
class SctpTransportInterface;
class VideoDecoderFactory;
class VideoEncoderFactory;
@ -1038,6 +1039,10 @@ class PeerConnectionInterface : public rtc::RefCountInterface {
virtual rtc::scoped_refptr<DtlsTransportInterface> LookupDtlsTransportByMid(
const std::string& mid);
// Returns the SCTP transport, if any.
// TODO(hta): Remove default implementation after updating Chrome.
virtual rtc::scoped_refptr<SctpTransportInterface> GetSctpTransport() const;
// Returns the current SignalingState.
virtual SignalingState signaling_state() = 0;

View File

@ -0,0 +1,32 @@
/*
* Copyright 2019 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 <utility>
#include "api/sctp_transport_interface.h"
namespace webrtc {
SctpTransportInformation::SctpTransportInformation(SctpTransportState state)
: state_(state) {}
SctpTransportInformation::SctpTransportInformation(
SctpTransportState state,
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport,
absl::optional<double> max_message_size,
absl::optional<int> max_channels)
: state_(state),
dtls_transport_(std::move(dtls_transport)),
max_message_size_(max_message_size),
max_channels_(max_channels) {}
SctpTransportInformation::~SctpTransportInformation() {}
} // namespace webrtc

View File

@ -0,0 +1,90 @@
/*
* Copyright 2019 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 API_SCTP_TRANSPORT_INTERFACE_H_
#define API_SCTP_TRANSPORT_INTERFACE_H_
#include "absl/types/optional.h"
#include "api/dtls_transport_interface.h"
#include "api/rtc_error.h"
#include "api/scoped_refptr.h"
#include "rtc_base/ref_count.h"
namespace webrtc {
// States of a SCTP transport, corresponding to the JS API specification.
// http://w3c.github.io/webrtc-pc/#dom-rtcsctptransportstate
enum class SctpTransportState {
kNew, // Has not started negotiating yet. Non-standard state.
kConnecting, // In the process of negotiating an association.
kConnected, // Completed negotiation of an association.
kClosed, // Closed by local or remote party.
kNumValues
};
// This object gives snapshot information about the changeable state of a
// SctpTransport.
// It reflects the readonly attributes of the object in the specification.
// http://w3c.github.io/webrtc-pc/#rtcsctptransport-interface
class SctpTransportInformation {
public:
explicit SctpTransportInformation(SctpTransportState state);
SctpTransportInformation(
SctpTransportState state,
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport,
absl::optional<double> max_message_size,
absl::optional<int> max_channels);
~SctpTransportInformation();
// The DTLS transport that supports this SCTP transport.
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport() const {
return dtls_transport_;
}
SctpTransportState state() const { return state_; }
absl::optional<double> MaxMessageSize() const { return max_message_size_; }
absl::optional<int> MaxChannels() const { return max_channels_; }
private:
SctpTransportState state_;
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport_;
absl::optional<double> max_message_size_;
absl::optional<int> max_channels_;
};
class SctpTransportObserverInterface {
public:
// This callback carries information about the state of the transport.
// The argument is a pass-by-value snapshot of the state.
// The callback will be called on the network thread.
virtual void OnStateChange(SctpTransportInformation info) = 0;
protected:
virtual ~SctpTransportObserverInterface() = default;
};
// A SCTP transport, as represented to the outside world.
// This object is created on the network thread, and can only be
// accessed on that thread, except for functions explicitly marked otherwise.
// References can be held by other threads, and destruction can therefore
// be initiated by other threads.
class SctpTransportInterface : public rtc::RefCountInterface {
public:
// This function can be called from other threads.
virtual rtc::scoped_refptr<DtlsTransportInterface> dtls_transport() const = 0;
// Returns information on the state of the SctpTransport.
// This function can be called from other threads.
virtual SctpTransportInformation Information() const = 0;
// Observer management.
virtual void RegisterObserver(SctpTransportObserverInterface* observer) = 0;
virtual void UnregisterObserver() = 0;
};
} // namespace webrtc
#endif // API_SCTP_TRANSPORT_INTERFACE_H_

View File

@ -383,9 +383,8 @@ SctpTransport::SctpTransport(rtc::Thread* network_thread,
rtc::PacketTransportInternal* transport)
: network_thread_(network_thread),
transport_(transport),
was_ever_writable_(transport->writable()) {
was_ever_writable_(transport ? transport->writable() : false) {
RTC_DCHECK(network_thread_);
RTC_DCHECK(transport_);
RTC_DCHECK_RUN_ON(network_thread_);
ConnectTransportSignals();
}

View File

@ -36,10 +36,10 @@ struct sctp_stream_reset_event;
struct socket;
namespace cricket {
// Holds data to be passed on to a channel.
// Holds data to be passed on to a transport.
struct SctpInboundPacket;
// From channel calls, data flows like this:
// From transport calls, data flows like this:
// [network thread (although it can in princple be another thread)]
// 1. SctpTransport::SendData(data)
// 2. usrsctp_sendv(data)
@ -59,16 +59,15 @@ struct SctpInboundPacket;
// 12. SctpTransport::SignalDataReceived(data)
// [from the same thread, methods registered/connected to
// SctpTransport are called with the recieved data]
// TODO(zhihuang): Rename "channel" to "transport" on network-level.
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).
// |transport| is not required (can be null).
SctpTransport(rtc::Thread* network_thread,
rtc::PacketTransportInternal* channel);
rtc::PacketTransportInternal* transport);
~SctpTransport() override;
// SctpTransportInternal overrides (see sctptransportinternal.h for comments).
@ -108,7 +107,7 @@ class SctpTransport : public SctpTransportInternal,
// Sets the "ready to send" flag and fires signal if needed.
void SetReadyToSendData();
// Callbacks from DTLS channel.
// Callbacks from DTLS transport.
void OnWritableState(rtc::PacketTransportInternal* transport);
virtual void OnPacketRead(rtc::PacketTransportInternal* transport,
const char* data,
@ -135,12 +134,12 @@ class SctpTransport : public SctpTransportInternal,
void OnStreamResetEvent(const struct sctp_stream_reset_event* evt);
// Responsible for marshalling incoming data to the channels listeners, and
// Responsible for marshalling incoming data to the transports 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.
// Underlying DTLS transport.
rtc::PacketTransportInternal* transport_ = nullptr;
// Track the data received from usrsctp between callbacks until the EOR bit

View File

@ -55,6 +55,8 @@ rtc_static_library("rtc_pc_base") {
"rtp_transport.cc",
"rtp_transport.h",
"rtp_transport_internal.h",
"sctp_transport.cc",
"sctp_transport.h",
"session_description.cc",
"session_description.h",
"simulcast_description.cc",
@ -258,6 +260,7 @@ if (rtc_include_tests) {
"media_session_unittest.cc",
"rtcp_mux_filter_unittest.cc",
"rtp_transport_unittest.cc",
"sctp_transport_unittest.cc",
"session_description_unittest.cc",
"srtp_filter_unittest.cc",
"srtp_session_unittest.cc",

View File

@ -818,8 +818,8 @@ bool JsepTransportController::SetTransportForMid(
mid_to_transport_[mid] = jsep_transport;
return config_.transport_observer->OnTransportChanged(
mid, jsep_transport->rtp_transport(),
jsep_transport->rtp_dtls_transport(), jsep_transport->media_transport());
mid, jsep_transport->rtp_transport(), jsep_transport->RtpDtlsTransport(),
jsep_transport->media_transport());
}
void JsepTransportController::RemoveTransportForMid(const std::string& mid) {

View File

@ -58,7 +58,7 @@ class JsepTransportController : public sigslot::has_slots<> {
virtual bool OnTransportChanged(
const std::string& mid,
RtpTransportInternal* rtp_transport,
cricket::DtlsTransportInternal* dtls_transport,
rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport) = 0;
};

View File

@ -305,10 +305,14 @@ class JsepTransportControllerTest : public JsepTransportController::Observer,
// JsepTransportController::Observer overrides.
bool OnTransportChanged(const std::string& mid,
RtpTransportInternal* rtp_transport,
cricket::DtlsTransportInternal* dtls_transport,
rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport) override {
changed_rtp_transport_by_mid_[mid] = rtp_transport;
changed_dtls_transport_by_mid_[mid] = dtls_transport;
if (dtls_transport) {
changed_dtls_transport_by_mid_[mid] = dtls_transport->internal();
} else {
changed_dtls_transport_by_mid_[mid] = nullptr;
}
changed_media_transport_by_mid_[mid] = media_transport;
return true;
}

View File

@ -41,6 +41,7 @@
#include "pc/rtp_media_utils.h"
#include "pc/rtp_receiver.h"
#include "pc/rtp_sender.h"
#include "pc/sctp_transport.h"
#include "pc/sctp_utils.h"
#include "pc/sdp_utils.h"
#include "pc/stream_collection.h"
@ -3658,6 +3659,11 @@ PeerConnection::LookupDtlsTransportByMidInternal(const std::string& mid) {
return transport_controller_->LookupDtlsTransportByMid(mid);
}
rtc::scoped_refptr<SctpTransportInterface> PeerConnection::GetSctpTransport()
const {
return sctp_transport_;
}
const SessionDescriptionInterface* PeerConnection::local_description() const {
return pending_local_description_ ? pending_local_description_.get()
: current_local_description_.get();
@ -5497,7 +5503,7 @@ bool PeerConnection::PushdownSctpParameters_n(cricket::ContentSource source) {
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(
return cricket_sctp_transport()->Start(
GetSctpPort(local_description()->description()),
GetSctpPort(remote_description()->description()));
}
@ -5631,7 +5637,7 @@ bool PeerConnection::SendData(const cricket::SendDataParams& params,
: network_thread()->Invoke<bool>(
RTC_FROM_HERE,
Bind(&cricket::SctpTransportInternal::SendData,
sctp_transport_.get(), params, payload, result));
cricket_sctp_transport(), params, payload, result));
}
bool PeerConnection::ConnectDataChannel(DataChannel* webrtc_data_channel) {
@ -5705,7 +5711,7 @@ void PeerConnection::AddSctpDataStream(int sid) {
}
network_thread()->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream,
sctp_transport_.get(), sid));
cricket_sctp_transport(), sid));
}
void PeerConnection::RemoveSctpDataStream(int sid) {
@ -5720,7 +5726,7 @@ void PeerConnection::RemoveSctpDataStream(int sid) {
}
network_thread()->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream,
sctp_transport_.get(), sid));
cricket_sctp_transport(), sid));
}
bool PeerConnection::ReadyToSendData() const {
@ -6243,32 +6249,38 @@ Call::Stats PeerConnection::GetCallStats() {
bool PeerConnection::CreateSctpTransport_n(const std::string& mid) {
RTC_DCHECK(network_thread()->IsCurrent());
RTC_DCHECK(sctp_factory_);
rtc::scoped_refptr<DtlsTransport> webrtc_dtls_transport =
transport_controller_->LookupDtlsTransportByMid(mid);
cricket::DtlsTransportInternal* dtls_transport =
transport_controller_->GetDtlsTransport(mid);
webrtc_dtls_transport->internal();
RTC_DCHECK(dtls_transport);
sctp_transport_ = sctp_factory_->CreateSctpTransport(dtls_transport);
RTC_DCHECK(sctp_transport_);
std::unique_ptr<cricket::SctpTransportInternal> cricket_sctp_transport =
sctp_factory_->CreateSctpTransport(dtls_transport);
RTC_DCHECK(cricket_sctp_transport);
sctp_invoker_.reset(new rtc::AsyncInvoker());
sctp_transport_->SignalReadyToSendData.connect(
cricket_sctp_transport->SignalReadyToSendData.connect(
this, &PeerConnection::OnSctpTransportReadyToSendData_n);
sctp_transport_->SignalDataReceived.connect(
cricket_sctp_transport->SignalDataReceived.connect(
this, &PeerConnection::OnSctpTransportDataReceived_n);
// TODO(deadbeef): All we do here is AsyncInvoke to fire the signal on
// another thread. Would be nice if there was a helper class similar to
// sigslot::repeater that did this for us, eliminating a bunch of boilerplate
// code.
sctp_transport_->SignalClosingProcedureStartedRemotely.connect(
cricket_sctp_transport->SignalClosingProcedureStartedRemotely.connect(
this, &PeerConnection::OnSctpClosingProcedureStartedRemotely_n);
sctp_transport_->SignalClosingProcedureComplete.connect(
cricket_sctp_transport->SignalClosingProcedureComplete.connect(
this, &PeerConnection::OnSctpClosingProcedureComplete_n);
sctp_mid_ = mid;
sctp_transport_->SetDtlsTransport(dtls_transport);
sctp_transport_ = new rtc::RefCountedObject<SctpTransport>(
std::move(cricket_sctp_transport));
sctp_transport_->SetDtlsTransport(std::move(webrtc_dtls_transport));
return true;
}
void PeerConnection::DestroySctpTransport_n() {
RTC_DCHECK(network_thread()->IsCurrent());
sctp_transport_.reset(nullptr);
sctp_transport_->Clear();
sctp_transport_ = nullptr;
sctp_mid_.reset();
sctp_invoker_.reset(nullptr);
sctp_ready_to_send_data_ = false;
@ -6949,7 +6961,7 @@ void PeerConnection::DestroyChannelInterface(
bool PeerConnection::OnTransportChanged(
const std::string& mid,
RtpTransportInternal* rtp_transport,
cricket::DtlsTransportInternal* dtls_transport,
rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport) {
RTC_DCHECK_RUNS_SERIALIZED(&use_media_transport_race_checker_);
bool ret = true;

View File

@ -26,6 +26,7 @@
#include "pc/peer_connection_internal.h"
#include "pc/rtc_stats_collector.h"
#include "pc/rtp_transceiver.h"
#include "pc/sctp_transport.h"
#include "pc/stats_collector.h"
#include "pc/stream_collection.h"
#include "pc/webrtc_session_description_factory.h"
@ -201,6 +202,8 @@ class PeerConnection : public PeerConnectionInternal,
rtc::scoped_refptr<DtlsTransport> LookupDtlsTransportByMidInternal(
const std::string& mid);
rtc::scoped_refptr<SctpTransportInterface> GetSctpTransport() const override;
RTC_DEPRECATED bool StartRtcEventLog(rtc::PlatformFile file,
int64_t max_size_bytes) override;
bool StartRtcEventLog(std::unique_ptr<RtcEventLogOutput> output,
@ -1015,7 +1018,7 @@ class PeerConnection : public PeerConnectionInternal,
// rejected).
bool OnTransportChanged(const std::string& mid,
RtpTransportInternal* rtp_transport,
cricket::DtlsTransportInternal* dtls_transport,
rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport) override;
// Returns the observer. Will crash on CHECK if the observer is removed.
@ -1149,7 +1152,10 @@ class PeerConnection : public PeerConnectionInternal,
// when using SCTP.
cricket::RtpDataChannel* rtp_data_channel_ = nullptr;
std::unique_ptr<cricket::SctpTransportInternal> sctp_transport_;
cricket::SctpTransportInternal* cricket_sctp_transport() {
return sctp_transport_->internal();
}
rtc::scoped_refptr<SctpTransport> sctp_transport_;
// |sctp_mid_| is the content name (MID) in SDP.
absl::optional<std::string> sctp_mid_;
// Value cached on signaling thread. Only updated when SctpReadyToSendData

119
pc/sctp_transport.cc Normal file
View File

@ -0,0 +1,119 @@
/*
* Copyright 2018 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 "pc/sctp_transport.h"
#include <utility>
namespace webrtc {
SctpTransport::SctpTransport(
std::unique_ptr<cricket::SctpTransportInternal> internal)
: owner_thread_(rtc::Thread::Current()),
info_(SctpTransportState::kNew),
internal_sctp_transport_(std::move(internal)) {
RTC_DCHECK(internal_sctp_transport_.get());
internal_sctp_transport_->SignalReadyToSendData.connect(
this, &SctpTransport::OnInternalReadyToSendData);
// TODO(https://bugs.webrtc.org/10360): Add handlers for transport closing.
if (dtls_transport_) {
UpdateInformation(SctpTransportState::kConnecting);
} else {
UpdateInformation(SctpTransportState::kNew);
}
}
SctpTransport::~SctpTransport() {
// We depend on the network thread to call Clear() before dropping
// its last reference to this object.
RTC_DCHECK(owner_thread_->IsCurrent() || !internal_sctp_transport_);
}
SctpTransportInformation SctpTransport::Information() const {
rtc::CritScope scope(&lock_);
return info_;
}
void SctpTransport::RegisterObserver(SctpTransportObserverInterface* observer) {
RTC_DCHECK_RUN_ON(owner_thread_);
RTC_DCHECK(observer);
RTC_DCHECK(!observer_);
observer_ = observer;
}
void SctpTransport::UnregisterObserver() {
RTC_DCHECK_RUN_ON(owner_thread_);
observer_ = nullptr;
}
rtc::scoped_refptr<DtlsTransportInterface> SctpTransport::dtls_transport()
const {
RTC_DCHECK_RUN_ON(owner_thread_);
return dtls_transport_;
}
// Internal functions
void SctpTransport::Clear() {
RTC_DCHECK_RUN_ON(owner_thread_);
RTC_DCHECK(internal());
{
rtc::CritScope scope(&lock_);
// Note that we delete internal_sctp_transport_, but
// only drop the reference to dtls_transport_.
dtls_transport_ = nullptr;
internal_sctp_transport_ = nullptr;
}
UpdateInformation(SctpTransportState::kClosed);
}
void SctpTransport::SetDtlsTransport(
rtc::scoped_refptr<DtlsTransport> transport) {
RTC_DCHECK_RUN_ON(owner_thread_);
rtc::CritScope scope(&lock_);
dtls_transport_ = transport;
if (internal_sctp_transport_) {
if (transport) {
internal_sctp_transport_->SetDtlsTransport(transport->internal());
if (info_.state() == SctpTransportState::kNew) {
UpdateInformation(SctpTransportState::kConnecting);
}
} else {
internal_sctp_transport_->SetDtlsTransport(nullptr);
}
}
}
void SctpTransport::UpdateInformation(SctpTransportState state) {
RTC_DCHECK_RUN_ON(owner_thread_);
bool must_send_update;
SctpTransportInformation info_copy(SctpTransportState::kNew);
{
rtc::CritScope scope(&lock_);
must_send_update = (state != info_.state());
// TODO(https://bugs.webrtc.org/10358): Update max message size and
// max channels from internal SCTP transport when available.
info_ = SctpTransportInformation(
state, dtls_transport_, info_.MaxMessageSize(), info_.MaxChannels());
if (observer_ && must_send_update) {
info_copy = info_;
}
}
// We call the observer without holding the lock.
if (observer_ && must_send_update) {
observer_->OnStateChange(info_copy);
}
}
void SctpTransport::OnInternalReadyToSendData() {
UpdateInformation(SctpTransportState::kConnected);
}
} // namespace webrtc

75
pc/sctp_transport.h Normal file
View File

@ -0,0 +1,75 @@
/*
* Copyright 2019 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 PC_SCTP_TRANSPORT_H_
#define PC_SCTP_TRANSPORT_H_
#include <memory>
#include "api/scoped_refptr.h"
#include "api/sctp_transport_interface.h"
#include "media/sctp/sctp_transport.h"
#include "pc/dtls_transport.h"
namespace webrtc {
// This implementation wraps a cricket::SctpTransport, and takes
// ownership of it.
// This object must be constructed and updated on the networking thread,
// the same thread as the one the cricket::SctpTransportInternal object
// lives on.
class SctpTransport : public SctpTransportInterface,
public sigslot::has_slots<> {
public:
explicit SctpTransport(
std::unique_ptr<cricket::SctpTransportInternal> internal);
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport() const override;
SctpTransportInformation Information() const override;
void RegisterObserver(SctpTransportObserverInterface* observer) override;
void UnregisterObserver() override;
void Clear();
void SetDtlsTransport(rtc::scoped_refptr<DtlsTransport>);
cricket::SctpTransportInternal* internal() {
rtc::CritScope scope(&lock_);
return internal_sctp_transport_.get();
}
const cricket::SctpTransportInternal* internal() const {
rtc::CritScope scope(&lock_);
return internal_sctp_transport_.get();
}
protected:
~SctpTransport() override;
private:
void UpdateInformation(SctpTransportState state);
void OnInternalReadyToSendData();
void OnInternalClosingProcedureStartedRemotely(int sid);
void OnInternalClosingProcedureComplete(int sid);
const rtc::Thread* owner_thread_;
rtc::CriticalSection lock_;
// Variables accessible off-thread, guarded by lock_
SctpTransportInformation info_ RTC_GUARDED_BY(lock_);
std::unique_ptr<cricket::SctpTransportInternal> internal_sctp_transport_
RTC_GUARDED_BY(lock_);
// Variables only accessed on-thread
SctpTransportObserverInterface* observer_ RTC_GUARDED_BY(owner_thread_) =
nullptr;
rtc::scoped_refptr<DtlsTransport> dtls_transport_
RTC_GUARDED_BY(owner_thread_);
};
} // namespace webrtc
#endif // PC_SCTP_TRANSPORT_H_

View File

@ -0,0 +1,149 @@
/*
* Copyright 2019 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 "pc/sctp_transport.h"
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "p2p/base/fake_dtls_transport.h"
#include "pc/dtls_transport.h"
#include "rtc_base/gunit.h"
#include "test/gmock.h"
#include "test/gtest.h"
constexpr int kDefaultTimeout = 1000; // milliseconds
using cricket::FakeDtlsTransport;
using ::testing::ElementsAre;
namespace webrtc {
namespace {
class FakeCricketSctpTransport : public cricket::SctpTransportInternal {
public:
void SetDtlsTransport(rtc::PacketTransportInternal* transport) override {}
bool Start(int local_port, int remote_port) override { 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 {}
// Methods exposed for testing
void SendSignalReadyToSendData() { SignalReadyToSendData(); }
void SendSignalClosingProcedureStartedRemotely() {
SignalClosingProcedureStartedRemotely(1);
}
void SendSignalClosingProcedureComplete() {
SignalClosingProcedureComplete(1);
}
};
} // namespace
class TestSctpTransportObserver : public SctpTransportObserverInterface {
public:
void OnStateChange(SctpTransportInformation info) override {
states_.push_back(info.state());
}
SctpTransportState State() {
if (states_.size() > 0) {
return states_[states_.size() - 1];
} else {
return SctpTransportState::kNew;
}
}
const std::vector<SctpTransportState>& States() { return states_; }
private:
std::vector<SctpTransportState> states_;
};
class SctpTransportTest : public testing::Test {
public:
SctpTransport* transport() { return transport_.get(); }
SctpTransportObserverInterface* observer() { return &observer_; }
void CreateTransport() {
auto cricket_sctp_transport =
absl::WrapUnique(new FakeCricketSctpTransport());
transport_ = new rtc::RefCountedObject<SctpTransport>(
std::move(cricket_sctp_transport));
}
void AddDtlsTransport() {
std::unique_ptr<cricket::DtlsTransportInternal> cricket_transport =
absl::make_unique<FakeDtlsTransport>(
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
dtls_transport_ =
new rtc::RefCountedObject<DtlsTransport>(std::move(cricket_transport));
transport_->SetDtlsTransport(dtls_transport_);
}
void CompleteSctpHandshake() {
CricketSctpTransport()->SendSignalReadyToSendData();
}
FakeCricketSctpTransport* CricketSctpTransport() {
return static_cast<FakeCricketSctpTransport*>(transport_->internal());
}
rtc::scoped_refptr<SctpTransport> transport_;
rtc::scoped_refptr<DtlsTransport> dtls_transport_;
TestSctpTransportObserver observer_;
};
TEST(SctpTransportSimpleTest, CreateClearDelete) {
std::unique_ptr<cricket::SctpTransportInternal> fake_cricket_sctp_transport =
absl::WrapUnique(new FakeCricketSctpTransport());
rtc::scoped_refptr<SctpTransport> sctp_transport =
new rtc::RefCountedObject<SctpTransport>(
std::move(fake_cricket_sctp_transport));
ASSERT_TRUE(sctp_transport->internal());
ASSERT_EQ(SctpTransportState::kNew, sctp_transport->Information().state());
sctp_transport->Clear();
ASSERT_FALSE(sctp_transport->internal());
ASSERT_EQ(SctpTransportState::kClosed, sctp_transport->Information().state());
}
TEST_F(SctpTransportTest, EventsObservedWhenConnecting) {
CreateTransport();
transport()->RegisterObserver(observer());
AddDtlsTransport();
CompleteSctpHandshake();
ASSERT_EQ_WAIT(SctpTransportState::kConnected, observer_.State(),
kDefaultTimeout);
EXPECT_THAT(observer_.States(), ElementsAre(SctpTransportState::kConnecting,
SctpTransportState::kConnected));
}
TEST_F(SctpTransportTest, CloseWhenClearing) {
CreateTransport();
transport()->RegisterObserver(observer());
AddDtlsTransport();
CompleteSctpHandshake();
ASSERT_EQ_WAIT(SctpTransportState::kConnected, observer_.State(),
kDefaultTimeout);
transport()->Clear();
ASSERT_EQ_WAIT(SctpTransportState::kClosed, observer_.State(),
kDefaultTimeout);
}
} // namespace webrtc