Add accounting of actual audio bit usage

Part of a set of CL to allow video to borrow underused audio bitrate.

Bug: webrtc:35055527
Change-Id: Idb504cbbc5794c06b28bdc21b3d860c9da9df175
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/358202
Reviewed-by: Jakob Ivarsson‎ <jakobi@webrtc.org>
Commit-Queue: Dan Tan <dwtan@google.com>
Cr-Commit-Position: refs/heads/main@{#42733}
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index 5b3f624..fc194c9 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -492,8 +492,7 @@
 }
 
 absl::optional<DataRate> AudioSendStream::GetUsedRate() const {
-  // TODO(bugs.webrtc.org/35055527): Implement
-  return absl::nullopt;
+  return channel_send_->GetUsedRate();
 }
 
 void AudioSendStream::SetTransportOverhead(
@@ -518,6 +517,7 @@
   if (registered_with_allocator_) {
     ConfigureBitrateObserver();
   }
+  channel_send_->RegisterPacketOverhead(overhead_per_packet_bytes);
 }
 
 size_t AudioSendStream::TestOnlyGetPerPacketOverheadBytes() const {
diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc
index 666d263..6aa95c7 100644
--- a/audio/audio_send_stream_unittest.cc
+++ b/audio/audio_send_stream_unittest.cc
@@ -560,6 +560,7 @@
             }));
     EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead)
         .WillRepeatedly(Return(kOverheadPerPacket.bytes<size_t>()));
+    EXPECT_CALL(*helper.channel_send(), RegisterPacketOverhead);
 
     auto send_stream = helper.CreateAudioSendStream();
 
@@ -672,6 +673,7 @@
                                       "WebRTC-Audio-LegacyOverhead/Disabled/");
     EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead)
         .WillRepeatedly(Return(kOverheadPerPacket.bytes<size_t>()));
+    EXPECT_CALL(*helper.channel_send(), RegisterPacketOverhead);
     auto send_stream = helper.CreateAudioSendStream();
     const DataRate bitrate =
         DataRate::BitsPerSec(helper.config().max_bitrate_bps) +
@@ -694,6 +696,7 @@
         "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/");
     EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead)
         .WillRepeatedly(Return(kOverheadPerPacket.bytes<size_t>()));
+    EXPECT_CALL(*helper.channel_send(), RegisterPacketOverhead);
     auto send_stream = helper.CreateAudioSendStream();
     const DataRate bitrate = DataRate::KilobitsPerSec(6) + kMinOverheadRate;
     EXPECT_CALL(*helper.channel_send(),
@@ -714,6 +717,7 @@
         "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/");
     EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead)
         .WillRepeatedly(Return(kOverheadPerPacket.bytes<size_t>()));
+    EXPECT_CALL(*helper.channel_send(), RegisterPacketOverhead);
     auto send_stream = helper.CreateAudioSendStream();
     const DataRate bitrate = DataRate::KilobitsPerSec(64) + kMaxOverheadRate;
     EXPECT_CALL(*helper.channel_send(),
@@ -796,6 +800,7 @@
 
     // CallEncoder will be called on overhead change.
     EXPECT_CALL(*helper.channel_send(), CallEncoder);
+    EXPECT_CALL(*helper.channel_send(), RegisterPacketOverhead);
 
     const size_t transport_overhead_per_packet_bytes = 333;
     send_stream->SetTransportOverhead(transport_overhead_per_packet_bytes);
@@ -811,6 +816,8 @@
     auto send_stream = helper.CreateAudioSendStream();
     auto new_config = helper.config();
 
+    EXPECT_CALL(*helper.channel_send(), RegisterPacketOverhead).Times(2);
+
     // CallEncoder will be called on overhead change.
     EXPECT_CALL(*helper.channel_send(), CallEncoder);
     const size_t transport_overhead_per_packet_bytes = 333;
@@ -832,6 +839,7 @@
     const size_t audio_overhead_per_packet_bytes = 555;
     EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead)
         .WillRepeatedly(Return(audio_overhead_per_packet_bytes));
+    EXPECT_CALL(*helper.channel_send(), RegisterPacketOverhead).Times(2);
     auto send_stream = helper.CreateAudioSendStream();
 
     BitrateAllocationUpdate update;
@@ -862,6 +870,7 @@
     const size_t audio_overhead_per_packet_bytes = 555;
     EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead)
         .WillRepeatedly(Return(audio_overhead_per_packet_bytes));
+    EXPECT_CALL(*helper.channel_send(), RegisterPacketOverhead).Times(2);
     auto send_stream = helper.CreateAudioSendStream();
     auto new_config = helper.config();
 
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
index 4d59d20..b683cc3 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
@@ -57,6 +57,47 @@
 class RtpPacketSenderProxy;
 class TransportSequenceNumberProxy;
 
+class AudioBitrateAccountant {
+ public:
+  void RegisterPacketOverhead(int packet_byte_overhead) {
+    packet_overhead_ = DataSize::Bytes(packet_byte_overhead);
+  }
+
+  void Reset() {
+    rate_last_frame_ = DataRate::BitsPerSec(0);
+    next_frame_duration_ = TimeDelta::Millis(0);
+    report_rate_ = absl::nullopt;
+  }
+
+  // A new frame is formed when bytesize is nonzero.
+  void UpdateBpsEstimate(DataSize payload_size, TimeDelta frame_duration) {
+    next_frame_duration_ += frame_duration;
+    // Do not have a full frame yet.
+    if (payload_size.bytes() == 0)
+      return;
+
+    // We report the larger of the rates computed using the last frame, and
+    // second last frame. Under DTX, frame sizes sometimes alternate, it is
+    // preferable to report the upper envelop.
+    DataRate rate_cur_frame =
+        (payload_size + packet_overhead_) / next_frame_duration_;
+
+    report_rate_ =
+        (rate_cur_frame > rate_last_frame_) ? rate_cur_frame : rate_last_frame_;
+
+    rate_last_frame_ = rate_cur_frame;
+    next_frame_duration_ = TimeDelta::Millis(0);
+  }
+
+  absl::optional<DataRate> GetUsedRate() const { return report_rate_; }
+
+ private:
+  TimeDelta next_frame_duration_ = TimeDelta::Millis(0);
+  DataSize packet_overhead_ = DataSize::Bytes(72);
+  DataRate rate_last_frame_ = DataRate::BitsPerSec(0);
+  absl::optional<DataRate> report_rate_;
+};
+
 class ChannelSend : public ChannelSendInterface,
                     public AudioPacketizationCallback,  // receive encoded
                                                         // packets from the ACM
@@ -152,6 +193,17 @@
   // ReportBlockDataObserver.
   void OnReportBlockDataUpdated(ReportBlockData report_block) override;
 
+  // Reports actual bitrate used (vs allocated).
+  absl::optional<DataRate> GetUsedRate() const override {
+    MutexLock lock(&bitrate_accountant_mutex_);
+    return bitrate_accountant_.GetUsedRate();
+  }
+
+  void RegisterPacketOverhead(int packet_byte_overhead) override {
+    MutexLock lock(&bitrate_accountant_mutex_);
+    bitrate_accountant_.RegisterPacketOverhead(packet_byte_overhead);
+  }
+
  private:
   // From AudioPacketizationCallback in the ACM
   int32_t SendData(AudioFrameType frameType,
@@ -240,6 +292,10 @@
   RTC_NO_UNIQUE_ADDRESS SequenceChecker encoder_queue_checker_;
 
   SdpAudioFormat encoder_format_;
+
+  mutable Mutex bitrate_accountant_mutex_;
+  AudioBitrateAccountant bitrate_accountant_
+      RTC_GUARDED_BY(bitrate_accountant_mutex_);
 };
 
 const int kTelephoneEventAttenuationdB = 10;
@@ -800,10 +856,15 @@
         // encoding is done and payload is ready for packetization and
         // transmission. Otherwise, it will return without invoking the
         // callback.
-        if (audio_coding_->Add10MsData(*audio_frame) < 0) {
+        int32_t encoded_bytes = audio_coding_->Add10MsData(*audio_frame);
+        MutexLock lock(&bitrate_accountant_mutex_);
+        if (encoded_bytes < 0) {
           RTC_DLOG(LS_ERROR) << "ACM::Add10MsData() failed.";
+          bitrate_accountant_.Reset();
           return;
         }
+        bitrate_accountant_.UpdateBpsEstimate(DataSize::Bytes(encoded_bytes),
+                                              TimeDelta::Millis(10));
       });
 }
 
diff --git a/audio/channel_send.h b/audio/channel_send.h
index 704df8c..6082c0e 100644
--- a/audio/channel_send.h
+++ b/audio/channel_send.h
@@ -110,6 +110,12 @@
   virtual void SetEncoderToPacketizerFrameTransformer(
       rtc::scoped_refptr<webrtc::FrameTransformerInterface>
           frame_transformer) = 0;
+
+  // Returns payload bitrate actually used.
+  virtual absl::optional<DataRate> GetUsedRate() const = 0;
+
+  // Registers per packet byte overhead.
+  virtual void RegisterPacketOverhead(int packet_byte_overhead) = 0;
 };
 
 std::unique_ptr<ChannelSendInterface> CreateChannelSend(
diff --git a/audio/channel_send_unittest.cc b/audio/channel_send_unittest.cc
index 5755776..3819859 100644
--- a/audio/channel_send_unittest.cc
+++ b/audio/channel_send_unittest.cc
@@ -306,6 +306,62 @@
   EXPECT_TRUE_WAIT(sent_audio_level, 1000);
   EXPECT_EQ(*sent_audio_level, audio_level);
 }
+
+// Ensure that GetUsedRate returns null if no frames are coded.
+TEST_F(ChannelSendTest, NoUsedRateInitially) {
+  channel_->StartSend();
+  auto used_rate = channel_->GetUsedRate();
+  EXPECT_EQ(used_rate, absl::nullopt);
+}
+
+// Ensure that GetUsedRate returns value with one coded frame.
+TEST_F(ChannelSendTest, ValidUsedRateWithOneCodedFrame) {
+  channel_->StartSend();
+  EXPECT_CALL(transport_, SendRtp).Times(1);
+  ProcessNextFrame();
+  ProcessNextFrame();
+  auto used_rate = channel_->GetUsedRate();
+  EXPECT_GT(used_rate.value().bps(), 0);
+}
+
+// Ensure that GetUsedRate returns value with one coded frame.
+TEST_F(ChannelSendTest, UsedRateIsLargerofLastTwoFrames) {
+  channel_->StartSend();
+  channel_->CallEncoder(
+      [&](AudioEncoder* encoder) { encoder->OnReceivedOverhead(72); });
+  DataRate lowrate = DataRate::BitsPerSec(40000);
+  DataRate highrate = DataRate::BitsPerSec(80000);
+  BitrateAllocationUpdate update;
+  update.bwe_period = TimeDelta::Millis(100);
+
+  update.target_bitrate = lowrate;
+  channel_->OnBitrateAllocation(update);
+  EXPECT_CALL(transport_, SendRtp).Times(1);
+  ProcessNextFrame();
+  ProcessNextFrame();
+  // Last two frames have rates [32kbps, -], yielding 32kbps.
+  auto used_rate_1 = channel_->GetUsedRate();
+
+  update.target_bitrate = highrate;
+  channel_->OnBitrateAllocation(update);
+  EXPECT_CALL(transport_, SendRtp).Times(1);
+  ProcessNextFrame();
+  ProcessNextFrame();
+  // Last two frames have rates [54kbps, 32kbps], yielding 54kbps
+  auto used_rate_2 = channel_->GetUsedRate();
+
+  update.target_bitrate = lowrate;
+  channel_->OnBitrateAllocation(update);
+  EXPECT_CALL(transport_, SendRtp).Times(1);
+  ProcessNextFrame();
+  ProcessNextFrame();
+  // Last two frames have rates [32kbps 54kbps], yielding 54kbps
+  auto used_rate_3 = channel_->GetUsedRate();
+
+  EXPECT_GT(used_rate_2, used_rate_1);
+  EXPECT_EQ(used_rate_3, used_rate_2);
+}
+
 }  // namespace
 }  // namespace voe
 }  // namespace webrtc
diff --git a/audio/mock_voe_channel_proxy.h b/audio/mock_voe_channel_proxy.h
index 5836d88..607070a 100644
--- a/audio/mock_voe_channel_proxy.h
+++ b/audio/mock_voe_channel_proxy.h
@@ -182,6 +182,11 @@
       SetEncoderToPacketizerFrameTransformer,
       (rtc::scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer),
       (override));
+  MOCK_METHOD(absl::optional<DataRate>, GetUsedRate, (), (const, override));
+  MOCK_METHOD(void,
+              RegisterPacketOverhead,
+              (int packet_byte_overhead),
+              (override));
 };
 }  // namespace test
 }  // namespace webrtc