When stopping a transceiver, end the receiver's track.

Bug: webrtc:11840
Change-Id: Ib8171c58fcb13c33ab03398eb3021c07e55ff008
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/185181
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32188}
diff --git a/pc/audio_rtp_receiver.cc b/pc/audio_rtp_receiver.cc
index 54912a5..8ff685d 100644
--- a/pc/audio_rtp_receiver.cc
+++ b/pc/audio_rtp_receiver.cc
@@ -42,8 +42,9 @@
     : worker_thread_(worker_thread),
       id_(receiver_id),
       source_(new rtc::RefCountedObject<RemoteAudioSource>(worker_thread)),
-      track_(AudioTrackProxy::Create(rtc::Thread::Current(),
-                                     AudioTrack::Create(receiver_id, source_))),
+      track_(AudioTrackProxyWithInternal<AudioTrack>::Create(
+          rtc::Thread::Current(),
+          AudioTrack::Create(receiver_id, source_))),
       cached_track_enabled_(track_->enabled()),
       attachment_id_(GenerateUniqueId()),
       delay_(JitterBufferDelayProxy::Create(
@@ -146,6 +147,11 @@
   stopped_ = true;
 }
 
+void AudioRtpReceiver::StopAndEndTrack() {
+  Stop();
+  track_->internal()->set_ended();
+}
+
 void AudioRtpReceiver::RestartMediaChannel(absl::optional<uint32_t> ssrc) {
   RTC_DCHECK(media_channel_);
   if (!stopped_ && ssrc_ == ssrc) {
diff --git a/pc/audio_rtp_receiver.h b/pc/audio_rtp_receiver.h
index 88b16ee..f4b8210 100644
--- a/pc/audio_rtp_receiver.h
+++ b/pc/audio_rtp_receiver.h
@@ -19,10 +19,12 @@
 #include "absl/types/optional.h"
 #include "api/crypto/frame_decryptor_interface.h"
 #include "api/media_stream_interface.h"
+#include "api/media_stream_track_proxy.h"
 #include "api/media_types.h"
 #include "api/rtp_parameters.h"
 #include "api/scoped_refptr.h"
 #include "media/base/media_channel.h"
+#include "pc/audio_track.h"
 #include "pc/jitter_buffer_delay_interface.h"
 #include "pc/remote_audio_source.h"
 #include "pc/rtp_receiver.h"
@@ -84,6 +86,7 @@
 
   // RtpReceiverInternal implementation.
   void Stop() override;
+  void StopAndEndTrack() override;
   void SetupMediaChannel(uint32_t ssrc) override;
   void SetupUnsignaledMediaChannel() override;
   uint32_t ssrc() const override { return ssrc_.value_or(0); }
@@ -116,7 +119,7 @@
   rtc::Thread* const worker_thread_;
   const std::string id_;
   const rtc::scoped_refptr<RemoteAudioSource> source_;
-  const rtc::scoped_refptr<AudioTrackInterface> track_;
+  const rtc::scoped_refptr<AudioTrackProxyWithInternal<AudioTrack>> track_;
   cricket::VoiceMediaChannel* media_channel_ = nullptr;
   absl::optional<uint32_t> ssrc_;
   std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_;
diff --git a/pc/audio_track.h b/pc/audio_track.h
index f89bbcd..f8ad471 100644
--- a/pc/audio_track.h
+++ b/pc/audio_track.h
@@ -34,10 +34,10 @@
       const std::string& id,
       const rtc::scoped_refptr<AudioSourceInterface>& source);
 
- private:
   // MediaStreamTrack implementation.
   std::string kind() const override;
 
+ private:
   // AudioTrackInterface implementation.
   AudioSourceInterface* GetSource() const override;
 
diff --git a/pc/media_stream_track.h b/pc/media_stream_track.h
index 25c3663..fd63569 100644
--- a/pc/media_stream_track.h
+++ b/pc/media_stream_track.h
@@ -15,6 +15,7 @@
 
 #include "api/media_stream_interface.h"
 #include "api/notifier.h"
+#include "rtc_base/logging.h"
 
 namespace webrtc {
 
@@ -38,6 +39,7 @@
     }
     return fire_on_change;
   }
+  void set_ended() { set_state(MediaStreamTrackInterface::TrackState::kEnded); }
 
  protected:
   explicit MediaStreamTrack(const std::string& id)
diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc
index 4b098e1..a8471c9 100644
--- a/pc/peer_connection_integrationtest.cc
+++ b/pc/peer_connection_integrationtest.cc
@@ -5603,6 +5603,52 @@
                           PeerConnectionInterface::kIceGatheringNew));
 }
 
+TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
+       StopTransceiverEndsIncomingAudioTrack) {
+  RTCConfiguration config;
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config));
+  ConnectFakeSignaling();
+  auto audio_transceiver_or_error =
+      caller()->pc()->AddTransceiver(caller()->CreateLocalAudioTrack());
+  ASSERT_TRUE(audio_transceiver_or_error.ok());
+  auto audio_transceiver = audio_transceiver_or_error.MoveValue();
+
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+  auto caller_track = audio_transceiver->receiver()->track();
+  auto callee_track = callee()->pc()->GetReceivers()[0]->track();
+  audio_transceiver->StopStandard();
+  EXPECT_EQ(MediaStreamTrackInterface::TrackState::kEnded,
+            caller_track->state());
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+  EXPECT_EQ(MediaStreamTrackInterface::TrackState::kEnded,
+            callee_track->state());
+}
+
+TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
+       StopTransceiverEndsIncomingVideoTrack) {
+  RTCConfiguration config;
+  ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config));
+  ConnectFakeSignaling();
+  auto audio_transceiver_or_error =
+      caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack());
+  ASSERT_TRUE(audio_transceiver_or_error.ok());
+  auto audio_transceiver = audio_transceiver_or_error.MoveValue();
+
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+  auto caller_track = audio_transceiver->receiver()->track();
+  auto callee_track = callee()->pc()->GetReceivers()[0]->track();
+  audio_transceiver->StopStandard();
+  EXPECT_EQ(MediaStreamTrackInterface::TrackState::kEnded,
+            caller_track->state());
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+  EXPECT_EQ(MediaStreamTrackInterface::TrackState::kEnded,
+            callee_track->state());
+}
+
 #ifdef HAVE_SCTP
 
 TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
diff --git a/pc/rtp_receiver.h b/pc/rtp_receiver.h
index 84c2ff7..2cfccd4 100644
--- a/pc/rtp_receiver.h
+++ b/pc/rtp_receiver.h
@@ -41,7 +41,11 @@
 // Internal class used by PeerConnection.
 class RtpReceiverInternal : public RtpReceiverInterface {
  public:
+  // Stops receiving. The track may be reactivated.
   virtual void Stop() = 0;
+  // Stops the receiver permanently.
+  // Causes the associated track to enter kEnded state. Cannot be reversed.
+  virtual void StopAndEndTrack() = 0;
 
   // Sets the underlying MediaEngine channel associated with this RtpSender.
   // A VoiceMediaChannel should be used for audio RtpSenders and
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc
index fd6f4bb..fd8ff81 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -346,7 +346,7 @@
 
   // 5. Stop receiving media with receiver.
   for (const auto& receiver : receivers_)
-    receiver->internal()->Stop();
+    receiver->internal()->StopAndEndTrack();
 
   stopping_ = true;
   direction_ = webrtc::RtpTransceiverDirection::kInactive;
diff --git a/pc/test/mock_rtp_receiver_internal.h b/pc/test/mock_rtp_receiver_internal.h
index 779dcdc..ba24403 100644
--- a/pc/test/mock_rtp_receiver_internal.h
+++ b/pc/test/mock_rtp_receiver_internal.h
@@ -57,6 +57,7 @@
 
   // RtpReceiverInternal methods.
   MOCK_METHOD(void, Stop, (), (override));
+  MOCK_METHOD(void, StopAndEndTrack, (), (override));
   MOCK_METHOD(void, SetMediaChannel, (cricket::MediaChannel*), (override));
   MOCK_METHOD(void, SetupMediaChannel, (uint32_t), (override));
   MOCK_METHOD(void, SetupUnsignaledMediaChannel, (), (override));
diff --git a/pc/video_rtp_receiver.cc b/pc/video_rtp_receiver.cc
index f093bf4..dd60125 100644
--- a/pc/video_rtp_receiver.cc
+++ b/pc/video_rtp_receiver.cc
@@ -16,7 +16,6 @@
 #include <vector>
 
 #include "api/media_stream_proxy.h"
-#include "api/media_stream_track_proxy.h"
 #include "api/video_track_source_proxy.h"
 #include "pc/jitter_buffer_delay.h"
 #include "pc/jitter_buffer_delay_proxy.h"
@@ -43,7 +42,7 @@
     : worker_thread_(worker_thread),
       id_(receiver_id),
       source_(new RefCountedObject<VideoRtpTrackSource>(this)),
-      track_(VideoTrackProxy::Create(
+      track_(VideoTrackProxyWithInternal<VideoTrack>::Create(
           rtc::Thread::Current(),
           worker_thread,
           VideoTrack::Create(
@@ -136,6 +135,11 @@
   stopped_ = true;
 }
 
+void VideoRtpReceiver::StopAndEndTrack() {
+  Stop();
+  track_->internal()->set_ended();
+}
+
 void VideoRtpReceiver::RestartMediaChannel(absl::optional<uint32_t> ssrc) {
   RTC_DCHECK(media_channel_);
   if (!stopped_ && ssrc_ == ssrc) {
diff --git a/pc/video_rtp_receiver.h b/pc/video_rtp_receiver.h
index f66a8a7..74ae444 100644
--- a/pc/video_rtp_receiver.h
+++ b/pc/video_rtp_receiver.h
@@ -20,6 +20,7 @@
 #include "api/crypto/frame_decryptor_interface.h"
 #include "api/frame_transformer_interface.h"
 #include "api/media_stream_interface.h"
+#include "api/media_stream_track_proxy.h"
 #include "api/media_types.h"
 #include "api/rtp_parameters.h"
 #include "api/rtp_receiver_interface.h"
@@ -31,6 +32,7 @@
 #include "pc/jitter_buffer_delay_interface.h"
 #include "pc/rtp_receiver.h"
 #include "pc/video_rtp_track_source.h"
+#include "pc/video_track.h"
 #include "rtc_base/ref_counted_object.h"
 #include "rtc_base/thread.h"
 
@@ -89,6 +91,7 @@
 
   // RtpReceiverInternal implementation.
   void Stop() override;
+  void StopAndEndTrack() override;
   void SetupMediaChannel(uint32_t ssrc) override;
   void SetupUnsignaledMediaChannel() override;
   uint32_t ssrc() const override { return ssrc_.value_or(0); }
@@ -130,7 +133,7 @@
   // |source_| is held here to be able to change the state of the source when
   // the VideoRtpReceiver is stopped.
   rtc::scoped_refptr<VideoRtpTrackSource> source_;
-  rtc::scoped_refptr<VideoTrackInterface> track_;
+  rtc::scoped_refptr<VideoTrackProxyWithInternal<VideoTrack>> track_;
   std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams_;
   bool stopped_ = true;
   RtpReceiverObserverInterface* observer_ = nullptr;