Reland "Reland "Refactor SCTP data channels to use DataChannelTransportInterface.""
This is a reland of 487f9a17e426fd14bb06b13e861071b3f15d119b
Original change's description:
> Reland "Refactor SCTP data channels to use DataChannelTransportInterface."
>
> Also clears SctpTransport before deleting JsepTransport.
>
> SctpTransport is ref-counted, but the underlying transport is deleted when
> JsepTransport clears the rtp_dtls_transport. This results in crashes when
> usrsctp attempts to send outgoing packets through a dangling pointer to the
> underlying transport.
>
> Clearing SctpTransport before DtlsTransport removes the pointer to the
> underlying transport before it becomes invalid.
>
> This fixes a crash in chromium's web platform tests (see
> https://chromium-review.googlesource.com/c/chromium/src/+/1776711).
>
> Original change's description:
> > Refactor SCTP data channels to use DataChannelTransportInterface.
> >
> > This change moves SctpTransport to be owned by JsepTransport, which now
> > holds a DataChannelTransport implementation for SCTP when it is used for
> > data channels.
> >
> > This simplifies negotiation and fallback to SCTP. Negotiation can now
> > use a composite DataChannelTransport, just as negotiation for RTP uses a
> > composite RTP transport.
> >
> > PeerConnection also has one fewer way it needs to manage data channels.
> > It now handles SCTP and datagram- or media-transport-based data channels
> > the same way.
> >
> > There are a few leaky abstractions left. For example, PeerConnection
> > calls Start() on the SctpTransport at a particular point in negotiation,
> > but does not need to call this for other transports. Similarly, PC
> > exposes an interface to the SCTP transport directly to the user; there
> > is no equivalent for other transports.
>
> Bug: webrtc:9719
> Change-Id: I64e94b88afb119fdbf5f22750f88c8a084d53937
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/151981
> Reviewed-by: Benjamin Wright <benwright@webrtc.org>
> Reviewed-by: Steve Anton <steveanton@webrtc.org>
> Commit-Queue: Benjamin Wright <benwright@webrtc.org>
> Commit-Queue: Bjorn Mellem <mellem@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#29120}
Bug: webrtc:9719
Change-Id: I28481a3de64a3506bc57748106383eeba4ef205c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/152740
Reviewed-by: Artem Titov <titovartem@webrtc.org>
Reviewed-by: Benjamin Wright <benwright@webrtc.org>
Reviewed-by: Seth Hampson <shampson@webrtc.org>
Commit-Queue: Bjorn Mellem <mellem@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29290}
diff --git a/api/test/loopback_media_transport.cc b/api/test/loopback_media_transport.cc
index cadcff0..e341a38 100644
--- a/api/test/loopback_media_transport.cc
+++ b/api/test/loopback_media_transport.cc
@@ -271,7 +271,11 @@
}
void MediaTransportPair::LoopbackMediaTransport::Connect(
- rtc::PacketTransportInternal* packet_transport) {}
+ rtc::PacketTransportInternal* packet_transport) {
+ if (state_after_connect_) {
+ SetState(*state_after_connect_);
+ }
+}
absl::optional<std::string>
MediaTransportPair::LoopbackMediaTransport::GetTransportParametersOffer()
@@ -504,6 +508,11 @@
});
}
+void MediaTransportPair::LoopbackMediaTransport::SetStateAfterConnect(
+ MediaTransportState state) {
+ state_after_connect_ = state;
+}
+
void MediaTransportPair::LoopbackMediaTransport::FlushAsyncInvokes() {
invoker_.Flush(thread_);
dc_transport_.FlushAsyncInvokes();
@@ -610,7 +619,11 @@
}
void MediaTransportPair::LoopbackDatagramTransport::Connect(
- rtc::PacketTransportInternal* packet_transport) {}
+ rtc::PacketTransportInternal* packet_transport) {
+ if (state_after_connect_) {
+ SetState(*state_after_connect_);
+ }
+}
CongestionControlInterface*
MediaTransportPair::LoopbackDatagramTransport::congestion_control() {
@@ -670,6 +683,11 @@
dc_transport_.OnReadyToSend(state == MediaTransportState::kWritable);
}
+void MediaTransportPair::LoopbackDatagramTransport::SetStateAfterConnect(
+ MediaTransportState state) {
+ state_after_connect_ = state;
+}
+
void MediaTransportPair::LoopbackDatagramTransport::FlushAsyncInvokes() {
dc_transport_.FlushAsyncInvokes();
}
diff --git a/api/test/loopback_media_transport.h b/api/test/loopback_media_transport.h
index 1087d9a..bacfd5e 100644
--- a/api/test/loopback_media_transport.h
+++ b/api/test/loopback_media_transport.h
@@ -111,6 +111,16 @@
second_datagram_transport_.SetState(state);
}
+ void SetFirstState(MediaTransportState state) {
+ first_.SetState(state);
+ first_datagram_transport_.SetState(state);
+ }
+
+ void SetSecondStateAfterConnect(MediaTransportState state) {
+ second_.SetState(state);
+ second_datagram_transport_.SetState(state);
+ }
+
void SetFirstDatagramTransportParameters(const std::string& params) {
first_datagram_transport_.set_transport_parameters(params);
}
@@ -214,6 +224,10 @@
void SetState(MediaTransportState state);
+ // When Connect() is called, the media transport will enter this state.
+ // This is useful for mimicking zero-RTT connectivity, for example.
+ void SetStateAfterConnect(MediaTransportState state);
+
RTCError OpenChannel(int channel_id) override;
RTCError SendData(int channel_id,
@@ -270,6 +284,8 @@
MediaTransportState state_ RTC_GUARDED_BY(thread_) =
MediaTransportState::kPending;
+ absl::optional<MediaTransportState> state_after_connect_;
+
LoopbackMediaTransport* other_;
Stats stats_ RTC_GUARDED_BY(stats_lock_);
@@ -306,6 +322,10 @@
// Loopback-specific functionality.
void SetState(MediaTransportState state);
+
+ // When Connect() is called, the datagram transport will enter this state.
+ // This is useful for mimicking zero-RTT connectivity, for example.
+ void SetStateAfterConnect(MediaTransportState state);
void FlushAsyncInvokes();
void set_transport_parameters(const std::string& value) {
@@ -316,6 +336,8 @@
LoopbackDataChannelTransport dc_transport_;
std::string transport_parameters_;
+
+ absl::optional<MediaTransportState> state_after_connect_;
};
LoopbackMediaTransport first_;
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 07db208..18025b8 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -34,6 +34,8 @@
"channel_interface.h",
"channel_manager.cc",
"channel_manager.h",
+ "composite_data_channel_transport.cc",
+ "composite_data_channel_transport.h",
"composite_rtp_transport.cc",
"composite_rtp_transport.h",
"datagram_rtp_transport.cc",
@@ -59,8 +61,12 @@
"rtp_transport.cc",
"rtp_transport.h",
"rtp_transport_internal.h",
+ "sctp_data_channel_transport.cc",
+ "sctp_data_channel_transport.h",
"sctp_transport.cc",
"sctp_transport.h",
+ "sctp_utils.cc",
+ "sctp_utils.h",
"session_description.cc",
"session_description.h",
"simulcast_description.cc",
@@ -192,8 +198,6 @@
"rtp_sender.h",
"rtp_transceiver.cc",
"rtp_transceiver.h",
- "sctp_utils.cc",
- "sctp_utils.h",
"sdp_serializer.cc",
"sdp_serializer.h",
"sdp_utils.cc",
diff --git a/pc/composite_data_channel_transport.cc b/pc/composite_data_channel_transport.cc
new file mode 100644
index 0000000..185dd1e
--- /dev/null
+++ b/pc/composite_data_channel_transport.cc
@@ -0,0 +1,117 @@
+/*
+ * 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/composite_data_channel_transport.h"
+
+#include <utility>
+
+#include "absl/algorithm/container.h"
+
+namespace webrtc {
+
+CompositeDataChannelTransport::CompositeDataChannelTransport(
+ std::vector<DataChannelTransportInterface*> transports)
+ : transports_(std::move(transports)) {
+ for (auto transport : transports_) {
+ transport->SetDataSink(this);
+ }
+}
+
+void CompositeDataChannelTransport::SetSendTransport(
+ DataChannelTransportInterface* send_transport) {
+ if (!absl::c_linear_search(transports_, send_transport)) {
+ return;
+ }
+ send_transport_ = send_transport;
+ // NB: OnReadyToSend() checks if we're actually ready to send, and signals
+ // |sink_| if appropriate. This signal is required upon setting the sink.
+ OnReadyToSend();
+}
+
+void CompositeDataChannelTransport::RemoveTransport(
+ DataChannelTransportInterface* transport) {
+ RTC_DCHECK(transport != send_transport_) << "Cannot remove send transport";
+
+ auto it = absl::c_find(transports_, transport);
+ if (it == transports_.end()) {
+ return;
+ }
+
+ transport->SetDataSink(nullptr);
+ transports_.erase(it);
+}
+
+RTCError CompositeDataChannelTransport::OpenChannel(int channel_id) {
+ RTCError error = RTCError::OK();
+ for (auto transport : transports_) {
+ RTCError e = transport->OpenChannel(channel_id);
+ if (!e.ok()) {
+ error = std::move(e);
+ }
+ }
+ return error;
+}
+
+RTCError CompositeDataChannelTransport::SendData(
+ int channel_id,
+ const SendDataParams& params,
+ const rtc::CopyOnWriteBuffer& buffer) {
+ if (send_transport_) {
+ return send_transport_->SendData(channel_id, params, buffer);
+ }
+ return RTCError(RTCErrorType::NETWORK_ERROR, "Send transport is not ready");
+}
+
+RTCError CompositeDataChannelTransport::CloseChannel(int channel_id) {
+ if (send_transport_) {
+ return send_transport_->CloseChannel(channel_id);
+ }
+ return RTCError(RTCErrorType::NETWORK_ERROR, "Send transport is not ready");
+}
+
+void CompositeDataChannelTransport::SetDataSink(DataChannelSink* sink) {
+ sink_ = sink;
+ // NB: OnReadyToSend() checks if we're actually ready to send, and signals
+ // |sink_| if appropriate. This signal is required upon setting the sink.
+ OnReadyToSend();
+}
+
+bool CompositeDataChannelTransport::IsReadyToSend() const {
+ return send_transport_ && send_transport_->IsReadyToSend();
+}
+
+void CompositeDataChannelTransport::OnDataReceived(
+ int channel_id,
+ DataMessageType type,
+ const rtc::CopyOnWriteBuffer& buffer) {
+ if (sink_) {
+ sink_->OnDataReceived(channel_id, type, buffer);
+ }
+}
+
+void CompositeDataChannelTransport::OnChannelClosing(int channel_id) {
+ if (sink_) {
+ sink_->OnChannelClosing(channel_id);
+ }
+}
+
+void CompositeDataChannelTransport::OnChannelClosed(int channel_id) {
+ if (sink_) {
+ sink_->OnChannelClosed(channel_id);
+ }
+}
+
+void CompositeDataChannelTransport::OnReadyToSend() {
+ if (sink_ && send_transport_ && send_transport_->IsReadyToSend()) {
+ sink_->OnReadyToSend();
+ }
+}
+
+} // namespace webrtc
diff --git a/pc/composite_data_channel_transport.h b/pc/composite_data_channel_transport.h
new file mode 100644
index 0000000..ccff4fe
--- /dev/null
+++ b/pc/composite_data_channel_transport.h
@@ -0,0 +1,62 @@
+/*
+ * 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_COMPOSITE_DATA_CHANNEL_TRANSPORT_H_
+#define PC_COMPOSITE_DATA_CHANNEL_TRANSPORT_H_
+
+#include <vector>
+
+#include "api/data_channel_transport_interface.h"
+#include "rtc_base/critical_section.h"
+
+namespace webrtc {
+
+// Composite implementation of DataChannelTransportInterface. Allows users to
+// receive data channel messages over multiple transports and send over one of
+// those transports.
+class CompositeDataChannelTransport : public DataChannelTransportInterface,
+ public DataChannelSink {
+ public:
+ explicit CompositeDataChannelTransport(
+ std::vector<DataChannelTransportInterface*> transports);
+
+ // Specifies which transport to be used for sending. Must be called before
+ // sending data.
+ void SetSendTransport(DataChannelTransportInterface* send_transport);
+
+ // Removes a given transport from the composite, if present.
+ void RemoveTransport(DataChannelTransportInterface* transport);
+
+ // DataChannelTransportInterface overrides.
+ RTCError OpenChannel(int channel_id) override;
+ RTCError SendData(int channel_id,
+ const SendDataParams& params,
+ const rtc::CopyOnWriteBuffer& buffer) override;
+ RTCError CloseChannel(int channel_id) override;
+ void SetDataSink(DataChannelSink* sink) override;
+ bool IsReadyToSend() const override;
+
+ // DataChannelSink overrides.
+ void OnDataReceived(int channel_id,
+ DataMessageType type,
+ const rtc::CopyOnWriteBuffer& buffer) override;
+ void OnChannelClosing(int channel_id) override;
+ void OnChannelClosed(int channel_id) override;
+ void OnReadyToSend() override;
+
+ private:
+ std::vector<DataChannelTransportInterface*> transports_;
+ DataChannelTransportInterface* send_transport_ = nullptr;
+ DataChannelSink* sink_ = nullptr;
+};
+
+} // namespace webrtc
+
+#endif // PC_COMPOSITE_DATA_CHANNEL_TRANSPORT_H_
diff --git a/pc/jsep_transport.cc b/pc/jsep_transport.cc
index 22f4f8d..b95dc22 100644
--- a/pc/jsep_transport.cc
+++ b/pc/jsep_transport.cc
@@ -21,6 +21,7 @@
#include "api/candidate.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/p2p_transport_channel.h"
+#include "pc/sctp_data_channel_transport.h"
#include "rtc_base/checks.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/logging.h"
@@ -101,8 +102,10 @@
std::unique_ptr<webrtc::RtpTransportInternal> datagram_rtp_transport,
std::unique_ptr<DtlsTransportInternal> rtp_dtls_transport,
std::unique_ptr<DtlsTransportInternal> rtcp_dtls_transport,
+ std::unique_ptr<SctpTransportInternal> sctp_transport,
std::unique_ptr<webrtc::MediaTransportInterface> media_transport,
- std::unique_ptr<webrtc::DatagramTransportInterface> datagram_transport)
+ std::unique_ptr<webrtc::DatagramTransportInterface> datagram_transport,
+ webrtc::DataChannelTransportInterface* data_channel_transport)
: network_thread_(rtc::Thread::Current()),
mid_(mid),
local_certificate_(local_certificate),
@@ -121,8 +124,17 @@
? new rtc::RefCountedObject<webrtc::DtlsTransport>(
std::move(rtcp_dtls_transport))
: nullptr),
+ sctp_data_channel_transport_(
+ sctp_transport ? std::make_unique<webrtc::SctpDataChannelTransport>(
+ sctp_transport.get())
+ : nullptr),
+ sctp_transport_(sctp_transport
+ ? new rtc::RefCountedObject<webrtc::SctpTransport>(
+ std::move(sctp_transport))
+ : nullptr),
media_transport_(std::move(media_transport)),
- datagram_transport_(std::move(datagram_transport)) {
+ datagram_transport_(std::move(datagram_transport)),
+ data_channel_transport_(data_channel_transport) {
RTC_DCHECK(ice_transport_);
RTC_DCHECK(rtp_dtls_transport_);
// |rtcp_ice_transport_| must be present iff |rtcp_dtls_transport_| is
@@ -143,6 +155,10 @@
RTC_DCHECK(!sdes_transport);
}
+ if (sctp_transport_) {
+ sctp_transport_->SetDtlsTransport(rtp_dtls_transport_);
+ }
+
if (datagram_rtp_transport_ && default_rtp_transport()) {
composite_rtp_transport_ = std::make_unique<webrtc::CompositeRtpTransport>(
std::vector<webrtc::RtpTransportInternal*>{
@@ -152,6 +168,13 @@
if (media_transport_) {
media_transport_->SetMediaTransportStateCallback(this);
}
+
+ if (data_channel_transport_ && sctp_data_channel_transport_) {
+ composite_data_channel_transport_ =
+ std::make_unique<webrtc::CompositeDataChannelTransport>(
+ std::vector<webrtc::DataChannelTransportInterface*>{
+ data_channel_transport_, sctp_data_channel_transport_.get()});
+ }
}
JsepTransport::~JsepTransport() {
@@ -162,6 +185,10 @@
media_transport_.reset();
}
+ if (sctp_transport_) {
+ sctp_transport_->Clear();
+ }
+
// Clear all DtlsTransports. There may be pointers to these from
// other places, so we can't assume they'll be deleted by the destructor.
rtp_dtls_transport_->Clear();
@@ -789,26 +816,20 @@
use_datagram_transport ? datagram_rtp_transport_.get()
: default_rtp_transport());
}
+ if (composite_data_channel_transport_) {
+ composite_data_channel_transport_->SetSendTransport(
+ use_datagram_transport ? data_channel_transport_
+ : sctp_data_channel_transport_.get());
+ }
if (type != SdpType::kAnswer) {
- // A provisional answer lets the peer start sending on the chosen
- // transport, but does not allow it to destroy other transports yet.
- SignalDataChannelTransportNegotiated(
- this, use_datagram_transport ? datagram_transport_.get() : nullptr,
- /*provisional=*/true);
return;
}
- // A full answer lets the peer delete the remaining transports.
- // First, signal that the transports will be deleted so the application can
- // stop using them.
- SignalDataChannelTransportNegotiated(
- this, use_datagram_transport ? datagram_transport_.get() : nullptr,
- /*provisional=*/false);
-
if (use_datagram_transport) {
if (composite_rtp_transport_) {
- // Remove and delete the non-datagram RTP transport.
+ // Negotiated use of datagram transport for RTP, so remove the
+ // non-datagram RTP transport.
composite_rtp_transport_->RemoveTransport(default_rtp_transport());
if (unencrypted_rtp_transport_) {
unencrypted_rtp_transport_ = nullptr;
@@ -818,12 +839,29 @@
dtls_srtp_transport_ = nullptr;
}
}
+ if (composite_data_channel_transport_) {
+ // Negotiated use of datagram transport for data channels, so remove the
+ // non-datagram data channel transport.
+ composite_data_channel_transport_->RemoveTransport(
+ sctp_data_channel_transport_.get());
+ sctp_data_channel_transport_ = nullptr;
+ sctp_transport_ = nullptr;
+ }
} else {
// Remove and delete the datagram transport.
if (composite_rtp_transport_) {
composite_rtp_transport_->RemoveTransport(datagram_rtp_transport_.get());
}
+ if (composite_data_channel_transport_) {
+ composite_data_channel_transport_->RemoveTransport(
+ data_channel_transport_);
+ } else {
+ // If there's no composite data channel transport, we need to signal that
+ // the data channel is about to be deleted.
+ SignalDataChannelTransportNegotiated(this, nullptr);
+ }
datagram_rtp_transport_ = nullptr;
+ data_channel_transport_ = nullptr;
datagram_transport_ = nullptr;
}
}
diff --git a/pc/jsep_transport.h b/pc/jsep_transport.h
index 7bd0b07..868f7b9 100644
--- a/pc/jsep_transport.h
+++ b/pc/jsep_transport.h
@@ -21,14 +21,17 @@
#include "api/jsep.h"
#include "api/transport/datagram_transport_interface.h"
#include "api/transport/media/media_transport_interface.h"
+#include "media/sctp/sctp_transport_internal.h"
#include "p2p/base/dtls_transport.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/transport_info.h"
+#include "pc/composite_data_channel_transport.h"
#include "pc/composite_rtp_transport.h"
#include "pc/dtls_srtp_transport.h"
#include "pc/dtls_transport.h"
#include "pc/rtcp_mux_filter.h"
#include "pc/rtp_transport.h"
+#include "pc/sctp_transport.h"
#include "pc/session_description.h"
#include "pc/srtp_filter.h"
#include "pc/srtp_transport.h"
@@ -96,8 +99,10 @@
std::unique_ptr<webrtc::RtpTransportInternal> datagram_rtp_transport,
std::unique_ptr<DtlsTransportInternal> rtp_dtls_transport,
std::unique_ptr<DtlsTransportInternal> rtcp_dtls_transport,
+ std::unique_ptr<SctpTransportInternal> sctp_transport,
std::unique_ptr<webrtc::MediaTransportInterface> media_transport,
- std::unique_ptr<webrtc::DatagramTransportInterface> datagram_transport);
+ std::unique_ptr<webrtc::DatagramTransportInterface> datagram_transport,
+ webrtc::DataChannelTransportInterface* data_channel_transport);
~JsepTransport() override;
@@ -215,6 +220,21 @@
return rtp_dtls_transport_;
}
+ rtc::scoped_refptr<webrtc::SctpTransport> SctpTransport() const {
+ rtc::CritScope scope(&accessor_lock_);
+ return sctp_transport_;
+ }
+
+ webrtc::DataChannelTransportInterface* data_channel_transport() const {
+ rtc::CritScope scope(&accessor_lock_);
+ if (composite_data_channel_transport_) {
+ return composite_data_channel_transport_.get();
+ } else if (sctp_data_channel_transport_) {
+ return sctp_data_channel_transport_.get();
+ }
+ return data_channel_transport_;
+ }
+
// Returns media transport, if available.
// Note that media transport is owned by jseptransport and the pointer
// to media transport will becomes invalid after destruction of jseptransport.
@@ -249,7 +269,7 @@
// channel transport. The third parameter (bool) indicates whether the
// negotiation was provisional or final. If true, it is provisional, if
// false, it is final.
- sigslot::signal3<JsepTransport*, webrtc::DataChannelTransportInterface*, bool>
+ sigslot::signal2<JsepTransport*, webrtc::DataChannelTransportInterface*>
SignalDataChannelTransportNegotiated;
// TODO(deadbeef): The methods below are only public for testing. Should make
@@ -375,6 +395,11 @@
rtc::scoped_refptr<webrtc::DtlsTransport> datagram_dtls_transport_
RTC_GUARDED_BY(accessor_lock_);
+ std::unique_ptr<webrtc::DataChannelTransportInterface>
+ sctp_data_channel_transport_ RTC_GUARDED_BY(accessor_lock_);
+ rtc::scoped_refptr<webrtc::SctpTransport> sctp_transport_
+ RTC_GUARDED_BY(accessor_lock_);
+
SrtpFilter sdes_negotiator_ RTC_GUARDED_BY(network_thread_);
RtcpMuxFilter rtcp_mux_negotiator_ RTC_GUARDED_BY(network_thread_);
@@ -392,6 +417,16 @@
std::unique_ptr<webrtc::DatagramTransportInterface> datagram_transport_
RTC_GUARDED_BY(accessor_lock_);
+ // Non-SCTP data channel transport. Set to one of |media_transport_| or
+ // |datagram_transport_| if that transport should be used for data chanels.
+ // Unset if neither should be used for data channels.
+ webrtc::DataChannelTransportInterface* data_channel_transport_
+ RTC_GUARDED_BY(accessor_lock_) = nullptr;
+
+ // Composite data channel transport, used during negotiation.
+ std::unique_ptr<webrtc::CompositeDataChannelTransport>
+ composite_data_channel_transport_ RTC_GUARDED_BY(accessor_lock_);
+
// If |media_transport_| is provided, this variable represents the state of
// media transport.
//
diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc
index c9ed4d5..75f5d9d 100644
--- a/pc/jsep_transport_controller.cc
+++ b/pc/jsep_transport_controller.cc
@@ -175,14 +175,7 @@
if (!jsep_transport) {
return nullptr;
}
-
- if (config_.use_media_transport_for_data_channels) {
- return jsep_transport->media_transport();
- } else if (config_.use_datagram_transport_for_data_channels) {
- return jsep_transport->datagram_transport();
- }
- // Not configured to use a data channel transport.
- return nullptr;
+ return jsep_transport->data_channel_transport();
}
MediaTransportState JsepTransportController::GetMediaTransportState(
@@ -221,6 +214,15 @@
return jsep_transport->RtpDtlsTransport();
}
+rtc::scoped_refptr<SctpTransport> JsepTransportController::GetSctpTransport(
+ const std::string& mid) const {
+ auto jsep_transport = GetJsepTransportForMid(mid);
+ if (!jsep_transport) {
+ return nullptr;
+ }
+ return jsep_transport->SctpTransport();
+}
+
void JsepTransportController::SetIceConfig(const cricket::IceConfig& config) {
if (!network_thread_->IsCurrent()) {
network_thread_->Invoke<void>(RTC_FROM_HERE, [&] { SetIceConfig(config); });
@@ -873,13 +875,13 @@
mid_to_transport_[mid] = jsep_transport;
return config_.transport_observer->OnTransportChanged(
mid, jsep_transport->rtp_transport(), jsep_transport->RtpDtlsTransport(),
- jsep_transport->media_transport(), jsep_transport->datagram_transport(),
- NegotiationState::kInitial);
+ jsep_transport->media_transport(),
+ jsep_transport->data_channel_transport());
}
void JsepTransportController::RemoveTransportForMid(const std::string& mid) {
bool ret = config_.transport_observer->OnTransportChanged(
- mid, nullptr, nullptr, nullptr, nullptr, NegotiationState::kFinal);
+ mid, nullptr, nullptr, nullptr, nullptr);
// Calling OnTransportChanged with nullptr should always succeed, since it is
// only expected to fail when adding media to a transport (not removing).
RTC_DCHECK(ret);
@@ -1229,13 +1231,27 @@
content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
}
+ std::unique_ptr<cricket::SctpTransportInternal> sctp_transport;
+ if (config_.sctp_factory) {
+ sctp_transport =
+ config_.sctp_factory->CreateSctpTransport(rtp_dtls_transport.get());
+ }
+
+ DataChannelTransportInterface* data_channel_transport = nullptr;
+ if (config_.use_datagram_transport_for_data_channels) {
+ data_channel_transport = datagram_transport.get();
+ } else if (config_.use_media_transport_for_data_channels) {
+ data_channel_transport = media_transport.get();
+ }
+
std::unique_ptr<cricket::JsepTransport> jsep_transport =
std::make_unique<cricket::JsepTransport>(
content_info.name, certificate_, std::move(ice), std::move(rtcp_ice),
std::move(unencrypted_rtp_transport), std::move(sdes_transport),
std::move(dtls_srtp_transport), std::move(datagram_rtp_transport),
std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport),
- std::move(media_transport), std::move(datagram_transport));
+ std::move(sctp_transport), std::move(media_transport),
+ std::move(datagram_transport), data_channel_transport);
jsep_transport->rtp_transport()->SignalRtcpPacketReceived.connect(
this, &JsepTransportController::OnRtcpPacketReceived_n);
@@ -1277,8 +1293,7 @@
for (const auto& jsep_transport : jsep_transports_by_name_) {
config_.transport_observer->OnTransportChanged(
- jsep_transport.first, nullptr, nullptr, nullptr, nullptr,
- NegotiationState::kFinal);
+ jsep_transport.first, nullptr, nullptr, nullptr, nullptr);
}
jsep_transports_by_name_.clear();
@@ -1455,15 +1470,12 @@
void JsepTransportController::OnDataChannelTransportNegotiated_n(
cricket::JsepTransport* transport,
- DataChannelTransportInterface* data_channel_transport,
- bool provisional) {
+ DataChannelTransportInterface* data_channel_transport) {
for (auto it : mid_to_transport_) {
if (it.second == transport) {
config_.transport_observer->OnTransportChanged(
it.first, transport->rtp_transport(), transport->RtpDtlsTransport(),
- transport->media_transport(), data_channel_transport,
- provisional ? NegotiationState::kProvisional
- : NegotiationState::kFinal);
+ transport->media_transport(), data_channel_transport);
}
}
}
diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h
index c95a62d..12bcebc 100644
--- a/pc/jsep_transport_controller.h
+++ b/pc/jsep_transport_controller.h
@@ -47,18 +47,6 @@
class JsepTransportController : public sigslot::has_slots<> {
public:
- // State of negotiation for a transport.
- enum class NegotiationState {
- // Transport is in its initial state, not negotiated at all.
- kInitial = 0,
-
- // Transport is negotiated, but not finalized.
- kProvisional = 1,
-
- // Negotiation has completed for this transport.
- kFinal = 2,
- };
-
// Used when the RtpTransport/DtlsTransport of the m= section is changed
// because the section is rejected or BUNDLE is enabled.
class Observer {
@@ -84,8 +72,7 @@
RtpTransportInternal* rtp_transport,
rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport,
- DataChannelTransportInterface* data_channel_transport,
- NegotiationState negotiation_state) = 0;
+ DataChannelTransportInterface* data_channel_transport) = 0;
};
struct Config {
@@ -114,6 +101,9 @@
bool active_reset_srtp_params = false;
RtcEventLog* event_log = nullptr;
+ // Factory for SCTP transports.
+ cricket::SctpTransportInternalFactory* sctp_factory = nullptr;
+
// Whether media transport is used for media.
bool use_media_transport_for_media = false;
@@ -169,6 +159,8 @@
// Gets the externally sharable version of the DtlsTransport.
rtc::scoped_refptr<webrtc::DtlsTransport> LookupDtlsTransportByMid(
const std::string& mid);
+ rtc::scoped_refptr<SctpTransport> GetSctpTransport(
+ const std::string& mid) const;
MediaTransportConfig GetMediaTransportConfig(const std::string& mid) const;
@@ -433,8 +425,7 @@
const cricket::CandidatePairChangeEvent& event);
void OnDataChannelTransportNegotiated_n(
cricket::JsepTransport* transport,
- DataChannelTransportInterface* data_channel_transport,
- bool provisional);
+ DataChannelTransportInterface* data_channel_transport);
void UpdateAggregateStates_n();
diff --git a/pc/jsep_transport_controller_unittest.cc b/pc/jsep_transport_controller_unittest.cc
index ef93898..8461e86 100644
--- a/pc/jsep_transport_controller_unittest.cc
+++ b/pc/jsep_transport_controller_unittest.cc
@@ -311,8 +311,7 @@
RtpTransportInternal* rtp_transport,
rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport,
- DataChannelTransportInterface* data_channel_transport,
- JsepTransportController::NegotiationState negotiation_state) override {
+ DataChannelTransportInterface* data_channel_transport) override {
changed_rtp_transport_by_mid_[mid] = rtp_transport;
if (dtls_transport) {
changed_dtls_transport_by_mid_[mid] = dtls_transport->internal();
diff --git a/pc/jsep_transport_unittest.cc b/pc/jsep_transport_unittest.cc
index 123482c..00f58f6 100644
--- a/pc/jsep_transport_unittest.cc
+++ b/pc/jsep_transport_unittest.cc
@@ -109,8 +109,10 @@
std::move(sdes_transport), std::move(dtls_srtp_transport),
/*datagram_rtp_transport=*/nullptr, std::move(rtp_dtls_transport),
std::move(rtcp_dtls_transport),
+ /*sctp_transport=*/nullptr,
/*media_transport=*/nullptr,
- /*datagram_transport=*/nullptr);
+ /*datagram_transport=*/nullptr,
+ /*data_channel_transport=*/nullptr);
signal_rtcp_mux_active_received_ = false;
jsep_transport->SignalRtcpMuxActive.connect(
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index a80cf5a..4149081 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -610,35 +610,6 @@
return rtc_configuration_parameter;
}
-cricket::DataMessageType ToCricketDataMessageType(DataMessageType type) {
- switch (type) {
- case DataMessageType::kText:
- return cricket::DMT_TEXT;
- case DataMessageType::kBinary:
- return cricket::DMT_BINARY;
- case DataMessageType::kControl:
- return cricket::DMT_CONTROL;
- default:
- return cricket::DMT_NONE;
- }
- return cricket::DMT_NONE;
-}
-
-DataMessageType ToWebrtcDataMessageType(cricket::DataMessageType type) {
- switch (type) {
- case cricket::DMT_TEXT:
- return DataMessageType::kText;
- case cricket::DMT_BINARY:
- return DataMessageType::kBinary;
- case cricket::DMT_CONTROL:
- return DataMessageType::kControl;
- case cricket::DMT_NONE:
- default:
- RTC_NOTREACHED();
- }
- return DataMessageType::kControl;
-}
-
void ReportSimulcastApiVersion(const char* name,
const SessionDescription& session) {
bool has_legacy = false;
@@ -923,6 +894,7 @@
remote_streams_(StreamCollection::Create()),
call_(std::move(call)),
call_ptr_(call_.get()),
+ data_channel_transport_(nullptr),
local_ice_credentials_to_replace_(new LocalIceCredentialsToReplace()) {}
PeerConnection::~PeerConnection() {
@@ -949,7 +921,6 @@
RTC_LOG(LS_INFO) << "Session: " << session_id() << " is destroyed.";
webrtc_session_desc_factory_.reset();
- sctp_invoker_.reset();
sctp_factory_.reset();
data_channel_transport_invoker_.reset();
transport_controller_.reset();
@@ -1146,6 +1117,64 @@
config.media_transport_factory = factory_->media_transport_factory();
}
+ // Obtain a certificate from RTCConfiguration if any were provided (optional).
+ rtc::scoped_refptr<rtc::RTCCertificate> certificate;
+ if (!configuration.certificates.empty()) {
+ // TODO(hbos,torbjorng): Decide on certificate-selection strategy instead of
+ // just picking the first one. The decision should be made based on the DTLS
+ // handshake. The DTLS negotiations need to know about all certificates.
+ certificate = configuration.certificates[0];
+ }
+
+ if (options.disable_encryption) {
+ dtls_enabled_ = false;
+ } else {
+ // Enable DTLS by default if we have an identity store or a certificate.
+ dtls_enabled_ = (dependencies.cert_generator || certificate);
+ // |configuration| can override the default |dtls_enabled_| value.
+ if (configuration.enable_dtls_srtp) {
+ dtls_enabled_ = *(configuration.enable_dtls_srtp);
+ }
+ }
+
+ sctp_factory_ = factory_->CreateSctpTransportInternalFactory();
+
+ if (use_datagram_transport_for_data_channels_) {
+ if (configuration.enable_rtp_data_channel) {
+ RTC_LOG(LS_ERROR) << "enable_rtp_data_channel and "
+ "use_datagram_transport_for_data_channels are "
+ "incompatible and cannot both be set to true";
+ return false;
+ }
+ if (configuration.enable_dtls_srtp && !*configuration.enable_dtls_srtp) {
+ RTC_LOG(LS_INFO) << "Using data channel transport with no fallback";
+ data_channel_type_ = cricket::DCT_DATA_CHANNEL_TRANSPORT;
+ } else {
+ RTC_LOG(LS_INFO) << "Using data channel transport with fallback to SCTP";
+ data_channel_type_ = cricket::DCT_DATA_CHANNEL_TRANSPORT_SCTP;
+ config.sctp_factory = sctp_factory_.get();
+ }
+ } else if (configuration.use_media_transport_for_data_channels) {
+ if (configuration.enable_rtp_data_channel) {
+ RTC_LOG(LS_ERROR) << "enable_rtp_data_channel and "
+ "use_media_transport_for_data_channels are "
+ "incompatible and cannot both be set to true";
+ return false;
+ }
+ data_channel_type_ = cricket::DCT_MEDIA_TRANSPORT;
+ } else if (configuration.enable_rtp_data_channel) {
+ // Enable creation of RTP data channels if the kEnableRtpDataChannels is
+ // set. It takes precendence over the disable_sctp_data_channels
+ // PeerConnectionFactoryInterface::Options.
+ data_channel_type_ = cricket::DCT_RTP;
+ } else {
+ // DTLS has to be enabled to use SCTP.
+ if (!options.disable_sctp_data_channels && dtls_enabled_) {
+ data_channel_type_ = cricket::DCT_SCTP;
+ config.sctp_factory = sctp_factory_.get();
+ }
+ }
+
transport_controller_.reset(new JsepTransportController(
signaling_thread(), network_thread(), port_allocator_.get(),
async_resolver_factory_.get(), config));
@@ -1168,70 +1197,14 @@
transport_controller_->SignalIceCandidatePairChanged.connect(
this, &PeerConnection::OnTransportControllerCandidateChanged);
- sctp_factory_ = factory_->CreateSctpTransportInternalFactory();
-
stats_.reset(new StatsCollector(this));
stats_collector_ = RTCStatsCollector::Create(this);
configuration_ = configuration;
use_media_transport_ = configuration.use_media_transport;
- // Obtain a certificate from RTCConfiguration if any were provided (optional).
- rtc::scoped_refptr<rtc::RTCCertificate> certificate;
- if (!configuration.certificates.empty()) {
- // TODO(hbos,torbjorng): Decide on certificate-selection strategy instead of
- // just picking the first one. The decision should be made based on the DTLS
- // handshake. The DTLS negotiations need to know about all certificates.
- certificate = configuration.certificates[0];
- }
-
transport_controller_->SetIceConfig(ParseIceConfig(configuration));
- if (options.disable_encryption) {
- dtls_enabled_ = false;
- } else {
- // Enable DTLS by default if we have an identity store or a certificate.
- dtls_enabled_ = (dependencies.cert_generator || certificate);
- // |configuration| can override the default |dtls_enabled_| value.
- if (configuration.enable_dtls_srtp) {
- dtls_enabled_ = *(configuration.enable_dtls_srtp);
- }
- }
-
- if (use_datagram_transport_for_data_channels_) {
- if (configuration.enable_rtp_data_channel) {
- RTC_LOG(LS_ERROR) << "enable_rtp_data_channel and "
- "use_datagram_transport_for_data_channels are "
- "incompatible and cannot both be set to true";
- return false;
- }
- if (configuration.enable_dtls_srtp && !*configuration.enable_dtls_srtp) {
- RTC_LOG(LS_INFO) << "Using data channel transport with no fallback";
- data_channel_type_ = cricket::DCT_DATA_CHANNEL_TRANSPORT;
- } else {
- RTC_LOG(LS_INFO) << "Using data channel transport with fallback to SCTP";
- data_channel_type_ = cricket::DCT_DATA_CHANNEL_TRANSPORT_SCTP;
- }
- } else if (configuration.use_media_transport_for_data_channels) {
- if (configuration.enable_rtp_data_channel) {
- RTC_LOG(LS_ERROR) << "enable_rtp_data_channel and "
- "use_media_transport_for_data_channels are "
- "incompatible and cannot both be set to true";
- return false;
- }
- data_channel_type_ = cricket::DCT_MEDIA_TRANSPORT;
- } else if (configuration.enable_rtp_data_channel) {
- // Enable creation of RTP data channels if the kEnableRtpDataChannels is
- // set. It takes precendence over the disable_sctp_data_channels
- // PeerConnectionFactoryInterface::Options.
- data_channel_type_ = cricket::DCT_RTP;
- } else {
- // DTLS has to be enabled to use SCTP.
- if (!options.disable_sctp_data_channels && dtls_enabled_) {
- data_channel_type_ = cricket::DCT_SCTP;
- }
- }
-
video_options_.screencast_min_bitrate_kbps =
configuration.screencast_min_bitrate;
audio_options_.combined_audio_video_bwe =
@@ -3223,7 +3196,7 @@
RTC_LOG(LS_INFO) << "Rejected data channel, mid=" << content.mid();
DestroyDataChannel();
} else {
- if (!rtp_data_channel_ && !sctp_transport_ && !data_channel_transport_) {
+ if (!rtp_data_channel_ && !data_channel_transport_) {
RTC_LOG(LS_INFO) << "Creating data channel, mid=" << content.mid();
if (!CreateDataChannel(content.name)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
@@ -3957,7 +3930,10 @@
rtc::scoped_refptr<SctpTransportInterface> PeerConnection::GetSctpTransport()
const {
RTC_DCHECK_RUN_ON(signaling_thread());
- return sctp_transport_;
+ if (!sctp_mid_) {
+ return nullptr;
+ }
+ return transport_controller_->GetSctpTransport(*sctp_mid_);
}
const SessionDescriptionInterface* PeerConnection::local_description() const {
@@ -5732,19 +5708,18 @@
"SSL Role of the SCTP transport.";
return false;
}
- if (!sctp_transport_ && !data_channel_transport_) {
+ if (!data_channel_transport_) {
RTC_LOG(LS_INFO) << "Non-rejected SCTP m= section is needed to get the "
"SSL Role of the SCTP transport.";
return false;
}
absl::optional<rtc::SSLRole> dtls_role;
- if (sctp_mid_ && sctp_transport_) {
+ if (sctp_mid_) {
dtls_role = transport_controller_->GetDtlsRole(*sctp_mid_);
- } else if (is_caller_) {
- dtls_role = *is_caller_ ? rtc::SSL_SERVER : rtc::SSL_CLIENT;
- }
- if (dtls_role) {
+ if (!dtls_role && is_caller_.has_value()) {
+ dtls_role = *is_caller_ ? rtc::SSL_SERVER : rtc::SSL_CLIENT;
+ }
*role = *dtls_role;
return true;
}
@@ -5870,12 +5845,14 @@
// Need complete offer/answer with an SCTP m= section before starting SCTP,
// according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19
- if (sctp_transport_ && local_description() && remote_description()) {
+ if (sctp_mid_ && local_description() && remote_description()) {
+ rtc::scoped_refptr<SctpTransport> sctp_transport =
+ transport_controller_->GetSctpTransport(*sctp_mid_);
auto local_sctp_description = cricket::GetFirstSctpDataContentDescription(
local_description()->description());
auto remote_sctp_description = cricket::GetFirstSctpDataContentDescription(
remote_description()->description());
- if (local_sctp_description && remote_sctp_description) {
+ if (sctp_transport && local_sctp_description && remote_sctp_description) {
int max_message_size;
// A remote max message size of zero means "any size supported".
// We configure the connection with our own max message size.
@@ -5886,8 +5863,8 @@
std::min(local_sctp_description->max_message_size(),
remote_sctp_description->max_message_size());
}
- sctp_transport_->Start(local_sctp_description->port(),
- remote_sctp_description->port(), max_message_size);
+ sctp_transport->Start(local_sctp_description->port(),
+ remote_sctp_description->port(), max_message_size);
}
}
@@ -5975,7 +5952,7 @@
const rtc::CopyOnWriteBuffer& payload,
cricket::SendDataResult* result) {
RTC_DCHECK_RUN_ON(signaling_thread());
- if (data_channel_transport_ && data_channel_transport_negotiated_) {
+ if (data_channel_transport_) {
SendDataParams send_params;
send_params.type = ToWebrtcDataMessageType(params.type);
send_params.ordered = params.ordered;
@@ -5984,12 +5961,24 @@
} else if (params.max_rtx_ms >= 0) {
send_params.max_rtx_ms = params.max_rtx_ms;
}
- return data_channel_transport_->SendData(params.sid, send_params, payload)
- .ok();
- } else if (sctp_transport_ && sctp_negotiated_) {
- return network_thread()->Invoke<bool>(
- RTC_FROM_HERE, Bind(&cricket::SctpTransportInternal::SendData,
- cricket_sctp_transport(), params, payload, result));
+
+ RTCError error = network_thread()->Invoke<RTCError>(
+ RTC_FROM_HERE, [this, params, send_params, payload] {
+ return data_channel_transport_->SendData(params.sid, send_params,
+ payload);
+ });
+
+ if (error.ok()) {
+ *result = cricket::SendDataResult::SDR_SUCCESS;
+ return true;
+ } else if (error.type() == RTCErrorType::RESOURCE_EXHAUSTED) {
+ // SCTP transport uses RESOURCE_EXHAUSTED when it's blocked.
+ // TODO(mellem): Stop using RTCError here and get rid of the mapping.
+ *result = cricket::SendDataResult::SDR_BLOCK;
+ return false;
+ }
+ *result = cricket::SendDataResult::SDR_ERROR;
+ return false;
} else if (rtp_data_channel_) {
return rtp_data_channel_->SendData(params, payload, result);
}
@@ -5999,7 +5988,7 @@
bool PeerConnection::ConnectDataChannel(DataChannel* webrtc_data_channel) {
RTC_DCHECK_RUN_ON(signaling_thread());
- if (!rtp_data_channel_ && !sctp_transport_ && !data_channel_transport_) {
+ if (!rtp_data_channel_ && !data_channel_transport_) {
// Don't log an error here, because DataChannels are expected to call
// ConnectDataChannel in this state. It's the only way to initially tell
// whether or not the underlying transport is ready.
@@ -6021,22 +6010,12 @@
rtp_data_channel_->SignalDataReceived.connect(webrtc_data_channel,
&DataChannel::OnDataReceived);
}
- if (sctp_transport_) {
- SignalSctpReadyToSendData.connect(webrtc_data_channel,
- &DataChannel::OnChannelReady);
- SignalSctpDataReceived.connect(webrtc_data_channel,
- &DataChannel::OnDataReceived);
- SignalSctpClosingProcedureStartedRemotely.connect(
- webrtc_data_channel, &DataChannel::OnClosingProcedureStartedRemotely);
- SignalSctpClosingProcedureComplete.connect(
- webrtc_data_channel, &DataChannel::OnClosingProcedureComplete);
- }
return true;
}
void PeerConnection::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
RTC_DCHECK_RUN_ON(signaling_thread());
- if (!rtp_data_channel_ && !sctp_transport_ && !data_channel_transport_) {
+ if (!rtp_data_channel_ && !data_channel_transport_) {
RTC_LOG(LS_ERROR)
<< "DisconnectDataChannel called when rtp_data_channel_ and "
"sctp_transport_ are NULL.";
@@ -6052,48 +6031,32 @@
rtp_data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
rtp_data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
}
- if (sctp_transport_) {
- SignalSctpReadyToSendData.disconnect(webrtc_data_channel);
- SignalSctpDataReceived.disconnect(webrtc_data_channel);
- SignalSctpClosingProcedureStartedRemotely.disconnect(webrtc_data_channel);
- SignalSctpClosingProcedureComplete.disconnect(webrtc_data_channel);
- }
}
void PeerConnection::AddSctpDataStream(int sid) {
if (data_channel_transport_) {
- data_channel_transport_->OpenChannel(sid);
+ network_thread()->Invoke<void>(RTC_FROM_HERE, [this, sid] {
+ if (data_channel_transport_) {
+ data_channel_transport_->OpenChannel(sid);
+ }
+ });
}
- if (!sctp_transport_) {
- RTC_LOG(LS_ERROR)
- << "AddSctpDataStream called when sctp_transport_ is NULL.";
- return;
- }
- network_thread()->Invoke<void>(
- RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::OpenStream,
- cricket_sctp_transport(), sid));
}
void PeerConnection::RemoveSctpDataStream(int sid) {
if (data_channel_transport_) {
- data_channel_transport_->CloseChannel(sid);
+ network_thread()->Invoke<void>(RTC_FROM_HERE, [this, sid] {
+ if (data_channel_transport_) {
+ data_channel_transport_->CloseChannel(sid);
+ }
+ });
}
- if (!sctp_transport_) {
- RTC_LOG(LS_ERROR) << "RemoveSctpDataStream called when sctp_transport_ is "
- "NULL.";
- return;
- }
- network_thread()->Invoke<void>(
- RTC_FROM_HERE, rtc::Bind(&cricket::SctpTransportInternal::ResetStream,
- cricket_sctp_transport(), sid));
}
bool PeerConnection::ReadyToSendData() const {
RTC_DCHECK_RUN_ON(signaling_thread());
return (rtp_data_channel_ && rtp_data_channel_->ready_to_send_data()) ||
- (data_channel_transport_ && data_channel_transport_ready_to_send_ &&
- data_channel_transport_negotiated_) ||
- (sctp_ready_to_send_data_ && sctp_negotiated_);
+ (data_channel_transport_ && data_channel_transport_ready_to_send_);
}
void PeerConnection::OnDataReceived(int channel_id,
@@ -6136,10 +6099,8 @@
RTC_FROM_HERE, signaling_thread(), [this] {
RTC_DCHECK_RUN_ON(signaling_thread());
data_channel_transport_ready_to_send_ = true;
- if (data_channel_transport_negotiated_) {
- SignalDataChannelTransportWritable_s(
- data_channel_transport_ready_to_send_);
- }
+ SignalDataChannelTransportWritable_s(
+ data_channel_transport_ready_to_send_);
});
}
@@ -6179,7 +6140,7 @@
transport_names_by_mid[rtp_data_channel_->content_name()] =
rtp_data_channel_->transport_name();
}
- if (sctp_transport_) {
+ if (data_channel_transport_) {
absl::optional<std::string> transport_name = sctp_transport_name();
RTC_DCHECK(transport_name);
transport_names_by_mid[*sctp_mid_] = *transport_name;
@@ -6550,7 +6511,7 @@
const cricket::ContentInfo* data = cricket::GetFirstDataContent(&desc);
if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected &&
- !rtp_data_channel_ && !sctp_transport_ && !data_channel_transport_) {
+ !rtp_data_channel_ && !data_channel_transport_) {
if (!CreateDataChannel(data->name)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to create data channel.");
@@ -6610,32 +6571,21 @@
bool PeerConnection::CreateDataChannel(const std::string& mid) {
switch (data_channel_type_) {
case cricket::DCT_SCTP:
- // Only using SCTP transport. No more setup required. Since SCTP is
- // the only option, it doesn't need to wait for negotiation.
- sctp_negotiated_ = true;
- if (!CreateSctpDataChannel(mid)) {
- return false;
- }
- break;
case cricket::DCT_DATA_CHANNEL_TRANSPORT_SCTP:
- // Setup a data channel transport with SCTP as a fallback. Which one is
- // used will be determined later in negotiation.
- if (!CreateSctpDataChannel(mid)) {
- return false;
- }
- if (!SetupDataChannelTransport(mid)) {
- return false;
- }
- break;
case cricket::DCT_DATA_CHANNEL_TRANSPORT:
case cricket::DCT_MEDIA_TRANSPORT:
- // Using data channel transport without a fallback. It is the only
- // option. Data channel transport doesn't need to be negotiated.
- data_channel_transport_negotiated_ = true;
- if (!SetupDataChannelTransport(mid)) {
+ if (!network_thread()->Invoke<bool>(
+ RTC_FROM_HERE,
+ rtc::Bind(&PeerConnection::SetupDataChannelTransport_n, this,
+ mid))) {
return false;
}
- break;
+
+ // All non-RTP data channels must initialize |sctp_data_channels_|.
+ for (const auto& channel : sctp_data_channels_) {
+ channel->OnTransportChannelCreated();
+ }
+ return true;
case cricket::DCT_RTP:
default:
RtpTransportInternal* rtp_transport = GetRtpTransport(mid);
@@ -6652,36 +6602,7 @@
rtp_data_channel_->SetRtpTransport(rtp_transport);
return true;
}
-
- // All non-RTP data channels must initialize |sctp_data_channels_|.
- for (const auto& channel : sctp_data_channels_) {
- channel->OnTransportChannelCreated();
- }
- return true;
-}
-
-bool PeerConnection::CreateSctpDataChannel(const std::string& mid) {
- if (!sctp_factory_) {
- RTC_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(&PeerConnection::CreateSctpTransport_n, this, mid))) {
- return false;
- }
- return true;
-}
-
-bool PeerConnection::SetupDataChannelTransport(const std::string& mid) {
- if (!network_thread()->Invoke<bool>(
- RTC_FROM_HERE,
- rtc::Bind(&PeerConnection::SetupDataChannelTransport_n, this, mid))) {
- return false;
- }
- return true;
+ return false;
}
Call::Stats PeerConnection::GetCallStats() {
@@ -6697,124 +6618,10 @@
}
}
-bool PeerConnection::CreateSctpTransport_n(const std::string& mid) {
- RTC_DCHECK_RUN_ON(network_thread());
- RTC_DCHECK(sctp_factory_);
- RTC_LOG(LS_INFO) << "Creating SCTP transport for mid=" << mid;
-
- rtc::scoped_refptr<DtlsTransport> webrtc_dtls_transport =
- transport_controller_->LookupDtlsTransportByMid(mid);
- cricket::DtlsTransportInternal* dtls_transport =
- webrtc_dtls_transport->internal();
- RTC_DCHECK(dtls_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());
- cricket_sctp_transport->SignalReadyToSendData.connect(
- this, &PeerConnection::OnSctpTransportReadyToSendData_n);
- 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.
- cricket_sctp_transport->SignalClosingProcedureStartedRemotely.connect(
- this, &PeerConnection::OnSctpClosingProcedureStartedRemotely_n);
- cricket_sctp_transport->SignalClosingProcedureComplete.connect(
- this, &PeerConnection::OnSctpClosingProcedureComplete_n);
- sctp_mid_ = mid;
- 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_RUN_ON(network_thread());
- RTC_LOG(LS_INFO) << "Destroying SCTP transport for mid=" << *sctp_mid_;
-
- sctp_transport_->Clear();
- sctp_transport_ = nullptr;
- // |sctp_mid_| may still be active through a data channel transport. If not,
- // unset it.
- if (!data_channel_transport_) {
- sctp_mid_.reset();
- }
- sctp_invoker_.reset(nullptr);
-}
-
-void PeerConnection::OnSctpTransportReadyToSendData_n() {
- RTC_DCHECK_RUN_ON(network_thread());
- RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP ||
- data_channel_type_ == cricket::DCT_DATA_CHANNEL_TRANSPORT_SCTP);
- // Note: Cannot use rtc::Bind here because it will grab a reference to
- // PeerConnection and potentially cause PeerConnection to live longer than
- // expected. It is safe not to grab a reference since the sctp_invoker_ will
- // be destroyed before PeerConnection is destroyed, and at that point all
- // pending tasks will be cleared.
- sctp_invoker_->AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread(), [this] {
- OnSctpTransportReadyToSendData_s(true);
- });
-}
-
-void PeerConnection::OnSctpTransportReadyToSendData_s(bool ready) {
- RTC_DCHECK_RUN_ON(signaling_thread());
- sctp_ready_to_send_data_ = ready;
- if (sctp_negotiated_) {
- SignalSctpReadyToSendData(ready);
- }
-}
-
-void PeerConnection::OnSctpTransportDataReceived_n(
- const cricket::ReceiveDataParams& params,
- const rtc::CopyOnWriteBuffer& payload) {
- RTC_DCHECK_RUN_ON(network_thread());
- RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP ||
- data_channel_type_ == cricket::DCT_DATA_CHANNEL_TRANSPORT_SCTP);
- // Note: Cannot use rtc::Bind here because it will grab a reference to
- // PeerConnection and potentially cause PeerConnection to live longer than
- // expected. It is safe not to grab a reference since the sctp_invoker_ will
- // be destroyed before PeerConnection is destroyed, and at that point all
- // pending tasks will be cleared.
- sctp_invoker_->AsyncInvoke<void>(
- RTC_FROM_HERE, signaling_thread(), [this, params, payload] {
- OnSctpTransportDataReceived_s(params, payload);
- });
-}
-
-void PeerConnection::OnSctpTransportDataReceived_s(
- const cricket::ReceiveDataParams& params,
- const rtc::CopyOnWriteBuffer& payload) {
- RTC_DCHECK_RUN_ON(signaling_thread());
- if (!HandleOpenMessage_s(params, payload)) {
- SignalSctpDataReceived(params, payload);
- }
-}
-
-void PeerConnection::OnSctpClosingProcedureStartedRemotely_n(int sid) {
- RTC_DCHECK_RUN_ON(network_thread());
- RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP ||
- data_channel_type_ == cricket::DCT_DATA_CHANNEL_TRANSPORT_SCTP);
- sctp_invoker_->AsyncInvoke<void>(
- RTC_FROM_HERE, signaling_thread(),
- rtc::Bind(&sigslot::signal1<int>::operator(),
- &SignalSctpClosingProcedureStartedRemotely, sid));
-}
-
-void PeerConnection::OnSctpClosingProcedureComplete_n(int sid) {
- RTC_DCHECK_RUN_ON(network_thread());
- RTC_DCHECK(data_channel_type_ == cricket::DCT_SCTP ||
- data_channel_type_ == cricket::DCT_DATA_CHANNEL_TRANSPORT_SCTP);
- sctp_invoker_->AsyncInvoke<void>(
- RTC_FROM_HERE, signaling_thread(),
- rtc::Bind(&sigslot::signal1<int>::operator(),
- &SignalSctpClosingProcedureComplete, sid));
-}
-
bool PeerConnection::SetupDataChannelTransport_n(const std::string& mid) {
- data_channel_transport_ = transport_controller_->GetDataChannelTransport(mid);
- if (!data_channel_transport_) {
+ DataChannelTransportInterface* transport =
+ transport_controller_->GetDataChannelTransport(mid);
+ if (!transport) {
RTC_LOG(LS_ERROR)
<< "Data channel transport is not available for data channels, mid="
<< mid;
@@ -6822,14 +6629,20 @@
}
RTC_LOG(LS_INFO) << "Setting up data channel transport for mid=" << mid;
+ data_channel_transport_ = transport;
data_channel_transport_invoker_ = std::make_unique<rtc::AsyncInvoker>();
- data_channel_transport_->SetDataSink(this);
sctp_mid_ = mid;
+
+ // Note: setting the data sink and checking initial state must be done last,
+ // after setting up the data channel. Setting the data sink may trigger
+ // callbacks to PeerConnection which require the transport to be completely
+ // set up (eg. OnReadyToSend()).
+ transport->SetDataSink(this);
return true;
}
void PeerConnection::TeardownDataChannelTransport_n() {
- if (!data_channel_transport_) {
+ if (!sctp_mid_ && !data_channel_transport_) {
return;
}
RTC_LOG(LS_INFO) << "Tearing down data channel transport for mid="
@@ -6837,11 +6650,11 @@
// |sctp_mid_| may still be active through an SCTP transport. If not, unset
// it.
- if (!sctp_transport_) {
- sctp_mid_.reset();
- }
- data_channel_transport_->SetDataSink(nullptr);
+ sctp_mid_.reset();
data_channel_transport_invoker_ = nullptr;
+ if (data_channel_transport_) {
+ data_channel_transport_->SetDataSink(nullptr);
+ }
data_channel_transport_ = nullptr;
}
@@ -7361,7 +7174,7 @@
if (channel) {
return channel->transport_name();
}
- if (sctp_transport_) {
+ if (data_channel_transport_) {
RTC_DCHECK(sctp_mid_);
if (content_name == *sctp_mid_) {
return *sctp_transport_name();
@@ -7396,14 +7209,7 @@
// been destroyed (since it is a subclass of PeerConnection) and using
// rtc::Bind will cause "Pure virtual function called" error to appear.
- if (sctp_transport_) {
- OnDataChannelDestroyed();
- network_thread()->Invoke<void>(RTC_FROM_HERE,
- [this] { DestroySctpTransport_n(); });
- sctp_ready_to_send_data_ = false;
- }
-
- if (data_channel_transport_) {
+ if (sctp_mid_) {
OnDataChannelDestroyed();
network_thread()->Invoke<void>(RTC_FROM_HERE, [this] {
RTC_DCHECK_RUN_ON(network_thread());
@@ -7439,8 +7245,7 @@
RtpTransportInternal* rtp_transport,
rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport,
- DataChannelTransportInterface* data_channel_transport,
- JsepTransportController::NegotiationState negotiation_state) {
+ DataChannelTransportInterface* data_channel_transport) {
RTC_DCHECK_RUN_ON(network_thread());
RTC_DCHECK_RUNS_SERIALIZED(&use_media_transport_race_checker_);
bool ret = true;
@@ -7448,53 +7253,30 @@
if (base_channel) {
ret = base_channel->SetRtpTransport(rtp_transport);
}
- if (sctp_transport_ && mid == sctp_mid_) {
- sctp_transport_->SetDtlsTransport(dtls_transport);
- }
if (use_media_transport_) {
RTC_LOG(LS_ERROR) << "Media transport isn't supported.";
}
- if (mid == sctp_mid_) {
- switch (negotiation_state) {
- case JsepTransportController::NegotiationState::kFinal:
- if (data_channel_transport) {
- if (sctp_transport_) {
- DestroySctpTransport_n();
- }
- } else {
- TeardownDataChannelTransport_n();
- }
- // We also need to mark the remaining transport as ready-to-send.
- RTC_FALLTHROUGH();
- case JsepTransportController::NegotiationState::kProvisional: {
- rtc::AsyncInvoker* invoker = data_channel_transport_invoker_
- ? data_channel_transport_invoker_.get()
- : sctp_invoker_.get();
- if (!invoker) {
- break; // Have neither SCTP nor DataChannelTransport, nothing to do.
- }
- invoker->AsyncInvoke<void>(
- RTC_FROM_HERE, signaling_thread(), [this, data_channel_transport] {
- RTC_DCHECK_RUN_ON(signaling_thread());
- if (data_channel_transport) {
- data_channel_transport_negotiated_ = true;
- if (data_channel_transport_ready_to_send_) {
- SignalDataChannelTransportWritable_s(
- data_channel_transport_ready_to_send_);
- }
- } else {
- sctp_negotiated_ = true;
- if (sctp_ready_to_send_data_) {
- SignalSctpReadyToSendData(sctp_ready_to_send_data_);
- }
- }
- });
- } break;
- case JsepTransportController::NegotiationState::kInitial:
- // Negotiation isn't finished. Nothing to do here.
- break;
+ if (data_channel_transport_ && mid == sctp_mid_ &&
+ data_channel_transport_ != data_channel_transport) {
+ // Changed which data channel transport is used for |sctp_mid_| (eg. now
+ // it's bundled).
+ data_channel_transport_->SetDataSink(nullptr);
+ data_channel_transport_ = data_channel_transport;
+ if (data_channel_transport) {
+ data_channel_transport->SetDataSink(this);
+
+ // There's a new data channel transport. This needs to be signaled to the
+ // |sctp_data_channels_| so that they can reopen and reconnect. This is
+ // necessary when bundling is applied.
+ data_channel_transport_invoker_->AsyncInvoke<void>(
+ RTC_FROM_HERE, signaling_thread(), [this] {
+ RTC_DCHECK_RUN_ON(signaling_thread());
+ for (auto channel : sctp_data_channels_) {
+ channel->OnTransportChannelCreated();
+ }
+ });
}
}
diff --git a/pc/peer_connection.h b/pc/peer_connection.h
index da72687..393beed 100644
--- a/pc/peer_connection.h
+++ b/pc/peer_connection.h
@@ -1024,28 +1024,6 @@
cricket::VideoChannel* CreateVideoChannel(const std::string& mid)
RTC_RUN_ON(signaling_thread());
bool CreateDataChannel(const std::string& mid) RTC_RUN_ON(signaling_thread());
- bool CreateSctpDataChannel(const std::string& mid)
- RTC_RUN_ON(signaling_thread());
- bool SetupDataChannelTransport(const std::string& mid)
- RTC_RUN_ON(signaling_thread());
-
- bool CreateSctpTransport_n(const std::string& mid);
- // For bundling.
- 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 OnSctpClosingProcedureStartedRemotely_n(int sid);
- void OnSctpClosingProcedureComplete_n(int sid);
bool SetupDataChannelTransport_n(const std::string& mid)
RTC_RUN_ON(network_thread());
@@ -1157,8 +1135,7 @@
RtpTransportInternal* rtp_transport,
rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport,
- DataChannelTransportInterface* data_channel_transport,
- JsepTransportController::NegotiationState negotiation_state) override;
+ DataChannelTransportInterface* data_channel_transport) override;
// RtpSenderBase::SetStreamsObserver override.
void OnSetStreams() override;
@@ -1331,13 +1308,6 @@
nullptr; // TODO(bugs.webrtc.org/9987): Accessed on both
// signaling and some other thread.
- cricket::SctpTransportInternal* cricket_sctp_transport() {
- return sctp_transport_->internal();
- }
- rtc::scoped_refptr<SctpTransport>
- sctp_transport_; // TODO(bugs.webrtc.org/9987): Accessed on both
- // signaling and network thread.
-
// |sctp_mid_| is the content name (MID) in SDP.
// Note: this is used as the data channel MID by both SCTP and data channel
// transports. It is set when either transport is initialized and unset when
@@ -1346,56 +1316,25 @@
sctp_mid_; // TODO(bugs.webrtc.org/9987): Accessed on both signaling
// and network thread.
- // Value cached on signaling thread. Only updated when SctpReadyToSendData
- // fires on the signaling thread.
- bool sctp_ready_to_send_data_ RTC_GUARDED_BY(signaling_thread()) = false;
-
- // Whether the use of SCTP has been successfully negotiated.
- bool sctp_negotiated_ RTC_GUARDED_BY(signaling_thread()) = 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_
- RTC_GUARDED_BY(network_thread());
- sigslot::signal1<bool> SignalSctpReadyToSendData
- RTC_GUARDED_BY(signaling_thread());
- sigslot::signal2<const cricket::ReceiveDataParams&,
- const rtc::CopyOnWriteBuffer&>
- SignalSctpDataReceived RTC_GUARDED_BY(signaling_thread());
- sigslot::signal1<int> SignalSctpClosingProcedureStartedRemotely
- RTC_GUARDED_BY(signaling_thread());
- sigslot::signal1<int> SignalSctpClosingProcedureComplete
- RTC_GUARDED_BY(signaling_thread());
-
// Whether this peer is the caller. Set when the local description is applied.
absl::optional<bool> is_caller_ RTC_GUARDED_BY(signaling_thread());
- // Plugin transport used for data channels. Thread-safe.
- DataChannelTransportInterface* data_channel_transport_ =
- nullptr; // TODO(bugs.webrtc.org/9987): Object is thread safe, but
- // pointer accessed on both signaling and network thread.
+ // Plugin transport used for data channels. Pointer may be accessed and
+ // checked from any thread, but the object may only be touched on the
+ // network thread.
+ // TODO(bugs.webrtc.org/9987): Accessed on both signaling and network thread.
+ DataChannelTransportInterface* data_channel_transport_;
// Cached value of whether the data channel transport is ready to send.
bool data_channel_transport_ready_to_send_
RTC_GUARDED_BY(signaling_thread()) = false;
- // Whether the use of the data channel transport has been successfully
- // negotiated.
- bool data_channel_transport_negotiated_ RTC_GUARDED_BY(signaling_thread()) =
- false;
-
// Used to invoke data channel transport signals on the signaling thread.
std::unique_ptr<rtc::AsyncInvoker> data_channel_transport_invoker_
RTC_GUARDED_BY(network_thread());
- // Identical to the signals for SCTP, but from media transport:
+ // Signals from |data_channel_transport_|. These are invoked on the signaling
+ // thread.
sigslot::signal1<bool> SignalDataChannelTransportWritable_s
RTC_GUARDED_BY(signaling_thread());
sigslot::signal2<const cricket::ReceiveDataParams&,
diff --git a/pc/peer_connection_data_channel_unittest.cc b/pc/peer_connection_data_channel_unittest.cc
index 81ef220..a902c76 100644
--- a/pc/peer_connection_data_channel_unittest.cc
+++ b/pc/peer_connection_data_channel_unittest.cc
@@ -239,6 +239,20 @@
EXPECT_FALSE(caller->sctp_transport_factory()->last_fake_sctp_transport());
}
+TEST_P(PeerConnectionDataChannelTest, InternalSctpTransportDeletedOnTeardown) {
+ auto caller = CreatePeerConnectionWithDataChannel();
+
+ ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
+ EXPECT_TRUE(caller->sctp_transport_factory()->last_fake_sctp_transport());
+
+ rtc::scoped_refptr<SctpTransportInterface> sctp_transport =
+ caller->GetInternalPeerConnection()->GetSctpTransport();
+
+ caller.reset();
+ EXPECT_EQ(static_cast<SctpTransport*>(sctp_transport.get())->internal(),
+ nullptr);
+}
+
// Test that sctp_content_name/sctp_transport_name (used for stats) are correct
// before and after BUNDLE is negotiated.
TEST_P(PeerConnectionDataChannelTest, SctpContentAndTransportNameSetCorrectly) {
diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc
index 8798278..33fc9b9 100644
--- a/pc/peer_connection_integrationtest.cc
+++ b/pc/peer_connection_integrationtest.cc
@@ -3641,6 +3641,53 @@
kDefaultTimeout);
}
+// Tests that 'zero-rtt' data channel transports (which are ready-to-send as
+// soon as they're created) work correctly.
+TEST_P(PeerConnectionIntegrationTest, DatagramTransportDataChannelZeroRtt) {
+ PeerConnectionInterface::RTCConfiguration rtc_config;
+ rtc_config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
+ rtc_config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle;
+ rtc_config.use_datagram_transport_for_data_channels = true;
+ rtc_config.enable_dtls_srtp = false; // SDES is required for media transport.
+ ASSERT_TRUE(CreatePeerConnectionWrappersWithConfigAndMediaTransportFactory(
+ rtc_config, rtc_config, loopback_media_transports()->first_factory(),
+ loopback_media_transports()->second_factory()));
+ ConnectFakeSignaling();
+
+ // Ensure that the callee's media transport is ready-to-send immediately.
+ // Note that only the callee can become writable in zero RTTs. The caller
+ // must wait for the callee's answer.
+ loopback_media_transports()->SetSecondStateAfterConnect(
+ webrtc::MediaTransportState::kWritable);
+ loopback_media_transports()->FlushAsyncInvokes();
+
+ // Expect that data channel created on caller side will show up for callee as
+ // well.
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+ loopback_media_transports()->SetFirstState(
+ webrtc::MediaTransportState::kWritable);
+ loopback_media_transports()->FlushAsyncInvokes();
+
+ // Caller data channel should already exist (it created one). Callee data
+ // channel may not exist yet, since negotiation happens in-band, not in SDP.
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ // Ensure data can be sent in both directions.
+ std::string data = "hello world";
+ caller()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ callee()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(),
+ kDefaultTimeout);
+}
+
// Ensures that when the callee closes a datagram transport data channel, the
// closing procedure results in the data channel being closed for the caller
// as well.
@@ -3799,6 +3846,53 @@
kDefaultTimeout);
}
+// Tests that 'zero-rtt' data channel transports (which are ready-to-send as
+// soon as they're created) work correctly.
+TEST_P(PeerConnectionIntegrationTest, MediaTransportDataChannelZeroRtt) {
+ PeerConnectionInterface::RTCConfiguration rtc_config;
+ rtc_config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
+ rtc_config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle;
+ rtc_config.use_media_transport_for_data_channels = true;
+ rtc_config.enable_dtls_srtp = false; // SDES is required for media transport.
+ ASSERT_TRUE(CreatePeerConnectionWrappersWithConfigAndMediaTransportFactory(
+ rtc_config, rtc_config, loopback_media_transports()->first_factory(),
+ loopback_media_transports()->second_factory()));
+ ConnectFakeSignaling();
+
+ // Ensure that the callee's media transport is ready-to-send immediately.
+ // Note that only the callee can become writable in zero RTTs. The caller
+ // must wait for the callee's answer.
+ loopback_media_transports()->SetSecondStateAfterConnect(
+ webrtc::MediaTransportState::kWritable);
+ loopback_media_transports()->FlushAsyncInvokes();
+
+ // Expect that data channel created on caller side will show up for callee as
+ // well.
+ caller()->CreateDataChannel();
+ caller()->CreateAndSetAndSignalOffer();
+ ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+
+ loopback_media_transports()->SetFirstState(
+ webrtc::MediaTransportState::kWritable);
+ loopback_media_transports()->FlushAsyncInvokes();
+
+ // Caller data channel should already exist (it created one). Callee data
+ // channel may not exist yet, since negotiation happens in-band, not in SDP.
+ ASSERT_NE(nullptr, caller()->data_channel());
+ ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout);
+ EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout);
+ EXPECT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+
+ // Ensure data can be sent in both directions.
+ std::string data = "hello world";
+ caller()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, callee()->data_observer()->last_message(),
+ kDefaultTimeout);
+ callee()->data_channel()->Send(DataBuffer(data));
+ EXPECT_EQ_WAIT(data, caller()->data_observer()->last_message(),
+ kDefaultTimeout);
+}
+
// Ensure that when the callee closes a media transport data channel, the
// closing procedure results in the data channel being closed for the caller
// as well.
diff --git a/pc/sctp_data_channel_transport.cc b/pc/sctp_data_channel_transport.cc
new file mode 100644
index 0000000..d1505f3
--- /dev/null
+++ b/pc/sctp_data_channel_transport.cc
@@ -0,0 +1,112 @@
+/*
+ * 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_data_channel_transport.h"
+#include "pc/sctp_utils.h"
+
+namespace webrtc {
+
+SctpDataChannelTransport::SctpDataChannelTransport(
+ cricket::SctpTransportInternal* sctp_transport)
+ : sctp_transport_(sctp_transport) {
+ sctp_transport_->SignalReadyToSendData.connect(
+ this, &SctpDataChannelTransport::OnReadyToSendData);
+ sctp_transport_->SignalDataReceived.connect(
+ this, &SctpDataChannelTransport::OnDataReceived);
+ sctp_transport_->SignalClosingProcedureStartedRemotely.connect(
+ this, &SctpDataChannelTransport::OnClosingProcedureStartedRemotely);
+ sctp_transport_->SignalClosingProcedureComplete.connect(
+ this, &SctpDataChannelTransport::OnClosingProcedureComplete);
+}
+
+RTCError SctpDataChannelTransport::OpenChannel(int channel_id) {
+ sctp_transport_->OpenStream(channel_id);
+ return RTCError::OK();
+}
+
+RTCError SctpDataChannelTransport::SendData(
+ int channel_id,
+ const SendDataParams& params,
+ const rtc::CopyOnWriteBuffer& buffer) {
+ // Map webrtc::SendDataParams to cricket::SendDataParams.
+ // TODO(mellem): See about unifying these structs.
+ cricket::SendDataParams sd_params;
+ sd_params.sid = channel_id;
+ sd_params.type = ToCricketDataMessageType(params.type);
+ sd_params.ordered = params.ordered;
+ sd_params.reliable = !(params.max_rtx_count || params.max_rtx_ms);
+ sd_params.max_rtx_count = params.max_rtx_count.value_or(-1);
+ sd_params.max_rtx_ms = params.max_rtx_ms.value_or(-1);
+
+ cricket::SendDataResult result;
+ sctp_transport_->SendData(sd_params, buffer, &result);
+
+ // TODO(mellem): See about changing the interfaces to not require mapping
+ // SendDataResult to RTCError and back again.
+ switch (result) {
+ case cricket::SendDataResult::SDR_SUCCESS:
+ return RTCError::OK();
+ case cricket::SendDataResult::SDR_BLOCK: {
+ // Send buffer is full.
+ ready_to_send_ = false;
+ return RTCError(RTCErrorType::RESOURCE_EXHAUSTED);
+ }
+ case cricket::SendDataResult::SDR_ERROR:
+ return RTCError(RTCErrorType::NETWORK_ERROR);
+ }
+ return RTCError(RTCErrorType::NETWORK_ERROR);
+}
+
+RTCError SctpDataChannelTransport::CloseChannel(int channel_id) {
+ sctp_transport_->ResetStream(channel_id);
+ return RTCError::OK();
+}
+
+void SctpDataChannelTransport::SetDataSink(DataChannelSink* sink) {
+ sink_ = sink;
+ if (sink_ && ready_to_send_) {
+ sink_->OnReadyToSend();
+ }
+}
+
+bool SctpDataChannelTransport::IsReadyToSend() const {
+ return ready_to_send_;
+}
+
+void SctpDataChannelTransport::OnReadyToSendData() {
+ ready_to_send_ = true;
+ if (sink_) {
+ sink_->OnReadyToSend();
+ }
+}
+
+void SctpDataChannelTransport::OnDataReceived(
+ const cricket::ReceiveDataParams& params,
+ const rtc::CopyOnWriteBuffer& buffer) {
+ if (sink_) {
+ sink_->OnDataReceived(params.sid, ToWebrtcDataMessageType(params.type),
+ buffer);
+ }
+}
+
+void SctpDataChannelTransport::OnClosingProcedureStartedRemotely(
+ int channel_id) {
+ if (sink_) {
+ sink_->OnChannelClosing(channel_id);
+ }
+}
+
+void SctpDataChannelTransport::OnClosingProcedureComplete(int channel_id) {
+ if (sink_) {
+ sink_->OnChannelClosed(channel_id);
+ }
+}
+
+} // namespace webrtc
diff --git a/pc/sctp_data_channel_transport.h b/pc/sctp_data_channel_transport.h
new file mode 100644
index 0000000..2d54be9
--- /dev/null
+++ b/pc/sctp_data_channel_transport.h
@@ -0,0 +1,50 @@
+/*
+ * 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_DATA_CHANNEL_TRANSPORT_H_
+#define PC_SCTP_DATA_CHANNEL_TRANSPORT_H_
+
+#include "api/data_channel_transport_interface.h"
+#include "media/sctp/sctp_transport_internal.h"
+#include "rtc_base/third_party/sigslot/sigslot.h"
+
+namespace webrtc {
+
+// SCTP implementation of DataChannelTransportInterface.
+class SctpDataChannelTransport : public DataChannelTransportInterface,
+ public sigslot::has_slots<> {
+ public:
+ explicit SctpDataChannelTransport(
+ cricket::SctpTransportInternal* sctp_transport);
+
+ RTCError OpenChannel(int channel_id) override;
+ RTCError SendData(int channel_id,
+ const SendDataParams& params,
+ const rtc::CopyOnWriteBuffer& buffer) override;
+ RTCError CloseChannel(int channel_id) override;
+ void SetDataSink(DataChannelSink* sink) override;
+ bool IsReadyToSend() const override;
+
+ private:
+ void OnReadyToSendData();
+ void OnDataReceived(const cricket::ReceiveDataParams& params,
+ const rtc::CopyOnWriteBuffer& buffer);
+ void OnClosingProcedureStartedRemotely(int channel_id);
+ void OnClosingProcedureComplete(int channel_id);
+
+ cricket::SctpTransportInternal* const sctp_transport_;
+
+ DataChannelSink* sink_ = nullptr;
+ bool ready_to_send_ = false;
+};
+
+} // namespace webrtc
+
+#endif // PC_SCTP_DATA_CHANNEL_TRANSPORT_H_
diff --git a/pc/sctp_utils.cc b/pc/sctp_utils.cc
index 7b67fc1..129ee07 100644
--- a/pc/sctp_utils.cc
+++ b/pc/sctp_utils.cc
@@ -189,4 +189,33 @@
payload->SetData(&data, sizeof(data));
}
+cricket::DataMessageType ToCricketDataMessageType(DataMessageType type) {
+ switch (type) {
+ case DataMessageType::kText:
+ return cricket::DMT_TEXT;
+ case DataMessageType::kBinary:
+ return cricket::DMT_BINARY;
+ case DataMessageType::kControl:
+ return cricket::DMT_CONTROL;
+ default:
+ return cricket::DMT_NONE;
+ }
+ return cricket::DMT_NONE;
+}
+
+DataMessageType ToWebrtcDataMessageType(cricket::DataMessageType type) {
+ switch (type) {
+ case cricket::DMT_TEXT:
+ return DataMessageType::kText;
+ case cricket::DMT_BINARY:
+ return DataMessageType::kBinary;
+ case cricket::DMT_CONTROL:
+ return DataMessageType::kControl;
+ case cricket::DMT_NONE:
+ default:
+ RTC_NOTREACHED();
+ }
+ return DataMessageType::kControl;
+}
+
} // namespace webrtc
diff --git a/pc/sctp_utils.h b/pc/sctp_utils.h
index 468c960..6d41eb2 100644
--- a/pc/sctp_utils.h
+++ b/pc/sctp_utils.h
@@ -14,6 +14,8 @@
#include <string>
#include "api/data_channel_interface.h"
+#include "api/data_channel_transport_interface.h"
+#include "media/base/media_channel.h"
namespace rtc {
class CopyOnWriteBuffer;
@@ -36,6 +38,11 @@
rtc::CopyOnWriteBuffer* payload);
void WriteDataChannelOpenAckMessage(rtc::CopyOnWriteBuffer* payload);
+
+cricket::DataMessageType ToCricketDataMessageType(DataMessageType type);
+
+DataMessageType ToWebrtcDataMessageType(cricket::DataMessageType type);
+
} // namespace webrtc
#endif // PC_SCTP_UTILS_H_
diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn
index 2b272aa..7e4e545 100644
--- a/test/fuzzers/BUILD.gn
+++ b/test/fuzzers/BUILD.gn
@@ -608,7 +608,7 @@
deps = [
"../../api:libjingle_peerconnection_api",
"../../pc:libjingle_peerconnection",
- "../../pc:peerconnection",
+ "../../pc:rtc_pc_base",
"../../rtc_base:rtc_base_approved",
]
}
diff --git a/test/peer_scenario/scenario_connection.cc b/test/peer_scenario/scenario_connection.cc
index 6c51397..2c0ed36 100644
--- a/test/peer_scenario/scenario_connection.cc
+++ b/test/peer_scenario/scenario_connection.cc
@@ -45,8 +45,7 @@
RtpTransportInternal* rtp_transport,
rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport,
- DataChannelTransportInterface* data_channel_transport,
- JsepTransportController::NegotiationState negotiation_state) override;
+ DataChannelTransportInterface* data_channel_transport) override;
void OnRtpPacket(const RtpPacketReceived& packet) override;
void OnCandidates(const std::string& mid,
@@ -209,8 +208,7 @@
RtpTransportInternal* rtp_transport,
rtc::scoped_refptr<DtlsTransport> dtls_transport,
MediaTransportInterface* media_transport,
- DataChannelTransportInterface* data_channel_transport,
- JsepTransportController::NegotiationState negotiation_state) {
+ DataChannelTransportInterface* data_channel_transport) {
RTC_DCHECK_RUN_ON(network_thread_);
if (rtp_transport == nullptr) {
rtp_transport_->UnregisterRtpDemuxerSink(this);