Add support for target rate constraints

WebRTC video engine now configures bitrate on media transport
correctly.

Bug: webrtc:9719
Change-Id: I85884cd76644b7eca3763cec8ce9e31b5b64db27
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/127941
Commit-Queue: Peter Slatala <psla@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Bjorn Mellem <mellem@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27167}
diff --git a/api/media_transport_interface.h b/api/media_transport_interface.h
index 1b5f735..718000e 100644
--- a/api/media_transport_interface.h
+++ b/api/media_transport_interface.h
@@ -50,12 +50,22 @@
   virtual void OnFirstAudioPacketReceived(int64_t channel_id) = 0;
 };
 
+// Used to configure stream allocations.
 struct MediaTransportAllocatedBitrateLimits {
   DataRate min_pacing_rate = DataRate::Zero();
   DataRate max_padding_bitrate = DataRate::Zero();
   DataRate max_total_allocated_bitrate = DataRate::Zero();
 };
 
+// Used to configure target bitrate constraints.
+// If the value is provided, the constraint is updated.
+// If the value is omitted, the value is left unchanged.
+struct MediaTransportTargetRateConstraints {
+  absl::optional<DataRate> min_bitrate;
+  absl::optional<DataRate> max_bitrate;
+  absl::optional<DataRate> starting_bitrate;
+};
+
 // A collection of settings for creation of media transport.
 struct MediaTransportSettings final {
   MediaTransportSettings();
@@ -315,6 +325,11 @@
   virtual void SetAllocatedBitrateLimits(
       const MediaTransportAllocatedBitrateLimits& limits);
 
+  // Sets starting rate.
+  // TODO(psla): Make abstract when downstream implementation implement it.
+  virtual void SetTargetBitrateLimits(
+      const MediaTransportTargetRateConstraints& target_rate_constraints) {}
+
   // Opens a data |channel_id| for sending.  May return an error if the
   // specified |channel_id| is unusable.  Must be called before |SendData|.
   virtual RTCError OpenChannel(int channel_id) = 0;
diff --git a/api/test/fake_media_transport.h b/api/test/fake_media_transport.h
index 1cfd340..a085bc0 100644
--- a/api/test/fake_media_transport.h
+++ b/api/test/fake_media_transport.h
@@ -103,6 +103,16 @@
   void SetAllocatedBitrateLimits(
       const MediaTransportAllocatedBitrateLimits& limits) override {}
 
+  void SetTargetBitrateLimits(const MediaTransportTargetRateConstraints&
+                                  target_rate_constraints) override {
+    target_rate_constraints_in_order_.push_back(target_rate_constraints);
+  }
+
+  const std::vector<MediaTransportTargetRateConstraints>&
+  target_rate_constraints_in_order() {
+    return target_rate_constraints_in_order_;
+  }
+
   int target_rate_observers_size() { return target_rate_observers_.size(); }
 
   // Settings that were passed down to fake media transport.
@@ -133,6 +143,8 @@
   const absl::optional<std::string> transport_offer_;
   const absl::optional<std::string> remote_transport_parameters_;
   bool is_connected_ = false;
+  std::vector<MediaTransportTargetRateConstraints>
+      target_rate_constraints_in_order_;
 };
 
 // Fake media transport factory creates fake media transport.
diff --git a/call/call.cc b/call/call.cc
index 67d1c53..bf42ee3 100644
--- a/call/call.cc
+++ b/call/call.cc
@@ -567,6 +567,20 @@
         << ", (media_transport_==media_transport)="
         << (media_transport_ == media_transport);
     media_transport_ = media_transport;
+    MediaTransportTargetRateConstraints constraints;
+    if (config_.bitrate_config.start_bitrate_bps > 0) {
+      constraints.starting_bitrate =
+          DataRate::bps(config_.bitrate_config.start_bitrate_bps);
+    }
+    if (config_.bitrate_config.max_bitrate_bps > 0) {
+      constraints.max_bitrate =
+          DataRate::bps(config_.bitrate_config.max_bitrate_bps);
+    }
+    if (config_.bitrate_config.min_bitrate_bps > 0) {
+      constraints.min_bitrate =
+          DataRate::bps(config_.bitrate_config.min_bitrate_bps);
+    }
+    media_transport_->SetTargetBitrateLimits(constraints);
   }
 }
 
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 3484b40..0f6315c 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -492,6 +492,7 @@
       ":rtc_constants",
       ":rtc_data",
       "../:webrtc_common",
+      "../api:fake_media_transport",
       "../api:scoped_refptr",
       "../api/test/video:function_video_factory",
       "../api/units:time_delta",
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 54eeb1f..36473cb 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -717,8 +717,26 @@
       bitrate_config_.max_bitrate_bps =
           params.max_bandwidth_bps == 0 ? -1 : params.max_bandwidth_bps;
     }
-    call_->GetTransportControllerSend()->SetSdpBitrateParameters(
-        bitrate_config_);
+
+    if (media_transport()) {
+      webrtc::MediaTransportTargetRateConstraints constraints;
+      if (bitrate_config_.start_bitrate_bps >= 0) {
+        constraints.starting_bitrate =
+            webrtc::DataRate::bps(bitrate_config_.start_bitrate_bps);
+      }
+      if (bitrate_config_.max_bitrate_bps > 0) {
+        constraints.max_bitrate =
+            webrtc::DataRate::bps(bitrate_config_.max_bitrate_bps);
+      }
+      if (bitrate_config_.min_bitrate_bps >= 0) {
+        constraints.min_bitrate =
+            webrtc::DataRate::bps(bitrate_config_.min_bitrate_bps);
+      }
+      media_transport()->SetTargetBitrateLimits(constraints);
+    } else {
+      call_->GetTransportControllerSend()->SetSdpBitrateParameters(
+          bitrate_config_);
+    }
   }
 
     for (auto& kv : send_streams_) {
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index 1649244..da205e1 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -17,6 +17,7 @@
 #include "absl/memory/memory.h"
 #include "absl/strings/match.h"
 #include "api/rtp_parameters.h"
+#include "api/test/fake_media_transport.h"
 #include "api/test/mock_video_bitrate_allocator.h"
 #include "api/test/mock_video_bitrate_allocator_factory.h"
 #include "api/test/mock_video_decoder_factory.h"
@@ -3892,6 +3893,81 @@
   EXPECT_TRUE(channel_->SetSendParameters(send_parameters_));
 }
 
+// Test that when both the codec-specific bitrate params and max_bandwidth_bps
+// are present in the same send parameters, the settings are combined correctly.
+TEST_F(WebRtcVideoChannelTest,
+       SetSendCodecsWithBitratesAndMaxSendBandwidthForMediaTransport) {
+  // Same as SetSendCodecsWithBitratesAndMaxSendBandwidth but with Media
+  // Transport.
+  webrtc::MediaTransportSettings settings;
+  settings.is_caller = true;
+  webrtc::FakeMediaTransport fake_media_transport(settings);
+  std::unique_ptr<cricket::FakeNetworkInterface> network_interface(
+      new cricket::FakeNetworkInterface);
+  channel_->SetInterface(network_interface.get(), &fake_media_transport);
+
+  send_parameters_.codecs[0].params[kCodecParamMinBitrate] = "100";
+  send_parameters_.codecs[0].params[kCodecParamStartBitrate] = "200";
+  send_parameters_.codecs[0].params[kCodecParamMaxBitrate] = "300";
+  send_parameters_.max_bandwidth_bps = 400000;
+  {
+    // We expect max_bandwidth_bps to take priority, if set.
+    ASSERT_TRUE(channel_->SetSendParameters(send_parameters_));
+    ASSERT_EQ(1u,
+              fake_media_transport.target_rate_constraints_in_order().size());
+    const webrtc::MediaTransportTargetRateConstraints& constraint =
+        fake_media_transport.target_rate_constraints_in_order()[0];
+    ASSERT_EQ(webrtc::DataRate::bps(100000), constraint.min_bitrate);
+    ASSERT_EQ(webrtc::DataRate::bps(200000), constraint.starting_bitrate);
+    ASSERT_EQ(webrtc::DataRate::bps(400000), constraint.max_bitrate);
+  }
+
+  {
+    // Decrease max_bandwidth_bps.
+    send_parameters_.max_bandwidth_bps = 350000;
+    ASSERT_TRUE(channel_->SetSendParameters(send_parameters_));
+    ASSERT_EQ(2u,
+              fake_media_transport.target_rate_constraints_in_order().size());
+    const webrtc::MediaTransportTargetRateConstraints& constraint =
+        fake_media_transport.target_rate_constraints_in_order()[1];
+
+    // Since the codec isn't changing, start_bitrate_bps should be 0.
+    ASSERT_EQ(webrtc::DataRate::bps(100000), constraint.min_bitrate);
+    ASSERT_EQ(absl::nullopt, constraint.starting_bitrate);
+    ASSERT_EQ(webrtc::DataRate::bps(350000), constraint.max_bitrate);
+  }
+
+  {
+    // Now try again with the values flipped around.
+    send_parameters_.codecs[0].params[kCodecParamMaxBitrate] = "400";
+    send_parameters_.max_bandwidth_bps = 300000;
+    ASSERT_TRUE(channel_->SetSendParameters(send_parameters_));
+    ASSERT_EQ(3u,
+              fake_media_transport.target_rate_constraints_in_order().size());
+    const webrtc::MediaTransportTargetRateConstraints& constraint =
+        fake_media_transport.target_rate_constraints_in_order()[2];
+
+    ASSERT_EQ(webrtc::DataRate::bps(100000), constraint.min_bitrate);
+    ASSERT_EQ(webrtc::DataRate::bps(200000), constraint.starting_bitrate);
+    ASSERT_EQ(webrtc::DataRate::bps(300000), constraint.max_bitrate);
+  }
+
+  {
+    // Now try again with the values flipped around.
+    // If we change the codec max, max_bandwidth_bps should still apply.
+    send_parameters_.codecs[0].params[kCodecParamMaxBitrate] = "350";
+    ASSERT_TRUE(channel_->SetSendParameters(send_parameters_));
+    ASSERT_EQ(4u,
+              fake_media_transport.target_rate_constraints_in_order().size());
+    const webrtc::MediaTransportTargetRateConstraints& constraint =
+        fake_media_transport.target_rate_constraints_in_order()[3];
+
+    ASSERT_EQ(webrtc::DataRate::bps(100000), constraint.min_bitrate);
+    ASSERT_EQ(webrtc::DataRate::bps(200000), constraint.starting_bitrate);
+    ASSERT_EQ(webrtc::DataRate::bps(300000), constraint.max_bitrate);
+  }
+}
+
 TEST_F(WebRtcVideoChannelTest, SetMaxSendBandwidthShouldPreserveOtherBitrates) {
   SetSendCodecsShouldWorkForBitrates("100", 100000, "150", 150000, "200",
                                      200000);