Implement PeerConnection::AddTrack/RemoveTrack for Unified Plan
Bug: webrtc:7600
Change-Id: I2a48426a29ac67b6bdbd7817fe07273cdd5fd980
Reviewed-on: https://webrtc-review.googlesource.com/31647
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21305}diff --git a/pc/peerconnection_rtp_unittest.cc b/pc/peerconnection_rtp_unittest.cc
index 4989726..1e16728 100644
--- a/pc/peerconnection_rtp_unittest.cc
+++ b/pc/peerconnection_rtp_unittest.cc
@@ -452,7 +452,7 @@
EXPECT_FALSE(observer->called());
}
-// RtpTransceiver Tests
+// RtpTransceiver Tests.
// Test that by default there are no transceivers with Unified Plan.
TEST_F(PeerConnectionRtpTest, PeerConnectionHasNoTransceivers) {
@@ -602,4 +602,277 @@
caller->pc()->Close();
}
+// Unified Plan AddTrack tests.
+
+class PeerConnectionRtpUnifiedPlanTest : public PeerConnectionRtpTest {};
+
+// Test that adding an audio track creates a new audio RtpSender with the given
+// track.
+TEST_F(PeerConnectionRtpUnifiedPlanTest, AddAudioTrackCreatesAudioSender) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto audio_track = caller->CreateAudioTrack("a");
+ auto sender = caller->pc()->AddTrack(audio_track, {});
+ ASSERT_TRUE(sender);
+
+ EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, sender->media_type());
+ EXPECT_EQ(audio_track, sender->track());
+}
+
+// Test that adding a video track creates a new video RtpSender with the given
+// track.
+TEST_F(PeerConnectionRtpUnifiedPlanTest, AddVideoTrackCreatesVideoSender) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto video_track = caller->CreateVideoTrack("a");
+ auto sender = caller->pc()->AddTrack(video_track, {});
+ ASSERT_TRUE(sender);
+
+ EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, sender->media_type());
+ EXPECT_EQ(video_track, sender->track());
+}
+
+// Test that adding a track to a new PeerConnection creates an RtpTransceiver
+// with the sender that AddTrack returns and in the sendrecv direction.
+TEST_F(PeerConnectionRtpUnifiedPlanTest, AddFirstTrackCreatesTransceiver) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto sender = caller->AddAudioTrack("a");
+ ASSERT_TRUE(sender);
+
+ auto transceivers = caller->pc()->GetTransceivers();
+ ASSERT_EQ(1u, transceivers.size());
+ EXPECT_EQ(sender, transceivers[0]->sender());
+ EXPECT_EQ(RtpTransceiverDirection::kSendRecv, transceivers[0]->direction());
+}
+
+// Test that if a transceiver of the same type but no track had been added to
+// the PeerConnection and later a call to AddTrack is made, the resulting sender
+// is the transceiver's sender and the sender's track is the newly-added track.
+TEST_F(PeerConnectionRtpUnifiedPlanTest, AddTrackReusesTransceiver) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+ auto audio_track = caller->CreateAudioTrack("a");
+ auto sender = caller->pc()->AddTrack(audio_track, {});
+ ASSERT_TRUE(sender);
+
+ auto transceivers = caller->pc()->GetTransceivers();
+ ASSERT_EQ(1u, transceivers.size());
+ EXPECT_EQ(transceiver, transceivers[0]);
+ EXPECT_EQ(sender, transceiver->sender());
+ EXPECT_EQ(audio_track, sender->track());
+}
+
+// Test that adding two tracks to a new PeerConnection creates two
+// RtpTransceivers in the same order.
+TEST_F(PeerConnectionRtpUnifiedPlanTest, TwoAddTrackCreatesTwoTransceivers) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto sender1 = caller->AddAudioTrack("a");
+ auto sender2 = caller->AddVideoTrack("v");
+ ASSERT_TRUE(sender2);
+
+ auto transceivers = caller->pc()->GetTransceivers();
+ ASSERT_EQ(2u, transceivers.size());
+ EXPECT_EQ(sender1, transceivers[0]->sender());
+ EXPECT_EQ(sender2, transceivers[1]->sender());
+}
+
+// Test that if there are multiple transceivers with no sending track then a
+// later call to AddTrack will use the one of the same type as the newly-added
+// track.
+TEST_F(PeerConnectionRtpUnifiedPlanTest, AddTrackReusesTransceiverOfType) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto audio_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+ auto video_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
+ auto sender = caller->AddVideoTrack("v");
+
+ ASSERT_EQ(2u, caller->pc()->GetTransceivers().size());
+ EXPECT_NE(sender, audio_transceiver->sender());
+ EXPECT_EQ(sender, video_transceiver->sender());
+}
+
+// Test that if the only transceivers that do not have a sending track have a
+// different type from the added track, then AddTrack will create a new
+// transceiver for the track.
+TEST_F(PeerConnectionRtpUnifiedPlanTest,
+ AddTrackDoesNotReuseTransceiverOfWrongType) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+ auto sender = caller->AddVideoTrack("v");
+
+ auto transceivers = caller->pc()->GetTransceivers();
+ ASSERT_EQ(2u, transceivers.size());
+ EXPECT_NE(sender, transceivers[0]->sender());
+ EXPECT_EQ(sender, transceivers[1]->sender());
+}
+
+// Test that the first available transceiver is reused by AddTrack when multiple
+// are available.
+TEST_F(PeerConnectionRtpUnifiedPlanTest,
+ AddTrackReusesFirstMatchingTransceiver) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+ caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+ auto sender = caller->AddAudioTrack("a");
+
+ auto transceivers = caller->pc()->GetTransceivers();
+ ASSERT_EQ(2u, transceivers.size());
+ EXPECT_EQ(sender, transceivers[0]->sender());
+ EXPECT_NE(sender, transceivers[1]->sender());
+}
+
+// Test that a call to AddTrack that reuses a transceiver will change the
+// direction from inactive to sendonly.
+TEST_F(PeerConnectionRtpUnifiedPlanTest,
+ AddTrackChangesDirectionFromInactiveToSendOnly) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kInactive;
+ auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
+
+ caller->observer()->clear_negotiation_needed();
+ ASSERT_TRUE(caller->AddAudioTrack("a"));
+ EXPECT_TRUE(caller->observer()->negotiation_needed());
+
+ EXPECT_EQ(RtpTransceiverDirection::kSendOnly, transceiver->direction());
+}
+
+// Test that a call to AddTrack that reuses a transceiver will change the
+// direction from recvonly to sendrecv.
+TEST_F(PeerConnectionRtpUnifiedPlanTest,
+ AddTrackChangesDirectionFromRecvOnlyToSendRecv) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kRecvOnly;
+ auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
+
+ caller->observer()->clear_negotiation_needed();
+ ASSERT_TRUE(caller->AddAudioTrack("a"));
+ EXPECT_TRUE(caller->observer()->negotiation_needed());
+
+ EXPECT_EQ(RtpTransceiverDirection::kSendRecv, transceiver->direction());
+}
+
+// Unified Plan AddTrack error handling.
+
+TEST_F(PeerConnectionRtpUnifiedPlanTest, AddTrackErrorIfClosed) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto audio_track = caller->CreateAudioTrack("a");
+ caller->pc()->Close();
+
+ caller->observer()->clear_negotiation_needed();
+ EXPECT_FALSE(caller->pc()->AddTrack(audio_track, {}));
+ EXPECT_FALSE(caller->observer()->negotiation_needed());
+}
+
+TEST_F(PeerConnectionRtpUnifiedPlanTest, AddTrackErrorIfTrackAlreadyHasSender) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto audio_track = caller->CreateAudioTrack("a");
+ ASSERT_TRUE(caller->pc()->AddTrack(audio_track, {}));
+
+ caller->observer()->clear_negotiation_needed();
+ EXPECT_FALSE(caller->pc()->AddTrack(audio_track, {}));
+ EXPECT_FALSE(caller->observer()->negotiation_needed());
+}
+
+// Unified Plan RemoveTrack tests.
+
+// Test that calling RemoveTrack on a sender with a previously-added track
+// clears the sender's track.
+TEST_F(PeerConnectionRtpUnifiedPlanTest, RemoveTrackClearsSenderTrack) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto sender = caller->AddAudioTrack("a");
+ ASSERT_TRUE(caller->pc()->RemoveTrack(sender));
+
+ EXPECT_FALSE(sender->track());
+}
+
+// Test that calling RemoveTrack on a sender where the transceiver is configured
+// in the sendrecv direction changes the transceiver's direction to recvonly.
+TEST_F(PeerConnectionRtpUnifiedPlanTest,
+ RemoveTrackChangesDirectionFromSendRecvToRecvOnly) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendRecv;
+ auto transceiver =
+ caller->AddTransceiver(caller->CreateAudioTrack("a"), init);
+
+ caller->observer()->clear_negotiation_needed();
+ ASSERT_TRUE(caller->pc()->RemoveTrack(transceiver->sender()));
+ EXPECT_TRUE(caller->observer()->negotiation_needed());
+
+ EXPECT_EQ(RtpTransceiverDirection::kRecvOnly, transceiver->direction());
+ EXPECT_TRUE(caller->observer()->renegotiation_needed_);
+}
+
+// Test that calling RemoveTrack on a sender where the transceiver is configured
+// in the sendonly direction changes the transceiver's direction to inactive.
+TEST_F(PeerConnectionRtpUnifiedPlanTest,
+ RemoveTrackChangesDirectionFromSendOnlyToInactive) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ RtpTransceiverInit init;
+ init.direction = RtpTransceiverDirection::kSendOnly;
+ auto transceiver =
+ caller->AddTransceiver(caller->CreateAudioTrack("a"), init);
+
+ caller->observer()->clear_negotiation_needed();
+ ASSERT_TRUE(caller->pc()->RemoveTrack(transceiver->sender()));
+ EXPECT_TRUE(caller->observer()->negotiation_needed());
+
+ EXPECT_EQ(RtpTransceiverDirection::kInactive, transceiver->direction());
+}
+
+// Test that calling RemoveTrack with a sender that has a null track results in
+// no change in state.
+TEST_F(PeerConnectionRtpUnifiedPlanTest, RemoveTrackWithNullSenderTrackIsNoOp) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto sender = caller->AddAudioTrack("a");
+ auto transceiver = caller->pc()->GetTransceivers()[0];
+ ASSERT_TRUE(sender->SetTrack(nullptr));
+
+ caller->observer()->clear_negotiation_needed();
+ ASSERT_TRUE(caller->pc()->RemoveTrack(sender));
+ EXPECT_FALSE(caller->observer()->negotiation_needed());
+
+ EXPECT_EQ(RtpTransceiverDirection::kSendRecv, transceiver->direction());
+}
+
+// Unified Plan RemoveTrack error handling.
+
+TEST_F(PeerConnectionRtpUnifiedPlanTest, RemoveTrackErrorIfClosed) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto sender = caller->AddAudioTrack("a");
+ caller->pc()->Close();
+
+ caller->observer()->clear_negotiation_needed();
+ EXPECT_FALSE(caller->pc()->RemoveTrack(sender));
+ EXPECT_FALSE(caller->observer()->negotiation_needed());
+}
+
+TEST_F(PeerConnectionRtpUnifiedPlanTest,
+ RemoveTrackNoErrorIfTrackAlreadyRemoved) {
+ auto caller = CreatePeerConnectionWithUnifiedPlan();
+
+ auto sender = caller->AddAudioTrack("a");
+ ASSERT_TRUE(caller->pc()->RemoveTrack(sender));
+
+ caller->observer()->clear_negotiation_needed();
+ EXPECT_TRUE(caller->pc()->RemoveTrack(sender));
+ EXPECT_FALSE(caller->observer()->negotiation_needed());
+}
+
} // namespace webrtc