Fix logic reading spatial/temporal id in VideoStreamEncoder.

The temporal id must be read from `EncodedImage` rather than codec
specifics for AV1. Furthermore, in some configs the spatial id of
`EncodedImage` is populated and set to 0 while the simulcast id can
also be simultaneously populated and set to values, including non-zero.
To solve this, just take the max of the two.

Bug: b/349561566
Change-Id: I46c61b7f0fff7a7ab8d7262c3a8d413f49b3286a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/355904
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Commit-Queue: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42573}
This commit is contained in:
Erik Språng 2024-07-02 13:53:38 +02:00 committed by WebRTC LUCI CQ
parent 445d403eca
commit fe4c1dd6dc
3 changed files with 60 additions and 3 deletions

View File

@ -151,6 +151,7 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image,
if (qp) if (qp)
encoded.qp_ = *qp; encoded.qp_ = *qp;
encoded.SetSimulcastIndex(i); encoded.SetSimulcastIndex(i);
encoded.SetTemporalIndex(frame_info.layers[i].temporal_id);
CodecSpecificInfo codec_specific = EncodeHook(encoded, buffer); CodecSpecificInfo codec_specific = EncodeHook(encoded, buffer);
if (callback->OnEncodedImage(encoded, &codec_specific).error != if (callback->OnEncodedImage(encoded, &codec_specific).error !=

View File

@ -2168,7 +2168,10 @@ EncodedImageCallback::Result VideoStreamEncoder::OnEncodedImage(
image_copy.ClearEncodedData(); image_copy.ClearEncodedData();
int temporal_index = 0; int temporal_index = 0;
if (codec_specific_info) { if (encoded_image.TemporalIndex()) {
// Give precedence to the metadata on EncodedImage, if available.
temporal_index = *encoded_image.TemporalIndex();
} else if (codec_specific_info) {
if (codec_specific_info->codecType == kVideoCodecVP9) { if (codec_specific_info->codecType == kVideoCodecVP9) {
temporal_index = codec_specific_info->codecSpecific.VP9.temporal_idx; temporal_index = codec_specific_info->codecSpecific.VP9.temporal_idx;
} else if (codec_specific_info->codecType == kVideoCodecVP8) { } else if (codec_specific_info->codecType == kVideoCodecVP8) {
@ -2412,8 +2415,8 @@ void VideoStreamEncoder::RunPostEncode(const EncodedImage& encoded_image,
// TODO(https://crbug.com/webrtc/14891): If we want to support a mix of // TODO(https://crbug.com/webrtc/14891): If we want to support a mix of
// simulcast and SVC we'll also need to consider the case where we have both // simulcast and SVC we'll also need to consider the case where we have both
// simulcast and spatial indices. // simulcast and spatial indices.
int stream_index = encoded_image.SpatialIndex().value_or( int stream_index = std::max(encoded_image.SimulcastIndex().value_or(0),
encoded_image.SimulcastIndex().value_or(0)); encoded_image.SpatialIndex().value_or(0));
bitrate_adjuster_->OnEncodedFrame(frame_size, stream_index, temporal_index); bitrate_adjuster_->OnEncodedFrame(frame_size, stream_index, temporal_index);
} }
} }

View File

@ -76,6 +76,7 @@
#include "test/video_encoder_proxy_factory.h" #include "test/video_encoder_proxy_factory.h"
#include "video/config/encoder_stream_factory.h" #include "video/config/encoder_stream_factory.h"
#include "video/config/video_encoder_config.h" #include "video/config/video_encoder_config.h"
#include "video/encoder_bitrate_adjuster.h"
#include "video/frame_cadence_adapter.h" #include "video/frame_cadence_adapter.h"
#include "video/send_statistics_proxy.h" #include "video/send_statistics_proxy.h"
@ -2583,6 +2584,58 @@ TEST_F(VideoStreamEncoderTest,
video_stream_encoder_->Stop(); video_stream_encoder_->Stop();
} }
TEST_F(VideoStreamEncoderTest, CorrectlyAdjustsAv1Bitrate) {
ResetEncoder("AV1", /*num_streams*/ 2, /*num_temporal_layers=*/2,
/*num_spatial_layers=*/1, /*screenshare*/ false,
kDefaultFramerate,
VideoStreamEncoder::BitrateAllocationCallbackType::
kVideoLayersAllocation);
// Let link allocation and stable bitrate be 2x the target bitrate.
video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources(
kTargetBitrate, 2 * kTargetBitrate, 2 * kTargetBitrate, 0, 0, 0);
video_source_.IncomingCapturedFrame(
CreateFrame(CurrentTimeMs(), codec_width_, codec_height_));
WaitForEncodedFrame(CurrentTimeMs());
// Before enough data has been gathered, some default pushback is applied.
VideoEncoder::RateControlParameters rate_settings =
*fake_encoder_.GetAndResetLastRateControlSettings();
// Allow 5% diff from target bitrate.
const double allowed_error_bps =
rate_settings.target_bitrate.get_sum_bps() * 0.05;
EXPECT_NEAR(rate_settings.bitrate.get_sum_bps(),
rate_settings.target_bitrate.get_sum_bps() /
EncoderBitrateAdjuster::kDefaultUtilizationFactor,
allowed_error_bps);
// Insert frames until bitrate adjuster is saturated.
const TimeDelta runtime =
TimeDelta::Millis(EncoderBitrateAdjuster::kWindowSizeMs);
const Timestamp start_time = clock()->CurrentTime();
while (clock()->CurrentTime() - start_time < runtime) {
video_source_.IncomingCapturedFrame(
CreateFrame(CurrentTimeMs(), codec_width_, codec_height_));
WaitForEncodedFrame(CurrentTimeMs());
}
// Make sure rate has been reallocated.
video_stream_encoder_->OnBitrateUpdatedAndWaitForManagedResources(
kTargetBitrate - DataRate::BitsPerSec(500), 2 * kTargetBitrate,
2 * kTargetBitrate, 0, 0, 0);
video_source_.IncomingCapturedFrame(
CreateFrame(CurrentTimeMs(), codec_width_, codec_height_));
WaitForEncodedFrame(CurrentTimeMs());
// Pushback should no longer happen.
rate_settings = *fake_encoder_.GetAndResetLastRateControlSettings();
EXPECT_NEAR(rate_settings.bitrate.get_sum_bps(),
rate_settings.target_bitrate.get_sum_bps(), allowed_error_bps);
video_stream_encoder_->Stop();
}
class ResolutionAlignmentTest class ResolutionAlignmentTest
: public VideoStreamEncoderTest, : public VideoStreamEncoderTest,
public ::testing::WithParamInterface< public ::testing::WithParamInterface<