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 Review-Url: https://codereview.webrtc.org/2434073003 Cr-Commit-Position: refs/heads/master@{#14998}
This commit is contained in:
@ -113,7 +113,6 @@ VP8Decoder* VP8Decoder::Create() {
|
||||
|
||||
VP8EncoderImpl::VP8EncoderImpl()
|
||||
: encoded_complete_callback_(nullptr),
|
||||
rate_allocator_(new SimulcastRateAllocator(codec_)),
|
||||
inited_(false),
|
||||
timestamp_(0),
|
||||
feedback_mode_(false),
|
||||
@ -175,27 +174,32 @@ int VP8EncoderImpl::Release() {
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int VP8EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
|
||||
uint32_t new_framerate) {
|
||||
if (!inited_) {
|
||||
int VP8EncoderImpl::SetRateAllocation(const BitrateAllocation& bitrate,
|
||||
uint32_t new_framerate) {
|
||||
if (!inited_)
|
||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||
}
|
||||
if (encoders_[0].err) {
|
||||
|
||||
if (encoders_[0].err)
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
if (new_framerate < 1) {
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
codec_.maxFramerate = new_framerate;
|
||||
|
||||
if (encoders_.size() == 1) {
|
||||
@ -207,14 +211,14 @@ int VP8EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
|
||||
// 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 > new_bitrate_kbit) {
|
||||
if (k_pixels_per_frame > bitrate.get_sum_kbps()) {
|
||||
down_scale_requested_ = true;
|
||||
down_scale_bitrate_ = new_bitrate_kbit;
|
||||
down_scale_bitrate_ = bitrate.get_sum_kbps();
|
||||
key_frame_request_[0] = true;
|
||||
}
|
||||
} else {
|
||||
if (new_bitrate_kbit > (2 * down_scale_bitrate_) ||
|
||||
new_bitrate_kbit < (down_scale_bitrate_ / 2)) {
|
||||
if (bitrate.get_sum_kbps() > (2 * down_scale_bitrate_) ||
|
||||
bitrate.get_sum_kbps() < (down_scale_bitrate_ / 2)) {
|
||||
down_scale_requested_ = false;
|
||||
}
|
||||
}
|
||||
@ -233,31 +237,18 @@ int VP8EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (encoders_.size() > 1)
|
||||
SetStreamState(stream_bitrates[stream_idx] > 0, 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]);
|
||||
|
||||
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;
|
||||
}
|
||||
@ -287,26 +278,17 @@ void VP8EncoderImpl::SetStreamState(bool send_stream,
|
||||
void VP8EncoderImpl::SetupTemporalLayers(int num_streams,
|
||||
int num_temporal_layers,
|
||||
const VideoCodec& codec) {
|
||||
TemporalLayersFactory default_factory;
|
||||
const TemporalLayersFactory* tl_factory = codec.VP8().tl_factory;
|
||||
if (!tl_factory)
|
||||
tl_factory = &default_factory;
|
||||
RTC_DCHECK(codec.codecSpecific.VP8.tl_factory != nullptr);
|
||||
const TemporalLayersFactory* tl_factory = codec.codecSpecific.VP8.tl_factory;
|
||||
if (num_streams == 1) {
|
||||
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()));
|
||||
}
|
||||
temporal_layers_.push_back(
|
||||
tl_factory->Create(0, num_temporal_layers, rand()));
|
||||
} else {
|
||||
for (int i = 0; i < num_streams; ++i) {
|
||||
// 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()));
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -351,10 +333,8 @@ 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;
|
||||
@ -362,7 +342,6 @@ 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.
|
||||
@ -514,45 +493,44 @@ int VP8EncoderImpl::InitEncode(const VideoCodec* inst,
|
||||
vpx_img_wrap(&raw_images_[0], VPX_IMG_FMT_I420, inst->width, inst->height, 1,
|
||||
NULL);
|
||||
|
||||
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);
|
||||
// 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);
|
||||
SetStreamState(stream_bitrates[stream_idx] > 0, stream_idx);
|
||||
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]);
|
||||
}
|
||||
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]);
|
||||
}
|
||||
|
||||
rps_.Init();
|
||||
|
||||
Reference in New Issue
Block a user