Extract simulcast rate allocation outside of video encoder.

This is a first step to refactor this code.
I'm deprecating https://codereview.webrtc.org/1913073002 and
implementing this in smaller more isolated steps.

BUG=webrtc:5206
R=asapersson@webrtc.org, kjellander@webrtc.org, noahric@chromium.org

Review URL: https://codereview.webrtc.org/2288223002 .

Cr-Commit-Position: refs/heads/master@{#14186}
This commit is contained in:
Erik Språng
2016-09-12 16:04:43 +02:00
parent 7b11c65a17
commit 78ce619a0c
11 changed files with 374 additions and 150 deletions

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2016 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 "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
#include <algorithm>
namespace webrtc {
webrtc::SimulcastRateAllocator::SimulcastRateAllocator(const VideoCodec& codec)
: codec_(codec) {}
std::vector<uint32_t> webrtc::SimulcastRateAllocator::GetAllocation(
uint32_t bitrate_kbps) const {
// Always allocate enough bitrate for the minimum bitrate of the first layer.
// Suspending below min bitrate is controlled outside the codec implementation
// and is not overridden by this.
const uint32_t min_bitrate_bps = codec_.numberOfSimulcastStreams == 0
? codec_.minBitrate
: codec_.simulcastStream[0].minBitrate;
uint32_t left_to_allocate = std::max(min_bitrate_bps, bitrate_kbps);
if (codec_.maxBitrate)
left_to_allocate = std::min(left_to_allocate, codec_.maxBitrate);
if (codec_.numberOfSimulcastStreams == 0) {
// No simulcast, just set the target as this has been capped already.
return std::vector<uint32_t>(1, left_to_allocate);
}
// Initialize bitrates with zeroes.
std::vector<uint32_t> allocated_bitrates_bps(codec_.numberOfSimulcastStreams,
0);
// First try to allocate up to the target bitrate for each substream.
size_t layer = 0;
for (; layer < codec_.numberOfSimulcastStreams; ++layer) {
const SimulcastStream& stream = codec_.simulcastStream[layer];
if (left_to_allocate < stream.minBitrate)
break;
uint32_t allocation = std::min(left_to_allocate, stream.targetBitrate);
allocated_bitrates_bps[layer] = allocation;
left_to_allocate -= allocation;
}
// Next, try allocate remaining bitrate, up to max bitrate, in top layer.
// TODO(sprang): Allocate up to max bitrate for all layers once we have a
// better idea of possible performance implications.
if (left_to_allocate > 0) {
size_t active_layer = layer - 1;
const SimulcastStream& stream = codec_.simulcastStream[active_layer];
uint32_t allocation =
std::min(left_to_allocate,
stream.maxBitrate - allocated_bitrates_bps[active_layer]);
left_to_allocate -= allocation;
allocated_bitrates_bps[active_layer] += allocation;
}
return allocated_bitrates_bps;
}
const VideoCodec& webrtc::SimulcastRateAllocator::GetCodec() const {
return codec_;
}
} // namespace webrtc

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016 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 WEBRTC_MODULES_VIDEO_CODING_UTILITY_SIMULCAST_RATE_ALLOCATOR_H_
#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_SIMULCAST_RATE_ALLOCATOR_H_
#include <vector>
#include "webrtc/base/basictypes.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/video_encoder.h"
namespace webrtc {
class SimulcastRateAllocator {
public:
explicit SimulcastRateAllocator(const VideoCodec& codec);
std::vector<uint32_t> GetAllocation(uint32_t bitrate_kbps) const;
const VideoCodec& GetCodec() const;
private:
const VideoCodec codec_;
RTC_DISALLOW_COPY_AND_ASSIGN(SimulcastRateAllocator);
};
} // namespace webrtc
#endif // WEBRTC_MODULES_VIDEO_CODING_UTILITY_SIMULCAST_RATE_ALLOCATOR_H_

View File

@ -0,0 +1,211 @@
/*
* Copyright (c) 2016 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 "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
#include <limits>
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
namespace webrtc {
namespace {
constexpr uint32_t kMinBitrate = 50;
constexpr uint32_t kTargetBitrate = 100;
constexpr uint32_t kMaxBitrate = 1000;
} // namespace
class SimulcastRateAllocatorTest : public ::testing::Test {
public:
SimulcastRateAllocatorTest() {
memset(&codec_, 0, sizeof(VideoCodec));
codec_.minBitrate = kMinBitrate;
codec_.targetBitrate = kTargetBitrate;
codec_.maxBitrate = kMaxBitrate;
CreateAllocator();
}
virtual ~SimulcastRateAllocatorTest() {}
template <size_t S>
void ExpectEqual(uint32_t (&expected)[S],
const std::vector<uint32_t>& actual) {
EXPECT_EQ(S, actual.size());
for (size_t i = 0; i < S; ++i)
EXPECT_EQ(expected[i], actual[i]) << "Mismatch at index " << i;
}
void CreateAllocator() {
allocator_.reset(new SimulcastRateAllocator(codec_));
}
protected:
VideoCodec codec_;
std::unique_ptr<SimulcastRateAllocator> allocator_;
};
TEST_F(SimulcastRateAllocatorTest, NoSimulcastBelowMin) {
uint32_t expected[] = {codec_.minBitrate};
ExpectEqual(expected, allocator_->GetAllocation(codec_.minBitrate - 1));
ExpectEqual(expected, allocator_->GetAllocation(1));
ExpectEqual(expected, allocator_->GetAllocation(0));
}
TEST_F(SimulcastRateAllocatorTest, NoSimulcastAboveMax) {
uint32_t expected[] = {codec_.maxBitrate};
ExpectEqual(expected, allocator_->GetAllocation(codec_.maxBitrate + 1));
ExpectEqual(expected,
allocator_->GetAllocation(std::numeric_limits<uint32_t>::max()));
}
TEST_F(SimulcastRateAllocatorTest, NoSimulcastNoMax) {
constexpr uint32_t kMax = std::numeric_limits<uint32_t>::max();
codec_.maxBitrate = 0;
CreateAllocator();
uint32_t expected[] = {kMax};
ExpectEqual(expected, allocator_->GetAllocation(kMax));
}
TEST_F(SimulcastRateAllocatorTest, NoSimulcastWithinLimits) {
for (uint32_t bitrate = codec_.minBitrate; bitrate <= codec_.maxBitrate;
++bitrate) {
uint32_t expected[] = {bitrate};
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
}
}
TEST_F(SimulcastRateAllocatorTest, SingleSimulcastBelowMin) {
// With simulcast, use the min bitrate from the ss spec instead of the global.
codec_.numberOfSimulcastStreams = 1;
const uint32_t kMin = codec_.minBitrate - 10;
codec_.simulcastStream[0].minBitrate = kMin;
codec_.simulcastStream[0].targetBitrate = kTargetBitrate;
CreateAllocator();
uint32_t expected[] = {kMin};
ExpectEqual(expected, allocator_->GetAllocation(kMin - 1));
ExpectEqual(expected, allocator_->GetAllocation(1));
ExpectEqual(expected, allocator_->GetAllocation(0));
}
TEST_F(SimulcastRateAllocatorTest, SingleSimulcastAboveMax) {
codec_.numberOfSimulcastStreams = 1;
codec_.simulcastStream[0].minBitrate = kMinBitrate;
const uint32_t kMax = codec_.simulcastStream[0].maxBitrate + 1000;
codec_.simulcastStream[0].maxBitrate = kMax;
CreateAllocator();
uint32_t expected[] = {kMax};
ExpectEqual(expected, allocator_->GetAllocation(kMax + 1));
ExpectEqual(expected,
allocator_->GetAllocation(std::numeric_limits<uint32_t>::max()));
}
TEST_F(SimulcastRateAllocatorTest, SingleSimulcastWithinLimits) {
codec_.numberOfSimulcastStreams = 1;
codec_.simulcastStream[0].minBitrate = kMinBitrate;
codec_.simulcastStream[0].targetBitrate = kTargetBitrate;
codec_.simulcastStream[0].maxBitrate = kMaxBitrate;
CreateAllocator();
for (uint32_t bitrate = kMinBitrate; bitrate <= kMaxBitrate; ++bitrate) {
uint32_t expected[] = {bitrate};
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
}
}
TEST_F(SimulcastRateAllocatorTest, OneToThreeStreams) {
codec_.numberOfSimulcastStreams = 3;
codec_.maxBitrate = 0;
codec_.simulcastStream[0].minBitrate = 10;
codec_.simulcastStream[0].targetBitrate = 100;
codec_.simulcastStream[0].maxBitrate = 500;
codec_.simulcastStream[1].minBitrate = 50;
codec_.simulcastStream[1].targetBitrate = 500;
codec_.simulcastStream[1].maxBitrate = 1000;
codec_.simulcastStream[2].minBitrate = 2000;
codec_.simulcastStream[2].targetBitrate = 3000;
codec_.simulcastStream[2].maxBitrate = 4000;
CreateAllocator();
{
// Single stream, min bitrate.
const uint32_t bitrate = codec_.simulcastStream[0].minBitrate;
uint32_t expected[] = {bitrate, 0, 0};
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
}
{
// Single stream at target bitrate.
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate;
uint32_t expected[] = {bitrate, 0, 0};
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
}
{
// Bitrate above target for first stream, but below min for the next one.
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
codec_.simulcastStream[1].minBitrate - 1;
uint32_t expected[] = {bitrate, 0, 0};
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
}
{
// Just enough for two streams.
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
codec_.simulcastStream[1].minBitrate;
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
codec_.simulcastStream[1].minBitrate, 0};
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
}
{
// Second stream maxed out, but not enough for third.
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
codec_.simulcastStream[1].maxBitrate;
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
codec_.simulcastStream[1].maxBitrate, 0};
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
}
{
// First two streams maxed out, but not enough for third. Nowhere to put
// remaining bits.
const uint32_t bitrate = codec_.simulcastStream[0].maxBitrate +
codec_.simulcastStream[1].maxBitrate + 499;
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
codec_.simulcastStream[1].maxBitrate, 0};
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
}
{
// Just enough for all three streams.
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
codec_.simulcastStream[1].targetBitrate +
codec_.simulcastStream[2].minBitrate;
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
codec_.simulcastStream[1].targetBitrate,
codec_.simulcastStream[2].minBitrate};
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
}
{
// Third maxed out.
const uint32_t bitrate = codec_.simulcastStream[0].targetBitrate +
codec_.simulcastStream[1].targetBitrate +
codec_.simulcastStream[2].maxBitrate;
uint32_t expected[] = {codec_.simulcastStream[0].targetBitrate,
codec_.simulcastStream[1].targetBitrate,
codec_.simulcastStream[2].maxBitrate};
ExpectEqual(expected, allocator_->GetAllocation(bitrate));
}
}
} // namespace webrtc

View File

@ -30,6 +30,8 @@
'qp_parser.h',
'quality_scaler.cc',
'quality_scaler.h',
'simulcast_rate_allocator.cc',
'simulcast_rate_allocator.h',
'vp8_header_parser.cc',
'vp8_header_parser.h',
],