Create a composite implementation of RtpTransportInternal.
This will be used to multiplex multiple transports during SDP
negotiation. When the offerer watns to support multiple RTP transports,
it will combine them into a singla CompositeRtpTransport.
CompositeRtpTransport can receive from any of the offered transports
while waiting for an answer to arrive.
The choice of which transport is used to send must be driven by the SDP
answer. If a provisional answer arrives, the composite can be set to
send using the chosen transport, while maintaining other transports in
case the peer changes its mind. When the final answer arrives, the
composite will be deleted and replaced with the chosen transport.
Bug: webrtc:9719
Change-Id: Ib8cea77ef202f37086723bfa2c71e2aa5995a912
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/138281
Commit-Queue: Bjorn Mellem <mellem@webrtc.org>
Reviewed-by: Anton Sukhanov <sukhanov@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28093}
diff --git a/pc/composite_rtp_transport_test.cc b/pc/composite_rtp_transport_test.cc
new file mode 100644
index 0000000..5264c73
--- /dev/null
+++ b/pc/composite_rtp_transport_test.cc
@@ -0,0 +1,377 @@
+/*
+ * 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_rtp_transport.h"
+
+#include <memory>
+
+#include "absl/memory/memory.h"
+#include "modules/rtp_rtcp/source/rtp_packet_received.h"
+#include "p2p/base/fake_packet_transport.h"
+#include "pc/rtp_transport.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+constexpr char kTransportName[] = "test-transport";
+constexpr char kRtcpTransportName[] = "test-transport-rtcp";
+constexpr uint8_t kRtpPayloadType = 100;
+
+constexpr uint8_t kRtcpPacket[] = {0x80, 73, 0, 0};
+constexpr uint8_t kRtpPacket[] = {0x80, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+class CompositeRtpTransportTest : public ::testing::Test,
+ public sigslot::has_slots<>,
+ public RtpPacketSinkInterface {
+ public:
+ CompositeRtpTransportTest()
+ : packet_transport_1_(
+ absl::make_unique<rtc::FakePacketTransport>(kTransportName)),
+ packet_transport_2_(
+ absl::make_unique<rtc::FakePacketTransport>(kTransportName)),
+ rtcp_transport_1_(
+ absl::make_unique<rtc::FakePacketTransport>(kRtcpTransportName)),
+ rtcp_transport_2_(
+ absl::make_unique<rtc::FakePacketTransport>(kRtcpTransportName)) {}
+
+ void SetupRtpTransports(bool rtcp_mux) {
+ transport_1_ = absl::make_unique<RtpTransport>(rtcp_mux);
+ transport_2_ = absl::make_unique<RtpTransport>(rtcp_mux);
+
+ transport_1_->SetRtpPacketTransport(packet_transport_1_.get());
+ transport_2_->SetRtpPacketTransport(packet_transport_2_.get());
+ if (!rtcp_mux) {
+ transport_1_->SetRtcpPacketTransport(rtcp_transport_1_.get());
+ transport_2_->SetRtcpPacketTransport(rtcp_transport_2_.get());
+ }
+
+ composite_ = absl::make_unique<CompositeRtpTransport>(
+ std::vector<RtpTransportInternal*>{transport_1_.get(),
+ transport_2_.get()});
+
+ composite_->SignalReadyToSend.connect(
+ this, &CompositeRtpTransportTest::OnReadyToSend);
+ composite_->SignalWritableState.connect(
+ this, &CompositeRtpTransportTest::OnWritableState);
+ composite_->SignalSentPacket.connect(
+ this, &CompositeRtpTransportTest::OnSentPacket);
+ composite_->SignalNetworkRouteChanged.connect(
+ this, &CompositeRtpTransportTest::OnNetworkRouteChanged);
+ composite_->SignalRtcpPacketReceived.connect(
+ this, &CompositeRtpTransportTest::OnRtcpPacketReceived);
+
+ RtpDemuxerCriteria criteria;
+ criteria.payload_types.insert(kRtpPayloadType);
+ composite_->RegisterRtpDemuxerSink(criteria, this);
+ }
+
+ void TearDown() override { composite_->UnregisterRtpDemuxerSink(this); }
+
+ void OnReadyToSend(bool ready) { ++ready_to_send_count_; }
+ void OnWritableState(bool writable) { ++writable_state_count_; }
+ void OnSentPacket(const rtc::SentPacket& packet) { ++sent_packet_count_; }
+ void OnNetworkRouteChanged(absl::optional<rtc::NetworkRoute> route) {
+ ++network_route_count_;
+ last_network_route_ = route;
+ }
+ void OnRtcpPacketReceived(rtc::CopyOnWriteBuffer* buffer,
+ int64_t packet_time_us) {
+ ++rtcp_packet_count_;
+ last_packet_ = *buffer;
+ }
+ void OnRtpPacket(const RtpPacketReceived& packet) {
+ ++rtp_packet_count_;
+ last_packet_ = packet.Buffer();
+ }
+
+ protected:
+ std::unique_ptr<rtc::FakePacketTransport> packet_transport_1_;
+ std::unique_ptr<rtc::FakePacketTransport> packet_transport_2_;
+ std::unique_ptr<rtc::FakePacketTransport> rtcp_transport_1_;
+ std::unique_ptr<rtc::FakePacketTransport> rtcp_transport_2_;
+ std::unique_ptr<RtpTransport> transport_1_;
+ std::unique_ptr<RtpTransport> transport_2_;
+ std::unique_ptr<CompositeRtpTransport> composite_;
+
+ int ready_to_send_count_ = 0;
+ int writable_state_count_ = 0;
+ int sent_packet_count_ = 0;
+ int network_route_count_ = 0;
+ int rtcp_packet_count_ = 0;
+ int rtp_packet_count_ = 0;
+
+ absl::optional<rtc::NetworkRoute> last_network_route_;
+ rtc::CopyOnWriteBuffer last_packet_;
+};
+
+TEST_F(CompositeRtpTransportTest, EnableRtcpMux) {
+ SetupRtpTransports(/*rtcp_mux=*/false);
+ EXPECT_FALSE(composite_->rtcp_mux_enabled());
+ EXPECT_FALSE(transport_1_->rtcp_mux_enabled());
+ EXPECT_FALSE(transport_2_->rtcp_mux_enabled());
+
+ composite_->SetRtcpMuxEnabled(true);
+ EXPECT_TRUE(composite_->rtcp_mux_enabled());
+ EXPECT_TRUE(transport_1_->rtcp_mux_enabled());
+ EXPECT_TRUE(transport_2_->rtcp_mux_enabled());
+}
+
+TEST_F(CompositeRtpTransportTest, DisableRtcpMux) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+ EXPECT_TRUE(composite_->rtcp_mux_enabled());
+ EXPECT_TRUE(transport_1_->rtcp_mux_enabled());
+ EXPECT_TRUE(transport_2_->rtcp_mux_enabled());
+
+ // If the component transports didn't have an RTCP transport before, they need
+ // to be set independently before disabling RTCP mux. There's no other sane
+ // way to do this, as the interface only allows sending a single RTCP
+ // transport, and we need one for each component.
+ transport_1_->SetRtcpPacketTransport(rtcp_transport_1_.get());
+ transport_2_->SetRtcpPacketTransport(rtcp_transport_2_.get());
+
+ composite_->SetRtcpMuxEnabled(false);
+ EXPECT_FALSE(composite_->rtcp_mux_enabled());
+ EXPECT_FALSE(transport_1_->rtcp_mux_enabled());
+ EXPECT_FALSE(transport_2_->rtcp_mux_enabled());
+}
+
+TEST_F(CompositeRtpTransportTest, SetRtpOption) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+ EXPECT_EQ(0, composite_->SetRtpOption(rtc::Socket::OPT_DSCP, 2));
+
+ int value = 0;
+ EXPECT_TRUE(packet_transport_1_->GetOption(rtc::Socket::OPT_DSCP, &value));
+ EXPECT_EQ(value, 2);
+
+ EXPECT_TRUE(packet_transport_2_->GetOption(rtc::Socket::OPT_DSCP, &value));
+ EXPECT_EQ(value, 2);
+}
+
+TEST_F(CompositeRtpTransportTest, SetRtcpOption) {
+ SetupRtpTransports(/*rtcp_mux=*/false);
+ EXPECT_EQ(0, composite_->SetRtcpOption(rtc::Socket::OPT_DSCP, 2));
+
+ int value = 0;
+ EXPECT_TRUE(rtcp_transport_1_->GetOption(rtc::Socket::OPT_DSCP, &value));
+ EXPECT_EQ(value, 2);
+
+ EXPECT_TRUE(rtcp_transport_2_->GetOption(rtc::Socket::OPT_DSCP, &value));
+ EXPECT_EQ(value, 2);
+}
+
+TEST_F(CompositeRtpTransportTest, NeverWritableWithoutSendTransport) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ packet_transport_1_->SetWritable(true);
+ packet_transport_2_->SetWritable(true);
+
+ EXPECT_FALSE(composite_->IsWritable(false));
+ EXPECT_FALSE(composite_->IsWritable(true));
+ EXPECT_FALSE(composite_->IsReadyToSend());
+ EXPECT_EQ(0, ready_to_send_count_);
+ EXPECT_EQ(0, writable_state_count_);
+}
+
+TEST_F(CompositeRtpTransportTest, WritableWhenSendTransportBecomesWritable) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ composite_->SetSendTransport(transport_1_.get());
+
+ EXPECT_FALSE(composite_->IsWritable(false));
+ EXPECT_FALSE(composite_->IsWritable(true));
+ EXPECT_FALSE(composite_->IsReadyToSend());
+ EXPECT_EQ(0, ready_to_send_count_);
+ EXPECT_EQ(1, writable_state_count_);
+
+ packet_transport_2_->SetWritable(true);
+
+ EXPECT_FALSE(composite_->IsWritable(false));
+ EXPECT_FALSE(composite_->IsWritable(true));
+ EXPECT_FALSE(composite_->IsReadyToSend());
+ EXPECT_EQ(0, ready_to_send_count_);
+ EXPECT_EQ(1, writable_state_count_);
+
+ packet_transport_1_->SetWritable(true);
+
+ EXPECT_TRUE(composite_->IsWritable(false));
+ EXPECT_TRUE(composite_->IsWritable(true));
+ EXPECT_TRUE(composite_->IsReadyToSend());
+ EXPECT_EQ(1, ready_to_send_count_);
+ EXPECT_EQ(2, writable_state_count_);
+}
+
+TEST_F(CompositeRtpTransportTest, SendTransportAlreadyWritable) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+ packet_transport_1_->SetWritable(true);
+
+ composite_->SetSendTransport(transport_1_.get());
+
+ EXPECT_TRUE(composite_->IsWritable(false));
+ EXPECT_TRUE(composite_->IsWritable(true));
+ EXPECT_TRUE(composite_->IsReadyToSend());
+ EXPECT_EQ(1, ready_to_send_count_);
+ EXPECT_EQ(1, writable_state_count_);
+}
+
+TEST_F(CompositeRtpTransportTest, IsSrtpActive) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+ EXPECT_FALSE(composite_->IsSrtpActive());
+}
+
+TEST_F(CompositeRtpTransportTest, NetworkRouteChange) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::NetworkRoute route;
+ route.local_network_id = 7;
+ packet_transport_1_->SetNetworkRoute(route);
+
+ EXPECT_EQ(1, network_route_count_);
+ EXPECT_EQ(7, last_network_route_->local_network_id);
+
+ route.local_network_id = 8;
+ packet_transport_2_->SetNetworkRoute(route);
+
+ EXPECT_EQ(2, network_route_count_);
+ EXPECT_EQ(8, last_network_route_->local_network_id);
+}
+
+TEST_F(CompositeRtpTransportTest, SendRtcpBeforeSendTransportSet) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::FakePacketTransport remote("remote");
+ remote.SetDestination(packet_transport_1_.get(), false);
+
+ rtc::CopyOnWriteBuffer packet(kRtcpPacket);
+ EXPECT_FALSE(composite_->SendRtcpPacket(&packet, rtc::PacketOptions(), 0));
+ EXPECT_EQ(0, sent_packet_count_);
+}
+
+TEST_F(CompositeRtpTransportTest, SendRtcpOn1) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::FakePacketTransport remote("remote");
+ remote.SetDestination(packet_transport_1_.get(), false);
+ composite_->SetSendTransport(transport_1_.get());
+
+ rtc::CopyOnWriteBuffer packet(kRtcpPacket);
+ EXPECT_TRUE(composite_->SendRtcpPacket(&packet, rtc::PacketOptions(), 0));
+ EXPECT_EQ(1, sent_packet_count_);
+ EXPECT_EQ(packet, *packet_transport_1_->last_sent_packet());
+}
+
+TEST_F(CompositeRtpTransportTest, SendRtcpOn2) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::FakePacketTransport remote("remote");
+ remote.SetDestination(packet_transport_2_.get(), false);
+ composite_->SetSendTransport(transport_2_.get());
+
+ rtc::CopyOnWriteBuffer packet(kRtcpPacket);
+ EXPECT_TRUE(composite_->SendRtcpPacket(&packet, rtc::PacketOptions(), 0));
+ EXPECT_EQ(1, sent_packet_count_);
+ EXPECT_EQ(packet, *packet_transport_2_->last_sent_packet());
+}
+
+TEST_F(CompositeRtpTransportTest, SendRtpBeforeSendTransportSet) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::FakePacketTransport remote("remote");
+ remote.SetDestination(packet_transport_1_.get(), false);
+
+ rtc::CopyOnWriteBuffer packet(kRtpPacket);
+ EXPECT_FALSE(composite_->SendRtpPacket(&packet, rtc::PacketOptions(), 0));
+ EXPECT_EQ(0, sent_packet_count_);
+}
+
+TEST_F(CompositeRtpTransportTest, SendRtpOn1) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::FakePacketTransport remote("remote");
+ remote.SetDestination(packet_transport_1_.get(), false);
+ composite_->SetSendTransport(transport_1_.get());
+
+ rtc::CopyOnWriteBuffer packet(kRtpPacket);
+ EXPECT_TRUE(composite_->SendRtpPacket(&packet, rtc::PacketOptions(), 0));
+ EXPECT_EQ(1, sent_packet_count_);
+ EXPECT_EQ(packet, *packet_transport_1_->last_sent_packet());
+}
+
+TEST_F(CompositeRtpTransportTest, SendRtpOn2) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::FakePacketTransport remote("remote");
+ remote.SetDestination(packet_transport_2_.get(), false);
+ composite_->SetSendTransport(transport_2_.get());
+
+ rtc::CopyOnWriteBuffer packet(kRtpPacket);
+ EXPECT_TRUE(composite_->SendRtpPacket(&packet, rtc::PacketOptions(), 0));
+ EXPECT_EQ(1, sent_packet_count_);
+ EXPECT_EQ(packet, *packet_transport_2_->last_sent_packet());
+}
+
+TEST_F(CompositeRtpTransportTest, ReceiveRtcpFrom1) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::FakePacketTransport remote("remote");
+ remote.SetDestination(packet_transport_1_.get(), false);
+
+ rtc::CopyOnWriteBuffer packet(kRtcpPacket);
+ remote.SendPacket(packet.cdata<char>(), packet.size(), rtc::PacketOptions(),
+ 0);
+
+ EXPECT_EQ(1, rtcp_packet_count_);
+ EXPECT_EQ(packet, last_packet_);
+}
+
+TEST_F(CompositeRtpTransportTest, ReceiveRtcpFrom2) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::FakePacketTransport remote("remote");
+ remote.SetDestination(packet_transport_2_.get(), false);
+
+ rtc::CopyOnWriteBuffer packet(kRtcpPacket);
+ remote.SendPacket(packet.cdata<char>(), packet.size(), rtc::PacketOptions(),
+ 0);
+
+ EXPECT_EQ(1, rtcp_packet_count_);
+ EXPECT_EQ(packet, last_packet_);
+}
+
+TEST_F(CompositeRtpTransportTest, ReceiveRtpFrom1) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::FakePacketTransport remote("remote");
+ remote.SetDestination(packet_transport_1_.get(), false);
+
+ rtc::CopyOnWriteBuffer packet(kRtpPacket);
+ remote.SendPacket(packet.cdata<char>(), packet.size(), rtc::PacketOptions(),
+ 0);
+
+ EXPECT_EQ(1, rtp_packet_count_);
+ EXPECT_EQ(packet, last_packet_);
+}
+
+TEST_F(CompositeRtpTransportTest, ReceiveRtpFrom2) {
+ SetupRtpTransports(/*rtcp_mux=*/true);
+
+ rtc::FakePacketTransport remote("remote");
+ remote.SetDestination(packet_transport_2_.get(), false);
+
+ rtc::CopyOnWriteBuffer packet(kRtpPacket);
+ remote.SendPacket(packet.cdata<char>(), packet.size(), rtc::PacketOptions(),
+ 0);
+
+ EXPECT_EQ(1, rtp_packet_count_);
+ EXPECT_EQ(packet, last_packet_);
+}
+
+} // namespace
+} // namespace webrtc