Add functionality to set min/max bitrate per simulcast layer through RtpEncodingParameters.
Target bitrate is set to 0.75 of the max bitrate.
Bug: webrtc:9341, webrtc:8655
Change-Id: I9a8c8bb95bb1532d45f05578832418464452340e
Reviewed-on: https://webrtc-review.googlesource.com/79821
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23676}
diff --git a/api/rtpparameters.h b/api/rtpparameters.h
index 0725ec7..e2405d3 100644
--- a/api/rtpparameters.h
+++ b/api/rtpparameters.h
@@ -401,14 +401,6 @@
// bitrate. Currently this is implemented for the entire rtp sender by using
// the value of the first encoding parameter.
//
- // TODO(webrtc.bugs.org/8655): Implement this per encoding parameter.
- // Current implementation for a sender:
- // The max bitrate is decided by taking the minimum of the first encoding
- // parameter's max_bitrate_bps and the max bitrate specified by the sdp with
- // the b=AS attribute. In the case of simulcast video, default values are used
- // for each simulcast layer, and if there is some bitrate left over from the
- // sender's max bitrate then it will roll over into the highest quality layer.
- //
// Just called "maxBitrate" in ORTC spec.
//
// TODO(deadbeef): With ORTC RtpSenders, this currently sets the total
@@ -417,7 +409,9 @@
// fixed.
rtc::Optional<int> max_bitrate_bps;
- // TODO(asapersson): Not implemented.
+ // Specifies the minimum bitrate in bps for video.
+ // TODO(asapersson): Not implemented for ORTC API.
+ // TODO(asapersson): Not implemented for single layer.
rtc::Optional<int> min_bitrate_bps;
// TODO(deadbeef): Not implemented.
diff --git a/api/rtpsenderinterface.h b/api/rtpsenderinterface.h
index e933614..66267c7 100644
--- a/api/rtpsenderinterface.h
+++ b/api/rtpsenderinterface.h
@@ -57,6 +57,7 @@
virtual RtpParameters GetParameters() = 0;
// Note that only a subset of the parameters can currently be changed. See
// rtpparameters.h
+ // The encodings are in increasing quality order for simulcast.
virtual RTCError SetParameters(const RtpParameters& parameters) = 0;
// Returns null for a video sender.
diff --git a/media/engine/simulcast.cc b/media/engine/simulcast.cc
index ffc7971..607d141 100644
--- a/media/engine/simulcast.cc
+++ b/media/engine/simulcast.cc
@@ -151,6 +151,9 @@
void BoostMaxSimulcastLayer(int max_bitrate_bps,
std::vector<webrtc::VideoStream>* layers) {
+ if (layers->empty())
+ return;
+
// Spend additional bits to boost the max layer.
int bitrate_left_bps = max_bitrate_bps - GetTotalMaxBitrateBps(*layers);
if (bitrate_left_bps > 0) {
@@ -159,6 +162,9 @@
}
int GetTotalMaxBitrateBps(const std::vector<webrtc::VideoStream>& layers) {
+ if (layers.empty())
+ return 0;
+
int total_max_bitrate_bps = 0;
for (size_t s = 0; s < layers.size() - 1; ++s) {
total_max_bitrate_bps += layers[s].target_bitrate_bps;
@@ -170,18 +176,18 @@
std::vector<webrtc::VideoStream> GetSimulcastConfig(size_t max_layers,
int width,
int height,
- int max_bitrate_bps,
+ int /*max_bitrate_bps*/,
double bitrate_priority,
int max_qp,
int max_framerate,
bool is_screenshare) {
if (is_screenshare) {
- return GetScreenshareLayers(max_layers, width, height, max_bitrate_bps,
- bitrate_priority, max_qp, max_framerate,
+ return GetScreenshareLayers(max_layers, width, height, bitrate_priority,
+ max_qp, max_framerate,
ScreenshareSimulcastFieldTrialEnabled());
} else {
- return GetNormalSimulcastLayers(max_layers, width, height, max_bitrate_bps,
- bitrate_priority, max_qp, max_framerate);
+ return GetNormalSimulcastLayers(max_layers, width, height, bitrate_priority,
+ max_qp, max_framerate);
}
}
@@ -189,7 +195,6 @@
size_t max_layers,
int width,
int height,
- int max_bitrate_bps,
double bitrate_priority,
int max_qp,
int max_framerate) {
@@ -249,8 +254,6 @@
break;
}
}
- // If there is bitrate leftover, give it to the largest layer.
- BoostMaxSimulcastLayer(max_bitrate_bps, &layers);
// Currently the relative bitrate priority of the sender is controlled by
// the value of the lowest VideoStream.
// TODO(bugs.webrtc.org/8630): The web specification describes being able to
@@ -264,7 +267,6 @@
size_t max_layers,
int width,
int height,
- int max_bitrate_bps,
double bitrate_priority,
int max_qp,
int max_framerate,
diff --git a/media/engine/simulcast.h b/media/engine/simulcast.h
index 7a98809..3172f5e 100644
--- a/media/engine/simulcast.h
+++ b/media/engine/simulcast.h
@@ -21,35 +21,39 @@
// TODO(pthatcher): Write unit tests just for these functions,
// independent of WebrtcVideoEngine.
+// Gets the total maximum bitrate for the |streams|.
int GetTotalMaxBitrateBps(const std::vector<webrtc::VideoStream>& streams);
-// Get simulcast settings.
-std::vector<webrtc::VideoStream> GetSimulcastConfig(
- size_t max_layers,
- int width,
- int height,
- int max_bitrate_bps,
- double bitrate_priority,
- int max_qp,
- int max_framerate,
- bool is_screenshare);
+// Adds any bitrate of |max_bitrate_bps| that is above the total maximum bitrate
+// for the |layers| to the highest quality layer.
+void BoostMaxSimulcastLayer(int max_bitrate_bps,
+ std::vector<webrtc::VideoStream>* layers);
+
+// Gets simulcast settings.
+// TODO(asapersson): Remove max_bitrate_bps.
+std::vector<webrtc::VideoStream> GetSimulcastConfig(size_t max_layers,
+ int width,
+ int height,
+ int /*max_bitrate_bps*/,
+ double bitrate_priority,
+ int max_qp,
+ int max_framerate,
+ bool is_screenshare);
// Gets the simulcast config layers for a non-screensharing case.
std::vector<webrtc::VideoStream> GetNormalSimulcastLayers(
size_t max_layers,
int width,
int height,
- int max_bitrate_bps,
double bitrate_priority,
int max_qp,
int max_framerate);
-// Get simulcast config layers for screenshare settings.
+// Gets simulcast config layers for screenshare settings.
std::vector<webrtc::VideoStream> GetScreenshareLayers(
size_t max_layers,
int width,
int height,
- int max_bitrate_bps,
double bitrate_priority,
int max_qp,
int max_framerate,
diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc
index 7c3ecd1..26bb83d 100644
--- a/media/engine/webrtcvideoengine.cc
+++ b/media/engine/webrtcvideoengine.cc
@@ -1800,13 +1800,23 @@
return error;
}
+ bool new_bitrate = false;
+ for (size_t i = 0; i < rtp_parameters_.encodings.size(); ++i) {
+ if ((new_parameters.encodings[i].min_bitrate_bps !=
+ rtp_parameters_.encodings[i].min_bitrate_bps) ||
+ (new_parameters.encodings[i].max_bitrate_bps !=
+ rtp_parameters_.encodings[i].max_bitrate_bps)) {
+ new_bitrate = true;
+ }
+ }
+
// TODO(bugs.webrtc.org/8807): The bitrate priority really doesn't require an
// entire encoder reconfiguration, it just needs to update the bitrate
// allocator.
- bool reconfigure_encoder = (new_parameters.encodings[0].max_bitrate_bps !=
- rtp_parameters_.encodings[0].max_bitrate_bps) ||
- (new_parameters.encodings[0].bitrate_priority !=
- rtp_parameters_.encodings[0].bitrate_priority);
+ bool reconfigure_encoder =
+ new_bitrate || (new_parameters.encodings[0].bitrate_priority !=
+ rtp_parameters_.encodings[0].bitrate_priority);
+
// TODO(bugs.webrtc.org/8807): The active field as well should not require
// a full encoder reconfiguration, but it needs to update both the bitrate
// allocator and the video bitrate allocator.
@@ -1864,6 +1874,17 @@
"Attempted to set RtpParameters bitrate_priority to "
"an invalid number. bitrate_priority must be > 0.");
}
+ for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
+ if (rtp_parameters.encodings[i].min_bitrate_bps &&
+ rtp_parameters.encodings[i].max_bitrate_bps) {
+ if (*rtp_parameters.encodings[i].max_bitrate_bps <
+ *rtp_parameters.encodings[i].min_bitrate_bps) {
+ LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
+ "Attempted to set RtpParameters min bitrate "
+ "larger than max bitrate.");
+ }
+ }
+ }
return webrtc::RTCError::OK();
}
@@ -1918,7 +1939,13 @@
}
int stream_max_bitrate = parameters_.max_bitrate_bps;
- if (rtp_parameters_.encodings[0].max_bitrate_bps) {
+ // When simulcast is enabled (when there are multiple encodings),
+ // encodings[i].max_bitrate_bps will be enforced by
+ // encoder_config.simulcast_layers[i].max_bitrate_bps. Otherwise, it's
+ // enforced by stream_max_bitrate, taking the minimum of the two maximums
+ // (one coming from SDP, the other coming from RtpParameters).
+ if (rtp_parameters_.encodings[0].max_bitrate_bps &&
+ rtp_parameters_.encodings.size() == 1) {
stream_max_bitrate =
webrtc::MinPositive(*(rtp_parameters_.encodings[0].max_bitrate_bps),
parameters_.max_bitrate_bps);
@@ -1939,7 +1966,7 @@
// Application-controlled state is held in the encoder_config's
// simulcast_layers. Currently this is used to control which simulcast layers
- // are active.
+ // are active and for configuring the min/max bitrate.
RTC_DCHECK_GE(rtp_parameters_.encodings.size(),
encoder_config.number_of_streams);
RTC_DCHECK_GT(encoder_config.number_of_streams, 0);
@@ -1947,6 +1974,14 @@
for (size_t i = 0; i < encoder_config.simulcast_layers.size(); ++i) {
encoder_config.simulcast_layers[i].active =
rtp_parameters_.encodings[i].active;
+ if (rtp_parameters_.encodings[i].min_bitrate_bps) {
+ encoder_config.simulcast_layers[i].min_bitrate_bps =
+ *rtp_parameters_.encodings[i].min_bitrate_bps;
+ }
+ if (rtp_parameters_.encodings[i].max_bitrate_bps) {
+ encoder_config.simulcast_layers[i].max_bitrate_bps =
+ *rtp_parameters_.encodings[i].max_bitrate_bps;
+ }
}
int max_qp = kDefaultQpMax;
@@ -2681,12 +2716,50 @@
(CodecNamesEq(codec_name_, kVp8CodecName) && is_screenshare_ &&
screenshare_config_explicitly_enabled_)) {
layers = GetSimulcastConfig(encoder_config.number_of_streams, width, height,
- encoder_config.max_bitrate_bps,
- encoder_config.bitrate_priority, max_qp_,
- max_framerate_, is_screenshare_);
- // Update the active simulcast layers.
+ 0 /*not used*/, encoder_config.bitrate_priority,
+ max_qp_, max_framerate_, is_screenshare_);
+ // Update the active simulcast layers and configured bitrates.
+ bool is_highest_layer_max_bitrate_configured = false;
for (size_t i = 0; i < layers.size(); ++i) {
layers[i].active = encoder_config.simulcast_layers[i].active;
+ // Update simulcast bitrates with configured min and max bitrate.
+ if (encoder_config.simulcast_layers[i].min_bitrate_bps > 0) {
+ layers[i].min_bitrate_bps =
+ encoder_config.simulcast_layers[i].min_bitrate_bps;
+ }
+ if (encoder_config.simulcast_layers[i].max_bitrate_bps > 0) {
+ layers[i].max_bitrate_bps =
+ encoder_config.simulcast_layers[i].max_bitrate_bps;
+ }
+ if (encoder_config.simulcast_layers[i].min_bitrate_bps > 0 &&
+ encoder_config.simulcast_layers[i].max_bitrate_bps > 0) {
+ // Min and max bitrate are configured.
+ // Set target to 3/4 of the max bitrate (or to max if below min).
+ layers[i].target_bitrate_bps = layers[i].max_bitrate_bps * 3 / 4;
+ if (layers[i].target_bitrate_bps < layers[i].min_bitrate_bps)
+ layers[i].target_bitrate_bps = layers[i].max_bitrate_bps;
+ } else if (encoder_config.simulcast_layers[i].min_bitrate_bps > 0) {
+ // Only min bitrate is configured, make sure target/max are above min.
+ layers[i].target_bitrate_bps =
+ std::max(layers[i].target_bitrate_bps, layers[i].min_bitrate_bps);
+ layers[i].max_bitrate_bps =
+ std::max(layers[i].max_bitrate_bps, layers[i].min_bitrate_bps);
+ } else if (encoder_config.simulcast_layers[i].max_bitrate_bps > 0) {
+ // Only max bitrate is configured, make sure min/target are below max.
+ layers[i].min_bitrate_bps =
+ std::min(layers[i].min_bitrate_bps, layers[i].max_bitrate_bps);
+ layers[i].target_bitrate_bps =
+ std::min(layers[i].target_bitrate_bps, layers[i].max_bitrate_bps);
+ }
+ if (i == layers.size() - 1) {
+ is_highest_layer_max_bitrate_configured =
+ encoder_config.simulcast_layers[i].max_bitrate_bps > 0;
+ }
+ }
+ if (!is_screenshare_ && !is_highest_layer_max_bitrate_configured) {
+ // No application-configured maximum for the largest layer.
+ // If there is bitrate leftover, give it to the largest layer.
+ BoostMaxSimulcastLayer(encoder_config.max_bitrate_bps, &layers);
}
return layers;
}
diff --git a/media/engine/webrtcvideoengine_unittest.cc b/media/engine/webrtcvideoengine_unittest.cc
index 5248979..2b048fc 100644
--- a/media/engine/webrtcvideoengine_unittest.cc
+++ b/media/engine/webrtcvideoengine_unittest.cc
@@ -2212,6 +2212,21 @@
EXPECT_EQ(expected_encoder_bitrate, GetMaxEncoderBitrate());
}
+ // Values from kSimulcastConfigs in simulcast.cc.
+ const std::vector<webrtc::VideoStream> GetSimulcastBitrates720p() const {
+ std::vector<webrtc::VideoStream> layers(3);
+ layers[0].min_bitrate_bps = 30000;
+ layers[0].target_bitrate_bps = 150000;
+ layers[0].max_bitrate_bps = 200000;
+ layers[1].min_bitrate_bps = 150000;
+ layers[1].target_bitrate_bps = 500000;
+ layers[1].max_bitrate_bps = 700000;
+ layers[2].min_bitrate_bps = 600000;
+ layers[2].target_bitrate_bps = 2500000;
+ layers[2].max_bitrate_bps = 2500000;
+ return layers;
+ }
+
std::unique_ptr<FakeCall> fake_call_;
std::unique_ptr<VideoMediaChannel> channel_;
cricket::VideoSendParameters send_parameters_;
@@ -3980,6 +3995,31 @@
stream->GetVideoStreams()[0].max_bitrate_bps);
}
+TEST_F(WebRtcVideoChannelTest,
+ MaxBitrateIsMinimumOfMaxSendBandwidthAndMaxEncodingBitrate) {
+ send_parameters_.max_bandwidth_bps = 99999;
+ FakeVideoSendStream* stream = AddSendStream();
+ ExpectSetMaxBitrate(send_parameters_.max_bandwidth_bps);
+ ASSERT_TRUE(channel_->SetSendParameters(send_parameters_));
+ ASSERT_EQ(1u, stream->GetVideoStreams().size());
+ EXPECT_EQ(send_parameters_.max_bandwidth_bps,
+ stream->GetVideoStreams()[0].max_bitrate_bps);
+
+ // Get and set the rtp encoding parameters.
+ webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+ EXPECT_EQ(1u, parameters.encodings.size());
+
+ parameters.encodings[0].max_bitrate_bps = 99999 - 1;
+ EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+ EXPECT_EQ(parameters.encodings[0].max_bitrate_bps,
+ stream->GetVideoStreams()[0].max_bitrate_bps);
+
+ parameters.encodings[0].max_bitrate_bps = 99999 + 1;
+ EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+ EXPECT_EQ(send_parameters_.max_bandwidth_bps,
+ stream->GetVideoStreams()[0].max_bitrate_bps);
+}
+
TEST_F(WebRtcVideoChannelTest, SetMaxSendBitrateCanIncreaseSenderBitrate) {
cricket::VideoSendParameters parameters;
parameters.codecs.push_back(GetEngineCodec("VP8"));
@@ -5371,6 +5411,325 @@
EXPECT_TRUE(channel_->SetVideoSend(primary_ssrc, nullptr, nullptr));
}
+TEST_F(WebRtcVideoChannelTest, GetAndSetRtpSendParametersMinAndMaxBitrate) {
+ const size_t kNumSimulcastStreams = 3;
+ SetUpSimulcast(true, false);
+
+ // Get and set the rtp encoding parameters.
+ webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+ EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
+ for (const auto& encoding : parameters.encodings) {
+ EXPECT_FALSE(encoding.min_bitrate_bps);
+ EXPECT_FALSE(encoding.max_bitrate_bps);
+ }
+
+ // Change the value and set it on the VideoChannel.
+ parameters.encodings[0].min_bitrate_bps = 100000;
+ parameters.encodings[0].max_bitrate_bps = 200000;
+ parameters.encodings[1].min_bitrate_bps = 300000;
+ parameters.encodings[1].max_bitrate_bps = 400000;
+ parameters.encodings[2].min_bitrate_bps = 500000;
+ parameters.encodings[2].max_bitrate_bps = 600000;
+ EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+
+ // Verify that the bitrates are set on the VideoChannel.
+ parameters = channel_->GetRtpSendParameters(last_ssrc_);
+ EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
+ EXPECT_EQ(100000, parameters.encodings[0].min_bitrate_bps);
+ EXPECT_EQ(200000, parameters.encodings[0].max_bitrate_bps);
+ EXPECT_EQ(300000, parameters.encodings[1].min_bitrate_bps);
+ EXPECT_EQ(400000, parameters.encodings[1].max_bitrate_bps);
+ EXPECT_EQ(500000, parameters.encodings[2].min_bitrate_bps);
+ EXPECT_EQ(600000, parameters.encodings[2].max_bitrate_bps);
+}
+
+TEST_F(WebRtcVideoChannelTest, SetRtpSendParametersFailsWithIncorrectBitrate) {
+ const size_t kNumSimulcastStreams = 3;
+ SetUpSimulcast(true, false);
+
+ // Get and set the rtp encoding parameters.
+ webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+ EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
+
+ // Max bitrate lower than min bitrate should fail.
+ parameters.encodings[2].min_bitrate_bps = 100000;
+ parameters.encodings[2].max_bitrate_bps = 100000 - 1;
+ EXPECT_EQ(webrtc::RTCErrorType::INVALID_RANGE,
+ channel_->SetRtpSendParameters(last_ssrc_, parameters).type());
+}
+
+// Test that min and max bitrate values set via RtpParameters are correctly
+// propagated to the underlying encoder, and that the target is set to 3/4 of
+// the maximum (3/4 was chosen because it's similar to the simulcast defaults
+// that are used if no min/max are specified).
+TEST_F(WebRtcVideoChannelTest, MinAndMaxSimulcastBitratePropagatedToEncoder) {
+ const size_t kNumSimulcastStreams = 3;
+ FakeVideoSendStream* stream = SetUpSimulcast(true, false);
+
+ // Send a full size frame so all simulcast layers are used when reconfiguring.
+ FakeVideoCapturerWithTaskQueue capturer;
+ VideoOptions options;
+ EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, &options, &capturer));
+ EXPECT_EQ(cricket::CS_RUNNING,
+ capturer.Start(capturer.GetSupportedFormats()->front()));
+ channel_->SetSend(true);
+ EXPECT_TRUE(capturer.CaptureFrame());
+
+ // Get and set the rtp encoding parameters.
+ // Change the value and set it on the VideoChannel.
+ webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+ EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
+ parameters.encodings[0].min_bitrate_bps = 100000;
+ parameters.encodings[0].max_bitrate_bps = 200000;
+ parameters.encodings[1].min_bitrate_bps = 300000;
+ parameters.encodings[1].max_bitrate_bps = 400000;
+ parameters.encodings[2].min_bitrate_bps = 500000;
+ parameters.encodings[2].max_bitrate_bps = 600000;
+ EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+
+ // Verify that the new value propagated down to the encoder.
+ // Check that WebRtcVideoSendStream updates VideoEncoderConfig correctly.
+ EXPECT_EQ(2, stream->num_encoder_reconfigurations());
+ webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig().Copy();
+ EXPECT_EQ(kNumSimulcastStreams, encoder_config.number_of_streams);
+ EXPECT_EQ(kNumSimulcastStreams, encoder_config.simulcast_layers.size());
+ EXPECT_EQ(100000, encoder_config.simulcast_layers[0].min_bitrate_bps);
+ EXPECT_EQ(200000, encoder_config.simulcast_layers[0].max_bitrate_bps);
+ EXPECT_EQ(300000, encoder_config.simulcast_layers[1].min_bitrate_bps);
+ EXPECT_EQ(400000, encoder_config.simulcast_layers[1].max_bitrate_bps);
+ EXPECT_EQ(500000, encoder_config.simulcast_layers[2].min_bitrate_bps);
+ EXPECT_EQ(600000, encoder_config.simulcast_layers[2].max_bitrate_bps);
+
+ // FakeVideoSendStream calls CreateEncoderStreams, test that the vector of
+ // VideoStreams are created appropriately for the simulcast case.
+ EXPECT_EQ(kNumSimulcastStreams, stream->GetVideoStreams().size());
+ // Target bitrate: 200000 * 3 / 4 = 150000.
+ EXPECT_EQ(100000, stream->GetVideoStreams()[0].min_bitrate_bps);
+ EXPECT_EQ(150000, stream->GetVideoStreams()[0].target_bitrate_bps);
+ EXPECT_EQ(200000, stream->GetVideoStreams()[0].max_bitrate_bps);
+ // Target bitrate: 400000 * 3 / 4 = 300000.
+ EXPECT_EQ(300000, stream->GetVideoStreams()[1].min_bitrate_bps);
+ EXPECT_EQ(300000, stream->GetVideoStreams()[1].target_bitrate_bps);
+ EXPECT_EQ(400000, stream->GetVideoStreams()[1].max_bitrate_bps);
+ // Target bitrate: 600000 * 3 / 4 = 450000, less than min -> max.
+ EXPECT_EQ(500000, stream->GetVideoStreams()[2].min_bitrate_bps);
+ EXPECT_EQ(600000, stream->GetVideoStreams()[2].target_bitrate_bps);
+ EXPECT_EQ(600000, stream->GetVideoStreams()[2].max_bitrate_bps);
+
+ // No parameter changed, encoder should not be reconfigured.
+ EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+ EXPECT_EQ(2, stream->num_encoder_reconfigurations());
+
+ EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, nullptr));
+}
+
+// Test to only specify the min or max bitrate value for a layer via
+// RtpParameters. The unspecified min/max and target value should be set to the
+// simulcast default that is used if no min/max are specified.
+TEST_F(WebRtcVideoChannelTest, MinOrMaxSimulcastBitratePropagatedToEncoder) {
+ const size_t kNumSimulcastStreams = 3;
+ const std::vector<webrtc::VideoStream> kDefault = GetSimulcastBitrates720p();
+ FakeVideoSendStream* stream = SetUpSimulcast(true, false);
+
+ // Send a full size frame so all simulcast layers are used when reconfiguring.
+ FakeVideoCapturerWithTaskQueue capturer;
+ VideoOptions options;
+ EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, &options, &capturer));
+ EXPECT_EQ(cricket::CS_RUNNING,
+ capturer.Start(capturer.GetSupportedFormats()->front()));
+ channel_->SetSend(true);
+ EXPECT_TRUE(capturer.CaptureFrame());
+
+ // Get and set the rtp encoding parameters.
+ webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+ EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
+
+ // Change the value and set it on the VideoChannel.
+ // Layer 0: only configure min bitrate.
+ const int kMinBpsLayer0 = kDefault[0].min_bitrate_bps + 1;
+ parameters.encodings[0].min_bitrate_bps = kMinBpsLayer0;
+ // Layer 1: only configure max bitrate.
+ const int kMaxBpsLayer1 = kDefault[1].max_bitrate_bps - 1;
+ parameters.encodings[1].max_bitrate_bps = kMaxBpsLayer1;
+ EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+
+ // Verify that the new value propagated down to the encoder.
+ // Check that WebRtcVideoSendStream updates VideoEncoderConfig correctly.
+ webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig().Copy();
+ EXPECT_EQ(kNumSimulcastStreams, encoder_config.number_of_streams);
+ EXPECT_EQ(kNumSimulcastStreams, encoder_config.simulcast_layers.size());
+ EXPECT_EQ(kMinBpsLayer0, encoder_config.simulcast_layers[0].min_bitrate_bps);
+ EXPECT_EQ(-1, encoder_config.simulcast_layers[0].max_bitrate_bps);
+ EXPECT_EQ(-1, encoder_config.simulcast_layers[1].min_bitrate_bps);
+ EXPECT_EQ(kMaxBpsLayer1, encoder_config.simulcast_layers[1].max_bitrate_bps);
+ EXPECT_EQ(-1, encoder_config.simulcast_layers[2].min_bitrate_bps);
+ EXPECT_EQ(-1, encoder_config.simulcast_layers[2].max_bitrate_bps);
+
+ // FakeVideoSendStream calls CreateEncoderStreams, test that the vector of
+ // VideoStreams are created appropriately for the simulcast case.
+ EXPECT_EQ(kNumSimulcastStreams, stream->GetVideoStreams().size());
+ // Layer 0: min configured bitrate should overwrite min default.
+ EXPECT_EQ(kMinBpsLayer0, stream->GetVideoStreams()[0].min_bitrate_bps);
+ EXPECT_EQ(kDefault[0].target_bitrate_bps,
+ stream->GetVideoStreams()[0].target_bitrate_bps);
+ EXPECT_EQ(kDefault[0].max_bitrate_bps,
+ stream->GetVideoStreams()[0].max_bitrate_bps);
+ // Layer 1: max configured bitrate should overwrite max default.
+ EXPECT_EQ(kDefault[1].min_bitrate_bps,
+ stream->GetVideoStreams()[1].min_bitrate_bps);
+ EXPECT_EQ(kDefault[1].target_bitrate_bps,
+ stream->GetVideoStreams()[1].target_bitrate_bps);
+ EXPECT_EQ(kMaxBpsLayer1, stream->GetVideoStreams()[1].max_bitrate_bps);
+ // Layer 2: min and max bitrate not configured, default expected.
+ EXPECT_EQ(kDefault[2].min_bitrate_bps,
+ stream->GetVideoStreams()[2].min_bitrate_bps);
+ EXPECT_EQ(kDefault[2].target_bitrate_bps,
+ stream->GetVideoStreams()[2].target_bitrate_bps);
+ EXPECT_EQ(kDefault[2].max_bitrate_bps,
+ stream->GetVideoStreams()[2].max_bitrate_bps);
+
+ EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, nullptr));
+}
+
+// Test that specifying the min (or max) bitrate value for a layer via
+// RtpParameters above (or below) the simulcast default max (or min) adjusts the
+// unspecified values accordingly.
+TEST_F(WebRtcVideoChannelTest, SetMinAndMaxSimulcastBitrateAboveBelowDefault) {
+ const size_t kNumSimulcastStreams = 3;
+ const std::vector<webrtc::VideoStream> kDefault = GetSimulcastBitrates720p();
+ FakeVideoSendStream* stream = SetUpSimulcast(true, false);
+
+ // Send a full size frame so all simulcast layers are used when reconfiguring.
+ FakeVideoCapturerWithTaskQueue capturer;
+ VideoOptions options;
+ EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, &options, &capturer));
+ EXPECT_EQ(cricket::CS_RUNNING,
+ capturer.Start(capturer.GetSupportedFormats()->front()));
+ channel_->SetSend(true);
+ EXPECT_TRUE(capturer.CaptureFrame());
+
+ // Get and set the rtp encoding parameters.
+ webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+ EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
+
+ // Change the value and set it on the VideoChannel.
+ // For layer 0, set the min bitrate above the default max.
+ const int kMinBpsLayer0 = kDefault[0].max_bitrate_bps + 1;
+ parameters.encodings[0].min_bitrate_bps = kMinBpsLayer0;
+ // For layer 1, set the max bitrate below the default min.
+ const int kMaxBpsLayer1 = kDefault[1].min_bitrate_bps - 1;
+ parameters.encodings[1].max_bitrate_bps = kMaxBpsLayer1;
+ EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+
+ // Verify that the new value propagated down to the encoder.
+ // FakeVideoSendStream calls CreateEncoderStreams, test that the vector of
+ // VideoStreams are created appropriately for the simulcast case.
+ EXPECT_EQ(kNumSimulcastStreams, stream->GetVideoStreams().size());
+ // Layer 0: Min bitrate above default max (target/max should be adjusted).
+ EXPECT_EQ(kMinBpsLayer0, stream->GetVideoStreams()[0].min_bitrate_bps);
+ EXPECT_EQ(kMinBpsLayer0, stream->GetVideoStreams()[0].target_bitrate_bps);
+ EXPECT_EQ(kMinBpsLayer0, stream->GetVideoStreams()[0].max_bitrate_bps);
+ // Layer 1: Max bitrate below default min (min/target should be adjusted).
+ EXPECT_EQ(kMaxBpsLayer1, stream->GetVideoStreams()[1].min_bitrate_bps);
+ EXPECT_EQ(kMaxBpsLayer1, stream->GetVideoStreams()[1].target_bitrate_bps);
+ EXPECT_EQ(kMaxBpsLayer1, stream->GetVideoStreams()[1].max_bitrate_bps);
+ // Layer 2: min and max bitrate not configured, default expected.
+ EXPECT_EQ(kDefault[2].min_bitrate_bps,
+ stream->GetVideoStreams()[2].min_bitrate_bps);
+ EXPECT_EQ(kDefault[2].target_bitrate_bps,
+ stream->GetVideoStreams()[2].target_bitrate_bps);
+ EXPECT_EQ(kDefault[2].max_bitrate_bps,
+ stream->GetVideoStreams()[2].max_bitrate_bps);
+
+ EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, nullptr));
+}
+
+TEST_F(WebRtcVideoChannelTest, BandwidthAboveTotalMaxBitrateGivenToMaxLayer) {
+ const size_t kNumSimulcastStreams = 3;
+ const std::vector<webrtc::VideoStream> kDefault = GetSimulcastBitrates720p();
+ FakeVideoSendStream* stream = SetUpSimulcast(true, false);
+
+ // Send a full size frame so all simulcast layers are used when reconfiguring.
+ FakeVideoCapturerWithTaskQueue capturer;
+ VideoOptions options;
+ EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, &options, &capturer));
+ EXPECT_EQ(cricket::CS_RUNNING,
+ capturer.Start(capturer.GetSupportedFormats()->front()));
+ channel_->SetSend(true);
+ EXPECT_TRUE(capturer.CaptureFrame());
+
+ // Set max bitrate for all but the highest layer.
+ webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+ EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
+ parameters.encodings[0].max_bitrate_bps = kDefault[0].max_bitrate_bps;
+ parameters.encodings[1].max_bitrate_bps = kDefault[1].max_bitrate_bps;
+ EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+
+ // Set max bandwidth equal to total max bitrate.
+ send_parameters_.max_bandwidth_bps =
+ GetTotalMaxBitrateBps(stream->GetVideoStreams());
+ ExpectSetMaxBitrate(send_parameters_.max_bandwidth_bps);
+ ASSERT_TRUE(channel_->SetSendParameters(send_parameters_));
+
+ // No bitrate above the total max to give to the highest layer.
+ EXPECT_EQ(kNumSimulcastStreams, stream->GetVideoStreams().size());
+ EXPECT_EQ(kDefault[2].max_bitrate_bps,
+ stream->GetVideoStreams()[2].max_bitrate_bps);
+
+ // Set max bandwidth above the total max bitrate.
+ send_parameters_.max_bandwidth_bps =
+ GetTotalMaxBitrateBps(stream->GetVideoStreams()) + 1;
+ ExpectSetMaxBitrate(send_parameters_.max_bandwidth_bps);
+ ASSERT_TRUE(channel_->SetSendParameters(send_parameters_));
+
+ // The highest layer has no max bitrate set -> the bitrate above the total
+ // max should be given to the highest layer.
+ EXPECT_EQ(kNumSimulcastStreams, stream->GetVideoStreams().size());
+ EXPECT_EQ(send_parameters_.max_bandwidth_bps,
+ GetTotalMaxBitrateBps(stream->GetVideoStreams()));
+ EXPECT_EQ(kDefault[2].max_bitrate_bps + 1,
+ stream->GetVideoStreams()[2].max_bitrate_bps);
+
+ EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, nullptr));
+}
+
+TEST_F(WebRtcVideoChannelTest,
+ BandwidthAboveTotalMaxBitrateNotGivenToMaxLayerIfMaxBitrateSet) {
+ const size_t kNumSimulcastStreams = 3;
+ const std::vector<webrtc::VideoStream> kDefault = GetSimulcastBitrates720p();
+ EXPECT_EQ(kNumSimulcastStreams, kDefault.size());
+ FakeVideoSendStream* stream = SetUpSimulcast(true, false);
+
+ // Send a full size frame so all simulcast layers are used when reconfiguring.
+ FakeVideoCapturerWithTaskQueue capturer;
+ VideoOptions options;
+ EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, &options, &capturer));
+ EXPECT_EQ(cricket::CS_RUNNING,
+ capturer.Start(capturer.GetSupportedFormats()->front()));
+ channel_->SetSend(true);
+ EXPECT_TRUE(capturer.CaptureFrame());
+
+ // Set max bitrate for the highest layer.
+ webrtc::RtpParameters parameters = channel_->GetRtpSendParameters(last_ssrc_);
+ EXPECT_EQ(kNumSimulcastStreams, parameters.encodings.size());
+ parameters.encodings[2].max_bitrate_bps = kDefault[2].max_bitrate_bps;
+ EXPECT_TRUE(channel_->SetRtpSendParameters(last_ssrc_, parameters).ok());
+
+ // Set max bandwidth above the total max bitrate.
+ send_parameters_.max_bandwidth_bps =
+ GetTotalMaxBitrateBps(stream->GetVideoStreams()) + 1;
+ ExpectSetMaxBitrate(send_parameters_.max_bandwidth_bps);
+ ASSERT_TRUE(channel_->SetSendParameters(send_parameters_));
+
+ // The highest layer has the max bitrate set -> the bitrate above the total
+ // max should not be given to the highest layer.
+ EXPECT_EQ(kNumSimulcastStreams, stream->GetVideoStreams().size());
+ EXPECT_EQ(*parameters.encodings[2].max_bitrate_bps,
+ stream->GetVideoStreams()[2].max_bitrate_bps);
+
+ EXPECT_TRUE(channel_->SetVideoSend(last_ssrc_, nullptr, nullptr));
+}
+
// Test that a stream will not be sending if its encoding is made inactive
// through SetRtpSendParameters.
TEST_F(WebRtcVideoChannelTest, SetRtpSendParametersOneEncodingActive) {
diff --git a/pc/rtpsender.cc b/pc/rtpsender.cc
index cffcd8b..e8105a8 100644
--- a/pc/rtpsender.cc
+++ b/pc/rtpsender.cc
@@ -58,8 +58,7 @@
// layer.
bool PerSenderRtpEncodingParameterHasValue(
const RtpEncodingParameters& encoding_params) {
- if (encoding_params.max_bitrate_bps.has_value() ||
- encoding_params.bitrate_priority != kDefaultBitratePriority) {
+ if (encoding_params.bitrate_priority != kDefaultBitratePriority) {
return true;
}
return false;
diff --git a/pc/rtpsenderreceiver_unittest.cc b/pc/rtpsenderreceiver_unittest.cc
index 99e624d..8151ad6 100644
--- a/pc/rtpsenderreceiver_unittest.cc
+++ b/pc/rtpsenderreceiver_unittest.cc
@@ -959,31 +959,31 @@
video_rtp_sender_->SetParameters(params).type());
params = video_rtp_sender_->GetParameters();
- params.encodings[1].max_bitrate_bps = 200000;
- EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
- video_rtp_sender_->SetParameters(params).type());
-
DestroyVideoRtpSender();
}
-TEST_F(RtpSenderReceiverTest, SetVideoMaxSendBitrate) {
+TEST_F(RtpSenderReceiverTest, SetVideoMinMaxSendBitrate) {
CreateVideoRtpSender();
EXPECT_EQ(-1, video_media_channel_->max_bps());
webrtc::RtpParameters params = video_rtp_sender_->GetParameters();
EXPECT_EQ(1, params.encodings.size());
+ EXPECT_FALSE(params.encodings[0].min_bitrate_bps);
EXPECT_FALSE(params.encodings[0].max_bitrate_bps);
+ params.encodings[0].min_bitrate_bps = 100;
params.encodings[0].max_bitrate_bps = 1000;
EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok());
// Read back the parameters and verify they have been changed.
params = video_rtp_sender_->GetParameters();
EXPECT_EQ(1, params.encodings.size());
+ EXPECT_EQ(100, params.encodings[0].min_bitrate_bps);
EXPECT_EQ(1000, params.encodings[0].max_bitrate_bps);
// Verify that the video channel received the new parameters.
params = video_media_channel_->GetRtpSendParameters(kVideoSsrc);
EXPECT_EQ(1, params.encodings.size());
+ EXPECT_EQ(100, params.encodings[0].min_bitrate_bps);
EXPECT_EQ(1000, params.encodings[0].max_bitrate_bps);
// Verify that the global bitrate limit has not been changed.
@@ -992,6 +992,34 @@
DestroyVideoRtpSender();
}
+TEST_F(RtpSenderReceiverTest, SetVideoMinMaxSendBitrateSimulcast) {
+ // Add a simulcast specific send stream that contains 2 encoding parameters.
+ std::vector<uint32_t> ssrcs({1, 2});
+ cricket::StreamParams stream_params =
+ cricket::CreateSimStreamParams("cname", ssrcs);
+ video_media_channel_->AddSendStream(stream_params);
+ uint32_t primary_ssrc = stream_params.first_ssrc();
+ CreateVideoRtpSender(primary_ssrc);
+
+ RtpParameters params = video_rtp_sender_->GetParameters();
+ EXPECT_EQ(ssrcs.size(), params.encodings.size());
+ params.encodings[0].min_bitrate_bps = 100;
+ params.encodings[0].max_bitrate_bps = 1000;
+ params.encodings[1].min_bitrate_bps = 200;
+ params.encodings[1].max_bitrate_bps = 2000;
+ EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok());
+
+ // Verify that the video channel received the new parameters.
+ params = video_media_channel_->GetRtpSendParameters(primary_ssrc);
+ EXPECT_EQ(ssrcs.size(), params.encodings.size());
+ EXPECT_EQ(100, params.encodings[0].min_bitrate_bps);
+ EXPECT_EQ(1000, params.encodings[0].max_bitrate_bps);
+ EXPECT_EQ(200, params.encodings[1].min_bitrate_bps);
+ EXPECT_EQ(2000, params.encodings[1].max_bitrate_bps);
+
+ DestroyVideoRtpSender();
+}
+
TEST_F(RtpSenderReceiverTest, SetVideoBitratePriority) {
CreateVideoRtpSender();