Reset encoder on simulcast layer maxFramerate changes

Without this, Firefox wasn't passing WPT
webrtc/simulcast/setParameters-maxFramerate.https.html.

The main issue is the SetRates API's RateControlParameters doesn't have
a way to model maxFramerate for simulcast layers.

A long term fix would probably be to represent maxFramerate for all
simulcast layers in RateControlParameters. This change is a short term
fix, and resets the encoder iff a simulcast layer's maxFramerate has
changed, and also differs from the maxFramerate of the codec (passed to
SetRates), which matches the layer with the highest maxFramerate.

Bug: None
Change-Id: I088dda0fe88092fe5a5cc61114e10847f072a87b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/370124
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43497}
This commit is contained in:
Andreas Pehrson 2024-12-03 22:18:26 +01:00 committed by WebRTC LUCI CQ
parent 67de21e407
commit 0c282c471b
2 changed files with 66 additions and 21 deletions

View File

@ -174,6 +174,15 @@ bool RequiresEncoderReset(const VideoCodec& prev_send_codec,
prev_send_codec.simulcastStream[i].qpMax) {
return true;
}
if (new_send_codec.simulcastStream[i].maxFramerate !=
prev_send_codec.simulcastStream[i].maxFramerate &&
new_send_codec.simulcastStream[i].maxFramerate !=
new_send_codec.maxFramerate) {
// SetRates can only represent maxFramerate for one layer. Reset the
// encoder if there are multiple layers that differ in maxFramerate.
return true;
}
}
if (new_send_codec.codecType == kVideoCodecVP9) {

View File

@ -9404,9 +9404,9 @@ INSTANTIATE_TEST_SUITE_P(
TestParametersVideoCodecAndAllowI420ConversionToString);
#endif
class ReconfigureEncoderTest : public VideoStreamEncoderTest {
class ReconfigureSimulcastEncoderTest : public VideoStreamEncoderTest {
protected:
void RunTest(const std::vector<VideoStream>& configs,
void RunSimulcastTest(const std::vector<std::vector<VideoStream>>& configs,
const int expected_num_init_encode) {
ConfigureEncoder(configs[0]);
OnBitrateUpdated(kTargetBitrate);
@ -9428,11 +9428,17 @@ class ReconfigureEncoderTest : public VideoStreamEncoderTest {
video_stream_encoder_->Stop();
}
void ConfigureEncoder(const VideoStream& stream) {
void ConfigureEncoder(const std::vector<VideoStream>& streams) {
VideoEncoderConfig config;
test::FillEncoderConfiguration(kVideoCodecVP8, /*num_streams=*/1, &config);
config.max_bitrate_bps = stream.max_bitrate_bps;
config.simulcast_layers[0] = stream;
test::FillEncoderConfiguration(kVideoCodecVP8,
/*num_streams=*/streams.size(), &config);
auto highest_bitrate_stream =
absl::c_max_element(streams, [](const auto& a, const auto& b) {
return a.max_bitrate_bps < b.max_bitrate_bps;
});
config.max_bitrate_bps = highest_bitrate_stream->max_bitrate_bps;
config.simulcast_layers = streams;
config.number_of_streams = streams.size();
config.video_stream_factory = nullptr;
video_stream_encoder_->ConfigureEncoder(std::move(config),
kMaxPayloadLength);
@ -9451,20 +9457,23 @@ class ReconfigureEncoderTest : public VideoStreamEncoderTest {
}
void ExpectEqual(const VideoCodec& actual,
const VideoStream& expected) const {
EXPECT_EQ(actual.numberOfSimulcastStreams, 1);
EXPECT_EQ(actual.simulcastStream[0].maxFramerate, expected.max_framerate);
EXPECT_EQ(actual.simulcastStream[0].minBitrate * 1000,
static_cast<unsigned int>(expected.min_bitrate_bps));
EXPECT_EQ(actual.simulcastStream[0].maxBitrate * 1000,
static_cast<unsigned int>(expected.max_bitrate_bps));
EXPECT_EQ(actual.simulcastStream[0].width,
kWidth / expected.scale_resolution_down_by);
EXPECT_EQ(actual.simulcastStream[0].height,
kHeight / expected.scale_resolution_down_by);
EXPECT_EQ(actual.simulcastStream[0].numberOfTemporalLayers,
expected.num_temporal_layers);
EXPECT_EQ(actual.GetScalabilityMode(), expected.scalability_mode);
const std::vector<VideoStream>& expected) const {
EXPECT_EQ(actual.numberOfSimulcastStreams, expected.size());
for (size_t i = 0; i < actual.numberOfSimulcastStreams; ++i) {
EXPECT_EQ(actual.simulcastStream[i].maxFramerate,
expected[i].max_framerate);
EXPECT_EQ(actual.simulcastStream[i].minBitrate * 1000,
static_cast<unsigned int>(expected[i].min_bitrate_bps));
EXPECT_EQ(actual.simulcastStream[i].maxBitrate * 1000,
static_cast<unsigned int>(expected[i].max_bitrate_bps));
EXPECT_EQ(actual.simulcastStream[i].width,
kWidth / expected[i].scale_resolution_down_by);
EXPECT_EQ(actual.simulcastStream[i].height,
kHeight / expected[i].scale_resolution_down_by);
EXPECT_EQ(actual.simulcastStream[i].numberOfTemporalLayers,
expected[i].num_temporal_layers);
EXPECT_EQ(actual.GetScalabilityMode(), expected[i].scalability_mode);
}
}
VideoStream DefaultConfig() const {
@ -9484,6 +9493,33 @@ class ReconfigureEncoderTest : public VideoStreamEncoderTest {
int64_t timestamp_ms_ = 0;
};
TEST_F(ReconfigureSimulcastEncoderTest, ReconfiguredIfMaxFramerateChanges) {
VideoStream config_before_high = DefaultConfig();
config_before_high.scale_resolution_down_by = 1;
config_before_high.max_framerate = 30;
VideoStream config_before_low = config_before_high;
config_before_low.scale_resolution_down_by = 2;
config_before_low.max_framerate = 20;
// Keep the highest layer's max_framerate so as to not cause a change in
// VideoCodec::maxFramerate that may influence this test.
VideoStream config_after_high = config_before_high;
VideoStream config_after_low = config_before_low;
config_after_low.max_framerate = 10;
RunSimulcastTest({{config_before_high, config_before_low},
{config_after_high, config_after_low}},
/*expected_num_init_encode=*/2);
}
class ReconfigureEncoderTest : public ReconfigureSimulcastEncoderTest {
public:
void RunTest(const std::vector<VideoStream>& configs,
const int expected_num_init_encode) {
RunSimulcastTest({{configs[0]}, {configs[1]}}, expected_num_init_encode);
}
};
TEST_F(ReconfigureEncoderTest, NotReconfiguredIfMaxFramerateChanges) {
VideoStream config1 = DefaultConfig();
VideoStream config2 = config1;