Refactor VP8 TemporalLayers

This CL moves all temporal layer rate allocation from
DefaultTemporalLayers and ScreenshareLayers into SimulcastRateAllocator.
This means we don't need an extra call-out to the TemporalLayers
interface to get the last allocation, which simplifies the code path a
lot.

It also paves the wave for removing the TemporalLayersFactory interface
(in a separate cl), which will further simplify the ownership model.

Bug: webrtc:9012
Change-Id: I6540b1848efa1a136dce449f13902ad479d5ee37
Reviewed-on: https://webrtc-review.googlesource.com/62420
Commit-Queue: Erik Språng <sprang@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22502}
This commit is contained in:
Erik Språng 2018-03-19 18:25:10 +01:00 committed by Commit Bot
parent d7573563a4
commit bb60a3a5fa
15 changed files with 251 additions and 137 deletions

View File

@ -16,6 +16,7 @@
#include <type_traits>
#include "rtc_base/checks.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/stringutils.h"
namespace webrtc {
@ -232,13 +233,37 @@ uint32_t BitrateAllocation::GetSpatialLayerSum(size_t spatial_index) const {
return sum;
}
std::vector<uint32_t> BitrateAllocation::GetTemporalLayerAllocation(
size_t spatial_index) const {
RTC_CHECK_LT(spatial_index, kMaxSpatialLayers);
std::vector<uint32_t> temporal_rates;
// Find the highest temporal layer with a defined bitrate in order to
// determine the size of the temporal layer allocation.
for (size_t i = kMaxTemporalStreams; i > 0; --i) {
if (has_bitrate_[spatial_index][i - 1]) {
temporal_rates.resize(i);
break;
}
}
for (size_t i = 0; i < temporal_rates.size(); ++i) {
temporal_rates[i] = bitrates_[spatial_index][i];
}
return temporal_rates;
}
std::string BitrateAllocation::ToString() const {
if (sum_ == 0)
return "BitrateAllocation [ [] ]";
// TODO(sprang): Replace this stringstream with something cheaper.
std::ostringstream oss;
oss << "BitrateAllocation [";
// Max string length in practice is 260, but let's have some overhead and
// round up to nearest power of two.
char string_buf[512];
rtc::SimpleStringBuilder ssb(string_buf);
ssb << "BitrateAllocation [";
uint32_t spatial_cumulator = 0;
for (int si = 0; si < kMaxSpatialLayers; ++si) {
RTC_DCHECK_LE(spatial_cumulator, sum_);
@ -247,11 +272,11 @@ std::string BitrateAllocation::ToString() const {
const uint32_t layer_sum = GetSpatialLayerSum(si);
if (layer_sum == sum_) {
oss << " [";
ssb << " [";
} else {
if (si > 0)
oss << ",";
oss << std::endl << " [";
ssb << ",";
ssb << '\n' << " [";
}
spatial_cumulator += layer_sum;
@ -262,23 +287,18 @@ std::string BitrateAllocation::ToString() const {
break;
if (ti > 0)
oss << ", ";
ssb << ", ";
uint32_t bitrate = bitrates_[si][ti];
oss << bitrate;
ssb << bitrate;
temporal_cumulator += bitrate;
}
oss << "]";
ssb << "]";
}
RTC_DCHECK_EQ(spatial_cumulator, sum_);
oss << " ]";
return oss.str();
}
std::ostream& BitrateAllocation::operator<<(std::ostream& os) const {
os << ToString();
return os;
ssb << " ]";
return ssb.str();
}
} // namespace webrtc

View File

@ -611,6 +611,11 @@ class BitrateAllocation {
// Get the sum of all the temporal layer for a specific spatial layer.
uint32_t GetSpatialLayerSum(size_t spatial_index) const;
// Returns a vector of the temporal layer bitrates for the specific spatial
// layer. Length of the returned vector is cropped to the highest temporal
// layer with a defined bitrate.
std::vector<uint32_t> GetTemporalLayerAllocation(size_t spatial_index) const;
uint32_t get_sum_bps() const { return sum_; } // Sum of all bitrates.
uint32_t get_sum_kbps() const { return (sum_ + 500) / 1000; }
@ -623,7 +628,6 @@ class BitrateAllocation {
// Expensive, please use only in tests.
std::string ToString() const;
std::ostream& operator<<(std::ostream& os) const;
private:
uint32_t sum_;

View File

@ -321,42 +321,27 @@ uint8_t DefaultTemporalLayers::Tl0PicIdx() const {
return tl0_pic_idx_;
}
std::vector<uint32_t> DefaultTemporalLayers::OnRatesUpdated(
int bitrate_kbps,
int max_bitrate_kbps,
int framerate) {
std::vector<uint32_t> bitrates;
for (size_t i = 0; i < num_layers_; ++i) {
float layer_bitrate =
bitrate_kbps * kVp8LayerRateAlloction[num_layers_ - 1][i];
bitrates.push_back(static_cast<uint32_t>(layer_bitrate + 0.5));
void DefaultTemporalLayers::OnRatesUpdated(
const std::vector<uint32_t>& bitrates_bps,
int framerate_fps) {
RTC_DCHECK_GT(bitrates_bps.size(), 0);
RTC_DCHECK_LE(bitrates_bps.size(), num_layers_);
// |bitrates_bps| uses individual rate per layer, but Vp8EncoderConfig wants
// the accumulated rate, so sum them up.
new_bitrates_bps_ = bitrates_bps;
new_bitrates_bps_->resize(num_layers_);
for (size_t i = 1; i < num_layers_; ++i) {
(*new_bitrates_bps_)[i] += (*new_bitrates_bps_)[i - 1];
}
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>(bitrates);
// Allocation table is of aggregates, transform to individual rates.
uint32_t sum = 0;
for (size_t i = 0; i < num_layers_; ++i) {
uint32_t layer_bitrate = bitrates[i];
RTC_DCHECK_LE(sum, bitrates[i]);
bitrates[i] -= sum;
sum = layer_bitrate;
if (sum >= static_cast<uint32_t>(bitrate_kbps)) {
// Sum adds up; any subsequent layers will be 0.
bitrates.resize(i + 1);
break;
}
}
return bitrates;
}
bool DefaultTemporalLayers::UpdateConfiguration(Vp8EncoderConfig* cfg) {
if (!new_bitrates_kbps_)
if (!new_bitrates_bps_) {
return false;
}
for (size_t i = 0; i < num_layers_; ++i) {
cfg->ts_target_bitrate[i] = (*new_bitrates_kbps_)[i];
cfg->ts_target_bitrate[i] = (*new_bitrates_bps_)[i] / 1000;
// ..., 4, 2, 1
cfg->ts_rate_decimator[i] = 1 << (num_layers_ - i - 1);
}
@ -366,7 +351,7 @@ bool DefaultTemporalLayers::UpdateConfiguration(Vp8EncoderConfig* cfg) {
memcpy(cfg->ts_layer_id, &temporal_ids_[0],
sizeof(unsigned int) * temporal_ids_.size());
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>();
new_bitrates_bps_.reset();
return true;
}

View File

@ -31,11 +31,9 @@ class DefaultTemporalLayers : public TemporalLayers {
// and/or update the reference buffers.
TemporalLayers::FrameConfig UpdateLayerConfig(uint32_t timestamp) override;
// Update state based on new bitrate target and incoming framerate.
// Returns the bitrate allocation for the active temporal layers.
std::vector<uint32_t> OnRatesUpdated(int bitrate_kbps,
int max_bitrate_kbps,
int framerate) override;
// New target bitrate, per temporal layer.
void OnRatesUpdated(const std::vector<uint32_t>& bitrates_bps,
int framerate_fps) override;
bool UpdateConfiguration(Vp8EncoderConfig* cfg) override;
@ -57,7 +55,8 @@ class DefaultTemporalLayers : public TemporalLayers {
uint8_t tl0_pic_idx_;
uint8_t pattern_idx_;
bool last_base_layer_sync_;
rtc::Optional<std::vector<uint32_t>> new_bitrates_kbps_;
// Updated cumulative bitrates, per temporal layer.
rtc::Optional<std::vector<uint32_t>> new_bitrates_bps_;
};
class DefaultTemporalLayersChecker : public TemporalLayersChecker {

View File

@ -10,13 +10,14 @@
#include "modules/video_coding/codecs/vp8/default_temporal_layers.h"
#include "modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h"
#include "modules/video_coding/codecs/vp8/simulcast_rate_allocator.h"
#include "modules/video_coding/include/video_codec_interface.h"
#include "test/field_trial.h"
#include "test/gtest.h"
namespace webrtc {
namespace test {
namespace {
enum {
kTemporalUpdateLast = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF,
@ -49,12 +50,32 @@ enum {
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF,
};
std::vector<uint32_t> GetTemporalLayerRates(int target_bitrate_kbps,
int framerate_fps,
int num_temporal_layers) {
VideoCodec codec;
codec.codecType = VideoCodecType::kVideoCodecVP8;
codec.numberOfSimulcastStreams = 1;
codec.targetBitrate = target_bitrate_kbps;
codec.maxBitrate = target_bitrate_kbps;
codec.maxFramerate = framerate_fps;
codec.simulcastStream[0].targetBitrate = target_bitrate_kbps;
codec.simulcastStream[0].maxBitrate = target_bitrate_kbps;
codec.simulcastStream[0].numberOfTemporalLayers = num_temporal_layers;
codec.simulcastStream[0].active = true;
SimulcastRateAllocator allocator(codec, nullptr);
return allocator.GetAllocation(target_bitrate_kbps, framerate_fps)
.GetTemporalLayerAllocation(0);
}
} // namespace
TEST(TemporalLayersTest, 2Layers) {
DefaultTemporalLayers tl(2, 0);
DefaultTemporalLayersChecker checker(2, 0);
Vp8EncoderConfig cfg;
CodecSpecificInfoVP8 vp8_info;
tl.OnRatesUpdated(500, 500, 30);
tl.OnRatesUpdated(GetTemporalLayerRates(500, 30, 1), 30);
tl.UpdateConfiguration(&cfg);
int expected_flags[16] = {
@ -102,7 +123,7 @@ TEST(TemporalLayersTest, 3Layers) {
DefaultTemporalLayersChecker checker(3, 0);
Vp8EncoderConfig cfg;
CodecSpecificInfoVP8 vp8_info;
tl.OnRatesUpdated(500, 500, 30);
tl.OnRatesUpdated(GetTemporalLayerRates(500, 30, 1), 30);
tl.UpdateConfiguration(&cfg);
int expected_flags[16] = {
@ -151,7 +172,7 @@ TEST(TemporalLayersTest, Alternative3Layers) {
DefaultTemporalLayersChecker checker(3, 0);
Vp8EncoderConfig cfg;
CodecSpecificInfoVP8 vp8_info;
tl.OnRatesUpdated(500, 500, 30);
tl.OnRatesUpdated(GetTemporalLayerRates(500, 30, 1), 30);
tl.UpdateConfiguration(&cfg);
int expected_flags[8] = {kTemporalUpdateLast,
@ -187,7 +208,7 @@ TEST(TemporalLayersTest, 4Layers) {
DefaultTemporalLayersChecker checker(4, 0);
Vp8EncoderConfig cfg;
CodecSpecificInfoVP8 vp8_info;
tl.OnRatesUpdated(500, 500, 30);
tl.OnRatesUpdated(GetTemporalLayerRates(500, 30, 1), 30);
tl.UpdateConfiguration(&cfg);
int expected_flags[16] = {
kTemporalUpdateLast,
@ -234,7 +255,7 @@ TEST(TemporalLayersTest, KeyFrame) {
DefaultTemporalLayersChecker checker(3, 0);
Vp8EncoderConfig cfg;
CodecSpecificInfoVP8 vp8_info;
tl.OnRatesUpdated(500, 500, 30);
tl.OnRatesUpdated(GetTemporalLayerRates(500, 30, 1), 30);
tl.UpdateConfiguration(&cfg);
int expected_flags[8] = {
@ -347,7 +368,7 @@ TEST_P(TemporalLayersReferenceTest, ValidFrameConfigs) {
const int num_layers = GetParam();
DefaultTemporalLayers tl(num_layers, 0);
Vp8EncoderConfig cfg;
tl.OnRatesUpdated(500, 500, 30);
tl.OnRatesUpdated(GetTemporalLayerRates(500, 30, 1), 30);
tl.UpdateConfiguration(&cfg);
// Run through the pattern and store the frame dependencies, plus keep track

View File

@ -314,6 +314,10 @@ int LibvpxVp8Encoder::SetRateAllocation(const BitrateAllocation& bitrate,
SetStreamState(send_stream, stream_idx);
configurations_[i].rc_target_bitrate = target_bitrate_kbps;
if (send_stream) {
temporal_layers_[stream_idx]->OnRatesUpdated(
bitrate.GetTemporalLayerAllocation(stream_idx), new_framerate);
}
UpdateVpxConfiguration(temporal_layers_[stream_idx].get(),
&configurations_[i]);
@ -342,6 +346,7 @@ void LibvpxVp8Encoder::SetupTemporalLayers(int num_streams,
const VideoCodec& codec) {
RTC_DCHECK(codec.VP8().tl_factory != nullptr);
const TemporalLayersFactory* tl_factory = codec.VP8().tl_factory;
RTC_DCHECK(temporal_layers_.empty());
if (num_streams == 1) {
temporal_layers_.emplace_back(
tl_factory->Create(0, num_temporal_layers, tl0_pic_idx_[0]));
@ -546,8 +551,10 @@ int LibvpxVp8Encoder::InitEncode(const VideoCodec* inst,
}
configurations_[0].rc_target_bitrate = stream_bitrates[stream_idx];
temporal_layers_[stream_idx]->OnRatesUpdated(
stream_bitrates[stream_idx], inst->maxBitrate, inst->maxFramerate);
if (stream_bitrates[stream_idx] > 0) {
temporal_layers_[stream_idx]->OnRatesUpdated(
allocation.GetTemporalLayerAllocation(stream_idx), inst->maxFramerate);
}
UpdateVpxConfiguration(temporal_layers_[stream_idx].get(),
&configurations_[0]);
@ -570,8 +577,11 @@ int LibvpxVp8Encoder::InitEncode(const VideoCodec* inst,
inst->simulcastStream[stream_idx].height, kVp832ByteAlign);
SetStreamState(stream_bitrates[stream_idx] > 0, stream_idx);
configurations_[i].rc_target_bitrate = stream_bitrates[stream_idx];
temporal_layers_[stream_idx]->OnRatesUpdated(
stream_bitrates[stream_idx], inst->maxBitrate, inst->maxFramerate);
if (stream_bitrates[stream_idx] > 0) {
temporal_layers_[stream_idx]->OnRatesUpdated(
allocation.GetTemporalLayerAllocation(stream_idx),
inst->maxFramerate);
}
UpdateVpxConfiguration(temporal_layers_[stream_idx].get(),
&configurations_[i]);
}

View File

@ -239,37 +239,42 @@ TemporalLayers::FrameConfig ScreenshareLayers::UpdateLayerConfig(
return tl_config;
}
std::vector<uint32_t> ScreenshareLayers::OnRatesUpdated(int bitrate_kbps,
int max_bitrate_kbps,
int framerate) {
RTC_DCHECK_GT(framerate, 0);
void ScreenshareLayers::OnRatesUpdated(
const std::vector<uint32_t>& bitrates_bps,
int framerate_fps) {
RTC_DCHECK_GT(framerate_fps, 0);
RTC_DCHECK_GE(bitrates_bps.size(), 1);
RTC_DCHECK_LE(bitrates_bps.size(), 2);
// |bitrates_bps| uses individual rates per layer, but we want to use the
// accumulated rate here.
uint32_t tl0_kbps = bitrates_bps[0] / 1000;
uint32_t tl1_kbps = tl0_kbps;
if (bitrates_bps.size() > 1) {
tl1_kbps += bitrates_bps[1] / 1000;
}
if (!target_framerate_) {
// First OnRatesUpdated() is called during construction, with the configured
// targets as parameters.
target_framerate_.emplace(framerate);
// First OnRatesUpdated() is called during construction, with the
// configured targets as parameters.
target_framerate_ = framerate_fps;
capture_framerate_ = target_framerate_;
bitrate_updated_ = true;
} else {
bitrate_updated_ =
bitrate_kbps != static_cast<int>(layers_[0].target_rate_kbps_) ||
max_bitrate_kbps != static_cast<int>(layers_[1].target_rate_kbps_) ||
(capture_framerate_ &&
framerate != static_cast<int>(*capture_framerate_));
if (framerate < 0) {
bitrate_updated_ = capture_framerate_ &&
framerate_fps != static_cast<int>(*capture_framerate_);
bitrate_updated_ |= tl0_kbps != layers_[0].target_rate_kbps_;
bitrate_updated_ |= tl1_kbps != layers_[1].target_rate_kbps_;
if (framerate_fps < 0) {
capture_framerate_.reset();
} else {
capture_framerate_.emplace(framerate);
capture_framerate_ = framerate_fps;
}
}
layers_[0].target_rate_kbps_ = bitrate_kbps;
layers_[1].target_rate_kbps_ = max_bitrate_kbps;
std::vector<uint32_t> allocation;
allocation.push_back(bitrate_kbps);
if (max_bitrate_kbps > bitrate_kbps)
allocation.push_back(max_bitrate_kbps - bitrate_kbps);
return allocation;
layers_[0].target_rate_kbps_ = tl0_kbps;
layers_[1].target_rate_kbps_ = tl1_kbps;
}
void ScreenshareLayers::FrameEncoded(unsigned int size, int qp) {

View File

@ -37,11 +37,9 @@ class ScreenshareLayers : public TemporalLayers {
// and/or update the reference buffers.
TemporalLayers::FrameConfig UpdateLayerConfig(uint32_t timestamp) override;
// Update state based on new bitrate target and incoming framerate.
// Returns the bitrate allocation for the active temporal layers.
std::vector<uint32_t> OnRatesUpdated(int bitrate_kbps,
int max_bitrate_kbps,
int framerate) override;
// New target bitrate, per temporal layer.
void OnRatesUpdated(const std::vector<uint32_t>& bitrates_bps,
int framerate_fps) override;
// Update the encoder configuration with target bitrates or other parameters.
// Returns true iff the configuration was actually modified.

View File

@ -26,7 +26,7 @@ using ::testing::NiceMock;
using ::testing::Return;
namespace webrtc {
namespace {
// 5 frames per second at 90 kHz.
const uint32_t kTimestampDelta5Fps = 90000 / 5;
const int kDefaultQp = 54;
@ -43,6 +43,11 @@ const int kTl1Flags =
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
const int kTl1SyncFlags = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF |
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
const std::vector<uint32_t> kDefault2TlBitratesBps = {
kDefaultTl0BitrateKbps * 1000,
(kDefaultTl1BitrateKbps - kDefaultTl0BitrateKbps) * 1000};
} // namespace
class ScreenshareLayerTest : public ::testing::Test {
protected:
@ -99,10 +104,7 @@ class ScreenshareLayerTest : public ::testing::Test {
memset(&vp8_cfg, 0, sizeof(Vp8EncoderConfig));
vp8_cfg.rc_min_quantizer = min_qp_;
vp8_cfg.rc_max_quantizer = max_qp_;
EXPECT_THAT(layers_->OnRatesUpdated(kDefaultTl0BitrateKbps,
kDefaultTl1BitrateKbps, kFrameRate),
ElementsAre(kDefaultTl0BitrateKbps,
kDefaultTl1BitrateKbps - kDefaultTl0BitrateKbps));
layers_->OnRatesUpdated(kDefault2TlBitratesBps, kFrameRate);
EXPECT_TRUE(layers_->UpdateConfiguration(&vp8_cfg));
frame_size_ = FrameSizeForBitrate(vp8_cfg.rc_target_bitrate);
return vp8_cfg;
@ -344,10 +346,9 @@ TEST_F(ScreenshareLayerTest, TooHighBitrate) {
TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL0) {
const int kTl0_kbps = 100;
const int kTl1_kbps = 1000;
layers_->OnRatesUpdated(kTl0_kbps, kTl1_kbps, 5);
EXPECT_THAT(layers_->OnRatesUpdated(kTl0_kbps, kTl1_kbps, 5),
ElementsAre(kTl0_kbps, kTl1_kbps - kTl0_kbps));
const std::vector<uint32_t> layer_rates = {kTl0_kbps * 1000,
(kTl1_kbps - kTl0_kbps) * 1000};
layers_->OnRatesUpdated(layer_rates, kFrameRate);
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg_));
EXPECT_EQ(static_cast<unsigned int>(
@ -358,8 +359,9 @@ TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL0) {
TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL1) {
const int kTl0_kbps = 100;
const int kTl1_kbps = 450;
EXPECT_THAT(layers_->OnRatesUpdated(kTl0_kbps, kTl1_kbps, 5),
ElementsAre(kTl0_kbps, kTl1_kbps - kTl0_kbps));
const std::vector<uint32_t> layer_rates = {kTl0_kbps * 1000,
(kTl1_kbps - kTl0_kbps) * 1000};
layers_->OnRatesUpdated(layer_rates, kFrameRate);
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg_));
EXPECT_EQ(static_cast<unsigned int>(
@ -369,12 +371,11 @@ TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL1) {
TEST_F(ScreenshareLayerTest, TargetBitrateBelowTL0) {
const int kTl0_kbps = 100;
const int kTl1_kbps = 100;
EXPECT_THAT(layers_->OnRatesUpdated(kTl0_kbps, kTl1_kbps, 5),
ElementsAre(kTl0_kbps));
const std::vector<uint32_t> layer_rates = {kTl0_kbps * 1000};
layers_->OnRatesUpdated(layer_rates, kFrameRate);
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg_));
EXPECT_EQ(static_cast<uint32_t>(kTl1_kbps), cfg_.rc_target_bitrate);
EXPECT_EQ(static_cast<uint32_t>(kTl0_kbps), cfg_.rc_target_bitrate);
}
TEST_F(ScreenshareLayerTest, EncoderDrop) {
@ -432,7 +433,8 @@ TEST_F(ScreenshareLayerTest, RespectsMaxIntervalBetweenFrames) {
const int kLargeFrameSizeBytes = 100000;
const uint32_t kStartTimestamp = 1234;
layers_->OnRatesUpdated(kLowBitrateKbps, kLowBitrateKbps, 5);
const std::vector<uint32_t> layer_rates = {kLowBitrateKbps * 1000};
layers_->OnRatesUpdated(layer_rates, kFrameRate);
layers_->UpdateConfiguration(&cfg_);
EXPECT_EQ(kTl0Flags,
@ -628,7 +630,7 @@ TEST_F(ScreenshareLayerTest, AdjustsBitrateWhenDroppingFrames) {
const uint32_t kTimestampDelta10Fps = kTimestampDelta5Fps / 2;
const int kNumFrames = 30;
uint32_t default_bitrate = cfg_.rc_target_bitrate;
layers_->OnRatesUpdated(kDefaultTl0BitrateKbps, kDefaultTl1BitrateKbps, 10);
layers_->OnRatesUpdated(kDefault2TlBitratesBps, 10);
int num_dropped_frames = 0;
for (int i = 0; i < kNumFrames; ++i) {

View File

@ -15,6 +15,7 @@
#include <vector>
#include <utility>
#include "modules/video_coding/codecs/vp8/include/vp8_common_types.h"
#include "rtc_base/checks.h"
namespace webrtc {
@ -46,7 +47,7 @@ BitrateAllocation SimulcastRateAllocator::GetAllocation(
void SimulcastRateAllocator::DistributeAllocationToSimulcastLayers(
uint32_t total_bitrate_bps,
BitrateAllocation* allocated_bitrates_bps) {
BitrateAllocation* allocated_bitrates_bps) const {
uint32_t left_to_allocate = total_bitrate_bps;
if (codec_.maxBitrate && codec_.maxBitrate * 1000 < left_to_allocate)
left_to_allocate = codec_.maxBitrate * 1000;
@ -122,7 +123,7 @@ void SimulcastRateAllocator::DistributeAllocationToSimulcastLayers(
void SimulcastRateAllocator::DistributeAllocationToTemporalLayers(
uint32_t framerate,
BitrateAllocation* allocated_bitrates_bps) {
BitrateAllocation* allocated_bitrates_bps) const {
const int num_spatial_streams =
std::max(1, static_cast<int>(codec_.numberOfSimulcastStreams));
@ -130,32 +131,25 @@ void SimulcastRateAllocator::DistributeAllocationToTemporalLayers(
// available temporal layers.
for (int simulcast_id = 0; simulcast_id < num_spatial_streams;
++simulcast_id) {
// TODO(shampson): Consider adding a continue here if the simulcast stream
// is inactive. Currently this is not added because the call
// below to OnRatesUpdated changes the TemporalLayer's
// state.
auto tl_it = temporal_layers_.find(simulcast_id);
if (tl_it == temporal_layers_.end())
continue; // TODO(sprang): If > 1 SS, assume default TL alloc?
uint32_t target_bitrate_kbps =
allocated_bitrates_bps->GetBitrate(simulcast_id, 0) / 1000;
if (target_bitrate_kbps == 0) {
continue;
}
const uint32_t expected_allocated_bitrate_kbps = target_bitrate_kbps;
RTC_DCHECK_EQ(
target_bitrate_kbps,
allocated_bitrates_bps->GetSpatialLayerSum(simulcast_id) / 1000);
const int num_temporal_streams = std::max<uint8_t>(
1, codec_.numberOfSimulcastStreams == 0
? codec_.VP8().numberOfTemporalLayers
: codec_.simulcastStream[simulcast_id].numberOfTemporalLayers);
const int num_temporal_streams = NumTemporalStreams(simulcast_id);
uint32_t max_bitrate_kbps;
// Legacy temporal-layered only screenshare, or simulcast screenshare
// with legacy mode for simulcast stream 0.
if (codec_.mode == kScreensharing && codec_.targetBitrate > 0 &&
const bool conference_screenshare_mode =
codec_.mode == kScreensharing && codec_.targetBitrate > 0 &&
((num_spatial_streams == 1 && num_temporal_streams == 2) || // Legacy.
(num_spatial_streams > 1 && simulcast_id == 0))) { // Simulcast.
(num_spatial_streams > 1 && simulcast_id == 0)); // Simulcast.
if (conference_screenshare_mode) {
// TODO(holmer): This is a "temporary" hack for screensharing, where we
// interpret the startBitrate as the encoder target bitrate. This is
// to allow for a different max bitrate, so if the codec can't meet
@ -170,8 +164,18 @@ void SimulcastRateAllocator::DistributeAllocationToTemporalLayers(
max_bitrate_kbps = codec_.simulcastStream[simulcast_id].maxBitrate;
}
std::vector<uint32_t> tl_allocation = tl_it->second->OnRatesUpdated(
target_bitrate_kbps, max_bitrate_kbps, framerate);
std::vector<uint32_t> tl_allocation;
if (num_temporal_streams == 1) {
tl_allocation.push_back(target_bitrate_kbps);
} else {
if (conference_screenshare_mode) {
tl_allocation = ScreenshareTemporalLayerAllocation(
target_bitrate_kbps, max_bitrate_kbps, framerate, simulcast_id);
} else {
tl_allocation = DefaultTemporalLayerAllocation(
target_bitrate_kbps, max_bitrate_kbps, framerate, simulcast_id);
}
}
RTC_DCHECK_GT(tl_allocation.size(), 0);
RTC_DCHECK_LE(tl_allocation.size(), num_temporal_streams);
@ -188,6 +192,54 @@ void SimulcastRateAllocator::DistributeAllocationToTemporalLayers(
}
}
std::vector<uint32_t> SimulcastRateAllocator::DefaultTemporalLayerAllocation(
int bitrate_kbps,
int max_bitrate_kbps,
int framerate,
int simulcast_id) const {
const size_t num_temporal_layers = NumTemporalStreams(simulcast_id);
std::vector<uint32_t> bitrates;
for (size_t i = 0; i < num_temporal_layers; ++i) {
float layer_bitrate =
bitrate_kbps * kVp8LayerRateAlloction[num_temporal_layers - 1][i];
bitrates.push_back(static_cast<uint32_t>(layer_bitrate + 0.5));
}
// Allocation table is of aggregates, transform to individual rates.
uint32_t sum = 0;
for (size_t i = 0; i < num_temporal_layers; ++i) {
uint32_t layer_bitrate = bitrates[i];
RTC_DCHECK_LE(sum, bitrates[i]);
bitrates[i] -= sum;
sum = layer_bitrate;
if (sum >= static_cast<uint32_t>(bitrate_kbps)) {
// Sum adds up; any subsequent layers will be 0.
bitrates.resize(i + 1);
break;
}
}
return bitrates;
}
std::vector<uint32_t>
SimulcastRateAllocator::ScreenshareTemporalLayerAllocation(
int bitrate_kbps,
int max_bitrate_kbps,
int framerate,
int simulcast_id) const {
if (simulcast_id > 0) {
return DefaultTemporalLayerAllocation(bitrate_kbps, max_bitrate_kbps,
framerate, simulcast_id);
}
std::vector<uint32_t> allocation;
allocation.push_back(bitrate_kbps);
if (max_bitrate_kbps > bitrate_kbps)
allocation.push_back(max_bitrate_kbps - bitrate_kbps);
return allocation;
}
uint32_t SimulcastRateAllocator::GetPreferredBitrateBps(uint32_t framerate) {
// Create a temporary instance without temporal layers, as they may be
// stateful, and updating the bitrate to max here can cause side effects.
@ -201,4 +253,11 @@ const VideoCodec& webrtc::SimulcastRateAllocator::GetCodec() const {
return codec_;
}
int SimulcastRateAllocator::NumTemporalStreams(size_t simulcast_id) const {
return std::max<uint8_t>(
1, codec_.numberOfSimulcastStreams == 0
? codec_.VP8().numberOfTemporalLayers
: codec_.simulcastStream[simulcast_id].numberOfTemporalLayers);
}
} // namespace webrtc

View File

@ -15,6 +15,7 @@
#include <map>
#include <memory>
#include <vector>
#include "api/video_codecs/video_encoder.h"
#include "common_video/include/video_bitrate_allocator.h"
@ -41,10 +42,21 @@ class SimulcastRateAllocator : public VideoBitrateAllocator,
private:
void DistributeAllocationToSimulcastLayers(
uint32_t total_bitrate_bps,
BitrateAllocation* allocated_bitrates_bps);
BitrateAllocation* allocated_bitrates_bps) const;
void DistributeAllocationToTemporalLayers(
uint32_t framerate,
BitrateAllocation* allocated_bitrates_bps);
BitrateAllocation* allocated_bitrates_bps) const;
std::vector<uint32_t> DefaultTemporalLayerAllocation(int bitrate_kbps,
int max_bitrate_kbps,
int framerate,
int simulcast_id) const;
std::vector<uint32_t> ScreenshareTemporalLayerAllocation(
int bitrate_kbps,
int max_bitrate_kbps,
int framerate,
int simulcast_id) const;
int NumTemporalStreams(size_t simulcast_id) const;
const VideoCodec codec_;
std::map<uint32_t, TemporalLayers*> temporal_layers_;
std::unique_ptr<TemporalLayersFactory> tl_factory_;

View File

@ -575,6 +575,7 @@ class TestVp8Simulcast : public ::testing::Test {
settings_.simulcastStream[i].maxBitrate = 0;
settings_.simulcastStream[i].width = settings_.width;
settings_.simulcastStream[i].height = settings_.height;
settings_.simulcastStream[i].numberOfTemporalLayers = 1;
}
// Setting input image to new resolution.
input_buffer_ = I420Buffer::Create(settings_.width, settings_.height);

View File

@ -103,11 +103,9 @@ class TemporalLayers {
// and/or update the reference buffers.
virtual FrameConfig UpdateLayerConfig(uint32_t timestamp) = 0;
// Update state based on new bitrate target and incoming framerate.
// Returns the bitrate allocation for the active temporal layers.
virtual std::vector<uint32_t> OnRatesUpdated(int bitrate_kbps,
int max_bitrate_kbps,
int framerate) = 0;
// New target bitrate, per temporal layer.
virtual void OnRatesUpdated(const std::vector<uint32_t>& bitrates_bps,
int framerate_fps) = 0;
// Update the encoder configuration with target bitrates or other parameters.
// Returns true iff the configuration was actually modified.

View File

@ -30,7 +30,7 @@ constexpr uint32_t kFramerateFps = 5;
class MockTemporalLayers : public TemporalLayers {
public:
MOCK_METHOD1(UpdateLayerConfig, TemporalLayers::FrameConfig(uint32_t));
MOCK_METHOD3(OnRatesUpdated, std::vector<uint32_t>(int, int, int));
MOCK_METHOD2(OnRatesUpdated, void(const std::vector<uint32_t>&, int));
MOCK_METHOD1(UpdateConfiguration, bool(Vp8EncoderConfig*));
MOCK_METHOD4(PopulateCodecSpecific,
void(bool,
@ -487,7 +487,7 @@ TEST_F(SimulcastRateAllocatorTest, GetPreferredBitrateBps) {
MockTemporalLayers mock_layers;
allocator_.reset(new SimulcastRateAllocator(codec_, nullptr));
allocator_->OnTemporalLayersCreated(0, &mock_layers);
EXPECT_CALL(mock_layers, OnRatesUpdated(_, _, _)).Times(0);
EXPECT_CALL(mock_layers, OnRatesUpdated(_, _)).Times(0);
EXPECT_EQ(codec_.maxBitrate * 1000,
allocator_->GetPreferredBitrateBps(codec_.maxFramerate));
}

View File

@ -52,7 +52,7 @@ int TargetBitrateKbps() {
return static_cast<int>(FLAG_target_bitrate);
}
DEFINE_int(max_bitrate, 2000, "Call and stream max bitrate in kbps.");
DEFINE_int(max_bitrate, 1000, "Call and stream max bitrate in kbps.");
int MaxBitrateKbps() {
return static_cast<int>(FLAG_max_bitrate);
}