Support changing the tagged BUNDLE media section section
The behavior implemented in this CL matches Firefox:
1. If there are no common media sections from the previous
BUNDLE group, then the previous transport is stopped
and a new transport created.
2. If there is at least one common media section from the
previous BUNDLE group, then the existing transport is
reused.
This will only happen if the tagged media section is rejected.
Bug: webrtc:9954
Change-Id: If0f0733c0ab91858594304828d126640e2ab9520
Reviewed-on: https://webrtc-review.googlesource.com/c/114920
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26150}
diff --git a/pc/mediasession_unittest.cc b/pc/mediasession_unittest.cc
index 16be74f..c73c19e 100644
--- a/pc/mediasession_unittest.cc
+++ b/pc/mediasession_unittest.cc
@@ -890,6 +890,159 @@
EXPECT_EQ(cricket::kMediaProtocolSctp, data->media_description()->protocol());
}
+// Test that if BUNDLE is enabled and all media sections are rejected then the
+// BUNDLE group is not present in the re-offer.
+TEST_F(MediaSessionDescriptionFactoryTest, ReOfferNoBundleGroupIfAllRejected) {
+ MediaSessionOptions opts;
+ opts.bundle_enabled = true;
+ AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
+ RtpTransceiverDirection::kSendRecv, kActive,
+ &opts);
+ std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
+
+ opts.media_description_options[0].stopped = true;
+ std::unique_ptr<SessionDescription> reoffer =
+ f1_.CreateOffer(opts, offer.get());
+
+ EXPECT_FALSE(reoffer->GetGroupByName(cricket::GROUP_TYPE_BUNDLE));
+}
+
+// Test that if BUNDLE is enabled and the remote re-offer does not include a
+// BUNDLE group since all media sections are rejected, then the re-answer also
+// does not include a BUNDLE group.
+TEST_F(MediaSessionDescriptionFactoryTest, ReAnswerNoBundleGroupIfAllRejected) {
+ MediaSessionOptions opts;
+ opts.bundle_enabled = true;
+ AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
+ RtpTransceiverDirection::kSendRecv, kActive,
+ &opts);
+ std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
+ std::unique_ptr<SessionDescription> answer =
+ f2_.CreateAnswer(offer.get(), opts, nullptr);
+
+ opts.media_description_options[0].stopped = true;
+ std::unique_ptr<SessionDescription> reoffer =
+ f1_.CreateOffer(opts, offer.get());
+ std::unique_ptr<SessionDescription> reanswer =
+ f2_.CreateAnswer(reoffer.get(), opts, answer.get());
+
+ EXPECT_FALSE(reanswer->GetGroupByName(cricket::GROUP_TYPE_BUNDLE));
+}
+
+// Test that if BUNDLE is enabled and the previous offerer-tagged media section
+// was rejected then the new offerer-tagged media section is the non-rejected
+// media section.
+TEST_F(MediaSessionDescriptionFactoryTest, ReOfferChangeBundleOffererTagged) {
+ MediaSessionOptions opts;
+ opts.bundle_enabled = true;
+ AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
+ RtpTransceiverDirection::kSendRecv, kActive,
+ &opts);
+ std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
+
+ // Reject the audio m= section and add a video m= section.
+ opts.media_description_options[0].stopped = true;
+ AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
+ RtpTransceiverDirection::kSendRecv, kActive,
+ &opts);
+ std::unique_ptr<SessionDescription> reoffer =
+ f1_.CreateOffer(opts, offer.get());
+
+ const cricket::ContentGroup* bundle_group =
+ reoffer->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
+ ASSERT_TRUE(bundle_group);
+ EXPECT_FALSE(bundle_group->HasContentName("audio"));
+ EXPECT_TRUE(bundle_group->HasContentName("video"));
+}
+
+// Test that if BUNDLE is enabled and the previous offerer-tagged media section
+// was rejected and a new media section is added, then the re-answer BUNDLE
+// group will contain only the non-rejected media section.
+TEST_F(MediaSessionDescriptionFactoryTest, ReAnswerChangedBundleOffererTagged) {
+ MediaSessionOptions opts;
+ opts.bundle_enabled = true;
+ AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio",
+ RtpTransceiverDirection::kSendRecv, kActive,
+ &opts);
+ std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
+ std::unique_ptr<SessionDescription> answer =
+ f2_.CreateAnswer(offer.get(), opts, nullptr);
+
+ // Reject the audio m= section and add a video m= section.
+ opts.media_description_options[0].stopped = true;
+ AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
+ RtpTransceiverDirection::kSendRecv, kActive,
+ &opts);
+ std::unique_ptr<SessionDescription> reoffer =
+ f1_.CreateOffer(opts, offer.get());
+ std::unique_ptr<SessionDescription> reanswer =
+ f2_.CreateAnswer(reoffer.get(), opts, answer.get());
+
+ const cricket::ContentGroup* bundle_group =
+ reanswer->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
+ ASSERT_TRUE(bundle_group);
+ EXPECT_FALSE(bundle_group->HasContentName("audio"));
+ EXPECT_TRUE(bundle_group->HasContentName("video"));
+}
+
+// Test that if the BUNDLE offerer-tagged media section is changed in a reoffer
+// and there is still a non-rejected media section that was in the initial
+// offer, then the ICE credentials do not change in the reoffer offerer-tagged
+// media section.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ ReOfferChangeBundleOffererTaggedKeepsIceCredentials) {
+ MediaSessionOptions opts;
+ opts.bundle_enabled = true;
+ AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &opts);
+ std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
+ std::unique_ptr<SessionDescription> answer =
+ f2_.CreateAnswer(offer.get(), opts, nullptr);
+
+ // Reject the audio m= section.
+ opts.media_description_options[0].stopped = true;
+ std::unique_ptr<SessionDescription> reoffer =
+ f1_.CreateOffer(opts, offer.get());
+
+ const TransportDescription* offer_tagged =
+ offer->GetTransportDescriptionByName("audio");
+ ASSERT_TRUE(offer_tagged);
+ const TransportDescription* reoffer_tagged =
+ reoffer->GetTransportDescriptionByName("video");
+ ASSERT_TRUE(reoffer_tagged);
+ EXPECT_EQ(offer_tagged->ice_ufrag, reoffer_tagged->ice_ufrag);
+ EXPECT_EQ(offer_tagged->ice_pwd, reoffer_tagged->ice_pwd);
+}
+
+// Test that if the BUNDLE offerer-tagged media section is changed in a reoffer
+// and there is still a non-rejected media section that was in the initial
+// offer, then the ICE credentials do not change in the reanswer answerer-tagged
+// media section.
+TEST_F(MediaSessionDescriptionFactoryTest,
+ ReAnswerChangeBundleOffererTaggedKeepsIceCredentials) {
+ MediaSessionOptions opts;
+ opts.bundle_enabled = true;
+ AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &opts);
+ std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
+ std::unique_ptr<SessionDescription> answer =
+ f2_.CreateAnswer(offer.get(), opts, nullptr);
+
+ // Reject the audio m= section.
+ opts.media_description_options[0].stopped = true;
+ std::unique_ptr<SessionDescription> reoffer =
+ f1_.CreateOffer(opts, offer.get());
+ std::unique_ptr<SessionDescription> reanswer =
+ f2_.CreateAnswer(reoffer.get(), opts, answer.get());
+
+ const TransportDescription* answer_tagged =
+ answer->GetTransportDescriptionByName("audio");
+ ASSERT_TRUE(answer_tagged);
+ const TransportDescription* reanswer_tagged =
+ reanswer->GetTransportDescriptionByName("video");
+ ASSERT_TRUE(reanswer_tagged);
+ EXPECT_EQ(answer_tagged->ice_ufrag, reanswer_tagged->ice_ufrag);
+ EXPECT_EQ(answer_tagged->ice_pwd, reanswer_tagged->ice_pwd);
+}
+
// Create an audio, video offer without legacy StreamParams.
TEST_F(MediaSessionDescriptionFactoryTest,
TestCreateOfferWithoutLegacyStreams) {