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:
sprang
2016-11-09 05:09:06 -08:00
committed by Commit bot
parent 592baaf89a
commit 8f46c679d2
63 changed files with 1681 additions and 843 deletions

View File

@ -13,6 +13,10 @@
#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"
@ -22,16 +26,17 @@
namespace webrtc {
DefaultTemporalLayers::DefaultTemporalLayers(int numberOfTemporalLayers,
DefaultTemporalLayers::DefaultTemporalLayers(int number_of_temporal_layers,
uint8_t initial_tl0_pic_idx)
: number_of_temporal_layers_(numberOfTemporalLayers),
: number_of_temporal_layers_(number_of_temporal_layers),
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) {
assert(kMaxTemporalStreams >= numberOfTemporalLayers);
RTC_CHECK_GE(kMaxTemporalStreams, number_of_temporal_layers);
RTC_CHECK_GE(number_of_temporal_layers, 0);
memset(temporal_ids_, 0, sizeof(temporal_ids_));
memset(temporal_pattern_, 0, sizeof(temporal_pattern_));
}
@ -43,18 +48,50 @@ int DefaultTemporalLayers::CurrentLayerId() const {
return temporal_ids_[index];
}
bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
int max_bitrate_kbit,
int framerate,
vpx_codec_enc_cfg_t* cfg) {
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;
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] = bitrateKbit;
cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
cfg->ts_rate_decimator[0] = 1;
memcpy(cfg->ts_layer_id, temporal_ids_,
sizeof(unsigned int) * temporal_ids_length_);
@ -69,8 +106,8 @@ bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
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] = bitrateKbit * kVp8LayerRateAlloction[1][0];
cfg->ts_target_bitrate[1] = bitrateKbit;
cfg->ts_target_bitrate[0] = (*new_bitrates_kbps_)[0];
cfg->ts_target_bitrate[1] = (*new_bitrates_kbps_)[1];
cfg->ts_rate_decimator[0] = 2;
cfg->ts_rate_decimator[1] = 1;
memcpy(cfg->ts_layer_id, temporal_ids_,
@ -95,9 +132,9 @@ bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
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] = bitrateKbit * kVp8LayerRateAlloction[2][0];
cfg->ts_target_bitrate[1] = bitrateKbit * kVp8LayerRateAlloction[2][1];
cfg->ts_target_bitrate[2] = bitrateKbit;
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_rate_decimator[0] = 4;
cfg->ts_rate_decimator[1] = 2;
cfg->ts_rate_decimator[2] = 1;
@ -127,10 +164,10 @@ bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
// 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] = 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_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_rate_decimator[0] = 8;
cfg->ts_rate_decimator[1] = 4;
cfg->ts_rate_decimator[2] = 2;
@ -156,9 +193,12 @@ bool DefaultTemporalLayers::ConfigureBitrates(int bitrateKbit,
temporal_pattern_[15] = kTemporalUpdateNone;
break;
default:
assert(false);
RTC_NOTREACHED();
return false;
}
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>();
return true;
}
@ -284,8 +324,18 @@ void DefaultTemporalLayers::PopulateCodecSpecific(
}
TemporalLayers* TemporalLayersFactory::Create(
int simulcast_id,
int temporal_layers,
uint8_t initial_tl0_pic_idx) const {
return new DefaultTemporalLayers(temporal_layers, initial_tl0_pic_idx);
TemporalLayers* tl =
new DefaultTemporalLayers(temporal_layers, initial_tl0_pic_idx);
if (listener_)
listener_->OnTemporalLayersCreated(simulcast_id, tl);
return tl;
}
void TemporalLayersFactory::SetListener(TemporalLayersListener* listener) {
listener_ = listener;
}
} // namespace webrtc

View File

@ -12,8 +12,12 @@
#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 {
@ -26,10 +30,13 @@ class DefaultTemporalLayers : public TemporalLayers {
// and/or update the reference buffers.
int EncodeFlags(uint32_t timestamp) override;
bool ConfigureBitrates(int bitrate_kbit,
int max_bitrate_kbit,
int framerate,
vpx_codec_enc_cfg_t* cfg) 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;
void PopulateCodecSpecific(bool base_layer_sync,
CodecSpecificInfoVP8* vp8_info,
@ -37,8 +44,6 @@ 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:
@ -77,7 +82,7 @@ class DefaultTemporalLayers : public TemporalLayers {
};
enum { kMaxTemporalPattern = 16 };
int number_of_temporal_layers_;
const int number_of_temporal_layers_;
int temporal_ids_length_;
int temporal_ids_[kMaxTemporalPattern];
int temporal_pattern_length_;
@ -86,6 +91,7 @@ class DefaultTemporalLayers : public TemporalLayers {
uint8_t pattern_idx_;
uint32_t timestamp_;
bool last_base_layer_sync_;
rtc::Optional<std::vector<uint32_t>> new_bitrates_kbps_;
};
} // namespace webrtc

View File

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

View File

@ -12,6 +12,8 @@
#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"
@ -95,26 +97,20 @@ class RealTimeTemporalLayers : public TemporalLayers {
layer_ids_(NULL),
encode_flags_length_(0),
encode_flags_(NULL) {
assert(max_temporal_layers_ >= 1);
assert(max_temporal_layers_ <= 3);
RTC_CHECK_GE(max_temporal_layers_, 1);
RTC_CHECK_GE(max_temporal_layers_, 3);
}
virtual ~RealTimeTemporalLayers() {}
bool ConfigureBitrates(int bitrate_kbit,
int max_bitrate_kbit,
int framerate,
vpx_codec_enc_cfg_t* cfg) override {
std::vector<uint32_t> OnRatesUpdated(int bitrate_kbps,
int max_bitrate_kbps,
int framerate) override {
temporal_layers_ =
CalculateNumberOfTemporalLayers(temporal_layers_, framerate);
temporal_layers_ = std::min(temporal_layers_, max_temporal_layers_);
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];
}
RTC_CHECK_GE(temporal_layers_, 1);
RTC_CHECK_LE(temporal_layers_, 3);
switch (temporal_layers_) {
case 1: {
@ -125,9 +121,6 @@ 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: {
@ -146,10 +139,6 @@ 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: {
@ -168,19 +157,59 @@ 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:
assert(false);
return false;
RTC_NOTREACHED();
return std::vector<uint32_t>();
}
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;
}
@ -248,8 +277,6 @@ 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_;
@ -266,12 +293,19 @@ 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 {
return new RealTimeTemporalLayers(max_temporal_layers, initial_tl0_pic_idx);
TemporalLayers* tl =
new RealTimeTemporalLayers(max_temporal_layers, initial_tl0_pic_idx);
if (listener_)
listener_->OnTemporalLayersCreated(simulcast_id, tl);
return tl;
}
} // namespace webrtc

View File

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

View File

@ -9,9 +9,7 @@
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SCREENSHARE_LAYERS_H_
#include <list>
#include "vpx/vpx_encoder.h"
#include <vector>
#include "webrtc/base/timeutils.h"
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
@ -41,10 +39,15 @@ class ScreenshareLayers : public TemporalLayers {
// and/or update the reference buffers.
int EncodeFlags(uint32_t timestamp) override;
bool ConfigureBitrates(int bitrate_kbps,
int max_bitrate_kbps,
int framerate,
vpx_codec_enc_cfg_t* cfg) 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;
void PopulateCodecSpecific(bool base_layer_sync,
CodecSpecificInfoVP8* vp8_info,
@ -54,11 +57,6 @@ 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;
@ -75,7 +73,8 @@ class ScreenshareLayers : public TemporalLayers {
int min_qp_;
int max_qp_;
uint32_t max_debt_bytes_;
int frame_rate_;
int framerate_;
bool bitrate_updated_;
static const int kMaxNumTemporalLayers = 2;
struct TemporalLayer {

View File

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

View File

@ -83,17 +83,6 @@ 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.
@ -116,6 +105,25 @@ 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 {
@ -125,7 +133,6 @@ 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() {
@ -173,14 +180,13 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst,
}
codec_ = *inst;
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();
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);
}
std::string implementation_name;
@ -200,6 +206,9 @@ 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) {
@ -340,61 +349,48 @@ int SimulcastEncoderAdapter::SetChannelParameters(uint32_t packet_loss,
return WEBRTC_VIDEO_CODEC_OK;
}
int SimulcastEncoderAdapter::SetRates(uint32_t new_bitrate_kbit,
uint32_t new_framerate) {
if (!Initialized()) {
int SimulcastEncoderAdapter::SetRateAllocation(const BitrateAllocation& bitrate,
uint32_t new_framerate) {
if (!Initialized())
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
}
if (new_framerate < 1) {
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) {
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 (new_bitrate_kbit < codec_.minBitrate) {
new_bitrate_kbit = codec_.minBitrate;
}
if (bitrate.get_sum_kbps() < codec_.minBitrate)
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
if (codec_.numberOfSimulcastStreams > 0 &&
new_bitrate_kbit < codec_.simulcastStream[0].minBitrate) {
new_bitrate_kbit = codec_.simulcastStream[0].minBitrate;
bitrate.get_sum_kbps() < codec_.simulcastStream[0].minBitrate) {
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
}
stream_bitrates = rate_allocator_->GetAllocation(new_bitrate_kbit);
}
codec_.maxFramerate = new_framerate;
// 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];
uint32_t stream_bitrate_kbps =
bitrate.GetSpatialLayerSum(stream_idx) / 1000;
// 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;
// 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);
// 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);
}
return WEBRTC_VIDEO_CODEC_OK;

View File

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

View File

@ -95,12 +95,6 @@ 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();
}
@ -132,8 +126,9 @@ class MockVideoEncoder : public VideoEncoder {
int32_t Release() /* override */ { return 0; }
int32_t SetRates(uint32_t newBitRate, uint32_t frameRate) /* override */ {
last_set_bitrate_ = static_cast<int32_t>(newBitRate);
int32_t SetRateAllocation(const BitrateAllocation& bitrate_allocation,
uint32_t framerate) {
last_set_bitrate_ = bitrate_allocation;
return 0;
}
@ -160,13 +155,13 @@ class MockVideoEncoder : public VideoEncoder {
void set_supports_native_handle(bool enabled) {
supports_native_handle_ = enabled;
}
int32_t last_set_bitrate() const { return last_set_bitrate_; }
BitrateAllocation last_set_bitrate() const { return last_set_bitrate_; }
MOCK_CONST_METHOD0(ImplementationName, const char*());
private:
bool supports_native_handle_ = false;
int32_t last_set_bitrate_ = -1;
BitrateAllocation last_set_bitrate_;
VideoCodec codec_;
EncodedImageCallback* callback_;
@ -273,6 +268,9 @@ 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);
}
@ -301,7 +299,6 @@ 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);
@ -314,6 +311,7 @@ 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;
@ -357,6 +355,8 @@ 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_->SetRates(1200, 30);
adapter_->SetRateAllocation(rate_allocator_->GetAllocation(1200, 30), 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,6 +409,7 @@ 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);
@ -422,27 +423,37 @@ 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.
adapter_->SetRates(100, 30);
EXPECT_EQ(100, helper_->factory()->encoders()[0]->last_set_bitrate());
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());
// Below min but non-zero should be replaced with the min bitrate.
adapter_->SetRates(15, 30);
EXPECT_EQ(50, helper_->factory()->encoders()[0]->last_set_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());
// Zero should be passed on as is, since it means "pause".
adapter_->SetRates(0, 30);
EXPECT_EQ(0, helper_->factory()->encoders()[0]->last_set_bitrate());
adapter_->SetRateAllocation(BitrateAllocation(), 30);
EXPECT_EQ(BitrateAllocation(),
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");
@ -465,6 +476,7 @@ 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);
@ -493,6 +505,7 @@ 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;
@ -517,6 +530,7 @@ TEST_F(TestSimulcastEncoderAdapterFake,
TEST_F(TestSimulcastEncoderAdapterFake, TestFailureReturnCodesFromEncodeCalls) {
TestVp8Simulcast::DefaultSettings(
&codec_, static_cast<const int*>(kTestTemporalLayerProfile));
codec_.codecSpecific.VP8.tl_factory = &tl_factory_;
codec_.numberOfSimulcastStreams = 3;
EXPECT_EQ(0, adapter_->InitEncode(&codec_, 1, 1200));
adapter_->RegisterEncodeCompleteCallback(this);

View File

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

View File

@ -12,6 +12,7 @@
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_SIMULCAST_UNITTEST_H_
#include <algorithm>
#include <map>
#include <memory>
#include <vector>
@ -20,6 +21,7 @@
#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"
@ -147,83 +149,6 @@ 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)
@ -265,7 +190,7 @@ class TestVp8Simulcast : public ::testing::Test {
static void DefaultSettings(VideoCodec* settings,
const int* temporal_layer_profile) {
assert(settings);
RTC_CHECK(settings);
memset(settings, 0, sizeof(VideoCodec));
strncpy(settings->plName, "VP8", 4);
settings->codecType = kVideoCodecVP8;
@ -315,12 +240,18 @@ class TestVp8Simulcast : public ::testing::Test {
}
protected:
virtual void SetUp() { SetUpCodec(kDefaultTemporalLayerProfile); }
void SetUp() override { SetUpCodec(kDefaultTemporalLayerProfile); }
virtual void SetUpCodec(const int* temporal_layer_profile) {
void TearDown() override {
encoder_->Release();
decoder_->Release();
}
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;
@ -331,9 +262,16 @@ class TestVp8Simulcast : public ::testing::Test {
new VideoFrame(input_buffer_, 0, 0, webrtc::kVideoRotation_0));
}
virtual void TearDown() {
encoder_->Release();
decoder_->Release();
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);
}
void ExpectStreams(FrameType frame_type, int expected_video_streams) {
@ -396,7 +334,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() {
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
SetRates(kMaxBitrates[2], 30); // To get all three streams.
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, kNumberOfSimulcastStreams);
@ -431,7 +369,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestPaddingAllStreams() {
// We should always encode the base layer.
encoder_->SetRates(kMinBitrates[0] - 1, 30);
SetRates(kMinBitrates[0] - 1, 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 1);
@ -444,7 +382,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestPaddingTwoStreams() {
// We have just enough to get only the first stream and padding for two.
encoder_->SetRates(kMinBitrates[0], 30);
SetRates(kMinBitrates[0], 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 1);
@ -458,7 +396,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.
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1] - 1, 30);
SetRates(kTargetBitrates[0] + kMinBitrates[1] - 1, 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 1);
@ -471,7 +409,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestPaddingOneStream() {
// We have just enough to send two streams, so padding for one stream.
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1], 30);
SetRates(kTargetBitrates[0] + kMinBitrates[1], 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 2);
@ -485,8 +423,7 @@ 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|.
encoder_->SetRates(
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] - 1, 30);
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] - 1, 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 2);
@ -499,8 +436,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestSendAllStreams() {
// We have just enough to send all streams.
encoder_->SetRates(
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2], 30);
SetRates(kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2], 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 3);
@ -513,7 +449,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestDisablingStreams() {
// We should get three media streams.
encoder_->SetRates(kMaxBitrates[0] + kMaxBitrates[1] + kMaxBitrates[2], 30);
SetRates(kMaxBitrates[0] + kMaxBitrates[1] + kMaxBitrates[2], 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
ExpectStreams(kVideoFrameKey, 3);
@ -524,36 +460,33 @@ 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.
encoder_->SetRates(
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] / 2, 30);
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.
encoder_->SetRates(kTargetBitrates[0] + kMinBitrates[1] / 2, 30);
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.
encoder_->SetRates(kTargetBitrates[0] - 1, 30);
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.
encoder_->SetRates(
kTargetBitrates[0] + kTargetBitrates[1] + kMinBitrates[2] / 2, 30);
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.
encoder_->SetRates(
kTargetBitrates[0] + kTargetBitrates[1] + kTargetBitrates[2], 30);
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);
@ -590,10 +523,11 @@ 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.
encoder_->SetRates(kMaxBitrates[0] + kMaxBitrates[1], 30);
SetRates(kMaxBitrates[0] + kMaxBitrates[1], 30);
std::vector<FrameType> frame_types(kNumberOfSimulcastStreams,
kVideoFrameDelta);
EXPECT_CALL(
@ -611,8 +545,9 @@ 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));
encoder_->SetRates(settings_.startBitrate, 30);
SetRates(settings_.startBitrate, 30);
ExpectStreams(kVideoFrameKey, 1);
// Resize |input_frame_| to the new resolution.
half_width = (settings_.width + 1) / 2;
@ -634,7 +569,7 @@ class TestVp8Simulcast : public ::testing::Test {
Vp8TestEncodedImageCallback encoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
SetRates(kMaxBitrates[2], 30); // To get all three streams.
EXPECT_EQ(0, encoder_->Encode(*input_frame_, NULL, NULL));
int picture_id = -1;
@ -703,7 +638,7 @@ class TestVp8Simulcast : public ::testing::Test {
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
decoder_->RegisterDecodeCompleteCallback(&decoder_callback);
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
SetRates(kMaxBitrates[2], 30); // To get all three streams.
// Set color.
int plane_offset[kNumOfPlanes];
@ -777,7 +712,7 @@ class TestVp8Simulcast : public ::testing::Test {
void TestSaptioTemporalLayers333PatternEncoder() {
Vp8TestEncodedImageCallback encoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
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};
@ -846,7 +781,7 @@ class TestVp8Simulcast : public ::testing::Test {
SetUpCodec(temporal_layer_profile);
Vp8TestEncodedImageCallback encoder_callback;
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
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};
@ -905,7 +840,7 @@ class TestVp8Simulcast : public ::testing::Test {
encoder_->RegisterEncodeCompleteCallback(&encoder_callback);
decoder_->RegisterDecodeCompleteCallback(&decoder_callback);
encoder_->SetRates(kMaxBitrates[2], 30); // To get all three streams.
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;
@ -941,30 +876,6 @@ 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_;
@ -972,6 +883,7 @@ class TestVp8Simulcast : public ::testing::Test {
VideoCodec settings_;
rtc::scoped_refptr<I420Buffer> input_buffer_;
std::unique_ptr<VideoFrame> input_frame_;
std::unique_ptr<SimulcastRateAllocator> rate_allocator_;
};
} // namespace testing

View File

@ -12,18 +12,21 @@
#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
#include "vpx/vpx_encoder.h"
#include <vector>
#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 behaviour is a fixed pattern
// Factory for TemporalLayer strategy. Default behavior is a fixed pattern
// of temporal layers. See default_temporal_layers.cc
virtual ~TemporalLayers() {}
@ -31,10 +34,15 @@ class TemporalLayers {
// and/or update the reference buffers.
virtual int EncodeFlags(uint32_t timestamp) = 0;
virtual bool ConfigureBitrates(int bitrate_kbit,
int max_bitrate_kbit,
int framerate,
vpx_codec_enc_cfg_t* cfg) = 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 void PopulateCodecSpecific(bool base_layer_sync,
CodecSpecificInfoVP8* vp8_info,
@ -43,26 +51,42 @@ 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 temporal_layers,
virtual TemporalLayers* Create(int simulcast_id,
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 framerate so that the base layer has an acceptable
// framerate. See realtime_temporal_layers.cc
// layers based on input frame rate so that the base layer has an acceptable
// frame rate. See realtime_temporal_layers.cc
class RealTimeTemporalLayersFactory : public TemporalLayersFactory {
public:
RealTimeTemporalLayersFactory() {}
~RealTimeTemporalLayersFactory() override {}
TemporalLayers* Create(int num_temporal_layers,
TemporalLayers* Create(int simulcast_id,
int num_temporal_layers,
uint8_t initial_tl0_pic_idx) const override;
};
class TemporalLayersListener {
public:
TemporalLayersListener() {}
virtual ~TemporalLayersListener() {}
virtual void OnTemporalLayersCreated(int simulcast_id,
TemporalLayers* layers) = 0;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_

View File

@ -16,6 +16,7 @@
#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"
@ -161,6 +162,8 @@ 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));
@ -201,6 +204,7 @@ class TestVp8Impl : public ::testing::Test {
EncodedImage encoded_frame_;
VideoFrame decoded_frame_;
VideoCodec codec_inst_;
TemporalLayersFactory tl_factory_;
};
TEST_F(TestVp8Impl, EncoderParameterTest) {
@ -215,11 +219,15 @@ 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_->SetRates(bit_rate, codec_inst_.maxFramerate));
encoder_->SetRateAllocation(bitrate_allocation,
codec_inst_.maxFramerate));
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->InitEncode(&codec_inst_, 1, 1440));

View File

@ -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();

View File

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