Disable padding for paused encoders even on reconfiguration

Bug: None
Change-Id: If5bdcd5197f82abc9d39ecacc24e58d5b92d6780
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/132324
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27561}
diff --git a/video/video_send_stream_impl.cc b/video/video_send_stream_impl.cc
index 81d269d..0b3d081 100644
--- a/video/video_send_stream_impl.cc
+++ b/video/video_send_stream_impl.cc
@@ -222,6 +222,7 @@
       call_stats_(call_stats),
       transport_(transport),
       bitrate_allocator_(bitrate_allocator),
+      disable_padding_(true),
       max_padding_bitrate_(0),
       encoder_min_bitrate_bps_(0),
       encoder_target_rate_bps_(0),
@@ -398,6 +399,7 @@
               SignalEncoderTimedOut();
             }
             timed_out_ = true;
+            disable_padding_ = true;
           } else if (timed_out_) {
             SignalEncoderActive();
             timed_out_ = false;
@@ -490,15 +492,17 @@
 
 void VideoSendStreamImpl::SignalEncoderActive() {
   RTC_DCHECK_RUN_ON(worker_queue_);
-  RTC_LOG(LS_INFO) << "SignalEncoderActive, Encoder is active.";
-  bitrate_allocator_->AddObserver(this, GetAllocationConfig());
+  if (rtp_video_sender_->IsActive()) {
+    RTC_LOG(LS_INFO) << "SignalEncoderActive, Encoder is active.";
+    bitrate_allocator_->AddObserver(this, GetAllocationConfig());
+  }
 }
 
 MediaStreamAllocationConfig VideoSendStreamImpl::GetAllocationConfig() const {
   return MediaStreamAllocationConfig{
       static_cast<uint32_t>(encoder_min_bitrate_bps_),
       encoder_max_bitrate_bps_,
-      static_cast<uint32_t>(max_padding_bitrate_),
+      static_cast<uint32_t>(disable_padding_ ? 0 : max_padding_bitrate_),
       /* priority_bitrate */ 0,
       !config_->suspend_below_min_bitrate,
       config_->track_id,
@@ -584,6 +588,20 @@
   // Indicate that there still is activity going on.
   activity_ = true;
 
+  auto enable_padding_task = [this]() {
+    if (disable_padding_) {
+      RTC_DCHECK_RUN_ON(worker_queue_);
+      disable_padding_ = false;
+      // To ensure that padding bitrate is propagated to the bitrate allocator.
+      SignalEncoderActive();
+    }
+  };
+  if (!worker_queue_->IsCurrent()) {
+    worker_queue_->PostTask(enable_padding_task);
+  } else {
+    enable_padding_task();
+  }
+
   EncodedImageCallback::Result result(EncodedImageCallback::Result::OK);
   if (media_transport_) {
     int64_t frame_id;
diff --git a/video/video_send_stream_impl.h b/video/video_send_stream_impl.h
index f9393d8..c451576 100644
--- a/video/video_send_stream_impl.h
+++ b/video/video_send_stream_impl.h
@@ -163,6 +163,7 @@
 
   rtc::CriticalSection ivf_writers_crit_;
 
+  bool disable_padding_;
   int max_padding_bitrate_;
   int encoder_min_bitrate_bps_;
   uint32_t encoder_max_bitrate_bps_;
diff --git a/video/video_send_stream_impl_unittest.cc b/video/video_send_stream_impl_unittest.cc
index cae3b86..8126835 100644
--- a/video/video_send_stream_impl_unittest.cc
+++ b/video/video_send_stream_impl_unittest.cc
@@ -213,16 +213,18 @@
     config_.rtp.ssrcs.emplace_back(2);
 
     EXPECT_CALL(bitrate_allocator_, AddObserver(vss_impl.get(), _))
-        .WillOnce(Invoke(
+        .WillRepeatedly(Invoke(
             [&](BitrateAllocatorObserver*, MediaStreamAllocationConfig config) {
               EXPECT_EQ(config.min_bitrate_bps,
                         static_cast<uint32_t>(min_transmit_bitrate_bps));
               EXPECT_EQ(config.max_bitrate_bps,
                         static_cast<uint32_t>(qvga_stream.max_bitrate_bps +
                                               vga_stream.max_bitrate_bps));
-              EXPECT_EQ(config.pad_up_bitrate_bps,
-                        static_cast<uint32_t>(qvga_stream.target_bitrate_bps +
-                                              vga_stream.min_bitrate_bps));
+              if (config.pad_up_bitrate_bps != 0) {
+                EXPECT_EQ(config.pad_up_bitrate_bps,
+                          static_cast<uint32_t>(qvga_stream.target_bitrate_bps +
+                                                vga_stream.min_bitrate_bps));
+              }
               EXPECT_EQ(config.enforce_min_bitrate, !kSuspend);
             }));
 
@@ -279,15 +281,17 @@
     config_.rtp.ssrcs.emplace_back(2);
 
     EXPECT_CALL(bitrate_allocator_, AddObserver(vss_impl.get(), _))
-        .WillOnce(Invoke(
+        .WillRepeatedly(Invoke(
             [&](BitrateAllocatorObserver*, MediaStreamAllocationConfig config) {
               EXPECT_EQ(config.min_bitrate_bps,
                         static_cast<uint32_t>(low_stream.min_bitrate_bps));
               EXPECT_EQ(config.max_bitrate_bps,
                         static_cast<uint32_t>(low_stream.max_bitrate_bps +
                                               high_stream.max_bitrate_bps));
-              EXPECT_EQ(config.pad_up_bitrate_bps,
-                        static_cast<uint32_t>(min_transmit_bitrate_bps));
+              if (config.pad_up_bitrate_bps != 0) {
+                EXPECT_EQ(config.pad_up_bitrate_bps,
+                          static_cast<uint32_t>(min_transmit_bitrate_bps));
+              }
               EXPECT_EQ(config.enforce_min_bitrate, !kSuspend);
             }));
 
@@ -335,16 +339,19 @@
     config_.rtp.ssrcs.emplace_back(2);
 
     EXPECT_CALL(bitrate_allocator_, AddObserver(vss_impl.get(), _))
-        .WillOnce(Invoke([&](BitrateAllocatorObserver*,
-                             MediaStreamAllocationConfig config) {
+        .WillRepeatedly(Invoke([&](BitrateAllocatorObserver*,
+                                   MediaStreamAllocationConfig config) {
           EXPECT_EQ(config.min_bitrate_bps,
                     static_cast<uint32_t>(low_stream.min_bitrate_bps));
           EXPECT_EQ(config.max_bitrate_bps,
                     static_cast<uint32_t>(low_stream.max_bitrate_bps +
                                           high_stream.max_bitrate_bps));
-          EXPECT_EQ(config.pad_up_bitrate_bps,
-                    static_cast<uint32_t>(low_stream.target_bitrate_bps +
-                                          1.25 * high_stream.min_bitrate_bps));
+          if (config.pad_up_bitrate_bps != 0) {
+            EXPECT_EQ(
+                config.pad_up_bitrate_bps,
+                static_cast<uint32_t>(low_stream.target_bitrate_bps +
+                                      1.25 * high_stream.min_bitrate_bps));
+          }
         }));
 
     static_cast<VideoStreamEncoderInterface::EncoderSink*>(vss_impl.get())
@@ -726,5 +733,96 @@
   });
 }
 
+TEST_F(VideoSendStreamImplTest, DisablesPaddingOnPausedEncoder) {
+  int padding_bitrate = 0;
+  std::unique_ptr<VideoSendStreamImpl> vss_impl;
+
+  test_queue_.SendTask([&] {
+    vss_impl = CreateVideoSendStreamImpl(
+        kDefaultInitialBitrateBps, kDefaultBitratePriority,
+        VideoEncoderConfig::ContentType::kRealtimeVideo);
+
+    // Capture padding bitrate for testing.
+    EXPECT_CALL(bitrate_allocator_, AddObserver(vss_impl.get(), _))
+        .WillRepeatedly(Invoke(
+            [&](BitrateAllocatorObserver*, MediaStreamAllocationConfig config) {
+              padding_bitrate = config.pad_up_bitrate_bps;
+            }));
+    // If observer is removed, no padding will be sent.
+    EXPECT_CALL(bitrate_allocator_, RemoveObserver(vss_impl.get()))
+        .WillRepeatedly(
+            Invoke([&](BitrateAllocatorObserver*) { padding_bitrate = 0; }));
+
+    EXPECT_CALL(rtp_video_sender_, OnEncodedImage(_, _, _))
+        .WillRepeatedly(Return(
+            EncodedImageCallback::Result(EncodedImageCallback::Result::OK)));
+
+    config_.track_id = "test";
+    const bool kSuspend = false;
+    config_.suspend_below_min_bitrate = kSuspend;
+    config_.rtp.extensions.emplace_back(
+        RtpExtension::kTransportSequenceNumberUri, 1);
+    VideoStream qvga_stream;
+    qvga_stream.width = 320;
+    qvga_stream.height = 180;
+    qvga_stream.max_framerate = 30;
+    qvga_stream.min_bitrate_bps = 30000;
+    qvga_stream.target_bitrate_bps = 150000;
+    qvga_stream.max_bitrate_bps = 200000;
+    qvga_stream.max_qp = 56;
+    qvga_stream.bitrate_priority = 1;
+
+    int min_transmit_bitrate_bps = 30000;
+
+    config_.rtp.ssrcs.emplace_back(1);
+
+    vss_impl->Start();
+
+    // Starts without padding.
+    EXPECT_EQ(0, padding_bitrate);
+
+    // Reconfigure e.g. due to a fake frame.
+    static_cast<VideoStreamEncoderInterface::EncoderSink*>(vss_impl.get())
+        ->OnEncoderConfigurationChanged(
+            std::vector<VideoStream>{qvga_stream},
+            VideoEncoderConfig::ContentType::kRealtimeVideo,
+            min_transmit_bitrate_bps);
+    // Still no padding because no actual frames were passed, only
+    // reconfiguration happened.
+    EXPECT_EQ(0, padding_bitrate);
+
+    // Unpause encoder.
+    const uint32_t kBitrateBps = 100000;
+    EXPECT_CALL(rtp_video_sender_, GetPayloadBitrateBps())
+        .Times(1)
+        .WillOnce(Return(kBitrateBps));
+    static_cast<BitrateAllocatorObserver*>(vss_impl.get())
+        ->OnBitrateUpdated(CreateAllocation(kBitrateBps));
+
+    // A frame is encoded.
+    EncodedImage encoded_image;
+    CodecSpecificInfo codec_specific;
+    static_cast<EncodedImageCallback*>(vss_impl.get())
+        ->OnEncodedImage(encoded_image, &codec_specific, nullptr);
+    // Only after actual frame is encoded are we enabling the padding.
+    EXPECT_GT(padding_bitrate, 0);
+  });
+
+  rtc::Event done;
+  test_queue_.PostDelayedTask(
+      [&] {
+        // No padding supposed to be sent for paused observer
+        EXPECT_EQ(0, padding_bitrate);
+        testing::Mock::VerifyAndClearExpectations(&bitrate_allocator_);
+        vss_impl->Stop();
+        vss_impl.reset();
+        done.Set();
+      },
+      5000);
+
+  // Pause the test suite so that the last delayed task executes.
+  ASSERT_TRUE(done.Wait(10000));
+}
+
 }  // namespace internal
 }  // namespace webrtc