Reland "Implement transceiver.stop()"
This is a reland of 11dc6571cb4ff3e71dee1557dfff8d9076e108d3
One fix that makes Web Platform Tests pass in debug mode is applied.
Original change's description:
> Implement transceiver.stop()
>
> This adds RtpTransceiver.StopStandard(), which behaves according to
> the specification at
> https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-stop
>
> It modifies RTCPeerConnection.getTransceivers() to return only
> transceivers that have not been stopped.
>
> Rebase of armax' https://webrtc-review.googlesource.com/c/src/+/172762
>
> Bug: chromium:980879
> Change-Id: I7d383ee874ccc0a006fdcf280496b5d4235425ce
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/180580
> Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
> Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
> Reviewed-by: Guido Urdaneta <guidou@webrtc.org>
> Commit-Queue: Harald Alvestrand <hta@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#31893}
Bug: chromium:980879
Change-Id: Ide31d929ac5ea118d83fdf6a35a592af23f7dfa7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/181263
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Guido Urdaneta <guidou@webrtc.org>
Reviewed-by: Kári Helgason <kthelgason@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31907}
diff --git a/api/rtp_transceiver_interface.cc b/api/rtp_transceiver_interface.cc
index e795e51..1dc0fcc 100644
--- a/api/rtp_transceiver_interface.cc
+++ b/api/rtp_transceiver_interface.cc
@@ -25,6 +25,23 @@
return absl::nullopt;
}
+bool RtpTransceiverInterface::stopping() const {
+ return false;
+}
+
+void RtpTransceiverInterface::Stop() {
+ StopInternal();
+}
+
+RTCError RtpTransceiverInterface::StopStandard() {
+ RTC_NOTREACHED() << "DEBUG: RtpTransceiverInterface::StopStandard called";
+ return RTCError::OK();
+}
+
+void RtpTransceiverInterface::StopInternal() {
+ RTC_NOTREACHED() << "DEBUG: RtpTransceiverInterface::StopInternal called";
+}
+
RTCError RtpTransceiverInterface::SetCodecPreferences(
rtc::ArrayView<RtpCodecCapability>) {
RTC_NOTREACHED() << "Not implemented";
@@ -47,4 +64,17 @@
return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
}
+// TODO(bugs.webrtc.org/11839) Remove default implementations when clients
+// are updated.
+void RtpTransceiverInterface::SetDirection(
+ RtpTransceiverDirection new_direction) {
+ SetDirectionWithError(new_direction);
+}
+
+RTCError RtpTransceiverInterface::SetDirectionWithError(
+ RtpTransceiverDirection new_direction) {
+ RTC_NOTREACHED() << "Default implementation called";
+ return RTCError::OK();
+}
+
} // namespace webrtc
diff --git a/api/rtp_transceiver_interface.h b/api/rtp_transceiver_interface.h
index 13277d9..cdda34b 100644
--- a/api/rtp_transceiver_interface.h
+++ b/api/rtp_transceiver_interface.h
@@ -89,6 +89,16 @@
// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-stopped
virtual bool stopped() const = 0;
+ // The stopping attribute indicates that the user has indicated that the
+ // sender of this transceiver will stop sending, and that the receiver will
+ // no longer receive. It is always true if stopped() is true.
+ // If stopping() is true and stopped() is false, it means that the
+ // transceiver's stop() method has been called, but the negotiation with
+ // the other end for shutting down the transceiver is not yet done.
+ // https://w3c.github.io/webrtc-pc/#dfn-stopping-0
+ // TODO(hta): Remove default implementation.
+ virtual bool stopping() const;
+
// The direction attribute indicates the preferred direction of this
// transceiver, which will be used in calls to CreateOffer and CreateAnswer.
// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-direction
@@ -99,7 +109,10 @@
// CreateOffer and CreateAnswer mark the corresponding media descriptions as
// sendrecv, sendonly, recvonly, or inactive.
// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-direction
- virtual void SetDirection(RtpTransceiverDirection new_direction) = 0;
+ // TODO(hta): Deprecate SetDirection without error and rename
+ // SetDirectionWithError to SetDirection, remove default implementations.
+ virtual void SetDirection(RtpTransceiverDirection new_direction);
+ virtual RTCError SetDirectionWithError(RtpTransceiverDirection new_direction);
// The current_direction attribute indicates the current direction negotiated
// for this transceiver. If this transceiver has never been represented in an
@@ -114,10 +127,19 @@
// Exposed in the public interface for use by Chromium.
virtual absl::optional<RtpTransceiverDirection> fired_direction() const;
- // The Stop method irreversibly stops the RtpTransceiver. The sender of this
- // transceiver will no longer send, the receiver will no longer receive.
+ // Initiates a stop of the transceiver.
+ // The stop is complete when stopped() returns true.
+ // A stopped transceiver can be reused for a different track.
// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-stop
- virtual void Stop() = 0;
+ // TODO(hta): Rename to Stop() when users of the non-standard Stop() are
+ // updated.
+ virtual RTCError StopStandard();
+
+ // Stops a transceiver immediately, without waiting for signalling.
+ // This is an internal function, and is exposed for historical reasons.
+ // https://w3c.github.io/webrtc-pc/#dfn-stop-the-rtcrtptransceiver
+ virtual void StopInternal();
+ RTC_DEPRECATED virtual void Stop();
// The SetCodecPreferences method overrides the default codec preferences used
// by WebRTC for this transceiver.
diff --git a/pc/media_session.cc b/pc/media_session.cc
index 69ddb0c..0cff84d 100644
--- a/pc/media_session.cc
+++ b/pc/media_session.cc
@@ -1507,7 +1507,6 @@
RtpDataCodecs offer_rtp_data_codecs;
GetCodecsForOffer(current_active_contents, &offer_audio_codecs,
&offer_video_codecs, &offer_rtp_data_codecs);
-
if (!session_options.vad_enabled) {
// If application doesn't want CN codecs in offer.
StripCNCodecs(&offer_audio_codecs);
@@ -1796,15 +1795,13 @@
switch (direction) {
// If stream is inactive - generate list as if sendrecv.
case RtpTransceiverDirection::kSendRecv:
+ case RtpTransceiverDirection::kStopped:
case RtpTransceiverDirection::kInactive:
return audio_sendrecv_codecs_;
case RtpTransceiverDirection::kSendOnly:
return audio_send_codecs_;
case RtpTransceiverDirection::kRecvOnly:
return audio_recv_codecs_;
- case RtpTransceiverDirection::kStopped:
- RTC_NOTREACHED();
- return audio_sendrecv_codecs_;
}
}
@@ -1815,6 +1812,7 @@
// For inactive and sendrecv answers, generate lists as if we were to accept
// the offer's direction. See RFC 3264 Section 6.1.
case RtpTransceiverDirection::kSendRecv:
+ case RtpTransceiverDirection::kStopped:
case RtpTransceiverDirection::kInactive:
return GetAudioCodecsForOffer(
webrtc::RtpTransceiverDirectionReversed(offer));
@@ -1822,9 +1820,6 @@
return audio_send_codecs_;
case RtpTransceiverDirection::kRecvOnly:
return audio_recv_codecs_;
- case RtpTransceiverDirection::kStopped:
- RTC_NOTREACHED();
- return audio_sendrecv_codecs_;
}
}
@@ -1833,15 +1828,13 @@
switch (direction) {
// If stream is inactive - generate list as if sendrecv.
case RtpTransceiverDirection::kSendRecv:
+ case RtpTransceiverDirection::kStopped:
case RtpTransceiverDirection::kInactive:
return video_sendrecv_codecs_;
case RtpTransceiverDirection::kSendOnly:
return video_send_codecs_;
case RtpTransceiverDirection::kRecvOnly:
return video_recv_codecs_;
- case RtpTransceiverDirection::kStopped:
- RTC_NOTREACHED();
- return video_sendrecv_codecs_;
}
}
@@ -1852,6 +1845,7 @@
// For inactive and sendrecv answers, generate lists as if we were to accept
// the offer's direction. See RFC 3264 Section 6.1.
case RtpTransceiverDirection::kSendRecv:
+ case RtpTransceiverDirection::kStopped:
case RtpTransceiverDirection::kInactive:
return GetVideoCodecsForOffer(
webrtc::RtpTransceiverDirectionReversed(offer));
@@ -1859,9 +1853,6 @@
return video_send_codecs_;
case RtpTransceiverDirection::kRecvOnly:
return video_recv_codecs_;
- case RtpTransceiverDirection::kStopped:
- RTC_NOTREACHED();
- return video_sendrecv_codecs_;
}
}
diff --git a/pc/media_session_unittest.cc b/pc/media_session_unittest.cc
index ac949fb..e1ff313 100644
--- a/pc/media_session_unittest.cc
+++ b/pc/media_session_unittest.cc
@@ -4863,7 +4863,8 @@
kResultSendrecv_SendrecvCodecs);
}
break;
- default:
+ case RtpTransceiverDirection::kStopped:
+ // This does not happen in any current test.
RTC_NOTREACHED();
}
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index 982e42a..541f04e 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -1048,7 +1048,7 @@
// AudioRtpSender has a reference to the StatsCollector it will update when
// stopping.
for (const auto& transceiver : transceivers_) {
- transceiver->Stop();
+ transceiver->StopInternal();
}
stats_.reset(nullptr);
@@ -1535,6 +1535,11 @@
RTC_LOG(LS_INFO) << "Reusing an existing "
<< cricket::MediaTypeToString(transceiver->media_type())
<< " transceiver for AddTrack.";
+ if (transceiver->stopping()) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "The existing transceiver is stopping.");
+ }
+
if (transceiver->direction() == RtpTransceiverDirection::kRecvOnly) {
transceiver->internal()->set_direction(
RtpTransceiverDirection::kSendRecv);
@@ -1933,6 +1938,9 @@
std::vector<rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>
all_senders;
for (const auto& transceiver : transceivers_) {
+ if (IsUnifiedPlan() && transceiver->internal()->stopped())
+ continue;
+
auto senders = transceiver->internal()->senders();
all_senders.insert(all_senders.end(), senders.begin(), senders.end());
}
@@ -1956,6 +1964,9 @@
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>>
all_receivers;
for (const auto& transceiver : transceivers_) {
+ if (IsUnifiedPlan() && transceiver->internal()->stopped())
+ continue;
+
auto receivers = transceiver->internal()->receivers();
all_receivers.insert(all_receivers.end(), receivers.begin(),
receivers.end());
@@ -1970,7 +1981,13 @@
<< "GetTransceivers is only supported with Unified Plan SdpSemantics.";
std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> all_transceivers;
for (const auto& transceiver : transceivers_) {
- all_transceivers.push_back(transceiver);
+ // 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);
+ }
}
return all_transceivers;
}
@@ -2581,6 +2598,35 @@
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;
+ }
+ }
+ }
+
// TODO(deadbeef): We already had to hop to the network thread for
// MaybeStartGathering...
network_thread()->Invoke<void>(
@@ -2670,6 +2716,10 @@
std::vector<rtc::scoped_refptr<RtpTransceiverInterface>> remove_list;
std::vector<rtc::scoped_refptr<MediaStreamInterface>> removed_streams;
for (const auto& transceiver : transceivers_) {
+ if (transceiver->stopped()) {
+ continue;
+ }
+
// 2.2.7.1.1.(6-9): Set sender and receiver's transport slots.
// Note that code paths that don't set MID won't be able to use
// information about DTLS transports.
@@ -2755,6 +2805,9 @@
if (IsUnifiedPlan()) {
for (const auto& transceiver : transceivers_) {
+ if (transceiver->stopped()) {
+ continue;
+ }
const ContentInfo* content =
FindMediaSectionForTransceiver(transceiver, local_description());
if (!content) {
@@ -3263,7 +3316,7 @@
if (content->rejected && !transceiver->stopped()) {
RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name
<< " since the media section was rejected.";
- transceiver->Stop();
+ transceiver->StopInternal();
}
if (!content->rejected &&
RtpTransceiverDirectionHasRecv(local_direction)) {
@@ -4339,7 +4392,9 @@
NoteUsageEvent(UsageEvent::CLOSE_CALLED);
for (const auto& transceiver : transceivers_) {
- transceiver->Stop();
+ transceiver->internal()->SetPeerConnectionClosed();
+ if (!transceiver->stopped())
+ transceiver->StopInternal();
}
// Ensure that all asynchronous stats requests are completed before destroying
@@ -4905,10 +4960,15 @@
GetMediaDescriptionOptionsForTransceiver(
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
transceiver,
- const std::string& mid) {
+ const std::string& mid,
+ bool is_create_offer) {
+ // NOTE: a stopping transceiver should be treated as a stopped one in
+ // createOffer as specified in
+ // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
+ bool stopped =
+ is_create_offer ? transceiver->stopping() : transceiver->stopped();
cricket::MediaDescriptionOptions media_description_options(
- transceiver->media_type(), mid, transceiver->direction(),
- transceiver->stopped());
+ transceiver->media_type(), mid, transceiver->direction(), stopped);
media_description_options.codec_preferences =
transceiver->codec_preferences();
media_description_options.header_extensions =
@@ -4918,9 +4978,8 @@
// sendrecv.
// 2. If the MSID is included, then it must be included in any subsequent
// offer/answer exactly the same until the RtpTransceiver is stopped.
- if (transceiver->stopped() ||
- (!RtpTransceiverDirectionHasSend(transceiver->direction()) &&
- !transceiver->internal()->has_ever_been_used_to_send())) {
+ if (stopped || (!RtpTransceiverDirectionHasSend(transceiver->direction()) &&
+ !transceiver->internal()->has_ever_been_used_to_send())) {
return media_description_options;
}
@@ -5014,25 +5073,39 @@
: remote_content->media_description()->type());
if (media_type == cricket::MEDIA_TYPE_AUDIO ||
media_type == cricket::MEDIA_TYPE_VIDEO) {
- auto transceiver = GetAssociatedTransceiver(mid);
- RTC_CHECK(transceiver);
// A media section is considered eligible for recycling if it is marked as
// rejected in either the current local or current remote description.
- if (had_been_rejected && transceiver->stopped()) {
+ auto transceiver = GetAssociatedTransceiver(mid);
+ if (!transceiver) {
+ // No associated transceiver. The media section has been stopped.
+ recycleable_mline_indices.push(i);
session_options->media_description_options.push_back(
- cricket::MediaDescriptionOptions(transceiver->media_type(), mid,
+ cricket::MediaDescriptionOptions(media_type, mid,
RtpTransceiverDirection::kInactive,
/*stopped=*/true));
- recycleable_mline_indices.push(i);
} else {
- session_options->media_description_options.push_back(
- GetMediaDescriptionOptionsForTransceiver(transceiver, mid));
- // CreateOffer shouldn't really cause any state changes in
- // PeerConnection, but we need a way to match new transceivers to new
- // media sections in SetLocalDescription and JSEP specifies this is done
- // by recording the index of the media section generated for the
- // transceiver in the offer.
- transceiver->internal()->set_mline_index(i);
+ // NOTE: a stopping transceiver should be treated as a stopped one in
+ // createOffer as specified in
+ // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer.
+ if (had_been_rejected && transceiver->stopping()) {
+ session_options->media_description_options.push_back(
+ cricket::MediaDescriptionOptions(
+ transceiver->media_type(), mid,
+ RtpTransceiverDirection::kInactive,
+ /*stopped=*/true));
+ recycleable_mline_indices.push(i);
+ } else {
+ session_options->media_description_options.push_back(
+ GetMediaDescriptionOptionsForTransceiver(
+ transceiver, mid,
+ /*is_create_offer=*/true));
+ // CreateOffer shouldn't really cause any state changes in
+ // PeerConnection, but we need a way to match new transceivers to new
+ // media sections in SetLocalDescription and JSEP specifies this is
+ // done by recording the index of the media section generated for the
+ // transceiver in the offer.
+ transceiver->internal()->set_mline_index(i);
+ }
}
} else {
RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
@@ -5057,7 +5130,7 @@
// otherwise append to the end of the offer. New media sections should be
// added in the order they were added to the PeerConnection.
for (const auto& transceiver : transceivers_) {
- if (transceiver->mid() || transceiver->stopped()) {
+ if (transceiver->mid() || transceiver->stopping()) {
continue;
}
size_t mline_index;
@@ -5065,13 +5138,13 @@
mline_index = recycleable_mline_indices.front();
recycleable_mline_indices.pop();
session_options->media_description_options[mline_index] =
- GetMediaDescriptionOptionsForTransceiver(transceiver,
- mid_generator_());
+ GetMediaDescriptionOptionsForTransceiver(
+ transceiver, mid_generator_(), /*is_create_offer=*/true);
} else {
mline_index = session_options->media_description_options.size();
session_options->media_description_options.push_back(
- GetMediaDescriptionOptionsForTransceiver(transceiver,
- mid_generator_()));
+ GetMediaDescriptionOptionsForTransceiver(
+ transceiver, mid_generator_(), /*is_create_offer=*/true));
}
// See comment above for why CreateOffer changes the transceiver's state.
transceiver->internal()->set_mline_index(mline_index);
@@ -5182,7 +5255,8 @@
auto transceiver = GetAssociatedTransceiver(content.name);
RTC_CHECK(transceiver);
session_options->media_description_options.push_back(
- GetMediaDescriptionOptionsForTransceiver(transceiver, content.name));
+ GetMediaDescriptionOptionsForTransceiver(transceiver, content.name,
+ /*is_create_offer=*/false));
} else {
RTC_CHECK_EQ(cricket::MEDIA_TYPE_DATA, media_type);
// Reject all data sections if data channels are disabled.
@@ -7273,7 +7347,8 @@
// 1. If any implementation-specific negotiation is required, as described at
// the start of this section, return true.
- // 2. If connection's [[RestartIce]] internal slot is true, return true.
+ // 2. If connection.[[LocalIceCredentialsToReplace]] is not empty, return
+ // true.
if (local_ice_credentials_to_replace_->HasIceCredentials()) {
return true;
}
@@ -7299,11 +7374,12 @@
const ContentInfo* current_remote_msection = FindTransceiverMSection(
transceiver.get(), current_remote_description());
- // 5.3 If transceiver is stopped and is associated with an m= section,
+ // 5.4 If transceiver is stopped and is associated with an m= section,
// but the associated m= section is not yet rejected in
// connection.[[CurrentLocalDescription]] or
// connection.[[CurrentRemoteDescription]], return true.
if (transceiver->stopped()) {
+ RTC_DCHECK(transceiver->stopping());
if (current_local_msection && !current_local_msection->rejected &&
((current_remote_msection && !current_remote_msection->rejected) ||
!current_remote_msection)) {
@@ -7312,17 +7388,22 @@
continue;
}
- // 5.1 If transceiver isn't stopped and isn't yet associated with an m=
+ // 5.1 If transceiver.[[Stopping]] is true and transceiver.[[Stopped]] is
+ // false, return true.
+ if (transceiver->stopping() && !transceiver->stopped())
+ return true;
+
+ // 5.2 If transceiver isn't stopped and isn't yet associated with an m=
// section in description, return true.
if (!current_local_msection)
return true;
const MediaContentDescription* current_local_media_description =
current_local_msection->media_description();
- // 5.2 If transceiver isn't stopped and is associated with an m= section
+ // 5.3 If transceiver isn't stopped and is associated with an m= section
// in description then perform the following checks:
- // 5.2.1 If transceiver.[[Direction]] is "sendrecv" or "sendonly", and the
+ // 5.3.1 If transceiver.[[Direction]] is "sendrecv" or "sendonly", and the
// associated m= section in description either doesn't contain a single
// "a=msid" line, or the number of MSIDs from the "a=msid" lines in this
// m= section, or the MSID values themselves, differ from what is in
@@ -7348,7 +7429,7 @@
return true;
}
- // 5.2.2 If description is of type "offer", and the direction of the
+ // 5.3.2 If description is of type "offer", and the direction of the
// associated m= section in neither connection.[[CurrentLocalDescription]]
// nor connection.[[CurrentRemoteDescription]] matches
// transceiver.[[Direction]], return true.
@@ -7370,7 +7451,7 @@
}
}
- // 5.2.3 If description is of type "answer", and the direction of the
+ // 5.3.3 If description is of type "answer", and the direction of the
// associated m= section in the description does not match
// transceiver.[[Direction]] intersected with the offered direction (as
// described in [JSEP] (section 5.3.1.)), return true.
diff --git a/pc/peer_connection_bundle_unittest.cc b/pc/peer_connection_bundle_unittest.cc
index 543c9be..c544db3 100644
--- a/pc/peer_connection_bundle_unittest.cc
+++ b/pc/peer_connection_bundle_unittest.cc
@@ -873,7 +873,7 @@
// Stop all transceivers, causing all m= sections to be rejected.
for (const auto& transceiver : callee->pc()->GetTransceivers()) {
- transceiver->Stop();
+ transceiver->StopInternal();
}
EXPECT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc
index afb5f2b..dd24163 100644
--- a/pc/peer_connection_integrationtest.cc
+++ b/pc/peer_connection_integrationtest.cc
@@ -799,9 +799,7 @@
EXPECT_TRUE(desc->ToString(&sdp));
RTC_LOG(LS_INFO) << debug_name_ << ": local SDP contents=\n" << sdp;
pc()->SetLocalDescription(observer, desc.release());
- if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) {
- RemoveUnusedVideoRenderers();
- }
+ RemoveUnusedVideoRenderers();
// As mentioned above, we need to send the message immediately after
// SetLocalDescription.
SendSdpMessage(type, sdp);
@@ -814,9 +812,7 @@
new rtc::RefCountedObject<MockSetSessionDescriptionObserver>());
RTC_LOG(LS_INFO) << debug_name_ << ": SetRemoteDescription";
pc()->SetRemoteDescription(observer, desc.release());
- if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) {
- RemoveUnusedVideoRenderers();
- }
+ RemoveUnusedVideoRenderers();
EXPECT_TRUE_WAIT(observer->called(), kDefaultTimeout);
return observer->result();
}
@@ -824,29 +820,26 @@
// This is a work around to remove unused fake_video_renderers from
// transceivers that have either stopped or are no longer receiving.
void RemoveUnusedVideoRenderers() {
+ if (sdp_semantics_ != SdpSemantics::kUnifiedPlan) {
+ return;
+ }
auto transceivers = pc()->GetTransceivers();
+ std::set<std::string> active_renderers;
for (auto& transceiver : transceivers) {
- if (transceiver->receiver()->media_type() != cricket::MEDIA_TYPE_VIDEO) {
- continue;
+ // Note - we don't check for direction here. This function is called
+ // before direction is set, and in that case, we should not remove
+ // the renderer.
+ if (transceiver->receiver()->media_type() == cricket::MEDIA_TYPE_VIDEO) {
+ active_renderers.insert(transceiver->receiver()->track()->id());
}
- // Remove fake video renderers from any stopped transceivers.
- if (transceiver->stopped()) {
- auto it =
- fake_video_renderers_.find(transceiver->receiver()->track()->id());
- if (it != fake_video_renderers_.end()) {
- fake_video_renderers_.erase(it);
- }
- }
- // Remove fake video renderers from any transceivers that are no longer
- // receiving.
- if ((transceiver->current_direction() &&
- !webrtc::RtpTransceiverDirectionHasRecv(
- *transceiver->current_direction()))) {
- auto it =
- fake_video_renderers_.find(transceiver->receiver()->track()->id());
- if (it != fake_video_renderers_.end()) {
- fake_video_renderers_.erase(it);
- }
+ }
+ for (auto it = fake_video_renderers_.begin();
+ it != fake_video_renderers_.end();) {
+ // Remove fake video renderers belonging to any non-active transceivers.
+ if (!active_renderers.count(it->first)) {
+ it = fake_video_renderers_.erase(it);
+ } else {
+ it++;
}
}
}
@@ -936,8 +929,11 @@
rtc::scoped_refptr<RtpReceiverInterface> receiver) override {
if (receiver->media_type() == cricket::MEDIA_TYPE_VIDEO) {
auto it = fake_video_renderers_.find(receiver->track()->id());
- RTC_DCHECK(it != fake_video_renderers_.end());
- fake_video_renderers_.erase(it);
+ if (it != fake_video_renderers_.end()) {
+ fake_video_renderers_.erase(it);
+ } else {
+ RTC_LOG(LS_ERROR) << "OnRemoveTrack called for non-active renderer";
+ }
}
}
void OnRenegotiationNeeded() override {}
@@ -1561,6 +1557,9 @@
// |media_expectations|. Returns false if any of the expectations were
// not met.
bool ExpectNewFrames(const MediaExpectations& media_expectations) {
+ // Make sure there are no bogus tracks confusing the issue.
+ caller()->RemoveUnusedVideoRenderers();
+ callee()->RemoveUnusedVideoRenderers();
// First initialize the expected frame counts based upon the current
// frame count.
int total_caller_audio_frames_expected = caller()->audio_frames_received();
@@ -2237,7 +2236,9 @@
callee()->SetOfferAnswerOptions(options);
} else {
callee()->SetRemoteOfferHandler([this] {
- callee()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)->Stop();
+ callee()
+ ->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)
+ ->StopInternal();
});
}
// Do offer/answer and make sure audio is still received end-to-end.
@@ -2269,11 +2270,12 @@
// The caller creates a new transceiver to receive video on when receiving
// the offer, but by default it is send only.
auto transceivers = caller()->pc()->GetTransceivers();
- ASSERT_EQ(3U, transceivers.size());
+ ASSERT_EQ(2U, transceivers.size());
ASSERT_EQ(cricket::MEDIA_TYPE_VIDEO,
- transceivers[2]->receiver()->media_type());
- transceivers[2]->sender()->SetTrack(caller()->CreateLocalVideoTrack());
- transceivers[2]->SetDirection(RtpTransceiverDirection::kSendRecv);
+ transceivers[1]->receiver()->media_type());
+ transceivers[1]->sender()->SetTrack(caller()->CreateLocalVideoTrack());
+ transceivers[1]->SetDirectionWithError(
+ RtpTransceiverDirection::kSendRecv);
});
}
callee()->CreateAndSetAndSignalOffer();
@@ -2485,7 +2487,9 @@
// Stopping the audio RtpTransceiver will cause the media section to be
// rejected in the answer.
callee()->SetRemoteOfferHandler([this] {
- callee()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO)->Stop();
+ callee()
+ ->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO)
+ ->StopInternal();
});
}
callee()->AddTrack(callee()->CreateLocalVideoTrack());
@@ -2504,10 +2508,10 @@
ASSERT_NE(nullptr, callee_audio_content);
EXPECT_TRUE(callee_audio_content->rejected);
if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) {
- // The caller's transceiver should have stopped after receiving the answer.
- EXPECT_TRUE(caller()
- ->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO)
- ->stopped());
+ // The caller's transceiver should have stopped after receiving the answer,
+ // and thus no longer listed in transceivers.
+ EXPECT_EQ(nullptr,
+ caller()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO));
}
}
@@ -2527,7 +2531,9 @@
// Stopping the video RtpTransceiver will cause the media section to be
// rejected in the answer.
callee()->SetRemoteOfferHandler([this] {
- callee()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)->Stop();
+ callee()
+ ->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)
+ ->StopInternal();
});
}
callee()->AddTrack(callee()->CreateLocalAudioTrack());
@@ -2546,10 +2552,10 @@
ASSERT_NE(nullptr, callee_video_content);
EXPECT_TRUE(callee_video_content->rejected);
if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) {
- // The caller's transceiver should have stopped after receiving the answer.
- EXPECT_TRUE(caller()
- ->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)
- ->stopped());
+ // The caller's transceiver should have stopped after receiving the answer,
+ // and thus is no longer present.
+ EXPECT_EQ(nullptr,
+ caller()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO));
}
}
@@ -2573,7 +2579,7 @@
callee()->SetRemoteOfferHandler([this] {
// Stopping all transceivers will cause all media sections to be rejected.
for (const auto& transceiver : callee()->pc()->GetTransceivers()) {
- transceiver->Stop();
+ transceiver->StopInternal();
}
});
}
@@ -2620,7 +2626,9 @@
}
});
} else {
- caller()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)->Stop();
+ caller()
+ ->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)
+ ->StopInternal();
}
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kMaxWaitForActivationMs);
@@ -2735,7 +2743,7 @@
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
// Add receive direction.
- video_sender->SetDirection(RtpTransceiverDirection::kSendRecv);
+ video_sender->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
rtc::scoped_refptr<webrtc::VideoTrackInterface> callee_track =
callee()->CreateLocalVideoTrack();
@@ -4345,7 +4353,9 @@
callee()->SetOfferAnswerOptions(options);
} else {
callee()->SetRemoteOfferHandler([this] {
- callee()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)->Stop();
+ callee()
+ ->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)
+ ->StopInternal();
});
}
caller()->CreateAndSetAndSignalOffer();
@@ -4366,7 +4376,7 @@
// The caller's transceiver is stopped, so we need to add another track.
auto caller_transceiver =
caller()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO);
- EXPECT_TRUE(caller_transceiver->stopped());
+ EXPECT_EQ(nullptr, caller_transceiver.get());
caller()->AddVideoTrack();
}
callee()->AddVideoTrack();
@@ -4429,9 +4439,9 @@
auto caller_video_sender = video_result.MoveValue()->sender();
callee()->SetRemoteOfferHandler([this] {
ASSERT_EQ(2u, callee()->pc()->GetTransceivers().size());
- callee()->pc()->GetTransceivers()[0]->SetDirection(
+ callee()->pc()->GetTransceivers()[0]->SetDirectionWithError(
RtpTransceiverDirection::kSendRecv);
- callee()->pc()->GetTransceivers()[1]->SetDirection(
+ callee()->pc()->GetTransceivers()[1]->SetDirectionWithError(
RtpTransceiverDirection::kSendRecv);
});
caller()->CreateAndSetAndSignalOffer();
@@ -5552,7 +5562,7 @@
ASSERT_TRUE(ExpectNewFrames(media_expectations));
}
- audio_transceiver->Stop();
+ audio_transceiver->StopInternal();
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack());
caller()->CreateAndSetAndSignalOffer();
diff --git a/pc/peer_connection_interface_unittest.cc b/pc/peer_connection_interface_unittest.cc
index 901e5c5..046d1c9 100644
--- a/pc/peer_connection_interface_unittest.cc
+++ b/pc/peer_connection_interface_unittest.cc
@@ -2668,23 +2668,24 @@
EXPECT_EQ(1u, pc_->local_streams()->count());
EXPECT_EQ(1u, pc_->remote_streams()->count());
} else {
- // Verify that the RtpTransceivers are still present but all stopped.
- EXPECT_EQ(2u, pc_->GetTransceivers().size());
- for (const auto& transceiver : pc_->GetTransceivers()) {
- EXPECT_TRUE(transceiver->stopped());
- }
+ // Verify that the RtpTransceivers are no longer returned.
+ EXPECT_EQ(0u, pc_->GetTransceivers().size());
}
auto audio_receiver = GetFirstReceiverOfType(cricket::MEDIA_TYPE_AUDIO);
- ASSERT_TRUE(audio_receiver);
auto video_receiver = GetFirstReceiverOfType(cricket::MEDIA_TYPE_VIDEO);
- ASSERT_TRUE(video_receiver);
-
- // Track state may be updated asynchronously.
- EXPECT_EQ_WAIT(MediaStreamTrackInterface::kEnded,
- audio_receiver->track()->state(), kTimeout);
- EXPECT_EQ_WAIT(MediaStreamTrackInterface::kEnded,
- video_receiver->track()->state(), kTimeout);
+ if (sdp_semantics_ == SdpSemantics::kPlanB) {
+ ASSERT_TRUE(audio_receiver);
+ ASSERT_TRUE(video_receiver);
+ // Track state may be updated asynchronously.
+ EXPECT_EQ_WAIT(MediaStreamTrackInterface::kEnded,
+ audio_receiver->track()->state(), kTimeout);
+ EXPECT_EQ_WAIT(MediaStreamTrackInterface::kEnded,
+ video_receiver->track()->state(), kTimeout);
+ } else {
+ ASSERT_FALSE(audio_receiver);
+ ASSERT_FALSE(video_receiver);
+ }
}
// Test that PeerConnection methods fails gracefully after
diff --git a/pc/peer_connection_jsep_unittest.cc b/pc/peer_connection_jsep_unittest.cc
index 0b2f375..c4b7de1 100644
--- a/pc/peer_connection_jsep_unittest.cc
+++ b/pc/peer_connection_jsep_unittest.cc
@@ -212,7 +212,7 @@
StoppedTransceiverHasNoMediaSectionInInitialOffer) {
auto caller = CreatePeerConnection();
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
- transceiver->Stop();
+ transceiver->StopInternal();
auto offer = caller->CreateOffer();
EXPECT_EQ(0u, offer->description()->contents().size());
@@ -300,7 +300,7 @@
auto caller = CreatePeerConnection();
caller->AddAudioTrack("a");
auto caller_audio = caller->pc()->GetTransceivers()[0];
- caller_audio->SetDirection(RtpTransceiverDirection::kSendOnly);
+ caller_audio->SetDirectionWithError(RtpTransceiverDirection::kSendOnly);
auto callee = CreatePeerConnection();
callee->AddAudioTrack("a");
@@ -358,16 +358,14 @@
caller->AddAudioTrack("a");
auto callee = CreatePeerConnection();
callee->AddAudioTrack("a");
- callee->pc()->GetTransceivers()[0]->Stop();
+ callee->pc()->GetTransceivers()[0]->StopInternal();
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
auto transceivers = callee->pc()->GetTransceivers();
- ASSERT_EQ(2u, transceivers.size());
- EXPECT_EQ(absl::nullopt, transceivers[0]->mid());
- EXPECT_TRUE(transceivers[0]->stopped());
- EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[1]->mid());
- EXPECT_FALSE(transceivers[1]->stopped());
+ ASSERT_EQ(1u, transceivers.size());
+ EXPECT_EQ(caller->pc()->GetTransceivers()[0]->mid(), transceivers[0]->mid());
+ EXPECT_FALSE(transceivers[0]->stopped());
}
// Test that audio and video transceivers created on the remote side with
@@ -432,7 +430,7 @@
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
- callee->pc()->GetTransceivers()[0]->Stop();
+ callee->pc()->GetTransceivers()[0]->StopInternal();
auto answer = callee->CreateAnswer();
auto contents = answer->description()->contents();
@@ -469,7 +467,7 @@
TEST_F(PeerConnectionJsepTest, SetLocalAnswerUpdatesCurrentDirection) {
auto caller = CreatePeerConnection();
auto caller_audio = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
- caller_audio->SetDirection(RtpTransceiverDirection::kRecvOnly);
+ caller_audio->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly);
auto callee = CreatePeerConnection();
callee->AddAudioTrack("a");
@@ -494,7 +492,7 @@
auto callee = CreatePeerConnection();
callee->AddAudioTrack("a");
auto callee_audio = callee->pc()->GetTransceivers()[0];
- callee_audio->SetDirection(RtpTransceiverDirection::kSendOnly);
+ callee_audio->SetDirectionWithError(RtpTransceiverDirection::kSendOnly);
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
ASSERT_TRUE(
@@ -518,7 +516,7 @@
caller->AddAudioTrack("a");
auto callee = CreatePeerConnection();
callee->AddAudioTrack("a");
- callee->pc()->GetTransceivers()[0]->SetDirection(
+ callee->pc()->GetTransceivers()[0]->SetDirectionWithError(
RtpTransceiverDirection::kInactive);
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
@@ -543,7 +541,7 @@
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
ASSERT_TRUE(transceiver->mid());
- transceiver->Stop();
+ transceiver->StopInternal();
auto reoffer = caller->CreateOffer();
auto contents = reoffer->description()->contents();
@@ -564,13 +562,12 @@
ASSERT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
- transceiver->Stop();
+ transceiver->StopInternal();
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
auto transceivers = callee->pc()->GetTransceivers();
- EXPECT_TRUE(transceivers[0]->stopped());
- EXPECT_TRUE(transceivers[0]->mid());
+ EXPECT_EQ(0u, transceivers.size());
}
// Test that CreateOffer will only generate a recycled media section if the
@@ -586,7 +583,7 @@
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
auto second_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
- first_transceiver->Stop();
+ first_transceiver->StopInternal();
auto reoffer = caller->CreateOffer();
auto contents = reoffer->description()->contents();
@@ -605,14 +602,17 @@
auto callee = CreatePeerConnection();
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
- callee->pc()->GetTransceivers()[0]->Stop();
+ ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
+ callee->pc()->GetTransceivers()[0]->StopInternal();
+ ASSERT_EQ(0u, callee->pc()->GetTransceivers().size());
ASSERT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
EXPECT_TRUE(first_transceiver->stopped());
- // First transceivers aren't dissociated yet.
+ // First transceivers aren't dissociated yet on caller side.
ASSERT_NE(absl::nullopt, first_transceiver->mid());
std::string first_mid = *first_transceiver->mid();
- EXPECT_EQ(first_mid, callee->pc()->GetTransceivers()[0]->mid());
+ // They are disassociated on callee side.
+ ASSERT_EQ(0u, callee->pc()->GetTransceivers().size());
// New offer exchange with new transceivers that recycles the m section
// correctly.
@@ -630,10 +630,11 @@
ASSERT_TRUE(
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
EXPECT_EQ(absl::nullopt, first_transceiver->mid());
- EXPECT_EQ(second_mid, caller->pc()->GetTransceivers()[1]->mid());
+ ASSERT_EQ(1u, caller->pc()->GetTransceivers().size());
+ EXPECT_EQ(second_mid, caller->pc()->GetTransceivers()[0]->mid());
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
- EXPECT_EQ(absl::nullopt, callee->pc()->GetTransceivers()[0]->mid());
- EXPECT_EQ(second_mid, callee->pc()->GetTransceivers()[1]->mid());
+ ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
+ EXPECT_EQ(second_mid, callee->pc()->GetTransceivers()[0]->mid());
// The new answer should also recycle the m section correctly.
auto answer = callee->CreateAnswer();
@@ -647,13 +648,11 @@
callee->SetLocalDescription(CloneSessionDescription(answer.get())));
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
auto caller_transceivers = caller->pc()->GetTransceivers();
- ASSERT_EQ(2u, caller_transceivers.size());
- EXPECT_EQ(absl::nullopt, caller_transceivers[0]->mid());
- EXPECT_EQ(second_mid, caller_transceivers[1]->mid());
+ ASSERT_EQ(1u, caller_transceivers.size());
+ EXPECT_EQ(second_mid, caller_transceivers[0]->mid());
auto callee_transceivers = callee->pc()->GetTransceivers();
- ASSERT_EQ(2u, callee_transceivers.size());
- EXPECT_EQ(absl::nullopt, callee_transceivers[0]->mid());
- EXPECT_EQ(second_mid, callee_transceivers[1]->mid());
+ ASSERT_EQ(1u, callee_transceivers.size());
+ EXPECT_EQ(second_mid, callee_transceivers[0]->mid());
}
// Test that creating/setting a local offer that recycles an m= section is
@@ -664,7 +663,7 @@
auto first_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
auto callee = CreatePeerConnection();
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
- first_transceiver->Stop();
+ first_transceiver->StopInternal();
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
caller->AddAudioTrack("audio2");
@@ -675,7 +674,8 @@
ASSERT_EQ(1u, offer_contents.size());
EXPECT_FALSE(offer_contents[0].rejected);
ASSERT_TRUE(caller->SetLocalDescription(std::move(offer)));
- EXPECT_FALSE(caller->pc()->GetTransceivers()[1]->stopped());
+ ASSERT_EQ(1u, caller->pc()->GetTransceivers().size());
+ EXPECT_FALSE(caller->pc()->GetTransceivers()[0]->stopped());
std::string second_mid = offer_contents[0].name;
// Create another new offer and set the local description again without the
@@ -690,10 +690,9 @@
ASSERT_TRUE(caller->SetLocalDescription(std::move(second_offer)));
// Make sure that the caller's transceivers are associated correctly.
auto caller_transceivers = caller->pc()->GetTransceivers();
- ASSERT_EQ(2u, caller_transceivers.size());
- EXPECT_EQ(absl::nullopt, caller_transceivers[0]->mid());
- EXPECT_EQ(second_mid, caller_transceivers[1]->mid());
- EXPECT_FALSE(caller_transceivers[1]->stopped());
+ ASSERT_EQ(1u, caller_transceivers.size());
+ EXPECT_EQ(second_mid, caller_transceivers[0]->mid());
+ EXPECT_FALSE(caller_transceivers[0]->stopped());
}
// Test that the offer/answer and transceivers for both the caller and callee
@@ -729,7 +728,7 @@
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
std::string first_mid = *first_transceiver->mid();
- first_transceiver->Stop();
+ first_transceiver->StopInternal();
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
@@ -756,11 +755,9 @@
// create a new transceiver for the media section.
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
auto callee_transceivers = callee->pc()->GetTransceivers();
- ASSERT_EQ(2u, callee_transceivers.size());
- EXPECT_EQ(absl::nullopt, callee_transceivers[0]->mid());
- EXPECT_EQ(first_type_, callee_transceivers[0]->media_type());
- EXPECT_EQ(second_mid, callee_transceivers[1]->mid());
- EXPECT_EQ(second_type_, callee_transceivers[1]->media_type());
+ ASSERT_EQ(1u, callee_transceivers.size());
+ EXPECT_EQ(second_mid, callee_transceivers[0]->mid());
+ EXPECT_EQ(second_type_, callee_transceivers[0]->media_type());
// The answer should have only one media section for the new transceiver.
auto answer = callee->CreateAnswer();
@@ -777,8 +774,8 @@
// Setting the remote answer should succeed and not create any new
// transceivers.
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
- ASSERT_EQ(2u, caller->pc()->GetTransceivers().size());
- ASSERT_EQ(2u, callee->pc()->GetTransceivers().size());
+ ASSERT_EQ(1u, caller->pc()->GetTransceivers().size());
+ ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
}
// Test that recycling works properly when a new transceiver recycles an m=
@@ -793,7 +790,7 @@
std::string first_mid = *caller_first_transceiver->mid();
ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
- callee_first_transceiver->Stop();
+ callee_first_transceiver->StopInternal();
// The answer will have a rejected m= section.
ASSERT_TRUE(
@@ -821,11 +818,9 @@
// create a new transceiver for the media section.
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
auto callee_transceivers = callee->pc()->GetTransceivers();
- ASSERT_EQ(2u, callee_transceivers.size());
- EXPECT_EQ(absl::nullopt, callee_transceivers[0]->mid());
- EXPECT_EQ(first_type_, callee_transceivers[0]->media_type());
- EXPECT_EQ(second_mid, callee_transceivers[1]->mid());
- EXPECT_EQ(second_type_, callee_transceivers[1]->media_type());
+ ASSERT_EQ(1u, callee_transceivers.size());
+ EXPECT_EQ(second_mid, callee_transceivers[0]->mid());
+ EXPECT_EQ(second_type_, callee_transceivers[0]->media_type());
// The answer should have only one media section for the new transceiver.
auto answer = callee->CreateAnswer();
@@ -842,8 +837,8 @@
// Setting the remote answer should succeed and not create any new
// transceivers.
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
- ASSERT_EQ(2u, caller->pc()->GetTransceivers().size());
- ASSERT_EQ(2u, callee->pc()->GetTransceivers().size());
+ ASSERT_EQ(1u, caller->pc()->GetTransceivers().size());
+ ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
}
// Test that recycling works properly when a new transceiver recycles an m=
@@ -858,7 +853,7 @@
std::string first_mid = *caller_first_transceiver->mid();
ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
- callee_first_transceiver->Stop();
+ callee_first_transceiver->StopInternal();
// The answer will have a rejected m= section.
ASSERT_TRUE(
@@ -886,11 +881,9 @@
// create a new transceiver for the media section.
ASSERT_TRUE(caller->SetRemoteDescription(std::move(offer)));
auto caller_transceivers = caller->pc()->GetTransceivers();
- ASSERT_EQ(2u, caller_transceivers.size());
- EXPECT_EQ(absl::nullopt, caller_transceivers[0]->mid());
- EXPECT_EQ(first_type_, caller_transceivers[0]->media_type());
- EXPECT_EQ(second_mid, caller_transceivers[1]->mid());
- EXPECT_EQ(second_type_, caller_transceivers[1]->media_type());
+ ASSERT_EQ(1u, caller_transceivers.size());
+ EXPECT_EQ(second_mid, caller_transceivers[0]->mid());
+ EXPECT_EQ(second_type_, caller_transceivers[0]->media_type());
// The answer should have only one media section for the new transceiver.
auto answer = caller->CreateAnswer();
@@ -907,8 +900,8 @@
// Setting the remote answer should succeed and not create any new
// transceivers.
ASSERT_TRUE(callee->SetRemoteDescription(std::move(answer)));
- ASSERT_EQ(2u, callee->pc()->GetTransceivers().size());
- ASSERT_EQ(2u, caller->pc()->GetTransceivers().size());
+ ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
+ ASSERT_EQ(1u, caller->pc()->GetTransceivers().size());
}
// Test that a m= section is *not* recycled if the media section is only
@@ -921,7 +914,7 @@
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
std::string first_mid = *caller_first_transceiver->mid();
- caller_first_transceiver->Stop();
+ caller_first_transceiver->StopInternal();
// The reoffer will have a rejected m= section.
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
@@ -959,7 +952,7 @@
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
std::string first_mid = *caller_first_transceiver->mid();
- caller_first_transceiver->Stop();
+ caller_first_transceiver->StopInternal();
// The reoffer will have a rejected m= section.
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
@@ -999,7 +992,7 @@
ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
std::string first_mid = *callee_first_transceiver->mid();
- caller_first_transceiver->Stop();
+ caller_first_transceiver->StopInternal();
// The reoffer will have a rejected m= section.
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
@@ -1036,7 +1029,7 @@
ASSERT_EQ(1u, callee->pc()->GetTransceivers().size());
auto callee_first_transceiver = callee->pc()->GetTransceivers()[0];
std::string first_mid = *callee_first_transceiver->mid();
- caller_first_transceiver->Stop();
+ caller_first_transceiver->StopInternal();
// The reoffer will have a rejected m= section.
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
@@ -1080,7 +1073,7 @@
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
- transceiver->Stop();
+ transceiver->StopInternal();
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
@@ -1367,7 +1360,7 @@
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
- caller->pc()->GetTransceivers()[0]->SetDirection(
+ caller->pc()->GetTransceivers()[0]->SetDirectionWithError(
RtpTransceiverDirection::kInactive);
// The transceiver direction on both sides will turn to inactive.
@@ -1395,7 +1388,7 @@
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
- transceiver->Stop();
+ transceiver->StopInternal();
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
@@ -1552,8 +1545,9 @@
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
ASSERT_TRUE(transceiver->current_direction());
- transceiver->Stop();
- EXPECT_FALSE(transceiver->current_direction());
+ transceiver->StopInternal();
+ EXPECT_EQ(transceiver->current_direction(),
+ RtpTransceiverDirection::kStopped);
}
// Test that you can't set an answer on a PeerConnection before setting the
@@ -2039,7 +2033,7 @@
EXPECT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
callee->AddAudioTrack("a");
- callee->pc()->GetTransceivers()[0]->SetDirection(
+ callee->pc()->GetTransceivers()[0]->SetDirectionWithError(
RtpTransceiverDirection::kSendOnly);
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
@@ -2063,7 +2057,7 @@
EXPECT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
// In stable make remote audio receive only.
- caller_transceiver->SetDirection(RtpTransceiverDirection::kRecvOnly);
+ caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly);
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
// The direction attribute is not modified by the offer.
diff --git a/pc/peer_connection_media_unittest.cc b/pc/peer_connection_media_unittest.cc
index 3c117c3..f078144 100644
--- a/pc/peer_connection_media_unittest.cc
+++ b/pc/peer_connection_media_unittest.cc
@@ -290,8 +290,8 @@
// Stop both audio and video transceivers on the caller.
auto transceivers = caller->pc()->GetTransceivers();
ASSERT_EQ(2u, transceivers.size());
- transceivers[0]->Stop();
- transceivers[1]->Stop();
+ transceivers[0]->StopInternal();
+ transceivers[1]->StopInternal();
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
@@ -388,8 +388,8 @@
// Stop both audio and video transceivers on the callee.
auto transceivers = callee->pc()->GetTransceivers();
ASSERT_EQ(2u, transceivers.size());
- transceivers[0]->Stop();
- transceivers[1]->Stop();
+ transceivers[0]->StopInternal();
+ transceivers[1]->StopInternal();
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
diff --git a/pc/peer_connection_rtp_unittest.cc b/pc/peer_connection_rtp_unittest.cc
index 9e4a816..e69a088 100644
--- a/pc/peer_connection_rtp_unittest.cc
+++ b/pc/peer_connection_rtp_unittest.cc
@@ -370,19 +370,25 @@
auto callee = CreatePeerConnection();
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
- transceiver->SetDirection(RtpTransceiverDirection::kInactive);
+ EXPECT_TRUE(
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kInactive)
+ .ok());
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
EXPECT_EQ(0u, caller->observer()->on_track_transceivers_.size());
EXPECT_EQ(0u, callee->observer()->on_track_transceivers_.size());
- transceiver->SetDirection(RtpTransceiverDirection::kSendOnly);
+ EXPECT_TRUE(
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendOnly)
+ .ok());
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
EXPECT_EQ(0u, caller->observer()->on_track_transceivers_.size());
EXPECT_EQ(1u, callee->observer()->on_track_transceivers_.size());
// If the direction changes but it is still receiving on the remote side, then
// OnTrack should not be fired again.
- transceiver->SetDirection(RtpTransceiverDirection::kSendRecv);
+ EXPECT_TRUE(
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv)
+ .ok());
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
EXPECT_EQ(0u, caller->observer()->on_track_transceivers_.size());
EXPECT_EQ(1u, callee->observer()->on_track_transceivers_.size());
@@ -401,8 +407,10 @@
EXPECT_EQ(1u, callee->observer()->on_track_transceivers_.size());
// Put the call on hold by no longer receiving the track.
- callee->pc()->GetTransceivers()[0]->SetDirection(
- RtpTransceiverDirection::kInactive);
+ EXPECT_TRUE(callee->pc()
+ ->GetTransceivers()[0]
+ ->SetDirectionWithError(RtpTransceiverDirection::kInactive)
+ .ok());
ASSERT_TRUE(callee->ExchangeOfferAnswerWith(caller.get()));
EXPECT_EQ(0u, caller->observer()->on_track_transceivers_.size());
@@ -410,8 +418,10 @@
// Resume the call by changing the direction to recvonly. This should call
// OnTrack again on the callee side.
- callee->pc()->GetTransceivers()[0]->SetDirection(
- RtpTransceiverDirection::kRecvOnly);
+ EXPECT_TRUE(callee->pc()
+ ->GetTransceivers()[0]
+ ->SetDirectionWithError(RtpTransceiverDirection::kRecvOnly)
+ .ok());
ASSERT_TRUE(callee->ExchangeOfferAnswerWith(caller.get()));
EXPECT_EQ(0u, caller->observer()->on_track_transceivers_.size());
@@ -470,7 +480,9 @@
EXPECT_EQ(0u, callee->observer()->remove_track_events_.size());
auto callee_transceiver = callee->pc()->GetTransceivers()[0];
- callee_transceiver->SetDirection(RtpTransceiverDirection::kSendOnly);
+ EXPECT_TRUE(callee_transceiver
+ ->SetDirectionWithError(RtpTransceiverDirection::kSendOnly)
+ .ok());
ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
EXPECT_EQ(1u, callee->observer()->add_track_events_.size());
@@ -1409,7 +1421,7 @@
ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
caller->observer()->clear_negotiation_needed();
- transceiver->SetDirection(RtpTransceiverDirection::kInactive);
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kInactive);
EXPECT_TRUE(caller->observer()->negotiation_needed());
}
@@ -1422,7 +1434,7 @@
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
caller->observer()->clear_negotiation_needed();
- transceiver->SetDirection(transceiver->direction());
+ transceiver->SetDirectionWithError(transceiver->direction());
EXPECT_FALSE(caller->observer()->negotiation_needed());
}
@@ -1433,13 +1445,71 @@
auto caller = CreatePeerConnection();
auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
- transceiver->Stop();
+ transceiver->StopInternal();
caller->observer()->clear_negotiation_needed();
- transceiver->SetDirection(RtpTransceiverDirection::kInactive);
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kInactive);
EXPECT_FALSE(caller->observer()->negotiation_needed());
}
+// Test that currentDirection returnes "stopped" if the transceiver was stopped.
+TEST_F(PeerConnectionRtpTestUnifiedPlan,
+ CheckStoppedCurrentDirectionOnStoppedTransceiver) {
+ auto caller = CreatePeerConnection();
+
+ auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+ transceiver->StopInternal();
+
+ EXPECT_TRUE(transceiver->stopping());
+ EXPECT_TRUE(transceiver->stopped());
+ EXPECT_EQ(RtpTransceiverDirection::kStopped,
+ transceiver->current_direction());
+}
+
+// Test that InvalidState is thrown on a stopping transceiver.
+TEST_F(PeerConnectionRtpTestUnifiedPlan,
+ CheckForInvalidStateOnStoppingTransceiver) {
+ auto caller = CreatePeerConnection();
+
+ auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+ transceiver->StopStandard();
+
+ EXPECT_TRUE(transceiver->stopping());
+ EXPECT_FALSE(transceiver->stopped());
+ EXPECT_EQ(
+ RTCErrorType::INVALID_STATE,
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kInactive)
+ .type());
+}
+
+// Test that InvalidState is thrown on a stopped transceiver.
+TEST_F(PeerConnectionRtpTestUnifiedPlan,
+ CheckForInvalidStateOnStoppedTransceiver) {
+ auto caller = CreatePeerConnection();
+
+ auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+ transceiver->StopInternal();
+
+ EXPECT_TRUE(transceiver->stopping());
+ EXPECT_TRUE(transceiver->stopped());
+ EXPECT_EQ(
+ RTCErrorType::INVALID_STATE,
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kInactive)
+ .type());
+}
+
+// Test that TypeError is thrown if the direction is set to "stopped".
+TEST_F(PeerConnectionRtpTestUnifiedPlan,
+ CheckForTypeErrorForStoppedOnTransceiver) {
+ auto caller = CreatePeerConnection();
+
+ auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+ EXPECT_EQ(
+ RTCErrorType::INVALID_PARAMETER,
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kStopped)
+ .type());
+}
+
// Test that AddTransceiver fails if trying to use unimplemented RTP encoding
// parameters with the send_encodings parameters.
TEST_F(PeerConnectionRtpTestUnifiedPlan,
diff --git a/pc/peer_connection_simulcast_unittest.cc b/pc/peer_connection_simulcast_unittest.cc
index 42bdae1..8822a98 100644
--- a/pc/peer_connection_simulcast_unittest.cc
+++ b/pc/peer_connection_simulcast_unittest.cc
@@ -455,7 +455,7 @@
std::string error;
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
auto transceiver = remote->pc()->GetTransceivers()[0];
- transceiver->SetDirection(RtpTransceiverDirection::kSendRecv);
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
EXPECT_TRUE(remote->CreateAnswerAndSetAsLocal());
ValidateTransceiverParameters(transceiver, layers);
}
@@ -478,7 +478,7 @@
auto transceivers = remote->pc()->GetTransceivers();
ASSERT_EQ(2u, transceivers.size());
auto transceiver = transceivers[1];
- transceiver->SetDirection(RtpTransceiverDirection::kSendRecv);
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
EXPECT_TRUE(remote->CreateAnswerAndSetAsLocal());
ValidateTransceiverParameters(transceiver, layers);
}
@@ -611,7 +611,7 @@
ElementsAre(Pair(kSimulcastApiVersionSpecCompliant, 1)));
auto transceiver = remote->pc()->GetTransceivers()[0];
- transceiver->SetDirection(RtpTransceiverDirection::kSendRecv);
+ transceiver->SetDirectionWithError(RtpTransceiverDirection::kSendRecv);
EXPECT_TRUE(remote->CreateAnswerAndSetAsLocal());
EXPECT_THAT(LocalDescriptionSamples(),
ElementsAre(Pair(kSimulcastApiVersionSpecCompliant, 2)));
diff --git a/pc/rtp_media_utils.cc b/pc/rtp_media_utils.cc
index 8fbfca1..c5d642b 100644
--- a/pc/rtp_media_utils.cc
+++ b/pc/rtp_media_utils.cc
@@ -42,6 +42,7 @@
switch (direction) {
case RtpTransceiverDirection::kSendRecv:
case RtpTransceiverDirection::kInactive:
+ case RtpTransceiverDirection::kStopped:
return direction;
case RtpTransceiverDirection::kSendOnly:
return RtpTransceiverDirection::kRecvOnly;
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc
index 3c56bf0..1430e29 100644
--- a/pc/rtp_sender.cc
+++ b/pc/rtp_sender.cc
@@ -184,6 +184,15 @@
RTCError RtpSenderBase::SetParameters(const RtpParameters& parameters) {
TRACE_EVENT0("webrtc", "RtpSenderBase::SetParameters");
+ if (is_transceiver_stopped_) {
+ LOG_AND_RETURN_ERROR(
+ RTCErrorType::INVALID_STATE,
+ "Cannot set parameters on sender of a stopped transceiver.");
+ }
+ if (stopped_) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
+ "Cannot set parameters on a stopped sender.");
+ }
if (stopped_) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
"Cannot set parameters on a stopped sender.");
diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h
index 15d47fd..c343ff0 100644
--- a/pc/rtp_sender.h
+++ b/pc/rtp_sender.h
@@ -69,6 +69,8 @@
// If the specified list is empty, this is a no-op.
virtual RTCError DisableEncodingLayers(
const std::vector<std::string>& rid) = 0;
+
+ virtual void SetTransceiverAsStopped() = 0;
};
// Shared implementation for RtpSenderInternal interface.
@@ -152,6 +154,8 @@
void SetEncoderToPacketizerFrameTransformer(
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer) override;
+ void SetTransceiverAsStopped() override { is_transceiver_stopped_ = true; }
+
protected:
// If |set_streams_observer| is not null, it is invoked when SetStreams()
// is called. |set_streams_observer| is not owned by this object. If not
@@ -180,6 +184,7 @@
rtc::Thread* worker_thread_;
uint32_t ssrc_ = 0;
bool stopped_ = false;
+ bool is_transceiver_stopped_ = false;
int attachment_id_ = 0;
const std::string id_;
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc
index b4e500b..701b83f 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -97,10 +97,19 @@
return RTCError::OK();
}
+TaskQueueBase* GetCurrentTaskQueueOrThread() {
+ TaskQueueBase* current = TaskQueueBase::Current();
+ if (!current)
+ current = rtc::ThreadManager::Instance()->CurrentThread();
+ return current;
+}
+
} // namespace
RtpTransceiver::RtpTransceiver(cricket::MediaType media_type)
- : unified_plan_(false), media_type_(media_type) {
+ : thread_(GetCurrentTaskQueueOrThread()),
+ unified_plan_(false),
+ media_type_(media_type) {
RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO ||
media_type == cricket::MEDIA_TYPE_VIDEO);
}
@@ -111,7 +120,8 @@
receiver,
cricket::ChannelManager* channel_manager,
std::vector<RtpHeaderExtensionCapability> header_extensions_offered)
- : unified_plan_(true),
+ : thread_(GetCurrentTaskQueueOrThread()),
+ unified_plan_(true),
media_type_(sender->media_type()),
channel_manager_(channel_manager),
header_extensions_to_offer_(std::move(header_extensions_offered)) {
@@ -123,7 +133,7 @@
}
RtpTransceiver::~RtpTransceiver() {
- Stop();
+ StopInternal();
}
void RtpTransceiver::SetChannel(cricket::ChannelInterface* channel) {
@@ -277,23 +287,47 @@
return stopped_;
}
+bool RtpTransceiver::stopping() const {
+ RTC_DCHECK_RUN_ON(thread_);
+ return stopping_;
+}
+
RtpTransceiverDirection RtpTransceiver::direction() const {
+ if (unified_plan_ && stopping())
+ return webrtc::RtpTransceiverDirection::kStopped;
+
return direction_;
}
void RtpTransceiver::SetDirection(RtpTransceiverDirection new_direction) {
- if (stopped()) {
- return;
+ SetDirectionWithError(new_direction);
+}
+
+RTCError RtpTransceiver::SetDirectionWithError(
+ RtpTransceiverDirection new_direction) {
+ if (unified_plan_ && stopping()) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
+ "Cannot set direction on a stopping transceiver.");
}
- if (new_direction == direction_) {
- return;
+ if (new_direction == direction_)
+ return RTCError::OK();
+
+ if (new_direction == RtpTransceiverDirection::kStopped) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
+ "The set direction 'stopped' is invalid.");
}
+
direction_ = new_direction;
SignalNegotiationNeeded();
+
+ return RTCError::OK();
}
absl::optional<RtpTransceiverDirection> RtpTransceiver::current_direction()
const {
+ if (unified_plan_ && stopping())
+ return webrtc::RtpTransceiverDirection::kStopped;
+
return current_direction_;
}
@@ -302,14 +336,69 @@
return fired_direction_;
}
-void RtpTransceiver::Stop() {
- for (const auto& sender : senders_) {
+void RtpTransceiver::StopSendingAndReceiving() {
+ // 1. Let sender be transceiver.[[Sender]].
+ // 2. Let receiver be transceiver.[[Receiver]].
+ //
+ // 3. Stop sending media with sender.
+ //
+ // 4. Send an RTCP BYE for each RTP stream that was being sent by sender, as
+ // specified in [RFC3550].
+ RTC_DCHECK_RUN_ON(thread_);
+ for (const auto& sender : senders_)
sender->internal()->Stop();
- }
- for (const auto& receiver : receivers_) {
+
+ // 5. Stop receiving media with receiver.
+ for (const auto& receiver : receivers_)
receiver->internal()->Stop();
+
+ stopping_ = true;
+ direction_ = webrtc::RtpTransceiverDirection::kInactive;
+}
+
+RTCError RtpTransceiver::StopStandard() {
+ RTC_DCHECK_RUN_ON(thread_);
+ RTC_DCHECK(unified_plan_);
+ // 1. Let transceiver be the RTCRtpTransceiver object on which the method is
+ // invoked.
+ //
+ // 2. Let connection be the RTCPeerConnection object associated with
+ // transceiver.
+ //
+ // 3. If connection.[[IsClosed]] is true, throw an InvalidStateError.
+ if (is_pc_closed_) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
+ "PeerConnection is closed.");
}
+
+ // 4. If transceiver.[[Stopping]] is true, abort these steps.
+ if (stopping_)
+ return RTCError::OK();
+
+ // 5. Stop sending and receiving given transceiver, and update the
+ // negotiation-needed flag for connection.
+ StopSendingAndReceiving();
+ SignalNegotiationNeeded();
+
+ return RTCError::OK();
+}
+
+void RtpTransceiver::StopInternal() {
+ RTC_DCHECK_RUN_ON(thread_);
+ // 1. If transceiver.[[Stopping]] is false, stop sending and receiving given
+ // transceiver.
+ if (!stopping_)
+ StopSendingAndReceiving();
+
+ // 2. Set transceiver.[[Stopped]] to true.
stopped_ = true;
+
+ // Signal the updated change to the senders.
+ for (const auto& sender : senders_)
+ sender->internal()->SetTransceiverAsStopped();
+
+ // 3. Set transceiver.[[Receptive]] to false.
+ // 4. Set transceiver.[[CurrentDirection]] to null.
current_direction_ = absl::nullopt;
}
@@ -403,4 +492,8 @@
return RTCError::OK();
}
+void RtpTransceiver::SetPeerConnectionClosed() {
+ is_pc_closed_ = true;
+}
+
} // namespace webrtc
diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h
index be46ccf..980d64c 100644
--- a/pc/rtp_transceiver.h
+++ b/pc/rtp_transceiver.h
@@ -173,6 +173,10 @@
return has_ever_been_used_to_send_;
}
+ // Informs the transceiver that its owning
+ // PeerConnection is closed.
+ void SetPeerConnectionClosed();
+
// Fired when the RtpTransceiver state changes such that negotiation is now
// needed (e.g., in response to a direction change).
sigslot::signal0<> SignalNegotiationNeeded;
@@ -183,11 +187,15 @@
rtc::scoped_refptr<RtpSenderInterface> sender() const override;
rtc::scoped_refptr<RtpReceiverInterface> receiver() const override;
bool stopped() const override;
+ bool stopping() const override;
RtpTransceiverDirection direction() const override;
void SetDirection(RtpTransceiverDirection new_direction) override;
+ RTCError SetDirectionWithError(
+ RtpTransceiverDirection new_direction) override;
absl::optional<RtpTransceiverDirection> current_direction() const override;
absl::optional<RtpTransceiverDirection> fired_direction() const override;
- void Stop() override;
+ RTCError StopStandard() override;
+ void StopInternal() override;
RTCError SetCodecPreferences(
rtc::ArrayView<RtpCodecCapability> codecs) override;
std::vector<RtpCodecCapability> codec_preferences() const override {
@@ -201,7 +209,10 @@
private:
void OnFirstPacketReceived(cricket::ChannelInterface* channel);
+ void StopSendingAndReceiving();
+ // Enforce that this object is created, used and destroyed on one thread.
+ const TaskQueueBase* thread_;
const bool unified_plan_;
const cricket::MediaType media_type_;
std::vector<rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>
@@ -211,6 +222,8 @@
receivers_;
bool stopped_ = false;
+ bool stopping_ RTC_GUARDED_BY(thread_) = false;
+ bool is_pc_closed_ = false;
RtpTransceiverDirection direction_ = RtpTransceiverDirection::kInactive;
absl::optional<RtpTransceiverDirection> current_direction_;
absl::optional<RtpTransceiverDirection> fired_direction_;
@@ -233,11 +246,14 @@
PROXY_CONSTMETHOD0(rtc::scoped_refptr<RtpSenderInterface>, sender)
PROXY_CONSTMETHOD0(rtc::scoped_refptr<RtpReceiverInterface>, receiver)
PROXY_CONSTMETHOD0(bool, stopped)
+PROXY_CONSTMETHOD0(bool, stopping)
PROXY_CONSTMETHOD0(RtpTransceiverDirection, direction)
PROXY_METHOD1(void, SetDirection, RtpTransceiverDirection)
+PROXY_METHOD1(webrtc::RTCError, SetDirectionWithError, RtpTransceiverDirection)
PROXY_CONSTMETHOD0(absl::optional<RtpTransceiverDirection>, current_direction)
PROXY_CONSTMETHOD0(absl::optional<RtpTransceiverDirection>, fired_direction)
-PROXY_METHOD0(void, Stop)
+PROXY_METHOD0(webrtc::RTCError, StopStandard)
+PROXY_METHOD0(void, StopInternal)
PROXY_METHOD1(webrtc::RTCError,
SetCodecPreferences,
rtc::ArrayView<RtpCodecCapability>)
diff --git a/pc/rtp_transceiver_unittest.cc b/pc/rtp_transceiver_unittest.cc
index e3f05c4..78abfff 100644
--- a/pc/rtp_transceiver_unittest.cc
+++ b/pc/rtp_transceiver_unittest.cc
@@ -45,7 +45,7 @@
EXPECT_EQ(&channel1, transceiver.channel());
// Stop the transceiver.
- transceiver.Stop();
+ transceiver.StopInternal();
EXPECT_EQ(&channel1, transceiver.channel());
cricket::MockChannelInterface channel2;
@@ -71,7 +71,7 @@
EXPECT_EQ(&channel, transceiver.channel());
// Stop the transceiver.
- transceiver.Stop();
+ transceiver.StopInternal();
EXPECT_EQ(&channel, transceiver.channel());
// Set the channel to |nullptr|.
diff --git a/pc/test/mock_rtp_sender_internal.h b/pc/test/mock_rtp_sender_internal.h
index 1a31c5d..5e7670e 100644
--- a/pc/test/mock_rtp_sender_internal.h
+++ b/pc/test/mock_rtp_sender_internal.h
@@ -65,23 +65,17 @@
(const, override));
// RtpSenderInternal methods.
- MOCK_METHOD(void, SetMediaChannel, (cricket::MediaChannel*), (override));
- MOCK_METHOD(void, SetSsrc, (uint32_t), (override));
- MOCK_METHOD(void,
- set_stream_ids,
- (const std::vector<std::string>&),
- (override));
- MOCK_METHOD(void, SetStreams, (const std::vector<std::string>&), (override));
- MOCK_METHOD(void,
- set_init_send_encodings,
- (const std::vector<RtpEncodingParameters>&),
- (override));
- MOCK_METHOD(void, Stop, (), (override));
- MOCK_METHOD(int, AttachmentId, (), (const, override));
- MOCK_METHOD(RTCError,
- DisableEncodingLayers,
- (const std::vector<std::string>&),
- (override));
+ MOCK_METHOD1(SetMediaChannel, void(cricket::MediaChannel*));
+ MOCK_METHOD1(SetSsrc, void(uint32_t));
+ MOCK_METHOD1(set_stream_ids, void(const std::vector<std::string>&));
+ MOCK_METHOD1(SetStreams, void(const std::vector<std::string>&));
+ MOCK_METHOD1(set_init_send_encodings,
+ void(const std::vector<RtpEncodingParameters>&));
+ MOCK_METHOD0(Stop, void());
+ MOCK_CONST_METHOD0(AttachmentId, int());
+ MOCK_METHOD1(DisableEncodingLayers,
+ RTCError(const std::vector<std::string>&));
+ MOCK_METHOD0(SetTransceiverAsStopped, void());
};
} // namespace webrtc
diff --git a/pc/webrtc_sdp.cc b/pc/webrtc_sdp.cc
index 7a2aeae..4af121d 100644
--- a/pc/webrtc_sdp.cc
+++ b/pc/webrtc_sdp.cc
@@ -1568,6 +1568,8 @@
// RFC 3264
// a=sendrecv || a=sendonly || a=sendrecv || a=inactive
switch (media_desc->direction()) {
+ // Special case that for sdp purposes should be treated same as inactive.
+ case RtpTransceiverDirection::kStopped:
case RtpTransceiverDirection::kInactive:
InitAttrLine(kAttributeInactive, &os);
break;
@@ -1580,9 +1582,7 @@
case RtpTransceiverDirection::kSendRecv:
InitAttrLine(kAttributeSendRecv, &os);
break;
- case RtpTransceiverDirection::kStopped:
default:
- // kStopped shouldn't be used in signalling.
RTC_NOTREACHED();
InitAttrLine(kAttributeSendRecv, &os);
break;
diff --git a/sdk/android/api/org/webrtc/RtpTransceiver.java b/sdk/android/api/org/webrtc/RtpTransceiver.java
index 64d8eb4..021cc90 100644
--- a/sdk/android/api/org/webrtc/RtpTransceiver.java
+++ b/sdk/android/api/org/webrtc/RtpTransceiver.java
@@ -206,13 +206,34 @@
}
/**
- * The Stop method irreversibly stops the RtpTransceiver. The sender of this
- * transceiver will no longer send, the receiver will no longer receive.
- * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-stop
+ * The Stop method will for the time being call the StopInternal method.
+ * After a migration procedure, stop() will be equivalent to StopStandard.
*/
public void stop() {
checkRtpTransceiverExists();
- nativeStop(nativeRtpTransceiver);
+ nativeStopInternal(nativeRtpTransceiver);
+ }
+
+ /**
+ * The StopInternal method stops the RtpTransceiver, like Stop, but goes
+ * immediately to Stopped state.
+ */
+ public void stopInternal() {
+ checkRtpTransceiverExists();
+ nativeStopInternal(nativeRtpTransceiver);
+ }
+
+ /**
+ * The StopStandard method irreversibly stops the RtpTransceiver. The sender
+ * of this transceiver will no longer send, the receiver will no longer
+ * receive.
+ *
+ * <p>The transceiver will enter Stopping state and signal NegotiationNeeded.
+ * https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-stop
+ */
+ public void stopStandard() {
+ checkRtpTransceiverExists();
+ nativeStopStandard(nativeRtpTransceiver);
}
@CalledByNative
@@ -237,7 +258,8 @@
private static native boolean nativeStopped(long rtpTransceiver);
private static native RtpTransceiverDirection nativeDirection(long rtpTransceiver);
private static native RtpTransceiverDirection nativeCurrentDirection(long rtpTransceiver);
- private static native void nativeStop(long rtpTransceiver);
+ private static native void nativeStopInternal(long rtpTransceiver);
+ private static native void nativeStopStandard(long rtpTransceiver);
private static native void nativeSetDirection(
long rtpTransceiver, RtpTransceiverDirection rtpTransceiverDirection);
}
diff --git a/sdk/android/src/jni/pc/rtp_transceiver.cc b/sdk/android/src/jni/pc/rtp_transceiver.cc
index 7d8cfde..a0b3c20 100644
--- a/sdk/android/src/jni/pc/rtp_transceiver.cc
+++ b/sdk/android/src/jni/pc/rtp_transceiver.cc
@@ -139,9 +139,16 @@
: nullptr;
}
-void JNI_RtpTransceiver_Stop(JNIEnv* jni,
- jlong j_rtp_transceiver_pointer) {
- reinterpret_cast<RtpTransceiverInterface*>(j_rtp_transceiver_pointer)->Stop();
+void JNI_RtpTransceiver_StopInternal(JNIEnv* jni,
+ jlong j_rtp_transceiver_pointer) {
+ reinterpret_cast<RtpTransceiverInterface*>(j_rtp_transceiver_pointer)
+ ->StopInternal();
+}
+
+void JNI_RtpTransceiver_StopStandard(JNIEnv* jni,
+ jlong j_rtp_transceiver_pointer) {
+ reinterpret_cast<RtpTransceiverInterface*>(j_rtp_transceiver_pointer)
+ ->StopStandard();
}
void JNI_RtpTransceiver_SetDirection(
diff --git a/sdk/objc/api/peerconnection/RTCRtpTransceiver.h b/sdk/objc/api/peerconnection/RTCRtpTransceiver.h
index f8996cc..17054f5 100644
--- a/sdk/objc/api/peerconnection/RTCRtpTransceiver.h
+++ b/sdk/objc/api/peerconnection/RTCRtpTransceiver.h
@@ -117,7 +117,7 @@
* this transceiver will no longer send, the receiver will no longer receive.
* https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-stop
*/
-- (void)stop;
+- (void)stopInternal;
@end
diff --git a/sdk/objc/api/peerconnection/RTCRtpTransceiver.mm b/sdk/objc/api/peerconnection/RTCRtpTransceiver.mm
index 2995e5f..5d0d8ed 100644
--- a/sdk/objc/api/peerconnection/RTCRtpTransceiver.mm
+++ b/sdk/objc/api/peerconnection/RTCRtpTransceiver.mm
@@ -90,8 +90,8 @@
}
}
-- (void)stop {
- _nativeRtpTransceiver->Stop();
+- (void)stopInternal {
+ _nativeRtpTransceiver->StopInternal();
}
- (NSString *)description {