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}
This commit is contained in:
parent
2d0ba28e25
commit
1262eb5ebc
|
@ -331,6 +331,7 @@ rtc_library("rtc_audio_video") {
|
|||
"../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 @@ rtc_library("rtc_audio_video") {
|
|||
"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 @@ if (rtc_include_tests) {
|
|||
"../test:test_support",
|
||||
"../test:video_test_common",
|
||||
"../test/time_controller",
|
||||
"../video/config:streams_config",
|
||||
]
|
||||
|
||||
if (enable_libaom) {
|
||||
|
@ -683,7 +683,6 @@ if (rtc_include_tests) {
|
|||
"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",
|
||||
|
|
|
@ -22,4 +22,10 @@ specific_include_rules = {
|
|||
"win32devicemanager\.cc": [
|
||||
"+third_party/logitech/files/logitechquickcam.h",
|
||||
],
|
||||
".*webrtc_video_engine\.h": [
|
||||
"+video/config",
|
||||
],
|
||||
".*webrtc_video_engine_unittest\.cc": [
|
||||
"+video/config",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -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 @@ namespace {
|
|||
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 @@ bool IsDisabled(const webrtc::FieldTrialsView& trials, absl::string_view name) {
|
|||
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 @@ std::vector<VideoCodec> GetPayloadTypesAndDefaultCodecs(
|
|||
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 @@ bool IsCodecDisabledForSimulcast(const std::string& codec_name,
|
|||
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 @@ bool IsLayerActive(const webrtc::RtpEncodingParameters& layer) {
|
|||
(!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 @@ void WebRtcVideoChannel::SetDepacketizerToDecoderFrameTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
@ -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 @@ class WebRtcVideoChannel : public VideoMediaChannel,
|
|||
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_
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -989,6 +989,7 @@ if (rtc_include_tests) {
|
|||
"../../test:test_support",
|
||||
"../../test:video_test_common",
|
||||
"../../test:video_test_support",
|
||||
"../../video/config:streams_config",
|
||||
]
|
||||
absl_deps = [
|
||||
"//third_party/abseil-cpp/absl/strings:strings",
|
||||
|
|
|
@ -19,5 +19,6 @@ specific_include_rules = {
|
|||
".*test.*\.cc": [
|
||||
"+media/base",
|
||||
"+media/engine",
|
||||
"+video/config",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -218,6 +218,7 @@ if (!is_component_build) {
|
|||
"../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",
|
||||
|
|
|
@ -17,6 +17,7 @@ include_rules = [
|
|||
"+system_wrappers",
|
||||
"+p2p",
|
||||
"+third_party/libyuv",
|
||||
"+video/config",
|
||||
]
|
||||
|
||||
specific_include_rules = {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -22,6 +22,7 @@ include_rules = [
|
|||
"+sdk",
|
||||
"+system_wrappers",
|
||||
"+third_party/libyuv",
|
||||
"+video/config",
|
||||
]
|
||||
|
||||
specific_include_rules = {
|
||||
|
|
|
@ -142,7 +142,7 @@ if (rtc_include_tests && !build_with_chromium) {
|
|||
"../../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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -71,6 +71,8 @@ rtc_library("video") {
|
|||
"../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 @@ rtc_library("video") {
|
|||
"../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_library("video") {
|
|||
"../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 @@ if (rtc_include_tests) {
|
|||
"../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 @@ if (rtc_include_tests) {
|
|||
"../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 @@ if (rtc_include_tests) {
|
|||
"../test:video_test_common",
|
||||
"../test/time_controller",
|
||||
"adaptation:video_adaptation",
|
||||
"config:streams_config",
|
||||
]
|
||||
absl_deps = [
|
||||
"//third_party/abseil-cpp/absl/algorithm:container",
|
||||
|
|
|
@ -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" ]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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_
|
|
@ -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>
|
|
@ -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 @@ std::vector<webrtc::VideoStream> GetScreenshareLayers(
|
|||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // MEDIA_ENGINE_SIMULCAST_H_
|
||||
#endif // VIDEO_CONFIG_SIMULCAST_H_
|
|
@ -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"
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in New Issue