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:
@ -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
|
||||
@ -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_
|
||||
@ -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
|
||||
@ -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',
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user