Move EncoderStreamFactory into own file
This cl/ is a NOP refactoring,
moving the EncoderStreamFactory from within webrtc_video_engine.cc
into own file in video/. simulcast.cc is collateral.
Bug: webrtc:14451
Change-Id: Ia69b9241d8cd8a12be6628d887701f2e244c07cc
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/276861
Reviewed-by: Artem Titov <titovartem@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38224}
diff --git a/media/BUILD.gn b/media/BUILD.gn
index b26be06..59b8c62 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -331,6 +331,7 @@
"../rtc_base/third_party/base64",
"../system_wrappers",
"../system_wrappers:metrics",
+ "../video/config:streams_config",
]
absl_deps = [
"//third_party/abseil-cpp/absl/algorithm:container",
@@ -344,8 +345,6 @@
"engine/null_webrtc_video_engine.h",
"engine/payload_type_mapper.cc",
"engine/payload_type_mapper.h",
- "engine/simulcast.cc",
- "engine/simulcast.h",
"engine/unhandled_packets_buffer.cc",
"engine/unhandled_packets_buffer.h",
"engine/webrtc_media_engine.cc",
@@ -654,6 +653,7 @@
"../test:test_support",
"../test:video_test_common",
"../test/time_controller",
+ "../video/config:streams_config",
]
if (enable_libaom) {
@@ -683,7 +683,6 @@
"engine/null_webrtc_video_engine_unittest.cc",
"engine/payload_type_mapper_unittest.cc",
"engine/simulcast_encoder_adapter_unittest.cc",
- "engine/simulcast_unittest.cc",
"engine/unhandled_packets_buffer_unittest.cc",
"engine/webrtc_media_engine_unittest.cc",
"engine/webrtc_video_engine_unittest.cc",
diff --git a/media/DEPS b/media/DEPS
index a495631..25b8c63 100644
--- a/media/DEPS
+++ b/media/DEPS
@@ -22,4 +22,10 @@
"win32devicemanager\.cc": [
"+third_party/logitech/files/logitechquickcam.h",
],
+ ".*webrtc_video_engine\.h": [
+ "+video/config",
+ ],
+ ".*webrtc_video_engine_unittest\.cc": [
+ "+video/config",
+ ],
}
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index feaa780..f86ac97 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -20,7 +20,6 @@
#include "absl/algorithm/container.h"
#include "absl/strings/match.h"
#include "api/media_stream_interface.h"
-#include "api/units/data_rate.h"
#include "api/video/video_codec_constants.h"
#include "api/video/video_codec_type.h"
#include "api/video_codecs/sdp_video_format.h"
@@ -28,7 +27,6 @@
#include "api/video_codecs/video_encoder.h"
#include "api/video_codecs/video_encoder_factory.h"
#include "call/call.h"
-#include "media/engine/simulcast.h"
#include "media/engine/webrtc_media_engine.h"
#include "media/engine/webrtc_voice_engine.h"
#include "modules/rtp_rtcp/source/rtp_util.h"
@@ -37,8 +35,6 @@
#include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/experiments/field_trial_parser.h"
#include "rtc_base/experiments/field_trial_units.h"
-#include "rtc_base/experiments/min_video_bitrate_experiment.h"
-#include "rtc_base/experiments/normalize_simulcast_size_experiment.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/strings/string_builder.h"
@@ -52,25 +48,12 @@
using ::webrtc::ParseRtpPayloadType;
using ::webrtc::ParseRtpSsrc;
-const int kMinLayerSize = 16;
constexpr int64_t kUnsignaledSsrcCooldownMs = rtc::kNumMillisecsPerSec / 2;
// TODO(bugs.webrtc.org/13166): Remove AV1X when backwards compatibility is not
// needed.
constexpr char kAv1xCodecName[] = "AV1X";
-int ScaleDownResolution(int resolution,
- double scale_down_by,
- int min_resolution) {
- // Resolution is never scalied down to smaller than min_resolution.
- // If the input resolution is already smaller than min_resolution,
- // no scaling should be done at all.
- if (resolution <= min_resolution)
- return resolution;
- return std::max(static_cast<int>(resolution / scale_down_by + 0.5),
- min_resolution);
-}
-
const char* StreamTypeToString(
webrtc::VideoSendStream::StreamStats::StreamType type) {
switch (type) {
@@ -92,20 +75,6 @@
return absl::StartsWith(trials.Lookup(name), "Disabled");
}
-bool PowerOfTwo(int value) {
- return (value > 0) && ((value & (value - 1)) == 0);
-}
-
-bool IsScaleFactorsPowerOfTwo(const webrtc::VideoEncoderConfig& config) {
- for (const auto& layer : config.simulcast_layers) {
- double scale = std::max(layer.scale_resolution_down_by, 1.0);
- if (std::round(scale) != scale || !PowerOfTwo(scale)) {
- return false;
- }
- }
- return true;
-}
-
void AddDefaultFeedbackParams(VideoCodec* codec,
const webrtc::FieldTrialsView& trials) {
// Don't add any feedback params for RED and ULPFEC.
@@ -269,12 +238,6 @@
return output_codecs;
}
-bool IsTemporalLayersSupported(const std::string& codec_name) {
- return absl::EqualsIgnoreCase(codec_name, kVp8CodecName) ||
- absl::EqualsIgnoreCase(codec_name, kVp9CodecName) ||
- absl::EqualsIgnoreCase(codec_name, kAv1CodecName);
-}
-
static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) {
rtc::StringBuilder out;
out << "{";
@@ -356,26 +319,6 @@
return false;
}
-// The selected thresholds for QVGA and VGA corresponded to a QP around 10.
-// The change in QP declined above the selected bitrates.
-static int GetMaxDefaultVideoBitrateKbps(int width,
- int height,
- bool is_screenshare) {
- int max_bitrate;
- if (width * height <= 320 * 240) {
- max_bitrate = 600;
- } else if (width * height <= 640 * 480) {
- max_bitrate = 1700;
- } else if (width * height <= 960 * 540) {
- max_bitrate = 2000;
- } else {
- max_bitrate = 2500;
- }
- if (is_screenshare)
- max_bitrate = std::max(max_bitrate, 1200);
- return max_bitrate;
-}
-
// Returns its smallest positive argument. If neither argument is positive,
// returns an arbitrary nonpositive value.
int MinPositive(int a, int b) {
@@ -394,17 +337,6 @@
(!layer.max_framerate || *layer.max_framerate > 0);
}
-size_t FindRequiredActiveLayers(
- const webrtc::VideoEncoderConfig& encoder_config) {
- // Need enough layers so that at least the first active one is present.
- for (size_t i = 0; i < encoder_config.number_of_streams; ++i) {
- if (encoder_config.simulcast_layers[i].active) {
- return i + 1;
- }
- }
- return 0;
-}
-
int NumActiveStreams(const webrtc::RtpParameters& rtp_parameters) {
int res = 0;
for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
@@ -3636,309 +3568,4 @@
}
}
-// TODO(bugs.webrtc.org/8785): Consider removing max_qp as member of
-// EncoderStreamFactory and instead set this value individually for each stream
-// in the VideoEncoderConfig.simulcast_layers.
-EncoderStreamFactory::EncoderStreamFactory(
- std::string codec_name,
- int max_qp,
- bool is_screenshare,
- bool conference_mode,
- const webrtc::FieldTrialsView* trials)
-
- : codec_name_(codec_name),
- max_qp_(max_qp),
- is_screenshare_(is_screenshare),
- conference_mode_(conference_mode),
- trials_(trials ? *trials : fallback_trials_) {}
-
-std::vector<webrtc::VideoStream> EncoderStreamFactory::CreateEncoderStreams(
- int width,
- int height,
- const webrtc::VideoEncoderConfig& encoder_config) {
- RTC_DCHECK_GT(encoder_config.number_of_streams, 0);
- RTC_DCHECK_GE(encoder_config.simulcast_layers.size(),
- encoder_config.number_of_streams);
-
- const absl::optional<webrtc::DataRate> experimental_min_bitrate =
- GetExperimentalMinVideoBitrate(encoder_config.codec_type);
-
- if (encoder_config.number_of_streams > 1 ||
- ((absl::EqualsIgnoreCase(codec_name_, kVp8CodecName) ||
- absl::EqualsIgnoreCase(codec_name_, kH264CodecName)) &&
- is_screenshare_ && conference_mode_)) {
- return CreateSimulcastOrConferenceModeScreenshareStreams(
- width, height, encoder_config, experimental_min_bitrate);
- }
-
- return CreateDefaultVideoStreams(width, height, encoder_config,
- experimental_min_bitrate);
-}
-
-std::vector<webrtc::VideoStream>
-EncoderStreamFactory::CreateDefaultVideoStreams(
- int width,
- int height,
- const webrtc::VideoEncoderConfig& encoder_config,
- const absl::optional<webrtc::DataRate>& experimental_min_bitrate) const {
- std::vector<webrtc::VideoStream> layers;
-
- // For unset max bitrates set default bitrate for non-simulcast.
- int max_bitrate_bps =
- (encoder_config.max_bitrate_bps > 0)
- ? encoder_config.max_bitrate_bps
- : GetMaxDefaultVideoBitrateKbps(width, height, is_screenshare_) *
- 1000;
-
- int min_bitrate_bps =
- experimental_min_bitrate
- ? rtc::saturated_cast<int>(experimental_min_bitrate->bps())
- : webrtc::kDefaultMinVideoBitrateBps;
- if (encoder_config.simulcast_layers[0].min_bitrate_bps > 0) {
- // Use set min bitrate.
- min_bitrate_bps = encoder_config.simulcast_layers[0].min_bitrate_bps;
- // If only min bitrate is configured, make sure max is above min.
- if (encoder_config.max_bitrate_bps <= 0)
- max_bitrate_bps = std::max(min_bitrate_bps, max_bitrate_bps);
- }
- int max_framerate = (encoder_config.simulcast_layers[0].max_framerate > 0)
- ? encoder_config.simulcast_layers[0].max_framerate
- : kDefaultVideoMaxFramerate;
-
- webrtc::VideoStream layer;
- layer.width = width;
- layer.height = height;
- layer.max_framerate = max_framerate;
- // Note: VP9 seems to have be sending if any layer is active,
- // (see `UpdateSendState`) and still use parameters only from
- // encoder_config.simulcast_layers[0].
- layer.active = absl::c_any_of(encoder_config.simulcast_layers,
- [](const auto& layer) { return layer.active; });
-
- if (encoder_config.simulcast_layers[0].scale_resolution_down_by > 1.) {
- layer.width = ScaleDownResolution(
- layer.width,
- encoder_config.simulcast_layers[0].scale_resolution_down_by,
- kMinLayerSize);
- layer.height = ScaleDownResolution(
- layer.height,
- encoder_config.simulcast_layers[0].scale_resolution_down_by,
- kMinLayerSize);
- }
-
- if (absl::EqualsIgnoreCase(codec_name_, kVp9CodecName)) {
- RTC_DCHECK(encoder_config.encoder_specific_settings);
- // Use VP9 SVC layering from codec settings which might be initialized
- // though field trial in ConfigureVideoEncoderSettings.
- webrtc::VideoCodecVP9 vp9_settings;
- encoder_config.encoder_specific_settings->FillVideoCodecVp9(&vp9_settings);
- layer.num_temporal_layers = vp9_settings.numberOfTemporalLayers;
-
- // Number of spatial layers is signalled differently from different call
- // sites (sigh), pick the max as we are interested in the upper bound.
- int num_spatial_layers =
- std::max({encoder_config.simulcast_layers.size(),
- encoder_config.spatial_layers.size(),
- size_t{vp9_settings.numberOfSpatialLayers}});
-
- if (width * height > 0 &&
- (layer.num_temporal_layers > 1u || num_spatial_layers > 1)) {
- // In SVC mode, the VP9 max bitrate is determined by SvcConfig, instead of
- // GetMaxDefaultVideoBitrateKbps().
- std::vector<webrtc::SpatialLayer> svc_layers =
- webrtc::GetSvcConfig(width, height, max_framerate,
- /*first_active_layer=*/0, num_spatial_layers,
- *layer.num_temporal_layers, is_screenshare_);
- int sum_max_bitrates_kbps = 0;
- for (const webrtc::SpatialLayer& spatial_layer : svc_layers) {
- sum_max_bitrates_kbps += spatial_layer.maxBitrate;
- }
- RTC_DCHECK_GE(sum_max_bitrates_kbps, 0);
- if (encoder_config.max_bitrate_bps <= 0) {
- max_bitrate_bps = sum_max_bitrates_kbps * 1000;
- } else {
- max_bitrate_bps =
- std::min(max_bitrate_bps, sum_max_bitrates_kbps * 1000);
- }
- max_bitrate_bps = std::max(min_bitrate_bps, max_bitrate_bps);
- }
- }
-
- // In the case that the application sets a max bitrate that's lower than the
- // min bitrate, we adjust it down (see bugs.webrtc.org/9141).
- layer.min_bitrate_bps = std::min(min_bitrate_bps, max_bitrate_bps);
- if (encoder_config.simulcast_layers[0].target_bitrate_bps <= 0) {
- layer.target_bitrate_bps = max_bitrate_bps;
- } else {
- layer.target_bitrate_bps = std::min(
- encoder_config.simulcast_layers[0].target_bitrate_bps, max_bitrate_bps);
- }
- layer.max_bitrate_bps = max_bitrate_bps;
- layer.max_qp = max_qp_;
- layer.bitrate_priority = encoder_config.bitrate_priority;
-
- if (IsTemporalLayersSupported(codec_name_)) {
- // Use configured number of temporal layers if set.
- if (encoder_config.simulcast_layers[0].num_temporal_layers) {
- layer.num_temporal_layers =
- *encoder_config.simulcast_layers[0].num_temporal_layers;
- }
- }
- layer.scalability_mode = encoder_config.simulcast_layers[0].scalability_mode;
- layers.push_back(layer);
- return layers;
-}
-
-std::vector<webrtc::VideoStream>
-EncoderStreamFactory::CreateSimulcastOrConferenceModeScreenshareStreams(
- int width,
- int height,
- const webrtc::VideoEncoderConfig& encoder_config,
- const absl::optional<webrtc::DataRate>& experimental_min_bitrate) const {
- std::vector<webrtc::VideoStream> layers;
-
- const bool temporal_layers_supported =
- absl::EqualsIgnoreCase(codec_name_, kVp8CodecName) ||
- absl::EqualsIgnoreCase(codec_name_, kH264CodecName);
- // Use legacy simulcast screenshare if conference mode is explicitly enabled
- // or use the regular simulcast configuration path which is generic.
- layers = GetSimulcastConfig(FindRequiredActiveLayers(encoder_config),
- encoder_config.number_of_streams, width, height,
- encoder_config.bitrate_priority, max_qp_,
- is_screenshare_ && conference_mode_,
- temporal_layers_supported, trials_);
- // Allow an experiment to override the minimum bitrate for the lowest
- // spatial layer. The experiment's configuration has the lowest priority.
- if (experimental_min_bitrate) {
- layers[0].min_bitrate_bps =
- rtc::saturated_cast<int>(experimental_min_bitrate->bps());
- }
- // Update the active simulcast layers and configured bitrates.
- bool is_highest_layer_max_bitrate_configured = false;
- const bool has_scale_resolution_down_by = absl::c_any_of(
- encoder_config.simulcast_layers, [](const webrtc::VideoStream& layer) {
- return layer.scale_resolution_down_by != -1.;
- });
-
- bool default_scale_factors_used = true;
- if (has_scale_resolution_down_by) {
- default_scale_factors_used = IsScaleFactorsPowerOfTwo(encoder_config);
- }
- const bool norm_size_configured =
- webrtc::NormalizeSimulcastSizeExperiment::GetBase2Exponent().has_value();
- const int normalized_width =
- (default_scale_factors_used || norm_size_configured) &&
- (width >= kMinLayerSize)
- ? NormalizeSimulcastSize(width, encoder_config.number_of_streams)
- : width;
- const int normalized_height =
- (default_scale_factors_used || norm_size_configured) &&
- (height >= kMinLayerSize)
- ? NormalizeSimulcastSize(height, encoder_config.number_of_streams)
- : height;
- for (size_t i = 0; i < layers.size(); ++i) {
- layers[i].active = encoder_config.simulcast_layers[i].active;
- layers[i].scalability_mode =
- encoder_config.simulcast_layers[i].scalability_mode;
- // Update with configured num temporal layers if supported by codec.
- if (encoder_config.simulcast_layers[i].num_temporal_layers &&
- IsTemporalLayersSupported(codec_name_)) {
- layers[i].num_temporal_layers =
- *encoder_config.simulcast_layers[i].num_temporal_layers;
- }
- if (encoder_config.simulcast_layers[i].max_framerate > 0) {
- layers[i].max_framerate =
- encoder_config.simulcast_layers[i].max_framerate;
- }
- if (has_scale_resolution_down_by) {
- const double scale_resolution_down_by = std::max(
- encoder_config.simulcast_layers[i].scale_resolution_down_by, 1.0);
- layers[i].width = ScaleDownResolution(
- normalized_width, scale_resolution_down_by, kMinLayerSize);
- layers[i].height = ScaleDownResolution(
- normalized_height, scale_resolution_down_by, kMinLayerSize);
- }
- // 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].target_bitrate_bps > 0) {
- layers[i].target_bitrate_bps =
- encoder_config.simulcast_layers[i].target_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).
- if (encoder_config.simulcast_layers[i].target_bitrate_bps <= 0)
- 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.
- // Keep target bitrate if it is set explicitly in encoding config.
- // Otherwise set target bitrate to 3/4 of the max bitrate
- // or the one calculated from GetSimulcastConfig() which is larger.
- layers[i].min_bitrate_bps =
- std::min(layers[i].min_bitrate_bps, layers[i].max_bitrate_bps);
- if (encoder_config.simulcast_layers[i].target_bitrate_bps <= 0) {
- layers[i].target_bitrate_bps = std::max(
- layers[i].target_bitrate_bps, layers[i].max_bitrate_bps * 3 / 4);
- }
- layers[i].target_bitrate_bps = std::max(
- std::min(layers[i].target_bitrate_bps, layers[i].max_bitrate_bps),
- layers[i].min_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 &&
- encoder_config.max_bitrate_bps > 0) {
- // No application-configured maximum for the largest layer.
- // If there is bitrate leftover, give it to the largest layer.
- BoostMaxSimulcastLayer(
- webrtc::DataRate::BitsPerSec(encoder_config.max_bitrate_bps), &layers);
- }
-
- // Sort the layers by max_bitrate_bps, they might not always be from
- // smallest to biggest
- std::vector<size_t> index(layers.size());
- std::iota(index.begin(), index.end(), 0);
- std::stable_sort(index.begin(), index.end(), [&layers](size_t a, size_t b) {
- return layers[a].max_bitrate_bps < layers[b].max_bitrate_bps;
- });
-
- if (!layers[index[0]].active) {
- // Adjust min bitrate of the first active layer to allow it to go as low as
- // the lowest (now inactive) layer could.
- // Otherwise, if e.g. a single HD stream is active, it would have 600kbps
- // min bitrate, which would always be allocated to the stream.
- // This would lead to congested network, dropped frames and overall bad
- // experience.
-
- const int min_configured_bitrate = layers[index[0]].min_bitrate_bps;
- for (size_t i = 0; i < layers.size(); ++i) {
- if (layers[index[i]].active) {
- layers[index[i]].min_bitrate_bps = min_configured_bitrate;
- break;
- }
- }
- }
-
- return layers;
-}
-
} // namespace cricket
diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h
index 3993c663..ec31bf7 100644
--- a/media/engine/webrtc_video_engine.h
+++ b/media/engine/webrtc_video_engine.h
@@ -37,6 +37,7 @@
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/system/no_unique_address.h"
#include "rtc_base/thread_annotations.h"
+#include "video/config/encoder_stream_factory.h"
namespace webrtc {
class VideoDecoderFactory;
@@ -683,54 +684,6 @@
bool allow_codec_switching_ = false;
};
-class EncoderStreamFactory
- : public webrtc::VideoEncoderConfig::VideoStreamFactoryInterface {
- public:
- EncoderStreamFactory(std::string codec_name,
- int max_qp,
- bool is_screenshare,
- bool conference_mode)
- : EncoderStreamFactory(codec_name,
- max_qp,
- is_screenshare,
- conference_mode,
- nullptr) {}
-
- EncoderStreamFactory(std::string codec_name,
- int max_qp,
- bool is_screenshare,
- bool conference_mode,
- const webrtc::FieldTrialsView* trials);
-
- private:
- std::vector<webrtc::VideoStream> CreateEncoderStreams(
- int width,
- int height,
- const webrtc::VideoEncoderConfig& encoder_config) override;
-
- std::vector<webrtc::VideoStream> CreateDefaultVideoStreams(
- int width,
- int height,
- const webrtc::VideoEncoderConfig& encoder_config,
- const absl::optional<webrtc::DataRate>& experimental_min_bitrate) const;
-
- std::vector<webrtc::VideoStream>
- CreateSimulcastOrConferenceModeScreenshareStreams(
- int width,
- int height,
- const webrtc::VideoEncoderConfig& encoder_config,
- const absl::optional<webrtc::DataRate>& experimental_min_bitrate) const;
-
- const std::string codec_name_;
- const int max_qp_;
- const bool is_screenshare_;
- // Allows a screenshare specific configuration, which enables temporal
- // layering and various settings.
- const bool conference_mode_;
- const webrtc::FieldTrialBasedConfig fallback_trials_;
- const webrtc::FieldTrialsView& trials_;
-};
-
} // namespace cricket
#endif // MEDIA_ENGINE_WEBRTC_VIDEO_ENGINE_H_
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index d3c4211..e7e43fc 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -51,7 +51,6 @@
#include "media/base/test_utils.h"
#include "media/engine/fake_webrtc_call.h"
#include "media/engine/fake_webrtc_video_engine.h"
-#include "media/engine/simulcast.h"
#include "media/engine/webrtc_voice_engine.h"
#include "modules/rtp_rtcp/source/rtp_packet.h"
#include "rtc_base/arraysize.h"
@@ -66,6 +65,7 @@
#include "test/gmock.h"
#include "test/scoped_key_value_config.h"
#include "test/time_controller/simulated_time_controller.h"
+#include "video/config/simulcast.h"
using ::testing::_;
using ::testing::Contains;
diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn
index da2a0a4..b54b8bd 100644
--- a/modules/video_coding/BUILD.gn
+++ b/modules/video_coding/BUILD.gn
@@ -989,6 +989,7 @@
"../../test:test_support",
"../../test:video_test_common",
"../../test:video_test_support",
+ "../../video/config:streams_config",
]
absl_deps = [
"//third_party/abseil-cpp/absl/strings:strings",
diff --git a/modules/video_coding/DEPS b/modules/video_coding/DEPS
index 3a7629e..2aac37a 100644
--- a/modules/video_coding/DEPS
+++ b/modules/video_coding/DEPS
@@ -19,5 +19,6 @@
".*test.*\.cc": [
"+media/base",
"+media/engine",
+ "+video/config",
],
}
diff --git a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc
index 9d20373..e30fe96 100644
--- a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc
+++ b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc
@@ -46,7 +46,6 @@
#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h"
#include "common_video/h264/h264_common.h"
#include "media/base/media_constants.h"
-#include "media/engine/simulcast.h"
#include "modules/video_coding/codecs/h264/include/h264_globals.h"
#include "modules/video_coding/codecs/vp9/svc_config.h"
#include "modules/video_coding/utility/ivf_file_writer.h"
@@ -61,6 +60,7 @@
#include "test/testsupport/file_utils.h"
#include "test/testsupport/frame_writer.h"
#include "test/video_codec_settings.h"
+#include "video/config/simulcast.h"
namespace webrtc {
namespace test {
diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn
index 19b885d..9cf7790 100644
--- a/rtc_tools/BUILD.gn
+++ b/rtc_tools/BUILD.gn
@@ -218,6 +218,7 @@
"../test:fileutils",
"../test:rtp_test_utils",
"../test:video_test_common",
+ "../video/config:streams_config",
"//third_party/abseil-cpp/absl/flags:flag",
"//third_party/abseil-cpp/absl/flags:parse",
"//third_party/abseil-cpp/absl/flags:usage",
diff --git a/rtc_tools/DEPS b/rtc_tools/DEPS
index 3cf6080..4d99061 100644
--- a/rtc_tools/DEPS
+++ b/rtc_tools/DEPS
@@ -17,6 +17,7 @@
"+system_wrappers",
"+p2p",
"+third_party/libyuv",
+ "+video/config",
]
specific_include_rules = {
diff --git a/rtc_tools/rtp_generator/rtp_generator.cc b/rtc_tools/rtp_generator/rtp_generator.cc
index 8a3acee..d93655a 100644
--- a/rtc_tools/rtp_generator/rtp_generator.cc
+++ b/rtc_tools/rtp_generator/rtp_generator.cc
@@ -25,6 +25,7 @@
#include "rtc_base/system/file_wrapper.h"
#include "rtc_base/thread.h"
#include "test/testsupport/file_utils.h"
+#include "video/config/encoder_stream_factory.h"
namespace webrtc {
namespace {
diff --git a/test/DEPS b/test/DEPS
index ed994b7..a9e9a7b 100644
--- a/test/DEPS
+++ b/test/DEPS
@@ -22,6 +22,7 @@
"+sdk",
"+system_wrappers",
"+third_party/libyuv",
+ "+video/config",
]
specific_include_rules = {
diff --git a/test/scenario/BUILD.gn b/test/scenario/BUILD.gn
index acd77b4..e5c1901 100644
--- a/test/scenario/BUILD.gn
+++ b/test/scenario/BUILD.gn
@@ -142,7 +142,7 @@
"../../rtc_base/task_utils:repeating_task",
"../../system_wrappers",
"../../system_wrappers:field_trial",
- "../../video",
+ "../../video/config:streams_config",
"../logging:log_writer",
"../network:emulated_network",
"../time_controller",
diff --git a/test/scenario/video_stream.cc b/test/scenario/video_stream.cc
index 3d1253a..60bf5ef 100644
--- a/test/scenario/video_stream.cc
+++ b/test/scenario/video_stream.cc
@@ -27,6 +27,7 @@
#include "test/fake_encoder.h"
#include "test/scenario/hardware_codecs.h"
#include "test/testsupport/file_utils.h"
+#include "video/config/encoder_stream_factory.h"
namespace webrtc {
namespace test {
diff --git a/video/BUILD.gn b/video/BUILD.gn
index 76367fd..7dc15ff 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -71,6 +71,8 @@
"../api/crypto:options",
"../api/task_queue",
"../api/task_queue:pending_task_safety_flag",
+ "../api/transport:field_trial_based_config",
+ "../api/units:data_rate",
"../api/units:frequency",
"../api/units:time_delta",
"../api/units:timestamp",
@@ -102,6 +104,7 @@
"../modules/video_coding:packet_buffer",
"../modules/video_coding:video_codec_interface",
"../modules/video_coding:video_coding_utility",
+ "../modules/video_coding:webrtc_vp9_helpers",
"../modules/video_coding/timing:timing_module",
"../rtc_base:checks",
"../rtc_base:event_tracer",
@@ -125,6 +128,7 @@
"../rtc_base/experiments:field_trial_parser",
"../rtc_base/experiments:keyframe_interval_settings_experiment",
"../rtc_base/experiments:min_video_bitrate_experiment",
+ "../rtc_base/experiments:normalize_simulcast_size_experiment",
"../rtc_base/experiments:rate_control_settings",
"../rtc_base/synchronization:mutex",
"../rtc_base/system:no_unique_address",
@@ -530,6 +534,7 @@
"../test:test_support_test_artifacts",
"../test:video_test_common",
"../test:video_test_support",
+ "config:streams_config",
]
absl_deps = [
"//third_party/abseil-cpp/absl/algorithm:container",
@@ -799,6 +804,7 @@
"../api/test/metrics:global_metrics_logger_and_exporter",
"../api/test/metrics:metric",
"../api/test/video:function_video_factory",
+ "../api/transport:field_trial_based_config",
"../api/units:data_rate",
"../api/units:frequency",
"../api/units:time_delta",
@@ -899,6 +905,7 @@
"../test:video_test_common",
"../test/time_controller",
"adaptation:video_adaptation",
+ "config:streams_config",
]
absl_deps = [
"//third_party/abseil-cpp/absl/algorithm:container",
diff --git a/video/config/BUILD.gn b/video/config/BUILD.gn
new file mode 100644
index 0000000..7902c0b
--- /dev/null
+++ b/video/config/BUILD.gn
@@ -0,0 +1,68 @@
+# 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.
+
+import("../../webrtc.gni")
+
+rtc_library("streams_config") {
+ sources = [
+ "encoder_stream_factory.cc",
+ "encoder_stream_factory.h",
+ "simulcast.cc",
+ "simulcast.h",
+ ]
+
+ deps = [
+ "../../api:field_trials_view",
+ "../../api/transport:field_trial_based_config",
+ "../../api/units:data_rate",
+ "../../api/video:video_codec_constants",
+ "../../api/video_codecs:video_codecs_api",
+ "../../media:rtc_media_base",
+ "../../modules/video_coding:video_coding_utility",
+ "../../modules/video_coding:webrtc_vp9_helpers",
+ "../../rtc_base:checks",
+ "../../rtc_base:logging",
+ "../../rtc_base/experiments:field_trial_parser",
+ "../../rtc_base/experiments:min_video_bitrate_experiment",
+ "../../rtc_base/experiments:normalize_simulcast_size_experiment",
+ "../../rtc_base/experiments:rate_control_settings",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/algorithm:container",
+ "//third_party/abseil-cpp/absl/memory",
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
+if (rtc_include_tests) {
+ rtc_library("video_config_tests") {
+ testonly = true
+
+ defines = []
+ sources = [ "simulcast_unittest.cc" ]
+ deps = [
+ ":streams_config",
+ "../../api/transport:field_trial_based_config",
+ "../../test:field_trial",
+ "../../test:test_support",
+ ]
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/algorithm:container",
+ "//third_party/abseil-cpp/absl/functional:any_invocable",
+ "//third_party/abseil-cpp/absl/functional:bind_front",
+ "//third_party/abseil-cpp/absl/memory",
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ "//third_party/abseil-cpp/absl/types:variant",
+ ]
+ if (!build_with_mozilla) {
+ deps += [ "../../media:rtc_media_base" ]
+ }
+ }
+}
diff --git a/video/config/encoder_stream_factory.cc b/video/config/encoder_stream_factory.cc
new file mode 100644
index 0000000..f615610
--- /dev/null
+++ b/video/config/encoder_stream_factory.cc
@@ -0,0 +1,401 @@
+/*
+ * 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 <algorithm>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "absl/algorithm/container.h"
+#include "absl/strings/match.h"
+#include "api/video/video_codec_constants.h"
+#include "media/base/media_constants.h"
+#include "modules/video_coding/codecs/vp9/svc_config.h"
+#include "rtc_base/experiments/min_video_bitrate_experiment.h"
+#include "rtc_base/experiments/normalize_simulcast_size_experiment.h"
+#include "video/config/simulcast.h"
+
+namespace cricket {
+namespace {
+
+const int kMinLayerSize = 16;
+
+int ScaleDownResolution(int resolution,
+ double scale_down_by,
+ int min_resolution) {
+ // Resolution is never scalied down to smaller than min_resolution.
+ // If the input resolution is already smaller than min_resolution,
+ // no scaling should be done at all.
+ if (resolution <= min_resolution)
+ return resolution;
+ return std::max(static_cast<int>(resolution / scale_down_by + 0.5),
+ min_resolution);
+}
+
+bool PowerOfTwo(int value) {
+ return (value > 0) && ((value & (value - 1)) == 0);
+}
+
+bool IsScaleFactorsPowerOfTwo(const webrtc::VideoEncoderConfig& config) {
+ for (const auto& layer : config.simulcast_layers) {
+ double scale = std::max(layer.scale_resolution_down_by, 1.0);
+ if (std::round(scale) != scale || !PowerOfTwo(scale)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool IsTemporalLayersSupported(const std::string& codec_name) {
+ return absl::EqualsIgnoreCase(codec_name, kVp8CodecName) ||
+ absl::EqualsIgnoreCase(codec_name, kVp9CodecName) ||
+ absl::EqualsIgnoreCase(codec_name, kAv1CodecName);
+}
+
+size_t FindRequiredActiveLayers(
+ const webrtc::VideoEncoderConfig& encoder_config) {
+ // Need enough layers so that at least the first active one is present.
+ for (size_t i = 0; i < encoder_config.number_of_streams; ++i) {
+ if (encoder_config.simulcast_layers[i].active) {
+ return i + 1;
+ }
+ }
+ return 0;
+}
+
+// The selected thresholds for QVGA and VGA corresponded to a QP around 10.
+// The change in QP declined above the selected bitrates.
+static int GetMaxDefaultVideoBitrateKbps(int width,
+ int height,
+ bool is_screenshare) {
+ int max_bitrate;
+ if (width * height <= 320 * 240) {
+ max_bitrate = 600;
+ } else if (width * height <= 640 * 480) {
+ max_bitrate = 1700;
+ } else if (width * height <= 960 * 540) {
+ max_bitrate = 2000;
+ } else {
+ max_bitrate = 2500;
+ }
+ if (is_screenshare)
+ max_bitrate = std::max(max_bitrate, 1200);
+ return max_bitrate;
+}
+
+} // namespace
+
+// TODO(bugs.webrtc.org/8785): Consider removing max_qp as member of
+// EncoderStreamFactory and instead set this value individually for each stream
+// in the VideoEncoderConfig.simulcast_layers.
+EncoderStreamFactory::EncoderStreamFactory(
+ std::string codec_name,
+ int max_qp,
+ bool is_screenshare,
+ bool conference_mode,
+ const webrtc::FieldTrialsView* trials)
+
+ : codec_name_(codec_name),
+ max_qp_(max_qp),
+ is_screenshare_(is_screenshare),
+ conference_mode_(conference_mode),
+ trials_(trials ? *trials : fallback_trials_) {}
+
+std::vector<webrtc::VideoStream> EncoderStreamFactory::CreateEncoderStreams(
+ int width,
+ int height,
+ const webrtc::VideoEncoderConfig& encoder_config) {
+ RTC_DCHECK_GT(encoder_config.number_of_streams, 0);
+ RTC_DCHECK_GE(encoder_config.simulcast_layers.size(),
+ encoder_config.number_of_streams);
+
+ const absl::optional<webrtc::DataRate> experimental_min_bitrate =
+ GetExperimentalMinVideoBitrate(encoder_config.codec_type);
+
+ if (encoder_config.number_of_streams > 1 ||
+ ((absl::EqualsIgnoreCase(codec_name_, kVp8CodecName) ||
+ absl::EqualsIgnoreCase(codec_name_, kH264CodecName)) &&
+ is_screenshare_ && conference_mode_)) {
+ return CreateSimulcastOrConferenceModeScreenshareStreams(
+ width, height, encoder_config, experimental_min_bitrate);
+ }
+
+ return CreateDefaultVideoStreams(width, height, encoder_config,
+ experimental_min_bitrate);
+}
+
+std::vector<webrtc::VideoStream>
+EncoderStreamFactory::CreateDefaultVideoStreams(
+ int width,
+ int height,
+ const webrtc::VideoEncoderConfig& encoder_config,
+ const absl::optional<webrtc::DataRate>& experimental_min_bitrate) const {
+ std::vector<webrtc::VideoStream> layers;
+
+ // For unset max bitrates set default bitrate for non-simulcast.
+ int max_bitrate_bps =
+ (encoder_config.max_bitrate_bps > 0)
+ ? encoder_config.max_bitrate_bps
+ : GetMaxDefaultVideoBitrateKbps(width, height, is_screenshare_) *
+ 1000;
+
+ int min_bitrate_bps =
+ experimental_min_bitrate
+ ? rtc::saturated_cast<int>(experimental_min_bitrate->bps())
+ : webrtc::kDefaultMinVideoBitrateBps;
+ if (encoder_config.simulcast_layers[0].min_bitrate_bps > 0) {
+ // Use set min bitrate.
+ min_bitrate_bps = encoder_config.simulcast_layers[0].min_bitrate_bps;
+ // If only min bitrate is configured, make sure max is above min.
+ if (encoder_config.max_bitrate_bps <= 0)
+ max_bitrate_bps = std::max(min_bitrate_bps, max_bitrate_bps);
+ }
+ int max_framerate = (encoder_config.simulcast_layers[0].max_framerate > 0)
+ ? encoder_config.simulcast_layers[0].max_framerate
+ : kDefaultVideoMaxFramerate;
+
+ webrtc::VideoStream layer;
+ layer.width = width;
+ layer.height = height;
+ layer.max_framerate = max_framerate;
+ // Note: VP9 seems to have be sending if any layer is active,
+ // (see `UpdateSendState`) and still use parameters only from
+ // encoder_config.simulcast_layers[0].
+ layer.active = absl::c_any_of(encoder_config.simulcast_layers,
+ [](const auto& layer) { return layer.active; });
+
+ if (encoder_config.simulcast_layers[0].scale_resolution_down_by > 1.) {
+ layer.width = ScaleDownResolution(
+ layer.width,
+ encoder_config.simulcast_layers[0].scale_resolution_down_by,
+ kMinLayerSize);
+ layer.height = ScaleDownResolution(
+ layer.height,
+ encoder_config.simulcast_layers[0].scale_resolution_down_by,
+ kMinLayerSize);
+ }
+
+ if (absl::EqualsIgnoreCase(codec_name_, kVp9CodecName)) {
+ RTC_DCHECK(encoder_config.encoder_specific_settings);
+ // Use VP9 SVC layering from codec settings which might be initialized
+ // though field trial in ConfigureVideoEncoderSettings.
+ webrtc::VideoCodecVP9 vp9_settings;
+ encoder_config.encoder_specific_settings->FillVideoCodecVp9(&vp9_settings);
+ layer.num_temporal_layers = vp9_settings.numberOfTemporalLayers;
+
+ // Number of spatial layers is signalled differently from different call
+ // sites (sigh), pick the max as we are interested in the upper bound.
+ int num_spatial_layers =
+ std::max({encoder_config.simulcast_layers.size(),
+ encoder_config.spatial_layers.size(),
+ size_t{vp9_settings.numberOfSpatialLayers}});
+
+ if (width * height > 0 &&
+ (layer.num_temporal_layers > 1u || num_spatial_layers > 1)) {
+ // In SVC mode, the VP9 max bitrate is determined by SvcConfig, instead of
+ // GetMaxDefaultVideoBitrateKbps().
+ std::vector<webrtc::SpatialLayer> svc_layers =
+ webrtc::GetSvcConfig(width, height, max_framerate,
+ /*first_active_layer=*/0, num_spatial_layers,
+ *layer.num_temporal_layers, is_screenshare_);
+ int sum_max_bitrates_kbps = 0;
+ for (const webrtc::SpatialLayer& spatial_layer : svc_layers) {
+ sum_max_bitrates_kbps += spatial_layer.maxBitrate;
+ }
+ RTC_DCHECK_GE(sum_max_bitrates_kbps, 0);
+ if (encoder_config.max_bitrate_bps <= 0) {
+ max_bitrate_bps = sum_max_bitrates_kbps * 1000;
+ } else {
+ max_bitrate_bps =
+ std::min(max_bitrate_bps, sum_max_bitrates_kbps * 1000);
+ }
+ max_bitrate_bps = std::max(min_bitrate_bps, max_bitrate_bps);
+ }
+ }
+
+ // In the case that the application sets a max bitrate that's lower than the
+ // min bitrate, we adjust it down (see bugs.webrtc.org/9141).
+ layer.min_bitrate_bps = std::min(min_bitrate_bps, max_bitrate_bps);
+ if (encoder_config.simulcast_layers[0].target_bitrate_bps <= 0) {
+ layer.target_bitrate_bps = max_bitrate_bps;
+ } else {
+ layer.target_bitrate_bps = std::min(
+ encoder_config.simulcast_layers[0].target_bitrate_bps, max_bitrate_bps);
+ }
+ layer.max_bitrate_bps = max_bitrate_bps;
+ layer.max_qp = max_qp_;
+ layer.bitrate_priority = encoder_config.bitrate_priority;
+
+ if (IsTemporalLayersSupported(codec_name_)) {
+ // Use configured number of temporal layers if set.
+ if (encoder_config.simulcast_layers[0].num_temporal_layers) {
+ layer.num_temporal_layers =
+ *encoder_config.simulcast_layers[0].num_temporal_layers;
+ }
+ }
+ layer.scalability_mode = encoder_config.simulcast_layers[0].scalability_mode;
+ layers.push_back(layer);
+ return layers;
+}
+
+std::vector<webrtc::VideoStream>
+EncoderStreamFactory::CreateSimulcastOrConferenceModeScreenshareStreams(
+ int width,
+ int height,
+ const webrtc::VideoEncoderConfig& encoder_config,
+ const absl::optional<webrtc::DataRate>& experimental_min_bitrate) const {
+ std::vector<webrtc::VideoStream> layers;
+
+ const bool temporal_layers_supported =
+ absl::EqualsIgnoreCase(codec_name_, kVp8CodecName) ||
+ absl::EqualsIgnoreCase(codec_name_, kH264CodecName);
+ // Use legacy simulcast screenshare if conference mode is explicitly enabled
+ // or use the regular simulcast configuration path which is generic.
+ layers = GetSimulcastConfig(FindRequiredActiveLayers(encoder_config),
+ encoder_config.number_of_streams, width, height,
+ encoder_config.bitrate_priority, max_qp_,
+ is_screenshare_ && conference_mode_,
+ temporal_layers_supported, trials_);
+ // Allow an experiment to override the minimum bitrate for the lowest
+ // spatial layer. The experiment's configuration has the lowest priority.
+ if (experimental_min_bitrate) {
+ layers[0].min_bitrate_bps =
+ rtc::saturated_cast<int>(experimental_min_bitrate->bps());
+ }
+ // Update the active simulcast layers and configured bitrates.
+ bool is_highest_layer_max_bitrate_configured = false;
+ const bool has_scale_resolution_down_by = absl::c_any_of(
+ encoder_config.simulcast_layers, [](const webrtc::VideoStream& layer) {
+ return layer.scale_resolution_down_by != -1.;
+ });
+
+ bool default_scale_factors_used = true;
+ if (has_scale_resolution_down_by) {
+ default_scale_factors_used = IsScaleFactorsPowerOfTwo(encoder_config);
+ }
+ const bool norm_size_configured =
+ webrtc::NormalizeSimulcastSizeExperiment::GetBase2Exponent().has_value();
+ const int normalized_width =
+ (default_scale_factors_used || norm_size_configured) &&
+ (width >= kMinLayerSize)
+ ? NormalizeSimulcastSize(width, encoder_config.number_of_streams)
+ : width;
+ const int normalized_height =
+ (default_scale_factors_used || norm_size_configured) &&
+ (height >= kMinLayerSize)
+ ? NormalizeSimulcastSize(height, encoder_config.number_of_streams)
+ : height;
+ for (size_t i = 0; i < layers.size(); ++i) {
+ layers[i].active = encoder_config.simulcast_layers[i].active;
+ layers[i].scalability_mode =
+ encoder_config.simulcast_layers[i].scalability_mode;
+ // Update with configured num temporal layers if supported by codec.
+ if (encoder_config.simulcast_layers[i].num_temporal_layers &&
+ IsTemporalLayersSupported(codec_name_)) {
+ layers[i].num_temporal_layers =
+ *encoder_config.simulcast_layers[i].num_temporal_layers;
+ }
+ if (encoder_config.simulcast_layers[i].max_framerate > 0) {
+ layers[i].max_framerate =
+ encoder_config.simulcast_layers[i].max_framerate;
+ }
+ if (has_scale_resolution_down_by) {
+ const double scale_resolution_down_by = std::max(
+ encoder_config.simulcast_layers[i].scale_resolution_down_by, 1.0);
+ layers[i].width = ScaleDownResolution(
+ normalized_width, scale_resolution_down_by, kMinLayerSize);
+ layers[i].height = ScaleDownResolution(
+ normalized_height, scale_resolution_down_by, kMinLayerSize);
+ }
+ // 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].target_bitrate_bps > 0) {
+ layers[i].target_bitrate_bps =
+ encoder_config.simulcast_layers[i].target_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).
+ if (encoder_config.simulcast_layers[i].target_bitrate_bps <= 0)
+ 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.
+ // Keep target bitrate if it is set explicitly in encoding config.
+ // Otherwise set target bitrate to 3/4 of the max bitrate
+ // or the one calculated from GetSimulcastConfig() which is larger.
+ layers[i].min_bitrate_bps =
+ std::min(layers[i].min_bitrate_bps, layers[i].max_bitrate_bps);
+ if (encoder_config.simulcast_layers[i].target_bitrate_bps <= 0) {
+ layers[i].target_bitrate_bps = std::max(
+ layers[i].target_bitrate_bps, layers[i].max_bitrate_bps * 3 / 4);
+ }
+ layers[i].target_bitrate_bps = std::max(
+ std::min(layers[i].target_bitrate_bps, layers[i].max_bitrate_bps),
+ layers[i].min_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 &&
+ encoder_config.max_bitrate_bps > 0) {
+ // No application-configured maximum for the largest layer.
+ // If there is bitrate leftover, give it to the largest layer.
+ BoostMaxSimulcastLayer(
+ webrtc::DataRate::BitsPerSec(encoder_config.max_bitrate_bps), &layers);
+ }
+
+ // Sort the layers by max_bitrate_bps, they might not always be from
+ // smallest to biggest
+ std::vector<size_t> index(layers.size());
+ std::iota(index.begin(), index.end(), 0);
+ std::stable_sort(index.begin(), index.end(), [&layers](size_t a, size_t b) {
+ return layers[a].max_bitrate_bps < layers[b].max_bitrate_bps;
+ });
+
+ if (!layers[index[0]].active) {
+ // Adjust min bitrate of the first active layer to allow it to go as low as
+ // the lowest (now inactive) layer could.
+ // Otherwise, if e.g. a single HD stream is active, it would have 600kbps
+ // min bitrate, which would always be allocated to the stream.
+ // This would lead to congested network, dropped frames and overall bad
+ // experience.
+
+ const int min_configured_bitrate = layers[index[0]].min_bitrate_bps;
+ for (size_t i = 0; i < layers.size(); ++i) {
+ if (layers[index[i]].active) {
+ layers[index[i]].min_bitrate_bps = min_configured_bitrate;
+ break;
+ }
+ }
+ }
+
+ return layers;
+}
+
+} // namespace cricket
diff --git a/video/config/encoder_stream_factory.h b/video/config/encoder_stream_factory.h
new file mode 100644
index 0000000..971eed4
--- /dev/null
+++ b/video/config/encoder_stream_factory.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+#ifndef VIDEO_CONFIG_ENCODER_STREAM_FACTORY_H_
+#define VIDEO_CONFIG_ENCODER_STREAM_FACTORY_H_
+
+#include <string>
+#include <vector>
+
+#include "api/transport/field_trial_based_config.h"
+#include "api/units/data_rate.h"
+#include "api/video_codecs/video_encoder_config.h"
+
+namespace cricket {
+
+class EncoderStreamFactory
+ : public webrtc::VideoEncoderConfig::VideoStreamFactoryInterface {
+ public:
+ EncoderStreamFactory(std::string codec_name,
+ int max_qp,
+ bool is_screenshare,
+ bool conference_mode)
+ : EncoderStreamFactory(codec_name,
+ max_qp,
+ is_screenshare,
+ conference_mode,
+ nullptr) {}
+
+ EncoderStreamFactory(std::string codec_name,
+ int max_qp,
+ bool is_screenshare,
+ bool conference_mode,
+ const webrtc::FieldTrialsView* trials);
+
+ private:
+ std::vector<webrtc::VideoStream> CreateEncoderStreams(
+ int width,
+ int height,
+ const webrtc::VideoEncoderConfig& encoder_config) override;
+
+ std::vector<webrtc::VideoStream> CreateDefaultVideoStreams(
+ int width,
+ int height,
+ const webrtc::VideoEncoderConfig& encoder_config,
+ const absl::optional<webrtc::DataRate>& experimental_min_bitrate) const;
+
+ std::vector<webrtc::VideoStream>
+ CreateSimulcastOrConferenceModeScreenshareStreams(
+ int width,
+ int height,
+ const webrtc::VideoEncoderConfig& encoder_config,
+ const absl::optional<webrtc::DataRate>& experimental_min_bitrate) const;
+
+ const std::string codec_name_;
+ const int max_qp_;
+ const bool is_screenshare_;
+ // Allows a screenshare specific configuration, which enables temporal
+ // layering and various settings.
+ const bool conference_mode_;
+ const webrtc::FieldTrialBasedConfig fallback_trials_;
+ const webrtc::FieldTrialsView& trials_;
+};
+
+} // namespace cricket
+
+#endif // VIDEO_CONFIG_ENCODER_STREAM_FACTORY_H_
diff --git a/media/engine/simulcast.cc b/video/config/simulcast.cc
similarity index 99%
rename from media/engine/simulcast.cc
rename to video/config/simulcast.cc
index fb10c0f..2bd4ac0 100644
--- a/media/engine/simulcast.cc
+++ b/video/config/simulcast.cc
@@ -8,7 +8,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "media/engine/simulcast.h"
+#include "video/config/simulcast.h"
#include <stdint.h>
#include <stdio.h>
diff --git a/media/engine/simulcast.h b/video/config/simulcast.h
similarity index 94%
rename from media/engine/simulcast.h
rename to video/config/simulcast.h
index e367830..aa48058 100644
--- a/media/engine/simulcast.h
+++ b/video/config/simulcast.h
@@ -8,8 +8,8 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#ifndef MEDIA_ENGINE_SIMULCAST_H_
-#define MEDIA_ENGINE_SIMULCAST_H_
+#ifndef VIDEO_CONFIG_SIMULCAST_H_
+#define VIDEO_CONFIG_SIMULCAST_H_
#include <stddef.h>
@@ -69,4 +69,4 @@
} // namespace cricket
-#endif // MEDIA_ENGINE_SIMULCAST_H_
+#endif // VIDEO_CONFIG_SIMULCAST_H_
diff --git a/media/engine/simulcast_unittest.cc b/video/config/simulcast_unittest.cc
similarity index 99%
rename from media/engine/simulcast_unittest.cc
rename to video/config/simulcast_unittest.cc
index 47a9db7..152a0f9 100644
--- a/media/engine/simulcast_unittest.cc
+++ b/video/config/simulcast_unittest.cc
@@ -8,7 +8,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "media/engine/simulcast.h"
+#include "video/config/simulcast.h"
#include "api/transport/field_trial_based_config.h"
#include "media/base/media_constants.h"
diff --git a/video/end_to_end_tests/resolution_bitrate_limits_tests.cc b/video/end_to_end_tests/resolution_bitrate_limits_tests.cc
index 8cdfaad..5d905b8 100644
--- a/video/end_to_end_tests/resolution_bitrate_limits_tests.cc
+++ b/video/end_to_end_tests/resolution_bitrate_limits_tests.cc
@@ -18,6 +18,7 @@
#include "test/field_trial.h"
#include "test/gtest.h"
#include "test/video_encoder_proxy_factory.h"
+#include "video/config/encoder_stream_factory.h"
namespace webrtc {
namespace test {
diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc
index 1b5b3a8..d055431 100644
--- a/video/video_quality_test.cc
+++ b/video/video_quality_test.cc
@@ -54,6 +54,7 @@
#ifdef WEBRTC_WIN
#include "modules/audio_device/include/audio_device_factory.h"
#endif
+#include "video/config/encoder_stream_factory.h"
namespace webrtc {
diff --git a/video/video_send_stream_tests.cc b/video/video_send_stream_tests.cc
index 965073b..33a607b 100644
--- a/video/video_send_stream_tests.cc
+++ b/video/video_send_stream_tests.cc
@@ -69,6 +69,7 @@
#include "test/null_transport.h"
#include "test/rtcp_packet_parser.h"
#include "test/video_encoder_proxy_factory.h"
+#include "video/config/encoder_stream_factory.h"
#include "video/send_statistics_proxy.h"
#include "video/transport_adapter.h"
#include "video/video_send_stream.h"
diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc
index 3baa9c5..961f592 100644
--- a/video/video_stream_encoder_unittest.cc
+++ b/video/video_stream_encoder_unittest.cc
@@ -1,3 +1,4 @@
+
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
@@ -7,7 +8,6 @@
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
-
#include "video/video_stream_encoder.h"
#include <algorithm>
@@ -70,6 +70,7 @@
#include "test/time_controller/simulated_time_controller.h"
#include "test/video_encoder_nullable_proxy_factory.h"
#include "test/video_encoder_proxy_factory.h"
+#include "video/config/encoder_stream_factory.h"
#include "video/frame_cadence_adapter.h"
#include "video/send_statistics_proxy.h"