Remove stopped transceivers at both local and remote SetDescription
This should ensure that the correct number of senders and receivers
are shown.
Bug: webtc:11840
Change-Id: Id57f8f9b1ceb8900abb3f92bcae79e5f0341de15
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/184606
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32158}
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index 303f5f9..22ef75c 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -1988,13 +1988,7 @@
<< "GetTransceivers is only supported with Unified Plan SdpSemantics.";
std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> all_transceivers;
for (const auto& transceiver : transceivers_) {
- // Temporary fix: Do not show stopped transceivers.
- // The long term fix is to remove them from transceivers_, but this
- // turns out to cause issues with audio channel lifetimes.
- // TODO(https://crbug.com/webrtc/11840): Fix issue.
- if (!transceiver->stopped()) {
- all_transceivers.push_back(transceiver);
- }
+ all_transceivers.push_back(transceiver);
}
return all_transceivers;
}
@@ -2534,6 +2528,47 @@
});
}
+void PeerConnection::RemoveStoppedTransceivers() {
+ // 3.2.10.1: For each transceiver in the connection's set of transceivers
+ // run the following steps:
+ if (!IsUnifiedPlan())
+ return;
+ for (auto it = transceivers_.begin(); it != transceivers_.end();) {
+ const auto& transceiver = *it;
+ // 3.2.10.1.1: If transceiver is stopped, associated with an m= section
+ // and the associated m= section is rejected in
+ // connection.[[CurrentLocalDescription]] or
+ // connection.[[CurrentRemoteDescription]], remove the
+ // transceiver from the connection's set of transceivers.
+ if (!transceiver->stopped()) {
+ ++it;
+ continue;
+ }
+ const ContentInfo* local_content =
+ FindMediaSectionForTransceiver(transceiver, local_description());
+ const ContentInfo* remote_content =
+ FindMediaSectionForTransceiver(transceiver, remote_description());
+ if ((local_content && local_content->rejected) ||
+ (remote_content && remote_content->rejected)) {
+ RTC_LOG(LS_INFO) << "Dissociating transceiver"
+ << " since the media section is being recycled.";
+ (*it)->internal()->set_mid(absl::nullopt);
+ (*it)->internal()->set_mline_index(absl::nullopt);
+ it = transceivers_.erase(it);
+ continue;
+ }
+ if (!local_content && !remote_content) {
+ // TODO(bugs.webrtc.org/11973): Consider if this should be removed already
+ // See https://github.com/w3c/webrtc-pc/issues/2576
+ RTC_LOG(LS_INFO)
+ << "Dropping stopped transceiver that was never associated";
+ it = transceivers_.erase(it);
+ continue;
+ }
+ ++it;
+ }
+}
+
void PeerConnection::DoSetLocalDescription(
std::unique_ptr<SessionDescriptionInterface> desc,
rtc::scoped_refptr<SetLocalDescriptionObserverInterface> observer) {
@@ -2605,34 +2640,7 @@
RTC_DCHECK(local_description());
if (local_description()->GetType() == SdpType::kAnswer) {
- // 3.2.10.1: For each transceiver in the connection's set of transceivers
- // run the following steps:
- if (IsUnifiedPlan()) {
- for (auto it = transceivers_.begin(); it != transceivers_.end();) {
- const auto& transceiver = *it;
- // 3.2.10.1.1: If transceiver is stopped, associated with an m= section
- // and the associated m= section is rejected in
- // connection.[[CurrentLocalDescription]] or
- // connection.[[CurrentRemoteDescription]], remove the
- // transceiver from the connection's set of transceivers.
- if (transceiver->stopped()) {
- const ContentInfo* content =
- FindMediaSectionForTransceiver(transceiver, local_description());
-
- if (content && content->rejected) {
- RTC_LOG(LS_INFO) << "Dissociating transceiver"
- << " since the media section is being recycled.";
- (*it)->internal()->set_mid(absl::nullopt);
- (*it)->internal()->set_mline_index(absl::nullopt);
- it = transceivers_.erase(it);
- } else {
- ++it;
- }
- } else {
- ++it;
- }
- }
- }
+ RemoveStoppedTransceivers();
// TODO(deadbeef): We already had to hop to the network thread for
// MaybeStartGathering...
@@ -3100,6 +3108,7 @@
RTC_DCHECK(remote_description());
if (type == SdpType::kAnswer) {
+ RemoveStoppedTransceivers();
// TODO(deadbeef): We already had to hop to the network thread for
// MaybeStartGathering...
network_thread()->Invoke<void>(
@@ -3765,23 +3774,17 @@
RTC_DCHECK(IsUnifiedPlan());
// If this is an offer then the m= section might be recycled. If the m=
// section is being recycled (defined as: rejected in the current local or
- // remote description and not rejected in new description), dissociate the
- // currently associated RtpTransceiver by setting its mid property to null,
- // and discard the mapping between the transceiver and its m= section index.
+ // remote description and not rejected in new description), the transceiver
+ // should have been removed by RemoveStoppedTransceivers().
if (IsMediaSectionBeingRecycled(type, content, old_local_content,
old_remote_content)) {
- // We want to dissociate the transceiver that has the rejected mid.
const std::string& old_mid =
(old_local_content && old_local_content->rejected)
? old_local_content->name
: old_remote_content->name;
auto old_transceiver = GetAssociatedTransceiver(old_mid);
- if (old_transceiver) {
- RTC_LOG(LS_INFO) << "Dissociating transceiver for MID=" << old_mid
- << " since the media section is being recycled.";
- old_transceiver->internal()->set_mid(absl::nullopt);
- old_transceiver->internal()->set_mline_index(absl::nullopt);
- }
+ // The transceiver should be disassociated in RemoveStoppedTransceivers()
+ RTC_DCHECK(!old_transceiver);
}
const MediaContentDescription* media_desc = content.media_description();
auto transceiver = GetAssociatedTransceiver(content.name);
diff --git a/pc/peer_connection.h b/pc/peer_connection.h
index 22c0d9a..1385ce1 100644
--- a/pc/peer_connection.h
+++ b/pc/peer_connection.h
@@ -450,6 +450,9 @@
std::unique_ptr<SessionDescriptionInterface> desc,
rtc::scoped_refptr<SetRemoteDescriptionObserverInterface> observer);
+ // Helper function to remove stopped transceivers.
+ void RemoveStoppedTransceivers() RTC_RUN_ON(signaling_thread());
+
void CreateAudioReceiver(MediaStreamInterface* stream,
const RtpSenderInfo& remote_sender_info)
RTC_RUN_ON(signaling_thread());
diff --git a/pc/peer_connection_interface_unittest.cc b/pc/peer_connection_interface_unittest.cc
index 046d1c9..3f2371c 100644
--- a/pc/peer_connection_interface_unittest.cc
+++ b/pc/peer_connection_interface_unittest.cc
@@ -2668,8 +2668,8 @@
EXPECT_EQ(1u, pc_->local_streams()->count());
EXPECT_EQ(1u, pc_->remote_streams()->count());
} else {
- // Verify that the RtpTransceivers are no longer returned.
- EXPECT_EQ(0u, pc_->GetTransceivers().size());
+ // Verify that the RtpTransceivers are still returned.
+ EXPECT_EQ(2u, pc_->GetTransceivers().size());
}
auto audio_receiver = GetFirstReceiverOfType(cricket::MEDIA_TYPE_AUDIO);
diff --git a/pc/peer_connection_jsep_unittest.cc b/pc/peer_connection_jsep_unittest.cc
index 4d9884d..ae151f0 100644
--- a/pc/peer_connection_jsep_unittest.cc
+++ b/pc/peer_connection_jsep_unittest.cc
@@ -354,6 +354,10 @@
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
auto transceivers = callee->pc()->GetTransceivers();
+ ASSERT_EQ(2u, transceivers.size());
+ // The stopped transceiver is removed in SetLocalDescription(answer)
+ ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
+ transceivers = callee->pc()->GetTransceivers();
ASSERT_EQ(1u, transceivers.size());
EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[0]->mid());
EXPECT_FALSE(transceivers[0]->stopped());
@@ -558,6 +562,9 @@
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
auto transceivers = callee->pc()->GetTransceivers();
+ EXPECT_EQ(1u, transceivers.size());
+ ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
+ transceivers = callee->pc()->GetTransceivers();
EXPECT_EQ(0u, transceivers.size());
}
@@ -593,15 +600,15 @@
auto callee = CreatePeerConnection();
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+ std::string first_mid = *first_transceiver->mid();
ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
callee->pc()->GetTransceivers()[0]->StopInternal();
- ASSERT_EQ(0u, callee->pc()->GetTransceivers().size());
+ ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
ASSERT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
EXPECT_TRUE(first_transceiver->stopped());
- // First transceivers aren't dissociated yet on caller side.
- ASSERT_NE(absl::nullopt, first_transceiver->mid());
- std::string first_mid = *first_transceiver->mid();
+ // First transceivers are dissociated on caller side.
+ ASSERT_EQ(absl::nullopt, first_transceiver->mid());
// They are disassociated on callee side.
ASSERT_EQ(0u, callee->pc()->GetTransceivers().size());
diff --git a/pc/peer_connection_rtp_unittest.cc b/pc/peer_connection_rtp_unittest.cc
index 44f105b..b1670c1 100644
--- a/pc/peer_connection_rtp_unittest.cc
+++ b/pc/peer_connection_rtp_unittest.cc
@@ -1555,6 +1555,24 @@
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
}
+TEST_F(PeerConnectionRtpTestUnifiedPlan,
+ StopAndNegotiateCausesTransceiverToDisappear) {
+ auto caller = CreatePeerConnection();
+ auto callee = CreatePeerConnection();
+ auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+ ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
+ callee->pc()->GetTransceivers()[0]->StopStandard();
+ ASSERT_TRUE(callee->ExchangeOfferAnswerWith(caller.get()));
+ EXPECT_EQ(RtpTransceiverDirection::kStopped,
+ transceiver->current_direction());
+ EXPECT_EQ(0U, caller->pc()->GetTransceivers().size());
+ EXPECT_EQ(0U, callee->pc()->GetTransceivers().size());
+ EXPECT_EQ(0U, caller->pc()->GetSenders().size());
+ EXPECT_EQ(0U, callee->pc()->GetSenders().size());
+ EXPECT_EQ(0U, caller->pc()->GetReceivers().size());
+ EXPECT_EQ(0U, callee->pc()->GetReceivers().size());
+}
+
// Test that AddTransceiver fails if trying to use unimplemented RTP encoding
// parameters with the send_encodings parameters.
TEST_F(PeerConnectionRtpTestUnifiedPlan,