mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-17 23:57:59 +01:00

We want to evaluate more data in order to make better choices in the bitrate allocators. In order to freely update the parameter list without breaking the API many times for projects customizing them, we'll use a struct instead. Bug: webrtc:10126 Change-Id: I443f86781c5134950294cdd1e3197a47447cf973 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/141418 Commit-Queue: Florent Castelli <orphis@webrtc.org> Reviewed-by: Tommi <tommi@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Cr-Commit-Position: refs/heads/master@{#28748}
273 lines
9.9 KiB
C++
273 lines
9.9 KiB
C++
/*
|
|
* Copyright (c) 2018 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 "modules/video_coding/codecs/vp9/svc_rate_allocator.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <numeric>
|
|
|
|
#include "rtc_base/checks.h"
|
|
|
|
namespace webrtc {
|
|
|
|
const float kSpatialLayeringRateScalingFactor = 0.55f;
|
|
const float kTemporalLayeringRateScalingFactor = 0.55f;
|
|
|
|
static size_t GetNumActiveSpatialLayers(const VideoCodec& codec) {
|
|
RTC_DCHECK_EQ(codec.codecType, kVideoCodecVP9);
|
|
RTC_DCHECK_GT(codec.VP9().numberOfSpatialLayers, 0u);
|
|
|
|
size_t num_spatial_layers = 0;
|
|
for (; num_spatial_layers < codec.VP9().numberOfSpatialLayers;
|
|
++num_spatial_layers) {
|
|
if (!codec.spatialLayers[num_spatial_layers].active) {
|
|
// TODO(bugs.webrtc.org/9350): Deactivation of middle layer is not
|
|
// implemented. For now deactivation of a VP9 layer deactivates all
|
|
// layers above the deactivated one.
|
|
break;
|
|
}
|
|
}
|
|
|
|
return num_spatial_layers;
|
|
}
|
|
|
|
static bool AdjustAndVerify(const VideoCodec& codec,
|
|
std::vector<size_t>* spatial_layer_bitrate_bps) {
|
|
bool enough_bitrate = true;
|
|
size_t excess_rate = 0;
|
|
for (size_t sl_idx = 0;
|
|
sl_idx < spatial_layer_bitrate_bps->size() && enough_bitrate; ++sl_idx) {
|
|
RTC_DCHECK_GT(codec.spatialLayers[sl_idx].maxBitrate, 0);
|
|
RTC_DCHECK_GE(codec.spatialLayers[sl_idx].maxBitrate,
|
|
codec.spatialLayers[sl_idx].minBitrate);
|
|
|
|
const size_t min_bitrate_bps =
|
|
codec.spatialLayers[sl_idx].minBitrate * 1000;
|
|
const size_t max_bitrate_bps =
|
|
codec.spatialLayers[sl_idx].maxBitrate * 1000;
|
|
|
|
spatial_layer_bitrate_bps->at(sl_idx) += excess_rate;
|
|
if (spatial_layer_bitrate_bps->at(sl_idx) < max_bitrate_bps) {
|
|
excess_rate = 0;
|
|
} else {
|
|
excess_rate = spatial_layer_bitrate_bps->at(sl_idx) - max_bitrate_bps;
|
|
spatial_layer_bitrate_bps->at(sl_idx) = max_bitrate_bps;
|
|
}
|
|
|
|
size_t bitrate_bps = spatial_layer_bitrate_bps->at(sl_idx);
|
|
enough_bitrate = (bitrate_bps >= min_bitrate_bps);
|
|
}
|
|
|
|
return enough_bitrate;
|
|
}
|
|
|
|
static std::vector<size_t> SplitBitrate(size_t num_layers,
|
|
size_t total_bitrate,
|
|
float rate_scaling_factor) {
|
|
std::vector<size_t> bitrates;
|
|
|
|
double denominator = 0.0;
|
|
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
|
|
denominator += std::pow(rate_scaling_factor, layer_idx);
|
|
}
|
|
|
|
double numerator = std::pow(rate_scaling_factor, num_layers - 1);
|
|
for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
|
|
bitrates.push_back(numerator * total_bitrate / denominator);
|
|
numerator /= rate_scaling_factor;
|
|
}
|
|
|
|
const size_t sum = std::accumulate(bitrates.begin(), bitrates.end(), 0);
|
|
// Ensure the sum of split bitrates doesn't exceed the total bitrate.
|
|
RTC_DCHECK_LE(sum, total_bitrate);
|
|
|
|
// Keep the sum of split bitrates equal to the total bitrate by adding bits,
|
|
// which were lost due to rounding, to the latest layer.
|
|
bitrates.back() += total_bitrate - sum;
|
|
|
|
return bitrates;
|
|
}
|
|
|
|
SvcRateAllocator::SvcRateAllocator(const VideoCodec& codec) : codec_(codec) {
|
|
RTC_DCHECK_EQ(codec.codecType, kVideoCodecVP9);
|
|
RTC_DCHECK_GT(codec.VP9().numberOfSpatialLayers, 0u);
|
|
RTC_DCHECK_GT(codec.VP9().numberOfTemporalLayers, 0u);
|
|
}
|
|
|
|
VideoBitrateAllocation SvcRateAllocator::Allocate(
|
|
VideoBitrateAllocationParameters parameters) {
|
|
DataRate total_bitrate = parameters.total_bitrate;
|
|
if (codec_.maxBitrate != 0) {
|
|
total_bitrate = std::min(total_bitrate, DataRate::kbps(codec_.maxBitrate));
|
|
}
|
|
|
|
if (codec_.spatialLayers[0].targetBitrate == 0) {
|
|
// Delegate rate distribution to VP9 encoder wrapper if bitrate thresholds
|
|
// are not set.
|
|
VideoBitrateAllocation bitrate_allocation;
|
|
bitrate_allocation.SetBitrate(0, 0, total_bitrate.bps());
|
|
return bitrate_allocation;
|
|
}
|
|
|
|
size_t num_spatial_layers = GetNumActiveSpatialLayers(codec_);
|
|
if (num_spatial_layers == 0) {
|
|
return VideoBitrateAllocation(); // All layers are deactivated.
|
|
}
|
|
|
|
if (codec_.mode == VideoCodecMode::kRealtimeVideo) {
|
|
return GetAllocationNormalVideo(total_bitrate.bps(), num_spatial_layers);
|
|
} else {
|
|
return GetAllocationScreenSharing(total_bitrate.bps(), num_spatial_layers);
|
|
}
|
|
}
|
|
|
|
VideoBitrateAllocation SvcRateAllocator::GetAllocationNormalVideo(
|
|
uint32_t total_bitrate_bps,
|
|
size_t num_spatial_layers) const {
|
|
std::vector<size_t> spatial_layer_bitrate_bps;
|
|
|
|
// Distribute total bitrate across spatial layers. If there is not enough
|
|
// bitrate to provide all layers with at least minimum required bitrate
|
|
// then number of layers is reduced by one and distribution is repeated
|
|
// until that condition is met or if number of layers is reduced to one.
|
|
for (;; --num_spatial_layers) {
|
|
spatial_layer_bitrate_bps =
|
|
SplitBitrate(num_spatial_layers, total_bitrate_bps,
|
|
kSpatialLayeringRateScalingFactor);
|
|
|
|
const bool enough_bitrate =
|
|
AdjustAndVerify(codec_, &spatial_layer_bitrate_bps);
|
|
if (enough_bitrate || num_spatial_layers == 1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
VideoBitrateAllocation bitrate_allocation;
|
|
|
|
const size_t num_temporal_layers = codec_.VP9().numberOfTemporalLayers;
|
|
for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
|
|
std::vector<size_t> temporal_layer_bitrate_bps =
|
|
SplitBitrate(num_temporal_layers, spatial_layer_bitrate_bps[sl_idx],
|
|
kTemporalLayeringRateScalingFactor);
|
|
|
|
// Distribute rate across temporal layers. Allocate more bits to lower
|
|
// layers since they are used for prediction of higher layers and their
|
|
// references are far apart.
|
|
if (num_temporal_layers == 1) {
|
|
bitrate_allocation.SetBitrate(sl_idx, 0, temporal_layer_bitrate_bps[0]);
|
|
} else if (num_temporal_layers == 2) {
|
|
bitrate_allocation.SetBitrate(sl_idx, 0, temporal_layer_bitrate_bps[1]);
|
|
bitrate_allocation.SetBitrate(sl_idx, 1, temporal_layer_bitrate_bps[0]);
|
|
} else {
|
|
RTC_CHECK_EQ(num_temporal_layers, 3);
|
|
// In case of three temporal layers the high layer has two frames and the
|
|
// middle layer has one frame within GOP (in between two consecutive low
|
|
// layer frames). Thus high layer requires more bits (comparing pure
|
|
// bitrate of layer, excluding bitrate of base layers) to keep quality on
|
|
// par with lower layers.
|
|
bitrate_allocation.SetBitrate(sl_idx, 0, temporal_layer_bitrate_bps[2]);
|
|
bitrate_allocation.SetBitrate(sl_idx, 1, temporal_layer_bitrate_bps[0]);
|
|
bitrate_allocation.SetBitrate(sl_idx, 2, temporal_layer_bitrate_bps[1]);
|
|
}
|
|
}
|
|
|
|
return bitrate_allocation;
|
|
}
|
|
|
|
// Bit-rate is allocated in such a way, that the highest enabled layer will have
|
|
// between min and max bitrate, and all others will have exactly target
|
|
// bit-rate allocated.
|
|
VideoBitrateAllocation SvcRateAllocator::GetAllocationScreenSharing(
|
|
uint32_t total_bitrate_bps,
|
|
size_t num_spatial_layers) const {
|
|
if (num_spatial_layers == 0 ||
|
|
total_bitrate_bps < codec_.spatialLayers[0].minBitrate * 1000) {
|
|
return VideoBitrateAllocation();
|
|
}
|
|
VideoBitrateAllocation bitrate_allocation;
|
|
|
|
size_t left_bitrate_bps = total_bitrate_bps;
|
|
size_t sl_idx;
|
|
for (sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
|
|
const size_t min_bitrate_bps =
|
|
codec_.spatialLayers[sl_idx].minBitrate * 1000;
|
|
const size_t target_bitrate_bps =
|
|
codec_.spatialLayers[sl_idx].targetBitrate * 1000;
|
|
RTC_DCHECK_LE(min_bitrate_bps, target_bitrate_bps);
|
|
|
|
const size_t bitrate_bps = std::min(left_bitrate_bps, target_bitrate_bps);
|
|
if (bitrate_bps >= min_bitrate_bps) {
|
|
bitrate_allocation.SetBitrate(sl_idx, 0, bitrate_bps);
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
left_bitrate_bps -= bitrate_bps;
|
|
}
|
|
|
|
if (left_bitrate_bps > 0 && sl_idx > 0) {
|
|
// Add leftover to the last allocated layer.
|
|
const size_t max_bitrate_bps =
|
|
codec_.spatialLayers[sl_idx - 1].maxBitrate * 1000;
|
|
|
|
const size_t bitrate_bps = std::min(
|
|
bitrate_allocation.GetBitrate(sl_idx - 1, 0) + left_bitrate_bps,
|
|
max_bitrate_bps);
|
|
bitrate_allocation.SetBitrate(sl_idx - 1, 0, bitrate_bps);
|
|
}
|
|
|
|
return bitrate_allocation;
|
|
}
|
|
|
|
uint32_t SvcRateAllocator::GetMaxBitrateBps(const VideoCodec& codec) {
|
|
const size_t num_spatial_layers = GetNumActiveSpatialLayers(codec);
|
|
|
|
uint32_t max_bitrate_kbps = 0;
|
|
for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
|
|
max_bitrate_kbps += codec.spatialLayers[sl_idx].maxBitrate;
|
|
}
|
|
|
|
if (codec.maxBitrate != 0) {
|
|
max_bitrate_kbps = std::min(max_bitrate_kbps, codec.maxBitrate);
|
|
}
|
|
|
|
return max_bitrate_kbps * 1000;
|
|
}
|
|
|
|
uint32_t SvcRateAllocator::GetPaddingBitrateBps(const VideoCodec& codec) {
|
|
const size_t num_spatial_layers = GetNumActiveSpatialLayers(codec);
|
|
if (num_spatial_layers == 0) {
|
|
return 0; // All layers are deactivated.
|
|
}
|
|
|
|
if (codec.mode == VideoCodecMode::kRealtimeVideo) {
|
|
float scale_factor = 0.0;
|
|
for (size_t sl_idx = 0; sl_idx < num_spatial_layers; ++sl_idx) {
|
|
scale_factor += std::pow(kSpatialLayeringRateScalingFactor, sl_idx);
|
|
}
|
|
uint32_t min_bitrate_bps =
|
|
codec.spatialLayers[num_spatial_layers - 1].minBitrate * 1000;
|
|
return static_cast<uint32_t>(min_bitrate_bps * scale_factor);
|
|
}
|
|
|
|
RTC_DCHECK(codec.mode == VideoCodecMode::kScreensharing);
|
|
|
|
uint32_t min_bitrate_kbps = 0;
|
|
for (size_t sl_idx = 0; sl_idx < num_spatial_layers - 1; ++sl_idx) {
|
|
min_bitrate_kbps += codec.spatialLayers[sl_idx].targetBitrate;
|
|
}
|
|
min_bitrate_kbps += codec.spatialLayers[num_spatial_layers - 1].minBitrate;
|
|
|
|
return min_bitrate_kbps * 1000;
|
|
}
|
|
|
|
} // namespace webrtc
|