RtpEncodingParameters::request_resolution patch 3
This cl/ adds resource adapation to the requested_resolution
feature. The restrictions that are sent to the video source
are also saved inside video_stream_encoder and used when
determining layer resolution.
Anticipated further patches
4) Let VideoSource do adaption if possible
Bug: webrtc:14451
Change-Id: Ia9b990a6b92b76af7ff6665a562f84585f79c35b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/277580
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38306}
diff --git a/call/adaptation/video_source_restrictions.cc b/call/adaptation/video_source_restrictions.cc
index e9d6c26..719bc53 100644
--- a/call/adaptation/video_source_restrictions.cc
+++ b/call/adaptation/video_source_restrictions.cc
@@ -10,6 +10,7 @@
#include "call/adaptation/video_source_restrictions.h"
+#include <algorithm>
#include <limits>
#include "rtc_base/checks.h"
@@ -79,6 +80,30 @@
max_frame_rate_ = std::move(max_frame_rate);
}
+void VideoSourceRestrictions::UpdateMin(const VideoSourceRestrictions& other) {
+ if (max_pixels_per_frame_.has_value()) {
+ max_pixels_per_frame_ = std::min(*max_pixels_per_frame_,
+ other.max_pixels_per_frame().value_or(
+ std::numeric_limits<size_t>::max()));
+ } else {
+ max_pixels_per_frame_ = other.max_pixels_per_frame();
+ }
+ if (target_pixels_per_frame_.has_value()) {
+ target_pixels_per_frame_ = std::min(
+ *target_pixels_per_frame_, other.target_pixels_per_frame().value_or(
+ std::numeric_limits<size_t>::max()));
+ } else {
+ target_pixels_per_frame_ = other.target_pixels_per_frame();
+ }
+ if (max_frame_rate_.has_value()) {
+ max_frame_rate_ = std::min(
+ *max_frame_rate_,
+ other.max_frame_rate().value_or(std::numeric_limits<double>::max()));
+ } else {
+ max_frame_rate_ = other.max_frame_rate();
+ }
+}
+
bool DidRestrictionsIncrease(VideoSourceRestrictions before,
VideoSourceRestrictions after) {
bool decreased_resolution = DidDecreaseResolution(before, after);
diff --git a/call/adaptation/video_source_restrictions.h b/call/adaptation/video_source_restrictions.h
index 004cc09..be8520a 100644
--- a/call/adaptation/video_source_restrictions.h
+++ b/call/adaptation/video_source_restrictions.h
@@ -60,6 +60,9 @@
absl::optional<size_t> target_pixels_per_frame);
void set_max_frame_rate(absl::optional<double> max_frame_rate);
+ // Update `this` with min(`this`, `other`).
+ void UpdateMin(const VideoSourceRestrictions& other);
+
private:
// These map to rtc::VideoSinkWants's `max_pixel_count` and
// `target_pixel_count`.
diff --git a/call/adaptation/video_source_restrictions_unittest.cc b/call/adaptation/video_source_restrictions_unittest.cc
index 92e34f9..8c1ae4c 100644
--- a/call/adaptation/video_source_restrictions_unittest.cc
+++ b/call/adaptation/video_source_restrictions_unittest.cc
@@ -126,4 +126,21 @@
EXPECT_FALSE(DidRestrictionsDecrease(kHd, k15fps));
}
+TEST(VideoSourceRestrictions, UpdateMin) {
+ VideoSourceRestrictions one(kHdPixels / 2, kHdPixels, 7.0);
+ VideoSourceRestrictions two(kHdPixels, kHdPixels / 3, 15.0);
+
+ one.UpdateMin(two);
+
+ EXPECT_EQ(one.max_pixels_per_frame(), kHdPixels / 2);
+ EXPECT_EQ(one.target_pixels_per_frame(), kHdPixels / 3);
+ EXPECT_EQ(one.max_frame_rate(), 7.0);
+
+ two.UpdateMin(one);
+
+ EXPECT_EQ(two.max_pixels_per_frame(), kHdPixels / 2);
+ EXPECT_EQ(two.target_pixels_per_frame(), kHdPixels / 3);
+ EXPECT_EQ(two.max_frame_rate(), 7.0);
+}
+
} // namespace webrtc
diff --git a/video/BUILD.gn b/video/BUILD.gn
index 425e754..6b5eb2a 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -938,6 +938,7 @@
"adaptation:video_adaptation",
"config:encoder_config",
"config:streams_config",
+ "config:video_config_tests",
]
absl_deps = [
"//third_party/abseil-cpp/absl/algorithm:container",
diff --git a/video/config/BUILD.gn b/video/config/BUILD.gn
index 11871b8..8a5f03b 100644
--- a/video/config/BUILD.gn
+++ b/video/config/BUILD.gn
@@ -23,6 +23,7 @@
"../../api/units:data_rate",
"../../api/video:video_codec_constants",
"../../api/video_codecs:video_codecs_api",
+ "../../call/adaptation:resource_adaptation",
"../../media:rtc_media_base",
"../../modules/video_coding:video_coding_utility",
"../../modules/video_coding:webrtc_vp9_helpers",
@@ -69,10 +70,14 @@
testonly = true
defines = []
- sources = [ "simulcast_unittest.cc" ]
+ sources = [
+ "encoder_stream_factory_unittest.cc",
+ "simulcast_unittest.cc",
+ ]
deps = [
":streams_config",
"../../api/transport:field_trial_based_config",
+ "../../call/adaptation:resource_adaptation",
"../../test:field_trial",
"../../test:test_support",
]
diff --git a/video/config/encoder_stream_factory.cc b/video/config/encoder_stream_factory.cc
index c43922d..a413e4a 100644
--- a/video/config/encoder_stream_factory.cc
+++ b/video/config/encoder_stream_factory.cc
@@ -10,6 +10,7 @@
#include "video/config/encoder_stream_factory.h"
#include <algorithm>
+#include <limits>
#include <set>
#include <string>
#include <utility>
@@ -115,6 +116,7 @@
bool is_screenshare,
bool conference_mode,
const webrtc::VideoEncoder::EncoderInfo& encoder_info,
+ absl::optional<webrtc::VideoSourceRestrictions> restrictions,
const webrtc::FieldTrialsView* trials)
: codec_name_(codec_name),
max_qp_(max_qp),
@@ -122,7 +124,8 @@
conference_mode_(conference_mode),
trials_(trials ? *trials : fallback_trials_),
encoder_info_requested_resolution_alignment_(
- encoder_info.requested_resolution_alignment) {}
+ encoder_info.requested_resolution_alignment),
+ restrictions_(restrictions) {}
std::vector<webrtc::VideoStream> EncoderStreamFactory::CreateEncoderStreams(
int frame_width,
@@ -434,6 +437,16 @@
adapter.OnOutputFormatRequest(requested_resolution.ToPair(),
requested_resolution.PixelCount(),
absl::nullopt);
+ if (restrictions_) {
+ rtc::VideoSinkWants wants;
+ wants.is_active = true;
+ wants.target_pixel_count = restrictions_->target_pixels_per_frame();
+ wants.max_pixel_count =
+ rtc::dchecked_cast<int>(restrictions_->max_pixels_per_frame().value_or(
+ std::numeric_limits<int>::max()));
+ wants.resolution_alignment = encoder_info_requested_resolution_alignment_;
+ adapter.OnSinkWants(wants);
+ }
int cropped_width, cropped_height;
int out_width = 0, out_height = 0;
if (!adapter.AdaptFrameResolution(frame_width, frame_height, 0,
diff --git a/video/config/encoder_stream_factory.h b/video/config/encoder_stream_factory.h
index 11910eb..37abb93 100644
--- a/video/config/encoder_stream_factory.h
+++ b/video/config/encoder_stream_factory.h
@@ -16,6 +16,7 @@
#include "api/transport/field_trial_based_config.h"
#include "api/units/data_rate.h"
#include "api/video_codecs/video_encoder.h"
+#include "call/adaptation/video_source_restrictions.h"
#include "video/config/video_encoder_config.h"
namespace cricket {
@@ -34,14 +35,16 @@
bool is_screenshare,
bool conference_mode,
const webrtc::VideoEncoder::EncoderInfo& encoder_info,
+ absl::optional<webrtc::VideoSourceRestrictions>
+ restrictions = absl::nullopt,
const webrtc::FieldTrialsView* trials = nullptr);
- private:
std::vector<webrtc::VideoStream> CreateEncoderStreams(
int width,
int height,
const webrtc::VideoEncoderConfig& encoder_config) override;
+ private:
std::vector<webrtc::VideoStream> CreateDefaultVideoStreams(
int width,
int height,
@@ -69,6 +72,7 @@
const webrtc::FieldTrialBasedConfig fallback_trials_;
const webrtc::FieldTrialsView& trials_;
const int encoder_info_requested_resolution_alignment_;
+ const absl::optional<webrtc::VideoSourceRestrictions> restrictions_;
};
} // namespace cricket
diff --git a/video/config/encoder_stream_factory_unittest.cc b/video/config/encoder_stream_factory_unittest.cc
new file mode 100644
index 0000000..7214f40
--- /dev/null
+++ b/video/config/encoder_stream_factory_unittest.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "video/config/encoder_stream_factory.h"
+
+#include "call/adaptation/video_source_restrictions.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+using cricket::EncoderStreamFactory;
+constexpr int kMaxQp = 48;
+
+namespace {
+
+std::vector<Resolution> GetStreamResolutions(
+ const std::vector<VideoStream>& streams) {
+ std::vector<Resolution> res;
+ for (const auto& s : streams) {
+ if (s.active) {
+ res.push_back(
+ {rtc::checked_cast<int>(s.width), rtc::checked_cast<int>(s.height)});
+ }
+ }
+ return res;
+}
+
+VideoStream LayerWithRequestedResolution(Resolution res) {
+ VideoStream s;
+ s.requested_resolution = res;
+ return s;
+}
+
+} // namespace
+
+TEST(EncoderStreamFactory, SinglecastRequestedResolution) {
+ VideoEncoder::EncoderInfo encoder_info;
+ auto factory = rtc::make_ref_counted<EncoderStreamFactory>(
+ "VP8", kMaxQp,
+ /* is_screenshare= */ false,
+ /* conference_mode= */ false, encoder_info);
+ VideoEncoderConfig encoder_config;
+ encoder_config.number_of_streams = 1;
+ encoder_config.simulcast_layers.push_back(
+ LayerWithRequestedResolution({.width = 640, .height = 360}));
+ auto streams = factory->CreateEncoderStreams(1280, 720, encoder_config);
+ EXPECT_EQ(GetStreamResolutions(streams), (std::vector<Resolution>{
+ {.width = 640, .height = 360},
+ }));
+}
+
+TEST(EncoderStreamFactory, SinglecastRequestedResolutionWithAdaptation) {
+ VideoSourceRestrictions restrictions(
+ /* max_pixels_per_frame= */ (320 * 320),
+ /* target_pixels_per_frame= */ absl::nullopt,
+ /* max_frame_rate= */ absl::nullopt);
+ VideoEncoder::EncoderInfo encoder_info;
+ auto factory = rtc::make_ref_counted<EncoderStreamFactory>(
+ "VP8", kMaxQp,
+ /* is_screenshare= */ false,
+ /* conference_mode= */ false, encoder_info, restrictions);
+ VideoEncoderConfig encoder_config;
+ encoder_config.number_of_streams = 1;
+ encoder_config.simulcast_layers.push_back(
+ LayerWithRequestedResolution({.width = 640, .height = 360}));
+ auto streams = factory->CreateEncoderStreams(1280, 720, encoder_config);
+ EXPECT_EQ(GetStreamResolutions(streams), (std::vector<Resolution>{
+ {.width = 320, .height = 180},
+ }));
+}
+
+} // namespace webrtc
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index deebc87..3178d9c 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -33,6 +33,7 @@
#include "api/video_codecs/sdp_video_format.h"
#include "api/video_codecs/video_encoder.h"
#include "call/adaptation/resource_adaptation_processor.h"
+#include "call/adaptation/video_source_restrictions.h"
#include "call/adaptation/video_stream_adapter.h"
#include "modules/video_coding/include/video_codec_initializer.h"
#include "modules/video_coding/svc/svc_rate_allocator.h"
@@ -508,7 +509,7 @@
}
absl::optional<int> ParseVp9LowTierCoreCountThreshold(
- const webrtc::FieldTrialsView& trials) {
+ const FieldTrialsView& trials) {
FieldTrialFlag disable_low_tier("Disabled");
FieldTrialParameter<int> max_core_count("max_core_count", 2);
ParseFieldTrial({&disable_low_tier, &max_core_count},
@@ -519,6 +520,22 @@
return max_core_count.Get();
}
+absl::optional<VideoSourceRestrictions> MergeRestrictions(
+ const std::vector<absl::optional<VideoSourceRestrictions>>& list) {
+ absl::optional<VideoSourceRestrictions> return_value;
+ for (const auto& res : list) {
+ if (!res) {
+ continue;
+ }
+ if (!return_value) {
+ return_value = *res;
+ continue;
+ }
+ return_value->UpdateMin(*res);
+ }
+ return return_value;
+}
+
} // namespace
VideoStreamEncoder::EncoderRateSettings::EncoderRateSettings()
@@ -949,7 +966,9 @@
encoder_config_.video_format.name, encoder_config_.max_qp,
encoder_config_.content_type ==
webrtc::VideoEncoderConfig::ContentType::kScreen,
- encoder_config_.legacy_conference_mode, encoder_->GetEncoderInfo());
+ encoder_config_.legacy_conference_mode, encoder_->GetEncoderInfo(),
+ MergeRestrictions({latest_restrictions_, animate_restrictions_}),
+ &field_trials_);
streams = factory->CreateEncoderStreams(
last_frame_info_->width, last_frame_info_->height, encoder_config_);
@@ -2313,6 +2332,11 @@
RTC_LOG(LS_INFO) << "Updating sink restrictions from "
<< (reason ? reason->Name() : std::string("<null>"))
<< " to " << restrictions.ToString();
+
+ // TODO(webrtc:14451) Split video_source_sink_controller_
+ // so that ownership on restrictions/wants is kept on &encoder_queue_
+ latest_restrictions_ = restrictions;
+
worker_queue_->PostTask(SafeTask(
task_safety_.flag(), [this, restrictions = std::move(restrictions)]() {
RTC_DCHECK_RUN_ON(worker_queue_);
@@ -2454,6 +2478,17 @@
RTC_LOG(LS_INFO) << "Removing resolution cap due to no consistent "
"animation detection.";
}
+ // TODO(webrtc:14451) Split video_source_sink_controller_
+ // so that ownership on restrictions/wants is kept on &encoder_queue_
+ if (should_cap_resolution) {
+ animate_restrictions_ =
+ VideoSourceRestrictions(kMaxAnimationPixels,
+ /* target_pixels_per_frame= */ absl::nullopt,
+ /* max_frame_rate= */ absl::nullopt);
+ } else {
+ animate_restrictions_.reset();
+ }
+
worker_queue_->PostTask(
SafeTask(task_safety_.flag(), [this, should_cap_resolution]() {
RTC_DCHECK_RUN_ON(worker_queue_);
diff --git a/video/video_stream_encoder.h b/video/video_stream_encoder.h
index 7a4fc32..9af2e0b 100644
--- a/video/video_stream_encoder.h
+++ b/video/video_stream_encoder.h
@@ -457,6 +457,20 @@
const absl::optional<int> vp9_low_tier_core_threshold_;
+ // These are copies of restrictions (glorified max_pixel_count) set by
+ // a) OnVideoSourceRestrictionsUpdated
+ // b) CheckForAnimatedContent
+ // They are used to scale down encoding resolution if needed when using
+ // requested_resolution.
+ //
+ // TODO(webrtc:14451) Split video_source_sink_controller_
+ // so that ownership on restrictions/wants is kept on &encoder_queue_, that
+ // these extra copies would not be needed.
+ absl::optional<VideoSourceRestrictions> latest_restrictions_
+ RTC_GUARDED_BY(&encoder_queue_);
+ absl::optional<VideoSourceRestrictions> animate_restrictions_
+ RTC_GUARDED_BY(&encoder_queue_);
+
// Used to cancel any potentially pending tasks to the worker thread.
// Refrenced by tasks running on `encoder_queue_` so need to be destroyed
// after stopping that queue. Must be created and destroyed on