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:
@ -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);
|
||||
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -86,5 +86,10 @@ TEST_F(TestVp8Impl, TestSpatioTemporalLayers321PatternEncoder) {
|
||||
TEST_F(TestVp8Impl, TestStrideEncodeDecode) {
|
||||
TestVp8Simulcast::TestStrideEncodeDecode();
|
||||
}
|
||||
|
||||
TEST_F(TestVp8Impl, TestSkipEncodingUnusedStreams) {
|
||||
TestVp8Simulcast::TestSkipEncodingUnusedStreams();
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace webrtc
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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));
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user