Add FullStackTest with simulated encoder overshooting
Bug: webrtc:10302
Change-Id: I1d4b9ef22ba1ca9a221cc01e2c44775014c90d4f
Reviewed-on: https://webrtc-review.googlesource.com/c/122082
Commit-Queue: Erik Språng <sprang@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26673}
diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc
index 1b2f9d6..ad3224d 100644
--- a/video/video_quality_test.cc
+++ b/video/video_quality_test.cc
@@ -56,6 +56,27 @@
constexpr int kDefaultMaxQp = cricket::WebRtcVideoChannel::kDefaultQpMax;
+std::pair<uint32_t, uint32_t> GetMinMaxBitratesBps(const VideoCodec& codec,
+ size_t spatial_idx) {
+ uint32_t min_bitrate = codec.minBitrate;
+ uint32_t max_bitrate = codec.maxBitrate;
+ if (spatial_idx < codec.numberOfSimulcastStreams) {
+ min_bitrate =
+ std::max(min_bitrate, codec.simulcastStream[spatial_idx].minBitrate);
+ max_bitrate =
+ std::min(max_bitrate, codec.simulcastStream[spatial_idx].maxBitrate);
+ }
+ if (codec.codecType == VideoCodecType::kVideoCodecVP9 &&
+ spatial_idx < codec.VP9().numberOfSpatialLayers) {
+ min_bitrate =
+ std::max(min_bitrate, codec.spatialLayers[spatial_idx].minBitrate);
+ max_bitrate =
+ std::min(max_bitrate, codec.spatialLayers[spatial_idx].maxBitrate);
+ }
+ max_bitrate = std::max(max_bitrate, min_bitrate);
+ return {min_bitrate * 1000, max_bitrate * 1000};
+}
+
class VideoStreamFactory
: public VideoEncoderConfig::VideoStreamFactoryInterface {
public:
@@ -87,8 +108,11 @@
public:
QualityTestVideoEncoder(std::unique_ptr<VideoEncoder> encoder,
VideoAnalyzer* analyzer,
- std::vector<FileWrapper> files)
- : encoder_(std::move(encoder)), analyzer_(analyzer) {
+ std::vector<FileWrapper> files,
+ double overshoot_factor)
+ : encoder_(std::move(encoder)),
+ overshoot_factor_(overshoot_factor),
+ analyzer_(analyzer) {
for (FileWrapper& file : files) {
writers_.push_back(
IvfFileWriter::Wrap(std::move(file), /* byte_limit= */ 100000000));
@@ -98,6 +122,7 @@
int32_t InitEncode(const VideoCodec* codec_settings,
int32_t number_of_cores,
size_t max_payload_size) override {
+ codec_settings_ = *codec_settings;
return encoder_->InitEncode(codec_settings, number_of_cores,
max_payload_size);
}
@@ -115,15 +140,57 @@
}
return encoder_->Encode(frame, codec_specific_info, frame_types);
}
- int32_t SetRates(uint32_t bitrate, uint32_t framerate) override {
- return encoder_->SetRates(bitrate, framerate);
- }
int32_t SetRateAllocation(const VideoBitrateAllocation& allocation,
uint32_t framerate) override {
- return encoder_->SetRateAllocation(allocation, framerate);
+ RTC_DCHECK_GT(overshoot_factor_, 0.0);
+ if (overshoot_factor_ == 1.0) {
+ return encoder_->SetRateAllocation(allocation, framerate);
+ }
+
+ // Simulating encoder overshooting target bitrate, by configuring actual
+ // encoder too high. Take care not to adjust past limits of config,
+ // otherwise encoders may crash on DCHECK.
+ VideoBitrateAllocation overshot_allocation;
+ for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
+ const uint32_t spatial_layer_bitrate_bps =
+ allocation.GetSpatialLayerSum(si);
+ if (spatial_layer_bitrate_bps == 0) {
+ continue;
+ }
+
+ uint32_t min_bitrate_bps;
+ uint32_t max_bitrate_bps;
+ std::tie(min_bitrate_bps, max_bitrate_bps) =
+ GetMinMaxBitratesBps(codec_settings_, si);
+ double overshoot_factor = overshoot_factor_;
+ const uint32_t corrected_bitrate = rtc::checked_cast<uint32_t>(
+ overshoot_factor * spatial_layer_bitrate_bps);
+ if (corrected_bitrate < min_bitrate_bps) {
+ overshoot_factor = min_bitrate_bps / spatial_layer_bitrate_bps;
+ } else if (corrected_bitrate > max_bitrate_bps) {
+ overshoot_factor = max_bitrate_bps / spatial_layer_bitrate_bps;
+ }
+
+ for (size_t ti = 0; ti < kMaxTemporalStreams; ++ti) {
+ if (allocation.HasBitrate(si, ti)) {
+ overshot_allocation.SetBitrate(
+ si, ti,
+ rtc::checked_cast<uint32_t>(overshoot_factor *
+ allocation.GetBitrate(si, ti)));
+ }
+ }
+ }
+
+ return encoder_->SetRateAllocation(overshot_allocation, framerate);
}
EncoderInfo GetEncoderInfo() const override {
- return encoder_->GetEncoderInfo();
+ EncoderInfo info = encoder_->GetEncoderInfo();
+ if (overshoot_factor_ != 1.0) {
+ // We're simulating bad encoder, don't forward trusted setting
+ // from eg libvpx.
+ info.has_trusted_rate_controller = false;
+ }
+ return info;
}
private:
@@ -157,10 +224,12 @@
callback_->OnDroppedFrame(reason);
}
- std::unique_ptr<VideoEncoder> encoder_;
- EncodedImageCallback* callback_ = nullptr;
+ const std::unique_ptr<VideoEncoder> encoder_;
+ const double overshoot_factor_;
VideoAnalyzer* const analyzer_;
std::vector<std::unique_ptr<IvfFileWriter>> writers_;
+ EncodedImageCallback* callback_ = nullptr;
+ VideoCodec codec_settings_;
};
} // namespace
@@ -202,22 +271,42 @@
} else {
encoder = internal_encoder_factory_.CreateVideoEncoder(format);
}
+
+ std::vector<FileWrapper> encoded_frame_dump_files;
if (!params_.logging.encoded_frame_base_path.empty()) {
char ss_buf[100];
rtc::SimpleStringBuilder sb(ss_buf);
sb << send_logs_++;
std::string prefix =
params_.logging.encoded_frame_base_path + "." + sb.str() + ".send.";
- std::vector<FileWrapper> files;
- files.push_back(FileWrapper::OpenWriteOnly(prefix + "1.ivf"));
- files.push_back(FileWrapper::OpenWriteOnly(prefix + "2.ivf"));
- files.push_back(FileWrapper::OpenWriteOnly(prefix + "3.ivf"));
+ encoded_frame_dump_files.push_back(
+ FileWrapper::OpenWriteOnly(prefix + "1.ivf"));
+ encoded_frame_dump_files.push_back(
+ FileWrapper::OpenWriteOnly(prefix + "2.ivf"));
+ encoded_frame_dump_files.push_back(
+ FileWrapper::OpenWriteOnly(prefix + "3.ivf"));
+ }
+ double overshoot_factor = 1.0;
+ // Match format to either of the streams in dual-stream mode in order to get
+ // the overshoot factor. This is not very robust but we can't know for sure
+ // which stream this encoder is meant for, from within the factory.
+ if (format ==
+ SdpVideoFormat(params_.video[0].codec, params_.video[0].sdp_params)) {
+ overshoot_factor = params_.video[0].encoder_overshoot_factor;
+ } else if (format == SdpVideoFormat(params_.video[1].codec,
+ params_.video[1].sdp_params)) {
+ overshoot_factor = params_.video[1].encoder_overshoot_factor;
+ }
+ if (overshoot_factor == 0.0) {
+ // If params were zero-initialized, set to 1.0 instead.
+ overshoot_factor = 1.0;
+ }
+
+ if (analyzer || !encoded_frame_dump_files.empty() || overshoot_factor > 1.0) {
encoder = absl::make_unique<QualityTestVideoEncoder>(
- std::move(encoder), analyzer, std::move(files));
- } else if (analyzer) {
- encoder = absl::make_unique<QualityTestVideoEncoder>(
- std::move(encoder), analyzer, std::vector<FileWrapper>());
+ std::move(encoder), analyzer, std::move(encoded_frame_dump_files),
+ overshoot_factor);
}
return encoder;
@@ -263,10 +352,44 @@
VideoQualityTest::Params::Params()
: call({false, false, BitrateConstraints(), 0}),
- video{{false, 640, 480, 30, 50, 800, 800, false, "VP8", 1, -1, 0, false,
- false, false, ""},
- {false, 640, 480, 30, 50, 800, 800, false, "VP8", 1, -1, 0, false,
- false, false, ""}},
+ video{{false,
+ 640,
+ 480,
+ 30,
+ 50,
+ 800,
+ 800,
+ false,
+ "VP8",
+ 1,
+ -1,
+ 0,
+ false,
+ false,
+ false,
+ "",
+ 0,
+ {},
+ 0.0},
+ {false,
+ 640,
+ 480,
+ 30,
+ 50,
+ 800,
+ 800,
+ false,
+ "VP8",
+ 1,
+ -1,
+ 0,
+ false,
+ false,
+ false,
+ "",
+ 0,
+ {},
+ 0.0}},
audio({false, false, false, false}),
screenshare{{false, false, 10, 0}, {false, false, 10, 0}},
analyzer({"", 0.0, 0.0, 0, "", ""}),