Revert of Extract bitrate allocation of spatial/temporal layers out of codec impl. (patchset #17 id:320001 of https://codereview.webrtc.org/2434073003/ )

Reason for revert:
Breaks perf tests.

Original issue's description:
> Extract bitrate allocation of spatial/temporal layers out of codec impl.
>
> This CL makes a number of intervowen changes:
>
> * Add BitrateAllocation struct, that contains a codec independent view
>   of how the target bitrate is distributed over spatial and temporal
>   layers.
>
> * Adds the BitrateAllocator interface, which takes a bitrate and frame
>   rate and produces a BitrateAllocation.
>
> * A default (non layered) implementation is added, and
>   SimulcastRateAllocator is extended to fully handle VP8 allocation.
>   This includes capturing TemporalLayer instances created by the
>   encoder.
>
> * ViEEncoder now owns both the bitrate allocator and the temporal layer
>   factories for VP8. This allows allocation to happen fully outside of
>   the encoder implementation.
>
> This refactoring will make it possible for ViEEncoder to signal the
> full picture of target bitrates to the RTCP module.
>
> BUG=webrtc:6301
>
> Committed: https://crrev.com/8f46c679d24a05b3f08e02c6d91ec9637f34e24f
> Cr-Commit-Position: refs/heads/master@{#14998}

TBR=stefan@webrtc.org,perkj@webrtc.org,mflodman@webrtc.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=webrtc:6301

Review-Url: https://codereview.webrtc.org/2489843002
Cr-Commit-Position: refs/heads/master@{#15001}
This commit is contained in:
sprang
2016-11-09 06:14:47 -08:00
committed by Commit bot
parent 6ba58d67ce
commit 4bc98d4e1b
63 changed files with 848 additions and 1686 deletions

View File

@ -152,14 +152,6 @@ static void RtpFragmentize(EncodedImage* encoded_image,
H264EncoderImpl::H264EncoderImpl()
: openh264_encoder_(nullptr),
width_(0),
height_(0),
max_frame_rate_(0.0f),
target_bps_(0),
max_bps_(0),
mode_(kRealtimeVideo),
frame_dropping_on_(false),
key_frame_interval_(0),
number_of_cores_(0),
encoded_image_callback_(nullptr),
has_reported_init_(false),
@ -271,13 +263,11 @@ int32_t H264EncoderImpl::RegisterEncodeCompleteCallback(
return WEBRTC_VIDEO_CODEC_OK;
}
int32_t H264EncoderImpl::SetRateAllocation(
const BitrateAllocation& bitrate_allocation,
uint32_t framerate) {
if (bitrate_allocation.get_sum_bps() <= 0 || framerate <= 0)
int32_t H264EncoderImpl::SetRates(uint32_t bitrate, uint32_t framerate) {
if (bitrate <= 0 || framerate <= 0) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
target_bps_ = bitrate_allocation.get_sum_bps();
}
target_bps_ = bitrate * 1000;
max_frame_rate_ = static_cast<float>(framerate);
quality_scaler_.ReportFramerate(framerate);

View File

@ -44,8 +44,7 @@ class H264EncoderImpl : public H264Encoder {
int32_t RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) override;
int32_t SetRateAllocation(const BitrateAllocation& bitrate_allocation,
uint32_t framerate) override;
int32_t SetRates(uint32_t bitrate, uint32_t framerate) override;
// The result of encoding - an EncodedImage and RTPFragmentationHeader - are
// passed to the encode complete callback.
@ -75,8 +74,8 @@ class H264EncoderImpl : public H264Encoder {
int width_;
int height_;
float max_frame_rate_;
uint32_t target_bps_;
uint32_t max_bps_;
unsigned int target_bps_;
unsigned int max_bps_;
VideoCodecMode mode_;
// H.264 specifc parameters
bool frame_dropping_on_;

View File

@ -13,7 +13,6 @@
#include <vector>
#include "webrtc/base/checks.h"
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/typedefs.h"
@ -66,6 +65,10 @@ class I420Encoder : public VideoEncoder {
// Return value : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.
int Release() override;
int SetRates(uint32_t /*newBitRate*/, uint32_t /*frameRate*/) override {
return WEBRTC_VIDEO_CODEC_OK;
}
int SetChannelParameters(uint32_t /*packetLoss*/, int64_t /*rtt*/) override {
return WEBRTC_VIDEO_CODEC_OK;
}

View File

@ -10,18 +10,14 @@
#include "webrtc/modules/video_coding/codecs/test/videoprocessor.h"
#include <assert.h>
#include <string.h>
#include <limits>
#include <memory>
#include <utility>
#include <vector>
#include "webrtc/base/checks.h"
#include "webrtc/base/timeutils.h"
#include "webrtc/modules/video_coding/include/video_codec_initializer.h"
#include "webrtc/modules/video_coding/utility/default_video_bitrate_allocator.h"
#include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
#include "webrtc/system_wrappers/include/cpu_info.h"
namespace webrtc {
@ -39,7 +35,7 @@ TestConfig::TestConfig()
frame_length_in_bytes(0),
use_single_core(false),
keyframe_interval(0),
codec_settings(nullptr),
codec_settings(NULL),
verbose(true) {}
TestConfig::~TestConfig() {}
@ -58,9 +54,8 @@ VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
packet_manipulator_(packet_manipulator),
config_(config),
stats_(stats),
encode_callback_(nullptr),
decode_callback_(nullptr),
last_successful_frame_buffer_(nullptr),
encode_callback_(NULL),
decode_callback_(NULL),
first_key_frame_has_been_excluded_(false),
last_frame_missing_(false),
initialized_(false),
@ -70,23 +65,13 @@ VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
num_dropped_frames_(0),
num_spatial_resizes_(0),
last_encoder_frame_width_(0),
last_encoder_frame_height_(0),
bit_rate_factor_(0.0),
encode_start_ns_(0),
decode_start_ns_(0) {
std::unique_ptr<TemporalLayersFactory> tl_factory;
if (config_.codec_settings->codecType == VideoCodecType::kVideoCodecVP8) {
tl_factory.reset(new TemporalLayersFactory());
config.codec_settings->VP8()->tl_factory = tl_factory.get();
}
bitrate_allocator_ = VideoCodecInitializer::CreateBitrateAllocator(
*config.codec_settings, std::move(tl_factory));
RTC_DCHECK(encoder);
RTC_DCHECK(decoder);
RTC_DCHECK(frame_reader);
RTC_DCHECK(frame_writer);
RTC_DCHECK(packet_manipulator);
RTC_DCHECK(stats);
last_encoder_frame_height_(0) {
assert(encoder);
assert(decoder);
assert(frame_reader);
assert(frame_writer);
assert(packet_manipulator);
assert(stats);
}
bool VideoProcessorImpl::Init() {
@ -164,10 +149,8 @@ VideoProcessorImpl::~VideoProcessorImpl() {
}
void VideoProcessorImpl::SetRates(int bit_rate, int frame_rate) {
int set_rates_result = encoder_->SetRateAllocation(
bitrate_allocator_->GetAllocation(bit_rate * 1000, frame_rate),
frame_rate);
RTC_CHECK_GE(set_rates_result, 0);
int set_rates_result = encoder_->SetRates(bit_rate, frame_rate);
assert(set_rates_result >= 0);
if (set_rates_result < 0) {
fprintf(stderr,
"Failed to update encoder with new rate %d, "
@ -195,7 +178,7 @@ int VideoProcessorImpl::NumberSpatialResizes() {
}
bool VideoProcessorImpl::ProcessFrame(int frame_number) {
RTC_DCHECK_GE(frame_number, 0);
assert(frame_number >= 0);
if (!initialized_) {
fprintf(stderr, "Attempting to use uninitialized VideoProcessor!\n");
return false;
@ -289,7 +272,7 @@ void VideoProcessorImpl::FrameEncoded(
exclude_this_frame = true;
break;
default:
RTC_NOTREACHED();
assert(false);
}
}
@ -358,11 +341,11 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
CalcBufferSize(kI420, up_image->width(), up_image->height());
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[length]);
int extracted_length = ExtractBuffer(up_image, length, image_buffer.get());
RTC_DCHECK_GT(extracted_length, 0);
assert(extracted_length > 0);
// Update our copy of the last successful frame:
memcpy(last_successful_frame_buffer_, image_buffer.get(), extracted_length);
bool write_success = frame_writer_->WriteFrame(image_buffer.get());
RTC_DCHECK(write_success);
assert(write_success);
if (!write_success) {
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
}
@ -372,11 +355,11 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
size_t length = CalcBufferSize(kI420, image.width(), image.height());
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[length]);
int extracted_length = ExtractBuffer(image, length, image_buffer.get());
RTC_DCHECK_GT(extracted_length, 0);
assert(extracted_length > 0);
memcpy(last_successful_frame_buffer_, image_buffer.get(), extracted_length);
bool write_success = frame_writer_->WriteFrame(image_buffer.get());
RTC_DCHECK(write_success);
assert(write_success);
if (!write_success) {
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
}
@ -386,8 +369,8 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
int VideoProcessorImpl::GetElapsedTimeMicroseconds(int64_t start,
int64_t stop) {
uint64_t encode_time = (stop - start) / rtc::kNumNanosecsPerMicrosec;
RTC_DCHECK_LT(encode_time,
static_cast<unsigned int>(std::numeric_limits<int>::max()));
assert(encode_time <
static_cast<unsigned int>(std::numeric_limits<int>::max()));
return static_cast<int>(encode_time);
}
@ -398,7 +381,7 @@ const char* ExcludeFrameTypesToStr(ExcludeFrameTypes e) {
case kExcludeAllKeyFrames:
return "ExcludeAllKeyFrames";
default:
RTC_NOTREACHED();
assert(false);
return "Unknown";
}
}
@ -416,7 +399,7 @@ const char* VideoCodecTypeToStr(webrtc::VideoCodecType e) {
case kVideoCodecUnknown:
return "Unknown";
default:
RTC_NOTREACHED();
assert(false);
return "Unknown";
}
}

View File

@ -11,7 +11,6 @@
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_VIDEOPROCESSOR_H_
#include <memory>
#include <string>
#include "webrtc/base/checks.h"
@ -24,9 +23,6 @@
#include "webrtc/video_frame.h"
namespace webrtc {
class VideoBitrateAllocator;
namespace test {
// Defines which frame types shall be excluded from packet loss and when.
@ -195,7 +191,6 @@ class VideoProcessorImpl : public VideoProcessor {
webrtc::VideoEncoder* encoder_;
webrtc::VideoDecoder* decoder_;
std::unique_ptr<VideoBitrateAllocator> bitrate_allocator_;
FrameReader* frame_reader_;
FrameWriter* frame_writer_;
PacketManipulator* packet_manipulator_;

View File

@ -15,7 +15,6 @@
#include "webrtc/modules/video_coding/codecs/test/videoprocessor.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h"
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/modules/video_coding/include/video_coding.h"
@ -114,7 +113,6 @@ class VideoProcessorIntegrationTest : public testing::Test {
webrtc::test::TestConfig config_;
VideoCodec codec_settings_;
webrtc::test::VideoProcessor* processor_;
TemporalLayersFactory tl_factory_;
// Quantities defined/updated for every encoder rate update.
// Some quantities defined per temporal layer (at most 3 layers in this test).

View File

@ -13,10 +13,6 @@
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include "webrtc/base/checks.h"
#include "webrtc/modules/include/module_common_types.h"
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
@ -26,17 +22,16 @@
namespace webrtc {
DefaultTemporalLayers::DefaultTemporalLayers(int number_of_temporal_layers,
DefaultTemporalLayers::DefaultTemporalLayers(int numberOfTemporalLayers,
uint8_t initial_tl0_pic_idx)
: number_of_temporal_layers_(number_of_temporal_layers),
: number_of_temporal_layers_(numberOfTemporalLayers),
temporal_ids_length_(0),
temporal_pattern_length_(0),
tl0_pic_idx_(initial_tl0_pic_idx),
pattern_idx_(255),
timestamp_(0),
last_base_layer_sync_(false) {
RTC_CHECK_GE(kMaxTemporalStreams, number_of_temporal_layers);
RTC_CHECK_GE(number_of_temporal_layers, 0);
assert(kMaxTemporalStreams >= numberOfTemporalLayers);
memset(temporal_ids_, 0, sizeof(temporal_ids_));
memset(temporal_pattern_, 0, sizeof(temporal_pattern_));
}
@ -48,50 +43,18 @@ int DefaultTemporalLayers::CurrentLayerId() const {
return temporal_ids_[index];
}
std::vector<uint32_t> DefaultTemporalLayers::OnRatesUpdated(
int bitrate_kbps,
int max_bitrate_kbps,
int framerate) {
std::vector<uint32_t> bitrates;
const int num_layers = std::max(1, number_of_temporal_layers_);
for (int 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));
}
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>(bitrates);
// Allocation table is of aggregates, transform to individual rates.
uint32_t sum = 0;
for (int 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(vpx_codec_enc_cfg_t* cfg) {
if (!new_bitrates_kbps_)
return false;
bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
int max_bitrate_kbit,
int framerate,
vpx_codec_enc_cfg_t* cfg) {
switch (number_of_temporal_layers_) {
case 0:
FALLTHROUGH();
case 1:
temporal_ids_length_ = 1;
temporal_ids_[0] = 0;
cfg->ts_number_layers = number_of_temporal_layers_;
cfg->ts_periodicity = temporal_ids_length_;
cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
cfg->ts_target_bitrate[0] = bitrateKbit;
cfg->ts_rate_decimator[0] = 1;
memcpy(cfg->ts_layer_id, temporal_ids_,
sizeof(unsigned int) * temporal_ids_length_);
@ -106,8 +69,8 @@ bool DefaultTemporalLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
cfg->ts_periodicity = temporal_ids_length_;
// Split stream 60% 40%.
// Bitrate API for VP8 is the agregated bitrate for all lower layers.
cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
cfg->ts_target_bitrate[1] = (*new_bitrates_kbps_)[1];
cfg->ts_target_bitrate[0] = bitrateKbit * kVp8LayerRateAlloction[1][0];
cfg->ts_target_bitrate[1] = bitrateKbit;
cfg->ts_rate_decimator[0] = 2;
cfg->ts_rate_decimator[1] = 1;
memcpy(cfg->ts_layer_id, temporal_ids_,
@ -132,9 +95,9 @@ bool DefaultTemporalLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
cfg->ts_periodicity = temporal_ids_length_;
// Split stream 40% 20% 40%.
// Bitrate API for VP8 is the agregated bitrate for all lower layers.
cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
cfg->ts_target_bitrate[1] = (*new_bitrates_kbps_)[1];
cfg->ts_target_bitrate[2] = (*new_bitrates_kbps_)[2];
cfg->ts_target_bitrate[0] = bitrateKbit * kVp8LayerRateAlloction[2][0];
cfg->ts_target_bitrate[1] = bitrateKbit * kVp8LayerRateAlloction[2][1];
cfg->ts_target_bitrate[2] = bitrateKbit;
cfg->ts_rate_decimator[0] = 4;
cfg->ts_rate_decimator[1] = 2;
cfg->ts_rate_decimator[2] = 1;
@ -164,10 +127,10 @@ bool DefaultTemporalLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
// Bitrate API for VP8 is the agregated bitrate for all lower layers.
cfg->ts_number_layers = 4;
cfg->ts_periodicity = temporal_ids_length_;
cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
cfg->ts_target_bitrate[1] = (*new_bitrates_kbps_)[1];
cfg->ts_target_bitrate[2] = (*new_bitrates_kbps_)[2];
cfg->ts_target_bitrate[3] = (*new_bitrates_kbps_)[3];
cfg->ts_target_bitrate[0] = bitrateKbit * kVp8LayerRateAlloction[3][0];
cfg->ts_target_bitrate[1] = bitrateKbit * kVp8LayerRateAlloction[3][1];
cfg->ts_target_bitrate[2] = bitrateKbit * kVp8LayerRateAlloction[3][2];
cfg->ts_target_bitrate[3] = bitrateKbit;
cfg->ts_rate_decimator[0] = 8;
cfg->ts_rate_decimator[1] = 4;
cfg->ts_rate_decimator[2] = 2;
@ -193,12 +156,9 @@ bool DefaultTemporalLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
temporal_pattern_[15] = kTemporalUpdateNone;
break;
default:
RTC_NOTREACHED();
assert(false);
return false;
}
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>();
return true;
}
@ -324,18 +284,8 @@ void DefaultTemporalLayers::PopulateCodecSpecific(
}
TemporalLayers* TemporalLayersFactory::Create(
int simulcast_id,
int temporal_layers,
uint8_t initial_tl0_pic_idx) const {
TemporalLayers* tl =
new DefaultTemporalLayers(temporal_layers, initial_tl0_pic_idx);
if (listener_)
listener_->OnTemporalLayersCreated(simulcast_id, tl);
return tl;
return new DefaultTemporalLayers(temporal_layers, initial_tl0_pic_idx);
}
void TemporalLayersFactory::SetListener(TemporalLayersListener* listener) {
listener_ = listener;
}
} // namespace webrtc

View File

@ -12,12 +12,8 @@
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_DEFAULT_TEMPORAL_LAYERS_H_
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_DEFAULT_TEMPORAL_LAYERS_H_
#include <vector>
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
#include "webrtc/base/optional.h"
namespace webrtc {
class DefaultTemporalLayers : public TemporalLayers {
@ -30,13 +26,10 @@ class DefaultTemporalLayers : public TemporalLayers {
// and/or update the reference buffers.
int EncodeFlags(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;
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override;
bool ConfigureBitrates(int bitrate_kbit,
int max_bitrate_kbit,
int framerate,
vpx_codec_enc_cfg_t* cfg) override;
void PopulateCodecSpecific(bool base_layer_sync,
CodecSpecificInfoVP8* vp8_info,
@ -44,6 +37,8 @@ class DefaultTemporalLayers : public TemporalLayers {
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {}
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override { return false; }
int CurrentLayerId() const override;
private:
@ -82,7 +77,7 @@ class DefaultTemporalLayers : public TemporalLayers {
};
enum { kMaxTemporalPattern = 16 };
const int number_of_temporal_layers_;
int number_of_temporal_layers_;
int temporal_ids_length_;
int temporal_ids_[kMaxTemporalPattern];
int temporal_pattern_length_;
@ -91,7 +86,6 @@ class DefaultTemporalLayers : public TemporalLayers {
uint8_t pattern_idx_;
uint32_t timestamp_;
bool last_base_layer_sync_;
rtc::Optional<std::vector<uint32_t>> new_bitrates_kbps_;
};
} // namespace webrtc

View File

@ -53,8 +53,7 @@ TEST(TemporalLayersTest, 2Layers) {
DefaultTemporalLayers tl(2, 0);
vpx_codec_enc_cfg_t cfg;
CodecSpecificInfoVP8 vp8_info;
tl.OnRatesUpdated(500, 500, 30);
tl.UpdateConfiguration(&cfg);
tl.ConfigureBitrates(500, 500, 30, &cfg);
int expected_flags[16] = {
kTemporalUpdateLastAndGoldenRefAltRef,
@ -95,8 +94,7 @@ TEST(TemporalLayersTest, 3Layers) {
DefaultTemporalLayers tl(3, 0);
vpx_codec_enc_cfg_t cfg;
CodecSpecificInfoVP8 vp8_info;
tl.OnRatesUpdated(500, 500, 30);
tl.UpdateConfiguration(&cfg);
tl.ConfigureBitrates(500, 500, 30, &cfg);
int expected_flags[16] = {
kTemporalUpdateLastAndGoldenRefAltRef,
@ -137,8 +135,7 @@ TEST(TemporalLayersTest, 4Layers) {
DefaultTemporalLayers tl(4, 0);
vpx_codec_enc_cfg_t cfg;
CodecSpecificInfoVP8 vp8_info;
tl.OnRatesUpdated(500, 500, 30);
tl.UpdateConfiguration(&cfg);
tl.ConfigureBitrates(500, 500, 30, &cfg);
int expected_flags[16] = {
kTemporalUpdateLast,
kTemporalUpdateNone,
@ -178,8 +175,7 @@ TEST(TemporalLayersTest, KeyFrame) {
DefaultTemporalLayers tl(3, 0);
vpx_codec_enc_cfg_t cfg;
CodecSpecificInfoVP8 vp8_info;
tl.OnRatesUpdated(500, 500, 30);
tl.UpdateConfiguration(&cfg);
tl.ConfigureBitrates(500, 500, 30, &cfg);
int expected_flags[8] = {
kTemporalUpdateLastAndGoldenRefAltRef,

View File

@ -12,8 +12,6 @@
#include "vpx/vpx_encoder.h"
#include "vpx/vp8cx.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/optional.h"
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
@ -97,20 +95,26 @@ class RealTimeTemporalLayers : public TemporalLayers {
layer_ids_(NULL),
encode_flags_length_(0),
encode_flags_(NULL) {
RTC_CHECK_GE(max_temporal_layers_, 1);
RTC_CHECK_GE(max_temporal_layers_, 3);
assert(max_temporal_layers_ >= 1);
assert(max_temporal_layers_ <= 3);
}
virtual ~RealTimeTemporalLayers() {}
std::vector<uint32_t> OnRatesUpdated(int bitrate_kbps,
int max_bitrate_kbps,
int framerate) override {
bool ConfigureBitrates(int bitrate_kbit,
int max_bitrate_kbit,
int framerate,
vpx_codec_enc_cfg_t* cfg) override {
temporal_layers_ =
CalculateNumberOfTemporalLayers(temporal_layers_, framerate);
temporal_layers_ = std::min(temporal_layers_, max_temporal_layers_);
RTC_CHECK_GE(temporal_layers_, 1);
RTC_CHECK_LE(temporal_layers_, 3);
assert(temporal_layers_ >= 1 && temporal_layers_ <= 3);
cfg->ts_number_layers = temporal_layers_;
for (int tl = 0; tl < temporal_layers_; ++tl) {
cfg->ts_target_bitrate[tl] =
bitrate_kbit * kVp8LayerRateAlloction[temporal_layers_ - 1][tl];
}
switch (temporal_layers_) {
case 1: {
@ -121,6 +125,9 @@ class RealTimeTemporalLayers : public TemporalLayers {
static const int encode_flags[] = {kTemporalUpdateLastRefAll};
encode_flags_length_ = sizeof(encode_flags) / sizeof(*layer_ids);
encode_flags_ = encode_flags;
cfg->ts_rate_decimator[0] = 1;
cfg->ts_periodicity = layer_ids_length_;
} break;
case 2: {
@ -139,6 +146,10 @@ class RealTimeTemporalLayers : public TemporalLayers {
kTemporalUpdateNone};
encode_flags_length_ = sizeof(encode_flags) / sizeof(*layer_ids);
encode_flags_ = encode_flags;
cfg->ts_rate_decimator[0] = 2;
cfg->ts_rate_decimator[1] = 1;
cfg->ts_periodicity = layer_ids_length_;
} break;
case 3: {
@ -157,59 +168,19 @@ class RealTimeTemporalLayers : public TemporalLayers {
kTemporalUpdateNone};
encode_flags_length_ = sizeof(encode_flags) / sizeof(*layer_ids);
encode_flags_ = encode_flags;
cfg->ts_rate_decimator[0] = 4;
cfg->ts_rate_decimator[1] = 2;
cfg->ts_rate_decimator[2] = 1;
cfg->ts_periodicity = layer_ids_length_;
} break;
default:
RTC_NOTREACHED();
return std::vector<uint32_t>();
assert(false);
return false;
}
std::vector<uint32_t> bitrates;
const int num_layers = std::max(1, temporal_layers_);
for (int 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));
}
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>(bitrates);
// Allocation table is of aggregates, transform to individual rates.
uint32_t sum = 0;
for (int 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);
break;
}
}
return bitrates;
}
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override {
if (!new_bitrates_kbps_)
return false;
cfg->ts_number_layers = temporal_layers_;
for (int tl = 0; tl < temporal_layers_; ++tl) {
cfg->ts_target_bitrate[tl] = (*new_bitrates_kbps_)[tl];
}
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>();
cfg->ts_periodicity = layer_ids_length_;
int decimator = 1;
for (int i = temporal_layers_ - 1; i >= 0; --i, decimator *= 2) {
cfg->ts_rate_decimator[i] = decimator;
}
memcpy(cfg->ts_layer_id, layer_ids_,
sizeof(unsigned int) * layer_ids_length_);
return true;
}
@ -277,6 +248,8 @@ class RealTimeTemporalLayers : public TemporalLayers {
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {}
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override { return false; }
private:
int temporal_layers_;
int max_temporal_layers_;
@ -293,19 +266,12 @@ class RealTimeTemporalLayers : public TemporalLayers {
// Pattern of encode flags.
int encode_flags_length_;
const int* encode_flags_;
rtc::Optional<std::vector<uint32_t>> new_bitrates_kbps_;
};
} // namespace
TemporalLayers* RealTimeTemporalLayersFactory::Create(
int simulcast_id,
int max_temporal_layers,
uint8_t initial_tl0_pic_idx) const {
TemporalLayers* tl =
new RealTimeTemporalLayers(max_temporal_layers, initial_tl0_pic_idx);
if (listener_)
listener_->OnTemporalLayersCreated(simulcast_id, tl);
return tl;
return new RealTimeTemporalLayers(max_temporal_layers, initial_tl0_pic_idx);
}
} // namespace webrtc

View File

@ -65,8 +65,7 @@ ScreenshareLayers::ScreenshareLayers(int num_temporal_layers,
min_qp_(-1),
max_qp_(-1),
max_debt_bytes_(0),
framerate_(-1),
bitrate_updated_(false) {
frame_rate_(-1) {
RTC_CHECK_GT(num_temporal_layers, 0);
RTC_CHECK_LE(num_temporal_layers, 2);
}
@ -137,7 +136,7 @@ int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
int64_t ts_diff;
if (last_timestamp_ == -1) {
ts_diff = kOneSecond90Khz / (framerate_ <= 0 ? 5 : framerate_);
ts_diff = kOneSecond90Khz / (frame_rate_ <= 0 ? 5 : frame_rate_);
} else {
ts_diff = unwrapped_timestamp - last_timestamp_;
}
@ -148,19 +147,47 @@ int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
return flags;
}
std::vector<uint32_t> ScreenshareLayers::OnRatesUpdated(int bitrate_kbps,
int max_bitrate_kbps,
int framerate) {
bool ScreenshareLayers::ConfigureBitrates(int bitrate_kbps,
int max_bitrate_kbps,
int framerate,
vpx_codec_enc_cfg_t* cfg) {
layers_[0].target_rate_kbps_ = bitrate_kbps;
layers_[1].target_rate_kbps_ = max_bitrate_kbps;
framerate_ = framerate;
bitrate_updated_ = true;
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;
int target_bitrate_kbps = bitrate_kbps;
if (cfg != nullptr) {
if (number_of_temporal_layers_ > 1) {
// Calculate a codec target bitrate. This may be higher than TL0, gaining
// quality at the expense of frame rate at TL0. Constraints:
// - TL0 frame rate no less than framerate / kMaxTL0FpsReduction.
// - Target rate * kAcceptableTargetOvershoot should not exceed TL1 rate.
target_bitrate_kbps =
std::min(bitrate_kbps * kMaxTL0FpsReduction,
max_bitrate_kbps / kAcceptableTargetOvershoot);
cfg->rc_target_bitrate = std::max(bitrate_kbps, target_bitrate_kbps);
}
// Don't reconfigure qp limits during quality boost frames.
if (active_layer_ == -1 ||
layers_[active_layer_].state != TemporalLayer::State::kQualityBoost) {
min_qp_ = cfg->rc_min_quantizer;
max_qp_ = cfg->rc_max_quantizer;
// After a dropped frame, a frame with max qp will be encoded and the
// quality will then ramp up from there. To boost the speed of recovery,
// encode the next frame with lower max qp. TL0 is the most important to
// improve since the errors in this layer will propagate to TL1.
// Currently, reduce max qp by 20% for TL0 and 15% for TL1.
layers_[0].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 80) / 100);
layers_[1].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 85) / 100);
}
}
int avg_frame_size = (target_bitrate_kbps * 1000) / (8 * framerate);
max_debt_bytes_ = 4 * avg_frame_size;
return true;
}
void ScreenshareLayers::FrameEncoded(unsigned int size,
@ -252,49 +279,9 @@ bool ScreenshareLayers::TimeToSync(int64_t timestamp) const {
}
bool ScreenshareLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
if (bitrate_updated_) {
uint32_t target_bitrate_kbps = layers_[0].target_rate_kbps_;
if (number_of_temporal_layers_ > 1) {
// Calculate a codec target bitrate. This may be higher than TL0, gaining
// quality at the expense of frame rate at TL0. Constraints:
// - TL0 frame rate no less than framerate / kMaxTL0FpsReduction.
// - Target rate * kAcceptableTargetOvershoot should not exceed TL1 rate.
target_bitrate_kbps =
std::min(layers_[0].target_rate_kbps_ * kMaxTL0FpsReduction,
layers_[1].target_rate_kbps_ / kAcceptableTargetOvershoot);
cfg->rc_target_bitrate =
std::max(layers_[0].target_rate_kbps_, target_bitrate_kbps);
}
// Don't reconfigure qp limits during quality boost frames.
if (active_layer_ == -1 ||
layers_[active_layer_].state != TemporalLayer::State::kQualityBoost) {
min_qp_ = cfg->rc_min_quantizer;
max_qp_ = cfg->rc_max_quantizer;
// After a dropped frame, a frame with max qp will be encoded and the
// quality will then ramp up from there. To boost the speed of recovery,
// encode the next frame with lower max qp. TL0 is the most important to
// improve since the errors in this layer will propagate to TL1.
// Currently, reduce max qp by 20% for TL0 and 15% for TL1.
layers_[0].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 80) / 100);
layers_[1].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 85) / 100);
}
int avg_frame_size = (target_bitrate_kbps * 1000) / (8 * framerate_);
max_debt_bytes_ = 4 * avg_frame_size;
bitrate_updated_ = false;
// Don't try to update boosts state if not active yet.
if (active_layer_ == -1)
return true;
}
RTC_DCHECK_NE(-1, active_layer_);
if (max_qp_ == -1 || number_of_temporal_layers_ <= 1)
return false;
RTC_DCHECK_NE(-1, active_layer_);
// If layer is in the quality boost state (following a dropped frame), update
// the configuration with the adjusted (lower) qp and set the state back to

View File

@ -9,7 +9,9 @@
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
#include <vector>
#include <list>
#include "vpx/vpx_encoder.h"
#include "webrtc/base/timeutils.h"
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
@ -39,15 +41,10 @@ class ScreenshareLayers : public TemporalLayers {
// and/or update the reference buffers.
int EncodeFlags(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;
// Update the encoder configuration with target bitrates or other parameters.
// Returns true iff the configuration was actually modified.
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override;
bool ConfigureBitrates(int bitrate_kbps,
int max_bitrate_kbps,
int framerate,
vpx_codec_enc_cfg_t* cfg) override;
void PopulateCodecSpecific(bool base_layer_sync,
CodecSpecificInfoVP8* vp8_info,
@ -57,6 +54,11 @@ class ScreenshareLayers : public TemporalLayers {
int CurrentLayerId() const override;
// Allows the layers adapter to update the encoder configuration prior to a
// frame being encoded. Return true if the configuration should be updated
// and false if now change is needed.
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override;
private:
bool TimeToSync(int64_t timestamp) const;
@ -73,8 +75,7 @@ class ScreenshareLayers : public TemporalLayers {
int min_qp_;
int max_qp_;
uint32_t max_debt_bytes_;
int framerate_;
bool bitrate_updated_;
int frame_rate_;
static const int kMaxNumTemporalLayers = 2;
struct TemporalLayer {

View File

@ -22,7 +22,6 @@
#include "webrtc/test/gtest.h"
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::NiceMock;
using ::testing::Return;
@ -62,11 +61,8 @@ class ScreenshareLayerTest : public ::testing::Test {
memset(&vpx_cfg, 0, sizeof(vpx_codec_enc_cfg_t));
vpx_cfg.rc_min_quantizer = min_qp_;
vpx_cfg.rc_max_quantizer = max_qp_;
EXPECT_THAT(layers_->OnRatesUpdated(kDefaultTl0BitrateKbps,
kDefaultTl1BitrateKbps, kFrameRate),
ElementsAre(kDefaultTl0BitrateKbps,
kDefaultTl1BitrateKbps - kDefaultTl0BitrateKbps));
EXPECT_TRUE(layers_->UpdateConfiguration(&vpx_cfg));
EXPECT_TRUE(layers_->ConfigureBitrates(
kDefaultTl0BitrateKbps, kDefaultTl1BitrateKbps, kFrameRate, &vpx_cfg));
frame_size_ = ((vpx_cfg.rc_target_bitrate * 1000) / 8) / kFrameRate;
}
@ -377,41 +373,27 @@ TEST_F(ScreenshareLayerTest, TooHighBitrate) {
TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL0) {
vpx_codec_enc_cfg_t cfg = GetConfig();
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));
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
layers_->ConfigureBitrates(100, 1000, 5, &cfg);
EXPECT_EQ(static_cast<unsigned int>(
ScreenshareLayers::kMaxTL0FpsReduction * kTl0_kbps + 0.5),
ScreenshareLayers::kMaxTL0FpsReduction * 100 + 0.5),
cfg.rc_target_bitrate);
}
TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL1) {
vpx_codec_enc_cfg_t cfg = GetConfig();
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));
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
layers_->ConfigureBitrates(100, 450, 5, &cfg);
EXPECT_EQ(static_cast<unsigned int>(
kTl1_kbps / ScreenshareLayers::kAcceptableTargetOvershoot),
450 / ScreenshareLayers::kAcceptableTargetOvershoot),
cfg.rc_target_bitrate);
}
TEST_F(ScreenshareLayerTest, TargetBitrateBelowTL0) {
vpx_codec_enc_cfg_t cfg = GetConfig();
const int kTl0_kbps = 100;
const int kTl1_kbps = 100;
EXPECT_THAT(layers_->OnRatesUpdated(kTl0_kbps, kTl1_kbps, 5),
ElementsAre(kTl0_kbps));
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
layers_->ConfigureBitrates(100, 100, 5, &cfg);
EXPECT_EQ(static_cast<uint32_t>(kTl1_kbps), cfg.rc_target_bitrate);
EXPECT_EQ(100U, cfg.rc_target_bitrate);
}
TEST_F(ScreenshareLayerTest, EncoderDrop) {
@ -471,8 +453,7 @@ TEST_F(ScreenshareLayerTest, RespectsMaxIntervalBetweenFrames) {
const uint32_t kStartTimestamp = 1234;
vpx_codec_enc_cfg_t cfg = GetConfig();
layers_->OnRatesUpdated(kLowBitrateKbps, kLowBitrateKbps, 5);
layers_->UpdateConfiguration(&cfg);
layers_->ConfigureBitrates(kLowBitrateKbps, kLowBitrateKbps, 5, &cfg);
EXPECT_EQ(ScreenshareLayers::kTl0Flags,
layers_->EncodeFlags(kStartTimestamp));

View File

@ -83,6 +83,17 @@ int VerifyCodec(const webrtc::VideoCodec* inst) {
return WEBRTC_VIDEO_CODEC_OK;
}
struct ScreenshareTemporalLayersFactory : webrtc::TemporalLayersFactory {
ScreenshareTemporalLayersFactory() {}
virtual ~ScreenshareTemporalLayersFactory() {}
virtual webrtc::TemporalLayers* Create(int num_temporal_layers,
uint8_t initial_tl0_pic_idx) const {
return new webrtc::ScreenshareLayers(num_temporal_layers, rand(),
webrtc::Clock::GetRealTimeClock());
}
};
// An EncodedImageCallback implementation that forwards on calls to a
// SimulcastEncoderAdapter, but with the stream index it's registered with as
// the first parameter to Encoded.
@ -105,25 +116,6 @@ class AdapterEncodedImageCallback : public webrtc::EncodedImageCallback {
const size_t stream_idx_;
};
// Utility class used to adapt the simulcast id as reported by the temporal
// layers factory, since each sub-encoder will report stream 0.
class TemporalLayersFactoryAdapter : public webrtc::TemporalLayersFactory {
public:
TemporalLayersFactoryAdapter(int adapted_simulcast_id,
const TemporalLayersFactory& tl_factory)
: adapted_simulcast_id_(adapted_simulcast_id), tl_factory_(tl_factory) {}
~TemporalLayersFactoryAdapter() override {}
webrtc::TemporalLayers* Create(int simulcast_id,
int temporal_layers,
uint8_t initial_tl0_pic_idx) const override {
return tl_factory_.Create(adapted_simulcast_id_, temporal_layers,
initial_tl0_pic_idx);
}
const int adapted_simulcast_id_;
const TemporalLayersFactory& tl_factory_;
};
} // namespace
namespace webrtc {
@ -133,6 +125,7 @@ SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory)
encoded_complete_callback_(nullptr),
implementation_name_("SimulcastEncoderAdapter") {
memset(&codec_, 0, sizeof(webrtc::VideoCodec));
rate_allocator_.reset(new SimulcastRateAllocator(codec_));
}
SimulcastEncoderAdapter::~SimulcastEncoderAdapter() {
@ -180,13 +173,14 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst,
}
codec_ = *inst;
SimulcastRateAllocator rate_allocator(codec_, nullptr);
BitrateAllocation allocation = rate_allocator.GetAllocation(
codec_.startBitrate * 1000, codec_.maxFramerate);
std::vector<uint32_t> start_bitrates;
for (int i = 0; i < kMaxSimulcastStreams; ++i) {
uint32_t stream_bitrate = allocation.GetSpatialLayerSum(i) / 1000;
start_bitrates.push_back(stream_bitrate);
rate_allocator_.reset(new SimulcastRateAllocator(codec_));
std::vector<uint32_t> start_bitrates =
rate_allocator_->GetAllocation(codec_.startBitrate);
// Special mode when screensharing on a single stream.
if (number_of_streams == 1 && inst->mode == kScreensharing) {
screensharing_tl_factory_.reset(new ScreenshareTemporalLayersFactory());
codec_.VP8()->tl_factory = screensharing_tl_factory_.get();
}
std::string implementation_name;
@ -206,9 +200,6 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst,
PopulateStreamCodec(&codec_, i, start_bitrate_kbps,
highest_resolution_stream, &stream_codec);
}
TemporalLayersFactoryAdapter tl_factory_adapter(
i, *codec_.codecSpecific.VP8.tl_factory);
stream_codec.codecSpecific.VP8.tl_factory = &tl_factory_adapter;
// TODO(ronghuawu): Remove once this is handled in VP8EncoderImpl.
if (stream_codec.qpMax < kDefaultMinQp) {
@ -349,48 +340,61 @@ int SimulcastEncoderAdapter::SetChannelParameters(uint32_t packet_loss,
return WEBRTC_VIDEO_CODEC_OK;
}
int SimulcastEncoderAdapter::SetRateAllocation(const BitrateAllocation& bitrate,
uint32_t new_framerate) {
if (!Initialized())
int SimulcastEncoderAdapter::SetRates(uint32_t new_bitrate_kbit,
uint32_t new_framerate) {
if (!Initialized()) {
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
if (new_framerate < 1)
}
if (new_framerate < 1) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
if (codec_.maxBitrate > 0 && bitrate.get_sum_kbps() > codec_.maxBitrate)
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
if (bitrate.get_sum_bps() > 0) {
// Make sure the bitrate fits the configured min bitrates. 0 is a special
// value that means paused, though, so leave it alone.
if (bitrate.get_sum_kbps() < codec_.minBitrate)
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
if (codec_.numberOfSimulcastStreams > 0 &&
bitrate.get_sum_kbps() < codec_.simulcastStream[0].minBitrate) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
}
if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) {
new_bitrate_kbit = codec_.maxBitrate;
}
std::vector<uint32_t> stream_bitrates;
if (new_bitrate_kbit > 0) {
// Make sure the bitrate fits the configured min bitrates. 0 is a special
// value that means paused, though, so leave it alone.
if (new_bitrate_kbit < codec_.minBitrate) {
new_bitrate_kbit = codec_.minBitrate;
}
if (codec_.numberOfSimulcastStreams > 0 &&
new_bitrate_kbit < codec_.simulcastStream[0].minBitrate) {
new_bitrate_kbit = codec_.simulcastStream[0].minBitrate;
}
stream_bitrates = rate_allocator_->GetAllocation(new_bitrate_kbit);
}
codec_.maxFramerate = new_framerate;
for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
uint32_t stream_bitrate_kbps =
bitrate.GetSpatialLayerSum(stream_idx) / 1000;
// Disable any stream not in the current allocation.
stream_bitrates.resize(streaminfos_.size(), 0U);
for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
uint32_t stream_bitrate_kbps = stream_bitrates[stream_idx];
// Need a key frame if we have not sent this stream before.
if (stream_bitrate_kbps > 0 && !streaminfos_[stream_idx].send_stream) {
streaminfos_[stream_idx].key_frame_request = true;
}
streaminfos_[stream_idx].send_stream = stream_bitrate_kbps > 0;
// Slice the temporal layers out of the full allocation and pass it on to
// the encoder handling the current simulcast stream.
BitrateAllocation stream_allocation;
for (int i = 0; i < kMaxTemporalStreams; ++i)
stream_allocation.SetBitrate(0, i, bitrate.GetBitrate(stream_idx, i));
streaminfos_[stream_idx].encoder->SetRateAllocation(stream_allocation,
new_framerate);
// 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
// the target we still allow it to overshoot up to the max before dropping
// frames. This hack should be improved.
if (codec_.targetBitrate > 0 &&
(codec_.VP8()->numberOfTemporalLayers == 2 ||
codec_.simulcastStream[0].numberOfTemporalLayers == 2)) {
stream_bitrate_kbps = std::min(codec_.maxBitrate, stream_bitrate_kbps);
// TODO(ronghuawu): Can't change max bitrate via the VideoEncoder
// interface. And VP8EncoderImpl doesn't take negative framerate.
// max_bitrate = std::min(codec_.maxBitrate, stream_bitrate_kbps);
// new_framerate = -1;
}
streaminfos_[stream_idx].encoder->SetRates(stream_bitrate_kbps,
new_framerate);
}
return WEBRTC_VIDEO_CODEC_OK;

View File

@ -48,8 +48,7 @@ class SimulcastEncoderAdapter : public VP8Encoder {
const std::vector<FrameType>* frame_types) override;
int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) override;
int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
int SetRateAllocation(const BitrateAllocation& bitrate,
uint32_t new_framerate) override;
int SetRates(uint32_t new_bitrate_kbit, uint32_t new_framerate) override;
// Eventual handler for the contained encoders' EncodedImageCallbacks, but
// called from an internal helper that also knows the correct stream
@ -104,10 +103,12 @@ class SimulcastEncoderAdapter : public VP8Encoder {
bool Initialized() const;
std::unique_ptr<VideoEncoderFactory> factory_;
std::unique_ptr<TemporalLayersFactory> screensharing_tl_factory_;
VideoCodec codec_;
std::vector<StreamInfo> streaminfos_;
EncodedImageCallback* encoded_complete_callback_;
std::string implementation_name_;
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
};
} // namespace webrtc

View File

@ -95,6 +95,12 @@ TEST_F(TestSimulcastEncoderAdapter, TestSpatioTemporalLayers321PatternEncoder) {
TestVp8Simulcast::TestSpatioTemporalLayers321PatternEncoder();
}
// TODO(ronghuawu): Enable this test when SkipEncodingUnusedStreams option is
// implemented for SimulcastEncoderAdapter.
TEST_F(TestSimulcastEncoderAdapter, DISABLED_TestSkipEncodingUnusedStreams) {
TestVp8Simulcast::TestSkipEncodingUnusedStreams();
}
TEST_F(TestSimulcastEncoderAdapter, DISABLED_TestRPSIEncoder) {
TestVp8Simulcast::TestRPSIEncoder();
}
@ -126,9 +132,8 @@ class MockVideoEncoder : public VideoEncoder {
int32_t Release() /* override */ { return 0; }
int32_t SetRateAllocation(const BitrateAllocation& bitrate_allocation,
uint32_t framerate) {
last_set_bitrate_ = bitrate_allocation;
int32_t SetRates(uint32_t newBitRate, uint32_t frameRate) /* override */ {
last_set_bitrate_ = static_cast<int32_t>(newBitRate);
return 0;
}
@ -155,13 +160,13 @@ class MockVideoEncoder : public VideoEncoder {
void set_supports_native_handle(bool enabled) {
supports_native_handle_ = enabled;
}
BitrateAllocation last_set_bitrate() const { return last_set_bitrate_; }
int32_t last_set_bitrate() const { return last_set_bitrate_; }
MOCK_CONST_METHOD0(ImplementationName, const char*());
private:
bool supports_native_handle_ = false;
BitrateAllocation last_set_bitrate_;
int32_t last_set_bitrate_ = -1;
VideoCodec codec_;
EncodedImageCallback* callback_;
@ -268,9 +273,6 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test,
void SetupCodec() {
TestVp8Simulcast::DefaultSettings(
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
rate_allocator_.reset(new SimulcastRateAllocator(codec_, nullptr));
tl_factory_.SetListener(rate_allocator_.get());
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
adapter_->RegisterEncodeCompleteCallback(this);
}
@ -299,6 +301,7 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test,
EXPECT_EQ(ref.VP8().automaticResizeOn, target.VP8().automaticResizeOn);
EXPECT_EQ(ref.VP8().frameDroppingOn, target.VP8().frameDroppingOn);
EXPECT_EQ(ref.VP8().keyFrameInterval, target.VP8().keyFrameInterval);
EXPECT_EQ(ref.VP8().tl_factory, target.VP8().tl_factory);
EXPECT_EQ(ref.qpMax, target.qpMax);
EXPECT_EQ(0, target.numberOfSimulcastStreams);
EXPECT_EQ(ref.mode, target.mode);
@ -311,7 +314,6 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test,
*ref_codec = codec_;
ref_codec->VP8()->numberOfTemporalLayers =
kTestTemporalLayerProfile[stream_index];
ref_codec->codecSpecific.VP8.tl_factory = &tl_factory_;
ref_codec->width = codec_.simulcastStream[stream_index].width;
ref_codec->height = codec_.simulcastStream[stream_index].height;
ref_codec->maxBitrate = codec_.simulcastStream[stream_index].maxBitrate;
@ -355,8 +357,6 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test,
int last_encoded_image_width_;
int last_encoded_image_height_;
int last_encoded_image_simulcast_index_;
TemporalLayersFactory tl_factory_;
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
};
TEST_F(TestSimulcastEncoderAdapterFake, InitEncode) {
@ -376,7 +376,7 @@ TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) {
SetupCodec();
// Set bitrates so that we send all layers.
adapter_->SetRateAllocation(rate_allocator_->GetAllocation(1200, 30), 30);
adapter_->SetRates(1200, 30);
// At this point, the simulcast encoder adapter should have 3 streams: HD,
// quarter HD, and quarter quarter HD. We're going to mostly ignore the exact
@ -409,7 +409,6 @@ TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) {
TEST_F(TestSimulcastEncoderAdapterFake, SupportsNativeHandleForSingleStreams) {
TestVp8Simulcast::DefaultSettings(
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
codec_.numberOfSimulcastStreams = 1;
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
adapter_->RegisterEncodeCompleteCallback(this);
@ -423,37 +422,27 @@ TEST_F(TestSimulcastEncoderAdapterFake, SupportsNativeHandleForSingleStreams) {
TEST_F(TestSimulcastEncoderAdapterFake, SetRatesUnderMinBitrate) {
TestVp8Simulcast::DefaultSettings(
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
codec_.minBitrate = 50;
codec_.numberOfSimulcastStreams = 1;
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
rate_allocator_.reset(new SimulcastRateAllocator(codec_, nullptr));
// Above min should be respected.
BitrateAllocation target_bitrate =
rate_allocator_->GetAllocation(codec_.minBitrate * 1000, 30);
adapter_->SetRateAllocation(target_bitrate, 30);
EXPECT_EQ(target_bitrate,
helper_->factory()->encoders()[0]->last_set_bitrate());
adapter_->SetRates(100, 30);
EXPECT_EQ(100, helper_->factory()->encoders()[0]->last_set_bitrate());
// Below min but non-zero should be replaced with the min bitrate.
BitrateAllocation too_low_bitrate =
rate_allocator_->GetAllocation((codec_.minBitrate - 1) * 1000, 30);
adapter_->SetRateAllocation(too_low_bitrate, 30);
EXPECT_EQ(target_bitrate,
helper_->factory()->encoders()[0]->last_set_bitrate());
adapter_->SetRates(15, 30);
EXPECT_EQ(50, helper_->factory()->encoders()[0]->last_set_bitrate());
// Zero should be passed on as is, since it means "pause".
adapter_->SetRateAllocation(BitrateAllocation(), 30);
EXPECT_EQ(BitrateAllocation(),
helper_->factory()->encoders()[0]->last_set_bitrate());
adapter_->SetRates(0, 30);
EXPECT_EQ(0, helper_->factory()->encoders()[0]->last_set_bitrate());
}
TEST_F(TestSimulcastEncoderAdapterFake, SupportsImplementationName) {
EXPECT_STREQ("SimulcastEncoderAdapter", adapter_->ImplementationName());
TestVp8Simulcast::DefaultSettings(
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
std::vector<const char*> encoder_names;
encoder_names.push_back("codec1");
encoder_names.push_back("codec2");
@ -476,7 +465,6 @@ TEST_F(TestSimulcastEncoderAdapterFake,
SupportsNativeHandleForMultipleStreams) {
TestVp8Simulcast::DefaultSettings(
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
codec_.numberOfSimulcastStreams = 3;
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
adapter_->RegisterEncodeCompleteCallback(this);
@ -505,7 +493,6 @@ TEST_F(TestSimulcastEncoderAdapterFake,
NativeHandleForwardingForMultipleStreams) {
TestVp8Simulcast::DefaultSettings(
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
codec_.numberOfSimulcastStreams = 3;
// High start bitrate, so all streams are enabled.
codec_.startBitrate = 3000;
@ -530,7 +517,6 @@ TEST_F(TestSimulcastEncoderAdapterFake,
TEST_F(TestSimulcastEncoderAdapterFake, TestFailureReturnCodesFromEncodeCalls) {
TestVp8Simulcast::DefaultSettings(
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
codec_.numberOfSimulcastStreams = 3;
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
adapter_->RegisterEncodeCompleteCallback(this);

View File

@ -86,5 +86,10 @@ TEST_F(TestVp8Impl, TestSpatioTemporalLayers321PatternEncoder) {
TEST_F(TestVp8Impl, TestStrideEncodeDecode) {
TestVp8Simulcast::TestStrideEncodeDecode();
}
TEST_F(TestVp8Impl, TestSkipEncodingUnusedStreams) {
TestVp8Simulcast::TestSkipEncodingUnusedStreams();
}
} // namespace testing
} // namespace webrtc

View File

@ -12,7 +12,6 @@
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SIMULCAST_UNITTEST_H_
#include <algorithm>
#include <map>
#include <memory>
#include <vector>
@ -21,7 +20,6 @@
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
#include "webrtc/modules/video_coding/include/mock/mock_video_codec_interface.h"
#include "webrtc/modules/video_coding/utility/simulcast_rate_allocator.h"
#include "webrtc/test/gtest.h"
#include "webrtc/video_frame.h"
@ -149,6 +147,83 @@ class Vp8TestDecodedImageCallback : public DecodedImageCallback {
int decoded_frames_;
};
class SkipEncodingUnusedStreamsTest {
public:
std::vector<unsigned int> RunTest(VP8Encoder* encoder,
VideoCodec* settings,
uint32_t target_bitrate) {
SpyingTemporalLayersFactory spy_factory;
settings->VP8()->tl_factory = &spy_factory;
EXPECT_EQ(0, encoder->InitEncode(settings, 1, 1200));
encoder->SetRates(target_bitrate, 30);
std::vector<unsigned int> configured_bitrates;
for (std::vector<TemporalLayers*>::const_iterator it =
spy_factory.spying_layers_.begin();
it != spy_factory.spying_layers_.end(); ++it) {
configured_bitrates.push_back(
static_cast<SpyingTemporalLayers*>(*it)->configured_bitrate_);
}
return configured_bitrates;
}
class SpyingTemporalLayers : public TemporalLayers {
public:
explicit SpyingTemporalLayers(TemporalLayers* layers)
: configured_bitrate_(0), layers_(layers) {}
virtual ~SpyingTemporalLayers() { delete layers_; }
int EncodeFlags(uint32_t timestamp) override {
return layers_->EncodeFlags(timestamp);
}
bool ConfigureBitrates(int bitrate_kbit,
int max_bitrate_kbit,
int framerate,
vpx_codec_enc_cfg_t* cfg) override {
configured_bitrate_ = bitrate_kbit;
return layers_->ConfigureBitrates(bitrate_kbit, max_bitrate_kbit,
framerate, cfg);
}
void PopulateCodecSpecific(bool base_layer_sync,
CodecSpecificInfoVP8* vp8_info,
uint32_t timestamp) override {
layers_->PopulateCodecSpecific(base_layer_sync, vp8_info, timestamp);
}
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {
layers_->FrameEncoded(size, timestamp, qp);
}
int CurrentLayerId() const override { return layers_->CurrentLayerId(); }
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override {
return false;
}
int configured_bitrate_;
TemporalLayers* layers_;
};
class SpyingTemporalLayersFactory : public TemporalLayersFactory {
public:
virtual ~SpyingTemporalLayersFactory() {}
TemporalLayers* Create(int temporal_layers,
uint8_t initial_tl0_pic_idx) const override {
SpyingTemporalLayers* layers =
new SpyingTemporalLayers(TemporalLayersFactory::Create(
temporal_layers, initial_tl0_pic_idx));
spying_layers_.push_back(layers);
return layers;
}
mutable std::vector<TemporalLayers*> spying_layers_;
};
};
class TestVp8Simulcast : public ::testing::Test {
public:
TestVp8Simulcast(VP8Encoder* encoder, VP8Decoder* decoder)
@ -190,7 +265,7 @@ class TestVp8Simulcast : public ::testing::Test {
static void DefaultSettings(VideoCodec* settings,
const int* temporal_layer_profile) {
RTC_CHECK(settings);
assert(settings);
memset(settings, 0, sizeof(VideoCodec));
strncpy(settings->plName, "VP8", 4);
settings->codecType = kVideoCodecVP8;
@ -240,18 +315,12 @@ class TestVp8Simulcast : public ::testing::Test {
}
protected:
void SetUp() override { SetUpCodec(kDefaultTemporalLayerProfile); }
virtual void SetUp() { SetUpCodec(kDefaultTemporalLayerProfile); }
void TearDown() override {
encoder_->Release();
decoder_->Release();
}
void SetUpCodec(const int* temporal_layer_profile) {
virtual void SetUpCodec(const int* temporal_layer_profile) {
encoder_->RegisterEncodeCompleteCallback(&encoder_callback_);
decoder_->RegisterDecodeCompleteCallback(&decoder_callback_);
DefaultSettings(&settings_, temporal_layer_profile);
SetUpRateAllocator();
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
EXPECT_EQ(0, decoder_->InitDecode(&settings_, 1));
int half_width = (kDefaultWidth + 1) / 2;
@ -262,16 +331,9 @@ class TestVp8Simulcast : public ::testing::Test {
new VideoFrame(input_buffer_, 0, 0, webrtc::kVideoRotation_0));
}
void SetUpRateAllocator() {
TemporalLayersFactory* tl_factory = new TemporalLayersFactory();
rate_allocator_.reset(new SimulcastRateAllocator(
settings_, std::unique_ptr<TemporalLayersFactory>(tl_factory)));
settings_.codecSpecific.VP8.tl_factory = tl_factory;
}
void SetRates(uint32_t bitrate_kbps, uint32_t fps) {
encoder_->SetRateAllocation(
rate_allocator_->GetAllocation(bitrate_kbps * 1000, fps), fps);
virtual void TearDown() {
encoder_->Release();
decoder_->Release();
}
void ExpectStreams(FrameType frame_type, int expected_video_streams) {
@ -334,7 +396,7 @@ class TestVp8Simulcast : public ::testing::Test {
// We currently expect all active streams to generate a key frame even though
// a key frame was only requested for some of them.
void TestKeyFrameRequestsOnAllStreams() {
SetRates(kMaxBitrates[2], 30); // To get all three streams.
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, kNumberOfSimulcastStreams);
@ -369,7 +431,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestPaddingAllStreams() {
// We should always encode the base layer.
SetRates(kMinBitrates[0] - 1, 30);
encoder_->SetRates(kMinBitrates[0] - 1, 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 1);
@ -382,7 +444,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestPaddingTwoStreams() {
// We have just enough to get only the first stream and padding for two.
SetRates(kMinBitrates[0], 30);
encoder_->SetRates(kMinBitrates[0], 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 1);
@ -396,7 +458,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestPaddingTwoStreamsOneMaxedOut() {
// We are just below limit of sending second stream, so we should get
// the first stream maxed out (at |maxBitrate|), and padding for two.
SetRates(kTargetBitrates[0] + kMinBitrates[1] - 1, 30);
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1] - 1, 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 1);
@ -409,7 +471,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestPaddingOneStream() {
// We have just enough to send two streams, so padding for one stream.
SetRates(kTargetBitrates[0] + kMinBitrates[1], 30);
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1], 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 2);
@ -423,7 +485,8 @@ class TestVp8Simulcast : public ::testing::Test {
void TestPaddingOneStreamTwoMaxedOut() {
// We are just below limit of sending third stream, so we should get
// first stream's rate maxed out at |targetBitrate|, second at |maxBitrate|.
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] - 1, 30);
encoder_->SetRates(
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] - 1, 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 2);
@ -436,7 +499,8 @@ class TestVp8Simulcast : public ::testing::Test {
void TestSendAllStreams() {
// We have just enough to send all streams.
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2], 30);
encoder_->SetRates(
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2], 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 3);
@ -449,7 +513,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestDisablingStreams() {
// We should get three media streams.
SetRates(kMaxBitrates[0] + kMaxBitrates[1] + kMaxBitrates[2], 30);
encoder_->SetRates(kMaxBitrates[0] + kMaxBitrates[1] + kMaxBitrates[2], 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 3);
@ -460,33 +524,36 @@ class TestVp8Simulcast : public ::testing::Test {
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
// We should only get two streams and padding for one.
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] / 2, 30);
encoder_->SetRates(
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] / 2, 30);
ExpectStreams(kVideoFrameDelta, 2);
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
// We should only get the first stream and padding for two.
SetRates(kTargetBitrates[0] + kMinBitrates[1] / 2, 30);
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1] / 2, 30);
ExpectStreams(kVideoFrameDelta, 1);
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
// We don't have enough bitrate for the thumbnail stream, but we should get
// it anyway with current configuration.
SetRates(kTargetBitrates[0] - 1, 30);
encoder_->SetRates(kTargetBitrates[0] - 1, 30);
ExpectStreams(kVideoFrameDelta, 1);
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
// We should only get two streams and padding for one.
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] / 2, 30);
encoder_->SetRates(
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] / 2, 30);
// We get a key frame because a new stream is being enabled.
ExpectStreams(kVideoFrameKey, 2);
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, &frame_types));
// We should get all three streams.
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kTargetBitrates[2], 30);
encoder_->SetRates(
kTargetBitrates[0] + kTargetBitrates[1] + kTargetBitrates[2], 30);
// We get a key frame because a new stream is being enabled.
ExpectStreams(kVideoFrameKey, 3);
input_frame_->set_timestamp(input_frame_->timestamp() + 3000);
@ -523,11 +590,10 @@ class TestVp8Simulcast : public ::testing::Test {
settings_.width;
settings_.simulcastStream[settings_.numberOfSimulcastStreams - 1].height =
settings_.height;
SetUpRateAllocator();
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
// Encode one frame and verify.
SetRates(kMaxBitrates[0] + kMaxBitrates[1], 30);
encoder_->SetRates(kMaxBitrates[0] + kMaxBitrates[1], 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
EXPECT_CALL(
@ -545,9 +611,8 @@ class TestVp8Simulcast : public ::testing::Test {
DefaultSettings(&settings_, kDefaultTemporalLayerProfile);
// Start at the lowest bitrate for enabling base stream.
settings_.startBitrate = kMinBitrates[0];
SetUpRateAllocator();
EXPECT_EQ(0, encoder_->InitEncode(&settings_, 1, 1200));
SetRates(settings_.startBitrate, 30);
encoder_->SetRates(settings_.startBitrate, 30);
ExpectStreams(kVideoFrameKey, 1);
// Resize |input_frame_| to the new resolution.
half_width = (settings_.width + 1) / 2;
@ -569,7 +634,7 @@ class TestVp8Simulcast : public ::testing::Test {
Vp8TestEncodedImageCallback encoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
SetRates(kMaxBitrates[2], 30); // To get all three streams.
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, NULL));
int picture_id = -1;
@ -638,7 +703,7 @@ class TestVp8Simulcast : public ::testing::Test {
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
decoder_->RegisterDecodeCompleteCallback(&decoder_callback);
SetRates(kMaxBitrates[2], 30); // To get all three streams.
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
// Set color.
int plane_offset[kNumOfPlanes];
@ -712,7 +777,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestSaptioTemporalLayers333PatternEncoder() {
Vp8TestEncodedImageCallback encoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
SetRates(kMaxBitrates[2], 30); // To get all three streams.
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
int expected_temporal_idx[3] = {-1, -1, -1};
bool expected_layer_sync[3] = {false, false, false};
@ -781,7 +846,7 @@ class TestVp8Simulcast : public ::testing::Test {
SetUpCodec(temporal_layer_profile);
Vp8TestEncodedImageCallback encoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
SetRates(kMaxBitrates[2], 30); // To get all three streams.
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
int expected_temporal_idx[3] = {-1, -1, -1};
bool expected_layer_sync[3] = {false, false, false};
@ -840,7 +905,7 @@ class TestVp8Simulcast : public ::testing::Test {
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
decoder_->RegisterDecodeCompleteCallback(&decoder_callback);
SetRates(kMaxBitrates[2], 30); // To get all three streams.
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
// Setting two (possibly) problematic use cases for stride:
// 1. stride > width 2. stride_y != stride_uv/2
int stride_y = kDefaultWidth + 20;
@ -876,6 +941,30 @@ class TestVp8Simulcast : public ::testing::Test {
EXPECT_EQ(2, decoder_callback.DecodedFrames());
}
void TestSkipEncodingUnusedStreams() {
SkipEncodingUnusedStreamsTest test;
std::vector<unsigned int> configured_bitrate =
test.RunTest(encoder_.get(), &settings_,
1); // Target bit rate 1, to force all streams but the
// base one to be exceeding bandwidth constraints.
EXPECT_EQ(static_cast<size_t>(kNumberOfSimulcastStreams),
configured_bitrate.size());
unsigned int min_bitrate =
std::max(settings_.simulcastStream[0].minBitrate, settings_.minBitrate);
int stream = 0;
for (std::vector<unsigned int>::const_iterator it =
configured_bitrate.begin();
it != configured_bitrate.end(); ++it) {
if (stream == 0) {
EXPECT_EQ(min_bitrate, *it);
} else {
EXPECT_EQ(0u, *it);
}
++stream;
}
}
std::unique_ptr<VP8Encoder> encoder_;
MockEncodedImageCallback encoder_callback_;
std::unique_ptr<VP8Decoder> decoder_;
@ -883,7 +972,6 @@ class TestVp8Simulcast : public ::testing::Test {
VideoCodec settings_;
rtc::scoped_refptr<I420Buffer> input_buffer_;
std::unique_ptr<VideoFrame> input_frame_;
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
};
} // namespace testing

View File

@ -12,21 +12,18 @@
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
#include <vector>
#include "vpx/vpx_encoder.h"
#include "webrtc/common_video/include/video_image.h"
#include "webrtc/typedefs.h"
struct vpx_codec_enc_cfg;
typedef struct vpx_codec_enc_cfg vpx_codec_enc_cfg_t;
namespace webrtc {
struct CodecSpecificInfoVP8;
class TemporalLayers {
public:
// Factory for TemporalLayer strategy. Default behavior is a fixed pattern
// Factory for TemporalLayer strategy. Default behaviour is a fixed pattern
// of temporal layers. See default_temporal_layers.cc
virtual ~TemporalLayers() {}
@ -34,15 +31,10 @@ class TemporalLayers {
// and/or update the reference buffers.
virtual int EncodeFlags(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;
// Update the encoder configuration with target bitrates or other parameters.
// Returns true iff the configuration was actually modified.
virtual bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) = 0;
virtual bool ConfigureBitrates(int bitrate_kbit,
int max_bitrate_kbit,
int framerate,
vpx_codec_enc_cfg_t* cfg) = 0;
virtual void PopulateCodecSpecific(bool base_layer_sync,
CodecSpecificInfoVP8* vp8_info,
@ -51,42 +43,26 @@ class TemporalLayers {
virtual void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) = 0;
virtual int CurrentLayerId() const = 0;
virtual bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) = 0;
};
class TemporalLayersListener;
class TemporalLayersFactory {
public:
TemporalLayersFactory() : listener_(nullptr) {}
virtual ~TemporalLayersFactory() {}
virtual TemporalLayers* Create(int simulcast_id,
int temporal_layers,
virtual TemporalLayers* Create(int temporal_layers,
uint8_t initial_tl0_pic_idx) const;
void SetListener(TemporalLayersListener* listener);
protected:
TemporalLayersListener* listener_;
};
// Factory for a temporal layers strategy that adaptively changes the number of
// layers based on input frame rate so that the base layer has an acceptable
// frame rate. See realtime_temporal_layers.cc
// layers based on input framerate so that the base layer has an acceptable
// framerate. See realtime_temporal_layers.cc
class RealTimeTemporalLayersFactory : public TemporalLayersFactory {
public:
RealTimeTemporalLayersFactory() {}
~RealTimeTemporalLayersFactory() override {}
TemporalLayers* Create(int simulcast_id,
int num_temporal_layers,
TemporalLayers* Create(int num_temporal_layers,
uint8_t initial_tl0_pic_idx) const override;
};
class TemporalLayersListener {
public:
TemporalLayersListener() {}
virtual ~TemporalLayersListener() {}
virtual void OnTemporalLayersCreated(int simulcast_id,
TemporalLayers* layers) = 0;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_

View File

@ -16,7 +16,6 @@
#include "webrtc/base/timeutils.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
#include "webrtc/test/frame_utils.h"
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"
@ -162,8 +161,6 @@ class TestVp8Impl : public ::testing::Test {
codec_inst_.maxBitrate = 4000;
codec_inst_.qpMax = 56;
codec_inst_.VP8()->denoisingOn = true;
codec_inst_.VP8()->tl_factory = &tl_factory_;
codec_inst_.VP8()->numberOfTemporalLayers = 1;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
encoder_->InitEncode(&codec_inst_, 1, 1440));
@ -204,7 +201,6 @@ class TestVp8Impl : public ::testing::Test {
EncodedImage encoded_frame_;
VideoFrame decoded_frame_;
VideoCodec codec_inst_;
TemporalLayersFactory tl_factory_;
};
TEST_F(TestVp8Impl, EncoderParameterTest) {
@ -219,15 +215,11 @@ TEST_F(TestVp8Impl, EncoderParameterTest) {
codec_inst_.qpMax = 56;
codec_inst_.VP8()->complexity = kComplexityNormal;
codec_inst_.VP8()->numberOfTemporalLayers = 1;
codec_inst_.VP8()->tl_factory = &tl_factory_;
// Calls before InitEncode().
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release());
int bit_rate = 300;
BitrateAllocation bitrate_allocation;
bitrate_allocation.SetBitrate(0, 0, bit_rate * 1000);
EXPECT_EQ(WEBRTC_VIDEO_CODEC_UNINITIALIZED,
encoder_->SetRateAllocation(bitrate_allocation,
codec_inst_.maxFramerate));
encoder_->SetRates(bit_rate, codec_inst_.maxFramerate));
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->InitEncode(&codec_inst_, 1, 1440));

View File

@ -113,6 +113,7 @@ VP8Decoder* VP8Decoder::Create() {
VP8EncoderImpl::VP8EncoderImpl()
: encoded_complete_callback_(nullptr),
rate_allocator_(new SimulcastRateAllocator(codec_)),
inited_(false),
timestamp_(0),
feedback_mode_(false),
@ -174,32 +175,27 @@ int VP8EncoderImpl::Release() {
return ret_val;
}
int VP8EncoderImpl::SetRateAllocation(const BitrateAllocation& bitrate,
uint32_t new_framerate) {
if (!inited_)
int VP8EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
uint32_t new_framerate) {
if (!inited_) {
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
if (encoders_[0].err)
return WEBRTC_VIDEO_CODEC_ERROR;
if (new_framerate < 1)
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
if (bitrate.get_sum_bps() == 0) {
// Encoder paused, turn off all encoding.
const int num_streams = static_cast<size_t>(encoders_.size());
for (int i = 0; i < num_streams; ++i)
SetStreamState(false, i);
return WEBRTC_VIDEO_CODEC_OK;
}
// At this point, bitrate allocation should already match codec settings.
if (codec_.maxBitrate > 0)
RTC_DCHECK_LE(bitrate.get_sum_kbps(), codec_.maxBitrate);
RTC_DCHECK_GE(bitrate.get_sum_kbps(), codec_.minBitrate);
if (codec_.numberOfSimulcastStreams > 0)
RTC_DCHECK_GE(bitrate.get_sum_kbps(), codec_.simulcastStream[0].minBitrate);
if (encoders_[0].err) {
return WEBRTC_VIDEO_CODEC_ERROR;
}
if (new_framerate < 1) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) {
new_bitrate_kbit = codec_.maxBitrate;
}
if (new_bitrate_kbit < codec_.minBitrate) {
new_bitrate_kbit = codec_.minBitrate;
}
if (codec_.numberOfSimulcastStreams > 0 &&
new_bitrate_kbit < codec_.simulcastStream[0].minBitrate) {
new_bitrate_kbit = codec_.simulcastStream[0].minBitrate;
}
codec_.maxFramerate = new_framerate;
if (encoders_.size() == 1) {
@ -211,14 +207,14 @@ int VP8EncoderImpl::SetRateAllocation(const BitrateAllocation& bitrate,
// Only trigger keyframes if we are allowed to scale down.
if (configurations_[0].rc_resize_allowed) {
if (!down_scale_requested_) {
if (k_pixels_per_frame > bitrate.get_sum_kbps()) {
if (k_pixels_per_frame > new_bitrate_kbit) {
down_scale_requested_ = true;
down_scale_bitrate_ = bitrate.get_sum_kbps();
down_scale_bitrate_ = new_bitrate_kbit;
key_frame_request_[0] = true;
}
} else {
if (bitrate.get_sum_kbps() > (2 * down_scale_bitrate_) ||
bitrate.get_sum_kbps() < (down_scale_bitrate_ / 2)) {
if (new_bitrate_kbit > (2 * down_scale_bitrate_) ||
new_bitrate_kbit < (down_scale_bitrate_ / 2)) {
down_scale_requested_ = false;
}
}
@ -237,18 +233,31 @@ int VP8EncoderImpl::SetRateAllocation(const BitrateAllocation& bitrate,
}
}
std::vector<uint32_t> stream_bitrates =
rate_allocator_->GetAllocation(new_bitrate_kbit);
size_t stream_idx = encoders_.size() - 1;
for (size_t i = 0; i < encoders_.size(); ++i, --stream_idx) {
unsigned int target_bitrate_kbps =
bitrate.GetSpatialLayerSum(stream_idx) / 1000;
bool send_stream = target_bitrate_kbps > 0;
if (send_stream || encoders_.size() > 1)
SetStreamState(send_stream, stream_idx);
configurations_[i].rc_target_bitrate = target_bitrate_kbps;
temporal_layers_[stream_idx]->UpdateConfiguration(&configurations_[i]);
if (encoders_.size() > 1)
SetStreamState(stream_bitrates[stream_idx] > 0, stream_idx);
unsigned int target_bitrate = stream_bitrates[stream_idx];
unsigned int max_bitrate = codec_.maxBitrate;
int framerate = new_framerate;
// 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
// the target we still allow it to overshoot up to the max before dropping
// frames. This hack should be improved.
if (codec_.targetBitrate > 0 &&
(codec_.VP8()->numberOfTemporalLayers == 2 ||
codec_.simulcastStream[0].numberOfTemporalLayers == 2)) {
int tl0_bitrate = std::min(codec_.targetBitrate, target_bitrate);
max_bitrate = std::min(codec_.maxBitrate, target_bitrate);
target_bitrate = tl0_bitrate;
}
configurations_[i].rc_target_bitrate = target_bitrate;
temporal_layers_[stream_idx]->ConfigureBitrates(
target_bitrate, max_bitrate, framerate, &configurations_[i]);
if (vpx_codec_enc_config_set(&encoders_[i], &configurations_[i])) {
return WEBRTC_VIDEO_CODEC_ERROR;
}
@ -278,17 +287,26 @@ void VP8EncoderImpl::SetStreamState(bool send_stream,
void VP8EncoderImpl::SetupTemporalLayers(int num_streams,
int num_temporal_layers,
const VideoCodec& codec) {
RTC_DCHECK(codec.codecSpecific.VP8.tl_factory != nullptr);
const TemporalLayersFactory* tl_factory = codec.codecSpecific.VP8.tl_factory;
TemporalLayersFactory default_factory;
const TemporalLayersFactory* tl_factory = codec.VP8().tl_factory;
if (!tl_factory)
tl_factory = &default_factory;
if (num_streams == 1) {
temporal_layers_.push_back(
tl_factory->Create(0, num_temporal_layers, rand()));
if (codec.mode == kScreensharing) {
// Special mode when screensharing on a single stream.
temporal_layers_.push_back(new ScreenshareLayers(
num_temporal_layers, rand(), webrtc::Clock::GetRealTimeClock()));
} else {
temporal_layers_.push_back(
tl_factory->Create(num_temporal_layers, rand()));
}
} else {
for (int i = 0; i < num_streams; ++i) {
RTC_CHECK_GT(num_temporal_layers, 0);
int layers = std::max(static_cast<uint8_t>(1),
codec.simulcastStream[i].numberOfTemporalLayers);
temporal_layers_.push_back(tl_factory->Create(i, layers, rand()));
// TODO(andresp): crash if layers is invalid.
int layers = codec.simulcastStream[i].numberOfTemporalLayers;
if (layers < 1)
layers = 1;
temporal_layers_.push_back(tl_factory->Create(layers, rand()));
}
}
}
@ -333,8 +351,10 @@ int VP8EncoderImpl::InitEncode(const VideoCodec* inst,
int num_temporal_layers =
doing_simulcast ? inst->simulcastStream[0].numberOfTemporalLayers
: inst->VP8().numberOfTemporalLayers;
RTC_DCHECK_GT(num_temporal_layers, 0);
// TODO(andresp): crash if num temporal layers is bananas.
if (num_temporal_layers < 1)
num_temporal_layers = 1;
SetupTemporalLayers(number_of_streams, num_temporal_layers, *inst);
feedback_mode_ = inst->VP8().feedbackModeOn;
@ -342,6 +362,7 @@ int VP8EncoderImpl::InitEncode(const VideoCodec* inst,
number_of_cores_ = number_of_cores;
timestamp_ = 0;
codec_ = *inst;
rate_allocator_.reset(new SimulcastRateAllocator(codec_));
// Code expects simulcastStream resolutions to be correct, make sure they are
// filled even when there are no simulcast layers.
@ -493,44 +514,45 @@ int VP8EncoderImpl::InitEncode(const VideoCodec* inst,
vpx_img_wrap(&raw_images_[0], VPX_IMG_FMT_I420, inst->width, inst->height, 1,
NULL);
// Note the order we use is different from webm, we have lowest resolution
// at position 0 and they have highest resolution at position 0.
int stream_idx = encoders_.size() - 1;
SimulcastRateAllocator init_allocator(codec_, nullptr);
BitrateAllocation allocation = init_allocator.GetAllocation(
inst->startBitrate * 1000, inst->maxFramerate);
std::vector<uint32_t> stream_bitrates;
for (int i = 0; i == 0 || i < inst->numberOfSimulcastStreams; ++i) {
uint32_t bitrate = allocation.GetSpatialLayerSum(i) / 1000;
stream_bitrates.push_back(bitrate);
}
configurations_[0].rc_target_bitrate = stream_bitrates[stream_idx];
temporal_layers_[stream_idx]->OnRatesUpdated(
stream_bitrates[stream_idx], inst->maxBitrate, inst->maxFramerate);
temporal_layers_[stream_idx]->UpdateConfiguration(&configurations_[0]);
--stream_idx;
for (size_t i = 1; i < encoders_.size(); ++i, --stream_idx) {
memcpy(&configurations_[i], &configurations_[0],
sizeof(configurations_[0]));
configurations_[i].g_w = inst->simulcastStream[stream_idx].width;
configurations_[i].g_h = inst->simulcastStream[stream_idx].height;
// Use 1 thread for lower resolutions.
configurations_[i].g_threads = 1;
// Setting alignment to 32 - as that ensures at least 16 for all
// planes (32 for Y, 16 for U,V). Libvpx sets the requested stride for
// the y plane, but only half of it to the u and v planes.
vpx_img_alloc(&raw_images_[i], VPX_IMG_FMT_I420,
inst->simulcastStream[stream_idx].width,
inst->simulcastStream[stream_idx].height, kVp832ByteAlign);
if (encoders_.size() == 1) {
configurations_[0].rc_target_bitrate = inst->startBitrate;
temporal_layers_[0]->ConfigureBitrates(inst->startBitrate, inst->maxBitrate,
inst->maxFramerate,
&configurations_[0]);
} else {
// Note the order we use is different from webm, we have lowest resolution
// at position 0 and they have highest resolution at position 0.
int stream_idx = encoders_.size() - 1;
std::vector<uint32_t> stream_bitrates =
rate_allocator_->GetAllocation(inst->startBitrate);
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);
temporal_layers_[stream_idx]->UpdateConfiguration(&configurations_[i]);
configurations_[0].rc_target_bitrate = stream_bitrates[stream_idx];
temporal_layers_[stream_idx]->ConfigureBitrates(
stream_bitrates[stream_idx], inst->maxBitrate, inst->maxFramerate,
&configurations_[0]);
--stream_idx;
for (size_t i = 1; i < encoders_.size(); ++i, --stream_idx) {
memcpy(&configurations_[i], &configurations_[0],
sizeof(configurations_[0]));
configurations_[i].g_w = inst->simulcastStream[stream_idx].width;
configurations_[i].g_h = inst->simulcastStream[stream_idx].height;
// Use 1 thread for lower resolutions.
configurations_[i].g_threads = 1;
// Setting alignment to 32 - as that ensures at least 16 for all
// planes (32 for Y, 16 for U,V). Libvpx sets the requested stride for
// the y plane, but only half of it to the u and v planes.
vpx_img_alloc(&raw_images_[i], VPX_IMG_FMT_I420,
inst->simulcastStream[stream_idx].width,
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]->ConfigureBitrates(
stream_bitrates[stream_idx], inst->maxBitrate, inst->maxFramerate,
&configurations_[i]);
}
}
rps_.Init();

View File

@ -32,6 +32,7 @@
namespace webrtc {
class SimulcastRateAllocator;
class TemporalLayers;
class VP8EncoderImpl : public VP8Encoder {
@ -54,8 +55,7 @@ class VP8EncoderImpl : public VP8Encoder {
int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
int SetRateAllocation(const BitrateAllocation& bitrate,
uint32_t new_framerate) override;
int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate) override;
void OnDroppedFrame() override;
@ -94,6 +94,7 @@ class VP8EncoderImpl : public VP8Encoder {
EncodedImageCallback* encoded_complete_callback_;
VideoCodec codec_;
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
bool inited_;
int64_t timestamp_;
bool feedback_mode_;

View File

@ -77,7 +77,6 @@ VP9EncoderImpl::VP9EncoderImpl()
frames_since_kf_(0),
num_temporal_layers_(0),
num_spatial_layers_(0),
is_flexible_mode_(false),
frames_encoded_(0),
// Use two spatial when screensharing with flexible mode.
spatial_layer_(new ScreenshareLayersVP9(2)) {
@ -194,28 +193,24 @@ bool VP9EncoderImpl::SetSvcRates() {
return true;
}
int VP9EncoderImpl::SetRateAllocation(
const BitrateAllocation& bitrate_allocation,
uint32_t frame_rate) {
int VP9EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
uint32_t new_framerate) {
if (!inited_) {
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
}
if (encoder_->err) {
return WEBRTC_VIDEO_CODEC_ERROR;
}
if (frame_rate < 1) {
if (new_framerate < 1) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
// Update bit rate
if (codec_.maxBitrate > 0 &&
bitrate_allocation.get_sum_kbps() > codec_.maxBitrate) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) {
new_bitrate_kbit = codec_.maxBitrate;
}
// TODO(sprang): Actually use BitrateAllocation layer info.
config_->rc_target_bitrate = bitrate_allocation.get_sum_kbps();
codec_.maxFramerate = frame_rate;
spatial_layer_->ConfigureBitrate(bitrate_allocation.get_sum_kbps(), 0);
config_->rc_target_bitrate = new_bitrate_kbit;
codec_.maxFramerate = new_framerate;
spatial_layer_->ConfigureBitrate(new_bitrate_kbit, 0);
if (!SetSvcRates()) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;

View File

@ -46,8 +46,7 @@ class VP9EncoderImpl : public VP9Encoder {
int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
int SetRateAllocation(const BitrateAllocation& bitrate_allocation,
uint32_t frame_rate) override;
int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate) override;
void OnDroppedFrame() override {}