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}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 32f4c8e..9e2bc91 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -113,6 +113,8 @@
"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",
diff --git a/api/DEPS b/api/DEPS
index f5a0201..db8fdda 100644
--- a/api/DEPS
+++ b/api/DEPS
@@ -166,6 +166,10 @@
"+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",
],
diff --git a/api/peer_connection_interface.cc b/api/peer_connection_interface.cc
index 944b28e..244ee73 100644
--- a/api/peer_connection_interface.cc
+++ b/api/peer_connection_interface.cc
@@ -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 @@
return nullptr;
}
+rtc::scoped_refptr<SctpTransportInterface>
+PeerConnectionInterface::GetSctpTransport() const {
+ RTC_NOTREACHED();
+ return nullptr;
+}
+
PeerConnectionInterface::BitrateParameters::BitrateParameters() = default;
PeerConnectionInterface::BitrateParameters::~BitrateParameters() = default;
diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h
index 541a6b4..be4dcb9 100644
--- a/api/peer_connection_interface.h
+++ b/api/peer_connection_interface.h
@@ -122,6 +122,7 @@
class AudioMixer;
class AudioProcessing;
class DtlsTransportInterface;
+class SctpTransportInterface;
class VideoDecoderFactory;
class VideoEncoderFactory;
@@ -1038,6 +1039,10 @@
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;
diff --git a/api/sctp_transport_interface.cc b/api/sctp_transport_interface.cc
new file mode 100644
index 0000000..c6c1fbe
--- /dev/null
+++ b/api/sctp_transport_interface.cc
@@ -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
diff --git a/api/sctp_transport_interface.h b/api/sctp_transport_interface.h
new file mode 100644
index 0000000..3698fc2
--- /dev/null
+++ b/api/sctp_transport_interface.h
@@ -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_
diff --git a/media/sctp/sctp_transport.cc b/media/sctp/sctp_transport.cc
index b656018..8bcec41 100644
--- a/media/sctp/sctp_transport.cc
+++ b/media/sctp/sctp_transport.cc
@@ -383,9 +383,8 @@
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();
}
diff --git a/media/sctp/sctp_transport.h b/media/sctp/sctp_transport.h
index f7ddd87..dccecd8 100644
--- a/media/sctp/sctp_transport.h
+++ b/media/sctp/sctp_transport.h
@@ -36,10 +36,10 @@
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 @@
// 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 @@
// 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 @@
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
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 9540ad6..b20f064 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -55,6 +55,8 @@
"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 @@
"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",
diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc
index c1b8e4e..97f6bdd 100644
--- a/pc/jsep_transport_controller.cc
+++ b/pc/jsep_transport_controller.cc
@@ -818,8 +818,8 @@
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) {
diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h
index 1363907..c98ff25 100644
--- a/pc/jsep_transport_controller.h
+++ b/pc/jsep_transport_controller.h
@@ -58,7 +58,7 @@
virtual bool OnTransportChanged(
const std::string& mid,
RtpTransportInternal* rtp_transport,
- cricket::DtlsTransportInternal* dtls_transport,
+ rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport) = 0;
};
diff --git a/pc/jsep_transport_controller_unittest.cc b/pc/jsep_transport_controller_unittest.cc
index 590f57d..4375080 100644
--- a/pc/jsep_transport_controller_unittest.cc
+++ b/pc/jsep_transport_controller_unittest.cc
@@ -305,10 +305,14 @@
// 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;
}
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index d3b9258..44617dd 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -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 @@
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 @@
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 @@
: 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 @@
}
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 @@
}
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 @@
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 @@
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;
diff --git a/pc/peer_connection.h b/pc/peer_connection.h
index 376dcc0..20b66ac 100644
--- a/pc/peer_connection.h
+++ b/pc/peer_connection.h
@@ -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 @@
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 @@
// 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 @@
// 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
diff --git a/pc/sctp_transport.cc b/pc/sctp_transport.cc
new file mode 100644
index 0000000..99270e2
--- /dev/null
+++ b/pc/sctp_transport.cc
@@ -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
diff --git a/pc/sctp_transport.h b/pc/sctp_transport.h
new file mode 100644
index 0000000..f1e5092
--- /dev/null
+++ b/pc/sctp_transport.h
@@ -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_
diff --git a/pc/sctp_transport_unittest.cc b/pc/sctp_transport_unittest.cc
new file mode 100644
index 0000000..e2bdf44
--- /dev/null
+++ b/pc/sctp_transport_unittest.cc
@@ -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