Changes to enable use of DatagramTransport as a data channel transport.
PeerConnection now has a new setting in RTCConfiguration to enable use of
datagram transport for data channels. There is also a corresponding field
trial, which has both a kill-switch and a way to change the default value.
PeerConnection's interaction with MediaTransport for data channels has been
refactored to work with DataChannelTransportInterface instead.
Adds a DataChannelState and OnStateChanged() to the DataChannelSink
callbacks. This allows PeerConnection to listen to the data channel's
state directly, instead of indirectly by monitoring media transport
state. This is necessary to enable use of non-media-transport (eg.
datagram transport) data channel transports.
For now, PeerConnection watches the state through MediaTransport as well.
This will persist until MediaTransport implements the new callback.
Datagram transport use is negotiated. As such, an offer that requests to use
datagram transport for data channels may be rejected by the answerer. If the
offer includes DTLS, the data channels will be negotiated as SCTP/DTLS data
channels with an extra x-opaque parameter for datagram transport. If the
opaque parameter is rejected (by an answerer without datagram support), the
offerer may fall back to SCTP.
If DTLS is not enabled, there is no viable fallback. In this case, the data
channels are negotiated as media transport data channels. If the receiver does
not understand the x-opaque line, it will reject these data channels, and the
offerer's data channels will be closed.
Bug: webrtc:9719
Change-Id: Ic1bf3664c4bcf9d754482df59897f5f72fe68fcc
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/147702
Commit-Queue: Bjorn Mellem <mellem@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28932}
diff --git a/api/data_channel_transport_interface.cc b/api/data_channel_transport_interface.cc
index e5d8fdd..d9947e2 100644
--- a/api/data_channel_transport_interface.cc
+++ b/api/data_channel_transport_interface.cc
@@ -31,4 +31,10 @@
void DataChannelTransportInterface::SetDataSink(DataChannelSink* /*sink*/) {}
+bool DataChannelTransportInterface::IsReadyToSend() const {
+ return false;
+}
+
+void DataChannelSink::OnReadyToSend() {}
+
} // namespace webrtc
diff --git a/api/data_channel_transport_interface.h b/api/data_channel_transport_interface.h
index a63abe0..a6825f6 100644
--- a/api/data_channel_transport_interface.h
+++ b/api/data_channel_transport_interface.h
@@ -77,6 +77,14 @@
// procedure. Closing channels become closed after all pending data has been
// transmitted.
virtual void OnChannelClosed(int channel_id) = 0;
+
+ // Callback issued when the data channel becomes ready to send.
+ // This callback will be issued immediately when the data channel sink is
+ // registered if the transport is ready at that time. This callback may be
+ // invoked again following send errors (eg. due to the transport being
+ // temporarily blocked or unavailable).
+ // TODO(mellem): Make pure virtual when downstream sinks override this.
+ virtual void OnReadyToSend();
};
// Transport for data channels.
@@ -104,6 +112,12 @@
// transport is destroyed, the sink must be unregistered by setting it to
// nullptr.
virtual void SetDataSink(DataChannelSink* sink);
+
+ // Returns whether this data channel transport is ready to send.
+ // Note: the default implementation always returns false (as it assumes no one
+ // has implemented the interface). This default implementation is temporary.
+ // TODO(mellem): Change this to pure virtual.
+ virtual bool IsReadyToSend() const;
};
} // namespace webrtc
diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h
index c6f68da..4ade0b3 100644
--- a/api/peer_connection_interface.h
+++ b/api/peer_connection_interface.h
@@ -631,6 +631,12 @@
// MediaTransportFactory wasn't provided.
absl::optional<bool> use_datagram_transport;
+ // If MediaTransportFactory is provided in PeerConnectionFactory, this flag
+ // informs PeerConnection that it should use the DatagramTransport's
+ // implementation of DataChannelTransportInterface for data channels instead
+ // of SCTP-DTLS.
+ absl::optional<bool> use_datagram_transport_for_data_channels;
+
// Defines advanced optional cryptographic settings related to SRTP and
// frame encryption for native WebRTC. Setting this will overwrite any
// settings set in PeerConnectionFactory (which is deprecated).
diff --git a/api/test/fake_datagram_transport.h b/api/test/fake_datagram_transport.h
index a73a7e8..9a1ddef 100644
--- a/api/test/fake_datagram_transport.h
+++ b/api/test/fake_datagram_transport.h
@@ -55,6 +55,8 @@
void SetDatagramSink(DatagramSinkInterface* sink) override {}
+ bool IsReadyToSend() const override { return false; }
+
std::string GetTransportParameters() const override {
if (settings_.remote_transport_parameters) {
return *settings_.remote_transport_parameters;
diff --git a/api/test/fake_media_transport.h b/api/test/fake_media_transport.h
index 38b94c9..025965b 100644
--- a/api/test/fake_media_transport.h
+++ b/api/test/fake_media_transport.h
@@ -74,6 +74,8 @@
void SetDataSink(DataChannelSink* sink) override {}
+ bool IsReadyToSend() const override { return false; }
+
void SetMediaTransportStateCallback(
MediaTransportStateCallback* callback) override {
state_callback_ = callback;
diff --git a/api/test/loopback_media_transport.cc b/api/test/loopback_media_transport.cc
index 4e8fb0e..8c7f240 100644
--- a/api/test/loopback_media_transport.cc
+++ b/api/test/loopback_media_transport.cc
@@ -86,6 +86,8 @@
wrapped_->SetDataSink(sink);
}
+ bool IsReadyToSend() const override { return wrapped_->IsReadyToSend(); }
+
void SetAllocatedBitrateLimits(
const MediaTransportAllocatedBitrateLimits& limits) override {}
@@ -97,11 +99,74 @@
MediaTransportInterface* wrapped_;
};
+class WrapperDatagramTransport : public DatagramTransportInterface {
+ public:
+ explicit WrapperDatagramTransport(DatagramTransportInterface* wrapped)
+ : wrapped_(wrapped) {}
+
+ // Datagram transport overrides.
+ void Connect(rtc::PacketTransportInternal* packet_transport) override {
+ return wrapped_->Connect(packet_transport);
+ }
+
+ CongestionControlInterface* congestion_control() override {
+ return wrapped_->congestion_control();
+ }
+
+ void SetTransportStateCallback(
+ MediaTransportStateCallback* callback) override {
+ return wrapped_->SetTransportStateCallback(callback);
+ }
+
+ RTCError SendDatagram(rtc::ArrayView<const uint8_t> data,
+ DatagramId datagram_id) override {
+ return wrapped_->SendDatagram(data, datagram_id);
+ }
+
+ size_t GetLargestDatagramSize() const override {
+ return wrapped_->GetLargestDatagramSize();
+ }
+
+ void SetDatagramSink(DatagramSinkInterface* sink) override {
+ return wrapped_->SetDatagramSink(sink);
+ }
+
+ std::string GetTransportParameters() const override {
+ return wrapped_->GetTransportParameters();
+ }
+
+ // Data channel overrides.
+ RTCError OpenChannel(int channel_id) override {
+ return wrapped_->OpenChannel(channel_id);
+ }
+
+ RTCError SendData(int channel_id,
+ const SendDataParams& params,
+ const rtc::CopyOnWriteBuffer& buffer) override {
+ return wrapped_->SendData(channel_id, params, buffer);
+ }
+
+ RTCError CloseChannel(int channel_id) override {
+ return wrapped_->CloseChannel(channel_id);
+ }
+
+ void SetDataSink(DataChannelSink* sink) override {
+ wrapped_->SetDataSink(sink);
+ }
+
+ bool IsReadyToSend() const override { return wrapped_->IsReadyToSend(); }
+
+ private:
+ DatagramTransportInterface* wrapped_;
+};
+
} // namespace
WrapperMediaTransportFactory::WrapperMediaTransportFactory(
- MediaTransportInterface* wrapped)
- : wrapped_(wrapped) {}
+ MediaTransportInterface* wrapped_media_transport,
+ DatagramTransportInterface* wrapped_datagram_transport)
+ : wrapped_media_transport_(wrapped_media_transport),
+ wrapped_datagram_transport_(wrapped_datagram_transport) {}
WrapperMediaTransportFactory::WrapperMediaTransportFactory(
MediaTransportFactory* wrapped)
@@ -117,7 +182,19 @@
return wrapped_factory_->CreateMediaTransport(packet_transport,
network_thread, settings);
}
- return {absl::make_unique<WrapperMediaTransport>(wrapped_)};
+ return {absl::make_unique<WrapperMediaTransport>(wrapped_media_transport_)};
+}
+
+RTCErrorOr<std::unique_ptr<DatagramTransportInterface>>
+WrapperMediaTransportFactory::CreateDatagramTransport(
+ rtc::Thread* network_thread,
+ const MediaTransportSettings& settings) {
+ created_transport_count_++;
+ if (wrapped_factory_) {
+ return wrapped_factory_->CreateDatagramTransport(network_thread, settings);
+ }
+ return {
+ absl::make_unique<WrapperDatagramTransport>(wrapped_datagram_transport_)};
}
std::string WrapperMediaTransportFactory::GetTransportName() const {
@@ -139,21 +216,41 @@
if (wrapped_factory_) {
return wrapped_factory_->CreateMediaTransport(network_thread, settings);
}
- return {absl::make_unique<WrapperMediaTransport>(wrapped_)};
+ return {absl::make_unique<WrapperMediaTransport>(wrapped_media_transport_)};
}
MediaTransportPair::MediaTransportPair(rtc::Thread* thread)
- : first_(thread, &second_),
- second_(thread, &first_),
- first_factory_(&first_),
- second_factory_(&second_) {}
+ : first_(thread),
+ second_(thread),
+ first_datagram_transport_(thread),
+ second_datagram_transport_(thread),
+ first_factory_(&first_, &first_datagram_transport_),
+ second_factory_(&second_, &second_datagram_transport_) {
+ first_.Connect(&second_);
+ second_.Connect(&first_);
+ first_datagram_transport_.Connect(&second_datagram_transport_);
+ second_datagram_transport_.Connect(&first_datagram_transport_);
+}
MediaTransportPair::~MediaTransportPair() = default;
+MediaTransportPair::LoopbackDataChannelTransport::LoopbackDataChannelTransport(
+ rtc::Thread* thread)
+ : thread_(thread) {}
+
+MediaTransportPair::LoopbackDataChannelTransport::
+ ~LoopbackDataChannelTransport() {
+ RTC_CHECK(data_sink_ == nullptr);
+}
+
+void MediaTransportPair::LoopbackDataChannelTransport::Connect(
+ LoopbackDataChannelTransport* other) {
+ other_ = other;
+}
+
MediaTransportPair::LoopbackMediaTransport::LoopbackMediaTransport(
- rtc::Thread* thread,
- LoopbackMediaTransport* other)
- : thread_(thread), other_(other) {
+ rtc::Thread* thread)
+ : dc_transport_(thread), thread_(thread), other_(nullptr) {
RTC_LOG(LS_INFO) << "LoopbackMediaTransport";
}
@@ -162,11 +259,19 @@
rtc::CritScope lock(&sink_lock_);
RTC_CHECK(audio_sink_ == nullptr);
RTC_CHECK(video_sink_ == nullptr);
- RTC_CHECK(data_sink_ == nullptr);
RTC_CHECK(target_transfer_rate_observers_.empty());
RTC_CHECK(rtt_observers_.empty());
}
+void MediaTransportPair::LoopbackMediaTransport::Connect(
+ LoopbackMediaTransport* other) {
+ other_ = other;
+ dc_transport_.Connect(&other->dc_transport_);
+}
+
+void MediaTransportPair::LoopbackMediaTransport::Connect(
+ rtc::PacketTransportInternal* packet_transport) {}
+
absl::optional<std::string>
MediaTransportPair::LoopbackMediaTransport::GetTransportParametersOffer()
const {
@@ -322,6 +427,12 @@
RTCError MediaTransportPair::LoopbackMediaTransport::OpenChannel(
int channel_id) {
// No-op. No need to open channels for the loopback.
+ return dc_transport_.OpenChannel(channel_id);
+}
+
+RTCError MediaTransportPair::LoopbackDataChannelTransport::OpenChannel(
+ int channel_id) {
+ // No-op. No need to open channels for the loopback.
return RTCError::OK();
}
@@ -329,6 +440,13 @@
int channel_id,
const SendDataParams& params,
const rtc::CopyOnWriteBuffer& buffer) {
+ return dc_transport_.SendData(channel_id, params, buffer);
+}
+
+RTCError MediaTransportPair::LoopbackDataChannelTransport::SendData(
+ int channel_id,
+ const SendDataParams& params,
+ const rtc::CopyOnWriteBuffer& buffer) {
invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_,
[this, channel_id, params, buffer] {
other_->OnData(channel_id, params.type, buffer);
@@ -338,6 +456,11 @@
RTCError MediaTransportPair::LoopbackMediaTransport::CloseChannel(
int channel_id) {
+ return dc_transport_.CloseChannel(channel_id);
+}
+
+RTCError MediaTransportPair::LoopbackDataChannelTransport::CloseChannel(
+ int channel_id) {
invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this, channel_id] {
other_->OnRemoteCloseChannel(channel_id);
rtc::CritScope lock(&sink_lock_);
@@ -350,9 +473,27 @@
void MediaTransportPair::LoopbackMediaTransport::SetDataSink(
DataChannelSink* sink) {
+ dc_transport_.SetDataSink(sink);
+}
+
+bool MediaTransportPair::LoopbackMediaTransport::IsReadyToSend() const {
+ return dc_transport_.IsReadyToSend();
+}
+
+void MediaTransportPair::LoopbackDataChannelTransport::SetDataSink(
+ DataChannelSink* sink) {
rtc::CritScope lock(&sink_lock_);
data_sink_ = sink;
+ if (data_sink_ && ready_to_send_) {
+ data_sink_->OnReadyToSend();
+ }
}
+
+bool MediaTransportPair::LoopbackDataChannelTransport::IsReadyToSend() const {
+ rtc::CritScope lock(&sink_lock_);
+ return ready_to_send_;
+}
+
void MediaTransportPair::LoopbackMediaTransport::SetState(
MediaTransportState state) {
invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this, state] {
@@ -364,6 +505,11 @@
void MediaTransportPair::LoopbackMediaTransport::FlushAsyncInvokes() {
invoker_.Flush(thread_);
+ dc_transport_.FlushAsyncInvokes();
+}
+
+void MediaTransportPair::LoopbackDataChannelTransport::FlushAsyncInvokes() {
+ invoker_.Flush(thread_);
}
MediaTransportPair::Stats
@@ -402,7 +548,7 @@
}
}
-void MediaTransportPair::LoopbackMediaTransport::OnData(
+void MediaTransportPair::LoopbackDataChannelTransport::OnData(
int channel_id,
DataMessageType type,
const rtc::CopyOnWriteBuffer& buffer) {
@@ -420,7 +566,7 @@
}
}
-void MediaTransportPair::LoopbackMediaTransport::OnRemoteCloseChannel(
+void MediaTransportPair::LoopbackDataChannelTransport::OnRemoteCloseChannel(
int channel_id) {
rtc::CritScope lock(&sink_lock_);
if (data_sink_) {
@@ -434,9 +580,97 @@
if (state_callback_) {
state_callback_->OnStateChanged(state_);
}
+
+ dc_transport_.OnReadyToSend(state_ == MediaTransportState::kWritable);
+}
+
+void MediaTransportPair::LoopbackDataChannelTransport::OnReadyToSend(
+ bool ready_to_send) {
+ invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this, ready_to_send] {
+ rtc::CritScope lock(&sink_lock_);
+ ready_to_send_ = ready_to_send;
+ // Propagate state to data channel sink, if present.
+ if (data_sink_ && ready_to_send_) {
+ data_sink_->OnReadyToSend();
+ }
+ });
}
void MediaTransportPair::LoopbackMediaTransport::SetAllocatedBitrateLimits(
const MediaTransportAllocatedBitrateLimits& limits) {}
+MediaTransportPair::LoopbackDatagramTransport::LoopbackDatagramTransport(
+ rtc::Thread* thread)
+ : dc_transport_(thread) {}
+
+void MediaTransportPair::LoopbackDatagramTransport::Connect(
+ LoopbackDatagramTransport* other) {
+ dc_transport_.Connect(&other->dc_transport_);
+}
+
+void MediaTransportPair::LoopbackDatagramTransport::Connect(
+ rtc::PacketTransportInternal* packet_transport) {}
+
+CongestionControlInterface*
+MediaTransportPair::LoopbackDatagramTransport::congestion_control() {
+ return nullptr;
+}
+
+void MediaTransportPair::LoopbackDatagramTransport::SetTransportStateCallback(
+ MediaTransportStateCallback* callback) {}
+
+RTCError MediaTransportPair::LoopbackDatagramTransport::SendDatagram(
+ rtc::ArrayView<const uint8_t> data,
+ DatagramId datagram_id) {
+ return RTCError::OK();
+}
+
+size_t MediaTransportPair::LoopbackDatagramTransport::GetLargestDatagramSize()
+ const {
+ return 0;
+}
+
+void MediaTransportPair::LoopbackDatagramTransport::SetDatagramSink(
+ DatagramSinkInterface* sink) {}
+
+std::string
+MediaTransportPair::LoopbackDatagramTransport::GetTransportParameters() const {
+ return transport_parameters_;
+}
+
+RTCError MediaTransportPair::LoopbackDatagramTransport::OpenChannel(
+ int channel_id) {
+ return dc_transport_.OpenChannel(channel_id);
+}
+
+RTCError MediaTransportPair::LoopbackDatagramTransport::SendData(
+ int channel_id,
+ const SendDataParams& params,
+ const rtc::CopyOnWriteBuffer& buffer) {
+ return dc_transport_.SendData(channel_id, params, buffer);
+}
+
+RTCError MediaTransportPair::LoopbackDatagramTransport::CloseChannel(
+ int channel_id) {
+ return dc_transport_.CloseChannel(channel_id);
+}
+
+void MediaTransportPair::LoopbackDatagramTransport::SetDataSink(
+ DataChannelSink* sink) {
+ dc_transport_.SetDataSink(sink);
+}
+
+bool MediaTransportPair::LoopbackDatagramTransport::IsReadyToSend() const {
+ return dc_transport_.IsReadyToSend();
+}
+
+void MediaTransportPair::LoopbackDatagramTransport::SetState(
+ MediaTransportState state) {
+ dc_transport_.OnReadyToSend(state == MediaTransportState::kWritable);
+}
+
+void MediaTransportPair::LoopbackDatagramTransport::FlushAsyncInvokes() {
+ dc_transport_.FlushAsyncInvokes();
+}
+
} // namespace webrtc
diff --git a/api/test/loopback_media_transport.h b/api/test/loopback_media_transport.h
index 2972b49..cc66d62 100644
--- a/api/test/loopback_media_transport.h
+++ b/api/test/loopback_media_transport.h
@@ -17,6 +17,7 @@
#include <vector>
#include "absl/memory/memory.h"
+#include "api/datagram_transport_interface.h"
#include "api/media_transport_interface.h"
#include "rtc_base/async_invoker.h"
#include "rtc_base/critical_section.h"
@@ -42,7 +43,9 @@
// CreateMediaTransport();
class WrapperMediaTransportFactory : public MediaTransportFactory {
public:
- explicit WrapperMediaTransportFactory(MediaTransportInterface* wrapped);
+ WrapperMediaTransportFactory(
+ MediaTransportInterface* wrapped_media_transport,
+ DatagramTransportInterface* wrapped_datagram_transport);
explicit WrapperMediaTransportFactory(MediaTransportFactory* wrapped);
RTCErrorOr<std::unique_ptr<MediaTransportInterface>> CreateMediaTransport(
@@ -54,12 +57,17 @@
rtc::Thread* network_thread,
const MediaTransportSettings& settings) override;
+ RTCErrorOr<std::unique_ptr<DatagramTransportInterface>>
+ CreateDatagramTransport(rtc::Thread* network_thread,
+ const MediaTransportSettings& settings) override;
+
std::string GetTransportName() const override;
int created_transport_count() const;
private:
- MediaTransportInterface* wrapped_;
+ MediaTransportInterface* wrapped_media_transport_ = nullptr;
+ DatagramTransportInterface* wrapped_datagram_transport_ = nullptr;
MediaTransportFactory* wrapped_factory_ = nullptr;
int created_transport_count_ = 0;
};
@@ -82,6 +90,13 @@
MediaTransportInterface* first() { return &first_; }
MediaTransportInterface* second() { return &second_; }
+ DatagramTransportInterface* first_datagram_transport() {
+ return &first_datagram_transport_;
+ }
+ DatagramTransportInterface* second_datagram_transport() {
+ return &second_datagram_transport_;
+ }
+
std::unique_ptr<MediaTransportFactory> first_factory() {
return absl::make_unique<WrapperMediaTransportFactory>(&first_factory_);
}
@@ -93,6 +108,12 @@
void SetState(MediaTransportState state) {
first_.SetState(state);
second_.SetState(state);
+ first_datagram_transport_.SetState(state);
+ second_datagram_transport_.SetState(state);
+ }
+
+ void SetFirstDatagramTransportParameters(const std::string& params) {
+ first_datagram_transport_.set_transport_parameters(params);
}
void FlushAsyncInvokes() {
@@ -112,12 +133,58 @@
}
private:
+ class LoopbackDataChannelTransport : public DataChannelTransportInterface {
+ public:
+ explicit LoopbackDataChannelTransport(rtc::Thread* thread);
+ ~LoopbackDataChannelTransport() override;
+
+ void Connect(LoopbackDataChannelTransport* other);
+
+ 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;
+
+ bool IsReadyToSend() const override;
+
+ void SetDataSink(DataChannelSink* sink) override;
+
+ void OnReadyToSend(bool ready_to_send);
+
+ void FlushAsyncInvokes();
+
+ private:
+ void OnData(int channel_id,
+ DataMessageType type,
+ const rtc::CopyOnWriteBuffer& buffer);
+
+ void OnRemoteCloseChannel(int channel_id);
+
+ rtc::Thread* const thread_;
+ rtc::CriticalSection sink_lock_;
+ DataChannelSink* data_sink_ RTC_GUARDED_BY(sink_lock_) = nullptr;
+
+ bool ready_to_send_ RTC_GUARDED_BY(sink_lock_) = false;
+
+ LoopbackDataChannelTransport* other_;
+
+ rtc::AsyncInvoker invoker_;
+ };
+
class LoopbackMediaTransport : public MediaTransportInterface {
public:
- LoopbackMediaTransport(rtc::Thread* thread, LoopbackMediaTransport* other);
+ explicit LoopbackMediaTransport(rtc::Thread* thread);
~LoopbackMediaTransport() override;
+ // Connects this loopback transport to another loopback transport.
+ void Connect(LoopbackMediaTransport* other);
+
+ void Connect(rtc::PacketTransportInternal* transport) override;
+
RTCError SendAudioFrame(uint64_t channel_id,
MediaTransportEncodedAudioFrame frame) override;
@@ -146,6 +213,8 @@
void SetMediaTransportStateCallback(
MediaTransportStateCallback* callback) override;
+ void SetState(MediaTransportState state);
+
RTCError OpenChannel(int channel_id) override;
RTCError SendData(int channel_id,
@@ -156,7 +225,7 @@
void SetDataSink(DataChannelSink* sink) override;
- void SetState(MediaTransportState state);
+ bool IsReadyToSend() const override;
void FlushAsyncInvokes();
@@ -172,16 +241,13 @@
void OnData(uint64_t channel_id, MediaTransportEncodedVideoFrame frame);
- void OnData(int channel_id,
- DataMessageType type,
- const rtc::CopyOnWriteBuffer& buffer);
-
void OnKeyFrameRequested(int channel_id);
- void OnRemoteCloseChannel(int channel_id);
-
void OnStateChanged() RTC_RUN_ON(thread_);
+ // Implementation of the data channel transport.
+ LoopbackDataChannelTransport dc_transport_;
+
rtc::Thread* const thread_;
rtc::CriticalSection sink_lock_;
rtc::CriticalSection stats_lock_;
@@ -190,7 +256,6 @@
nullptr;
MediaTransportVideoSinkInterface* video_sink_ RTC_GUARDED_BY(sink_lock_) =
nullptr;
- DataChannelSink* data_sink_ RTC_GUARDED_BY(sink_lock_) = nullptr;
MediaTransportKeyFrameRequestCallback* key_frame_callback_
RTC_GUARDED_BY(sink_lock_) = nullptr;
@@ -206,15 +271,58 @@
MediaTransportState state_ RTC_GUARDED_BY(thread_) =
MediaTransportState::kPending;
- LoopbackMediaTransport* const other_;
+ LoopbackMediaTransport* other_;
Stats stats_ RTC_GUARDED_BY(stats_lock_);
rtc::AsyncInvoker invoker_;
};
+ class LoopbackDatagramTransport : public DatagramTransportInterface {
+ public:
+ explicit LoopbackDatagramTransport(rtc::Thread* thread);
+
+ void Connect(LoopbackDatagramTransport* other);
+
+ // Datagram transport overrides.
+ // TODO(mellem): Implement these when tests actually need to use them.
+ void Connect(rtc::PacketTransportInternal* packet_transport) override;
+ CongestionControlInterface* congestion_control() override;
+ void SetTransportStateCallback(
+ MediaTransportStateCallback* callback) override;
+ RTCError SendDatagram(rtc::ArrayView<const uint8_t> data,
+ DatagramId datagram_id) override;
+ size_t GetLargestDatagramSize() const override;
+ void SetDatagramSink(DatagramSinkInterface* sink) override;
+ std::string GetTransportParameters() const override;
+
+ // Data channel 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;
+
+ // Loopback-specific functionality.
+ void SetState(MediaTransportState state);
+ void FlushAsyncInvokes();
+
+ void set_transport_parameters(const std::string& value) {
+ transport_parameters_ = value;
+ }
+
+ private:
+ LoopbackDataChannelTransport dc_transport_;
+
+ std::string transport_parameters_;
+ };
+
LoopbackMediaTransport first_;
LoopbackMediaTransport second_;
+ LoopbackDatagramTransport first_datagram_transport_;
+ LoopbackDatagramTransport second_datagram_transport_;
WrapperMediaTransportFactory first_factory_;
WrapperMediaTransportFactory second_factory_;
};
diff --git a/api/test/loopback_media_transport_unittest.cc b/api/test/loopback_media_transport_unittest.cc
index d1351c5..346ac5f 100644
--- a/api/test/loopback_media_transport_unittest.cc
+++ b/api/test/loopback_media_transport_unittest.cc
@@ -44,6 +44,7 @@
void(int, DataMessageType, const rtc::CopyOnWriteBuffer&));
MOCK_METHOD1(OnChannelClosing, void(int));
MOCK_METHOD1(OnChannelClosed, void(int));
+ MOCK_METHOD0(OnReadyToSend, void());
};
class MockStateCallback : public MediaTransportStateCallback {
@@ -203,8 +204,8 @@
MediaTransportPair transport_pair(thread.get());
MockStateCallback state_callback;
-
EXPECT_CALL(state_callback, OnStateChanged(MediaTransportState::kPending));
+
transport_pair.first()->SetMediaTransportStateCallback(&state_callback);
transport_pair.FlushAsyncInvokes();
}
@@ -238,4 +239,47 @@
transport_pair.FlushAsyncInvokes();
}
+TEST(LoopbackMediaTransport, NotReadyToSendWhenDataSinkSet) {
+ std::unique_ptr<rtc::Thread> thread = rtc::Thread::Create();
+ thread->Start();
+ MediaTransportPair transport_pair(thread.get());
+
+ MockDataChannelSink data_channel_sink;
+ EXPECT_CALL(data_channel_sink, OnReadyToSend()).Times(0);
+
+ transport_pair.first()->SetDataSink(&data_channel_sink);
+ transport_pair.FlushAsyncInvokes();
+ transport_pair.first()->SetDataSink(nullptr);
+}
+
+TEST(LoopbackMediaTransport, ReadyToSendWhenDataSinkSet) {
+ std::unique_ptr<rtc::Thread> thread = rtc::Thread::Create();
+ thread->Start();
+ MediaTransportPair transport_pair(thread.get());
+
+ transport_pair.SetState(MediaTransportState::kWritable);
+ transport_pair.FlushAsyncInvokes();
+
+ MockDataChannelSink data_channel_sink;
+ EXPECT_CALL(data_channel_sink, OnReadyToSend());
+
+ transport_pair.first()->SetDataSink(&data_channel_sink);
+ transport_pair.FlushAsyncInvokes();
+ transport_pair.first()->SetDataSink(nullptr);
+}
+
+TEST(LoopbackMediaTransport, StateChangeDeliveredToDataSink) {
+ std::unique_ptr<rtc::Thread> thread = rtc::Thread::Create();
+ thread->Start();
+ MediaTransportPair transport_pair(thread.get());
+
+ MockDataChannelSink data_channel_sink;
+ EXPECT_CALL(data_channel_sink, OnReadyToSend());
+
+ transport_pair.first()->SetDataSink(&data_channel_sink);
+ transport_pair.SetState(MediaTransportState::kWritable);
+ transport_pair.FlushAsyncInvokes();
+ transport_pair.first()->SetDataSink(nullptr);
+}
+
} // namespace webrtc