Index: webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc |
diff --git a/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc b/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc |
index 4f04743fc5d0359d62ff02117d9fe620e26fc309..c73a7b2ed04ec6fa6fe646db3ddf4d806c786584 100644 |
--- a/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc |
+++ b/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc |
@@ -26,26 +26,133 @@ |
namespace webrtc { |
+TemporalReferences::TemporalReferences(TemporalBufferUsage last, |
+ TemporalBufferUsage golden, |
+ TemporalBufferUsage arf) |
+ : TemporalReferences(last, golden, arf, false, false) {} |
+ |
+TemporalReferences::TemporalReferences(TemporalBufferUsage last, |
+ TemporalBufferUsage golden, |
+ TemporalBufferUsage arf, |
+ int extra_flags) |
+ : TemporalReferences(last, |
+ golden, |
+ arf, |
+ (extra_flags & kLayerSync) != 0, |
+ (extra_flags & kFreezeEntropy) != 0) {} |
+ |
+TemporalReferences::TemporalReferences(TemporalBufferUsage last, |
+ TemporalBufferUsage golden, |
+ TemporalBufferUsage arf, |
+ bool layer_sync, |
+ bool freeze_entropy) |
+ : reference_last((last & kReference) != 0), |
+ update_last((last & kUpdate) != 0), |
+ reference_golden((golden & kReference) != 0), |
+ update_golden((golden & kUpdate) != 0), |
+ reference_arf((arf & kReference) != 0), |
+ update_arf((arf & kUpdate) != 0), |
+ layer_sync(layer_sync), |
+ freeze_entropy(freeze_entropy) {} |
+ |
+namespace { |
brandtr
2017/03/21 12:20:09
nit: New line between l58 and l59. (Or remove spac
pbos-webrtc
2017/03/21 17:37:46
Done.
|
+std::vector<unsigned int> GetTemporalIds(size_t num_layers) { |
+ switch (num_layers) { |
+ case 1: |
+ return {0}; |
+ case 2: |
+ return {0, 1}; |
+ case 3: |
+ return {0, 2, 1, 2}; |
+ case 4: |
brandtr
2017/03/21 12:20:09
Might be good to add a diagram showing these IDs v
sprang_webrtc
2017/03/21 13:50:38
Do we ever use 4 temporal layers in practice?
pbos-webrtc
2017/03/21 17:37:46
Done.
pbos-webrtc
2017/03/21 17:37:46
No, but they are supported in RTP, hardware encode
|
+ return {0, 3, 2, 3, 1, 3, 2, 3}; |
+ default: |
+ RTC_NOTREACHED(); |
+ break; |
+ } |
+ RTC_NOTREACHED(); |
+ return {0}; |
+} |
+ |
+std::vector<TemporalReferences> GetTemporalPattern( |
+ size_t num_layers) { |
+ switch (num_layers) { |
+ case 1: |
+ return {TemporalReferences(kReferenceAndUpdate, kReference, kReference)}; |
brandtr
2017/03/21 12:20:09
It would be great with some simple ASCII art here,
pbos-webrtc
2017/03/21 17:37:46
Since this contains all references drawing this is
brandtr
2017/03/22 08:44:42
Much easier to read through the lists now, thanks.
|
+ case 2: |
+ return {TemporalReferences(kReferenceAndUpdate, kUpdate, kReference), |
+ TemporalReferences(kReference, kUpdate, kReference, kLayerSync), |
+ TemporalReferences(kReferenceAndUpdate, kNone, kReference), |
+ TemporalReferences(kReference, kReferenceAndUpdate, kReference), |
+ TemporalReferences(kReferenceAndUpdate, kNone, kReference), |
+ TemporalReferences(kReference, kReferenceAndUpdate, kReference), |
+ TemporalReferences(kReferenceAndUpdate, kNone, kReference), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kFreezeEntropy)}; |
+ case 3: |
+ return {TemporalReferences(kReferenceAndUpdate, kUpdate, kReference), |
+ TemporalReferences(kReference, kNone, kReference, |
+ kLayerSync | kFreezeEntropy), |
+ TemporalReferences(kReference, kUpdate, kReference, kLayerSync), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kFreezeEntropy), |
+ TemporalReferences(kReferenceAndUpdate, kNone, kReference), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kFreezeEntropy), |
+ TemporalReferences(kReference, kReferenceAndUpdate, kReference), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kFreezeEntropy)}; |
+ case 4: |
+ return {TemporalReferences(kReferenceAndUpdate, kNone, kNone), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kLayerSync | kFreezeEntropy), |
+ TemporalReferences(kReference, kNone, kUpdate, kLayerSync), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kLayerSync | kFreezeEntropy), |
+ TemporalReferences(kReference, kUpdate, kNone, kLayerSync), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kLayerSync | kFreezeEntropy), |
+ TemporalReferences(kReference, kReference, kReferenceAndUpdate), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kLayerSync | kFreezeEntropy), |
+ TemporalReferences(kReferenceAndUpdate, kNone, kNone), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kLayerSync | kFreezeEntropy), |
+ TemporalReferences(kReference, kReference, kReferenceAndUpdate), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kLayerSync | kFreezeEntropy), |
+ TemporalReferences(kReference, kReferenceAndUpdate, kNone), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kLayerSync | kFreezeEntropy), |
+ TemporalReferences(kReference, kReference, kReferenceAndUpdate), |
+ TemporalReferences(kReference, kReference, kReference, |
+ kLayerSync | kFreezeEntropy)}; |
+ default: |
+ RTC_NOTREACHED(); |
+ break; |
+ } |
+ RTC_NOTREACHED(); |
+ return {TemporalReferences(kNone, kNone, kNone)}; |
+} |
+ |
+} // namespace |
+ |
DefaultTemporalLayers::DefaultTemporalLayers(int number_of_temporal_layers, |
uint8_t initial_tl0_pic_idx) |
- : number_of_temporal_layers_(number_of_temporal_layers), |
- temporal_ids_length_(0), |
- temporal_pattern_length_(0), |
+ : num_layers_(std::max(1, number_of_temporal_layers)), |
+ temporal_ids_(GetTemporalIds(num_layers_)), |
+ temporal_pattern_(GetTemporalPattern(num_layers_)), |
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); |
- memset(temporal_ids_, 0, sizeof(temporal_ids_)); |
- memset(temporal_pattern_, 0, sizeof(temporal_pattern_)); |
+ RTC_CHECK_LE(number_of_temporal_layers, 4); |
} |
int DefaultTemporalLayers::CurrentLayerId() const { |
- assert(temporal_ids_length_ > 0); |
- int index = pattern_idx_ % temporal_ids_length_; |
- assert(index >= 0); |
- return temporal_ids_[index]; |
+ return temporal_ids_[pattern_idx_ % temporal_ids_.size()]; |
} |
std::vector<uint32_t> DefaultTemporalLayers::OnRatesUpdated( |
@@ -53,17 +160,16 @@ std::vector<uint32_t> DefaultTemporalLayers::OnRatesUpdated( |
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) { |
+ for (size_t i = 0; i < num_layers_; ++i) { |
float layer_bitrate = |
- bitrate_kbps * kVp8LayerRateAlloction[num_layers - 1][i]; |
+ 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) { |
+ for (size_t i = 0; i < num_layers_; ++i) { |
uint32_t layer_bitrate = bitrates[i]; |
RTC_DCHECK_LE(sum, bitrates[i]); |
bitrates[i] -= sum; |
@@ -83,197 +189,44 @@ 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] = (*new_bitrates_kbps_)[0]; |
- cfg->ts_rate_decimator[0] = 1; |
- memcpy(cfg->ts_layer_id, temporal_ids_, |
- sizeof(unsigned int) * temporal_ids_length_); |
- temporal_pattern_length_ = 1; |
- temporal_pattern_[0] = kTemporalUpdateLastRefAll; |
- break; |
- case 2: |
- temporal_ids_length_ = 2; |
- temporal_ids_[0] = 0; |
- temporal_ids_[1] = 1; |
- cfg->ts_number_layers = number_of_temporal_layers_; |
- 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_rate_decimator[0] = 2; |
- cfg->ts_rate_decimator[1] = 1; |
- memcpy(cfg->ts_layer_id, temporal_ids_, |
- sizeof(unsigned int) * temporal_ids_length_); |
- temporal_pattern_length_ = 8; |
- temporal_pattern_[0] = kTemporalUpdateLastAndGoldenRefAltRef; |
- temporal_pattern_[1] = kTemporalUpdateGoldenWithoutDependencyRefAltRef; |
- temporal_pattern_[2] = kTemporalUpdateLastRefAltRef; |
- temporal_pattern_[3] = kTemporalUpdateGoldenRefAltRef; |
- temporal_pattern_[4] = kTemporalUpdateLastRefAltRef; |
- temporal_pattern_[5] = kTemporalUpdateGoldenRefAltRef; |
- temporal_pattern_[6] = kTemporalUpdateLastRefAltRef; |
- temporal_pattern_[7] = kTemporalUpdateNone; |
- break; |
- case 3: |
- temporal_ids_length_ = 4; |
- temporal_ids_[0] = 0; |
- temporal_ids_[1] = 2; |
- temporal_ids_[2] = 1; |
- temporal_ids_[3] = 2; |
- cfg->ts_number_layers = number_of_temporal_layers_; |
- 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_rate_decimator[0] = 4; |
- cfg->ts_rate_decimator[1] = 2; |
- cfg->ts_rate_decimator[2] = 1; |
- memcpy(cfg->ts_layer_id, temporal_ids_, |
- sizeof(unsigned int) * temporal_ids_length_); |
- temporal_pattern_length_ = 8; |
- temporal_pattern_[0] = kTemporalUpdateLastAndGoldenRefAltRef; |
- temporal_pattern_[1] = kTemporalUpdateNoneNoRefGoldenRefAltRef; |
- temporal_pattern_[2] = kTemporalUpdateGoldenWithoutDependencyRefAltRef; |
- temporal_pattern_[3] = kTemporalUpdateNone; |
- temporal_pattern_[4] = kTemporalUpdateLastRefAltRef; |
- temporal_pattern_[5] = kTemporalUpdateNone; |
- temporal_pattern_[6] = kTemporalUpdateGoldenRefAltRef; |
- temporal_pattern_[7] = kTemporalUpdateNone; |
- break; |
- case 4: |
- temporal_ids_length_ = 8; |
- temporal_ids_[0] = 0; |
- temporal_ids_[1] = 3; |
- temporal_ids_[2] = 2; |
- temporal_ids_[3] = 3; |
- temporal_ids_[4] = 1; |
- temporal_ids_[5] = 3; |
- temporal_ids_[6] = 2; |
- temporal_ids_[7] = 3; |
- // Split stream 25% 15% 20% 40%. |
- // 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_rate_decimator[0] = 8; |
- cfg->ts_rate_decimator[1] = 4; |
- cfg->ts_rate_decimator[2] = 2; |
- cfg->ts_rate_decimator[3] = 1; |
- memcpy(cfg->ts_layer_id, temporal_ids_, |
- sizeof(unsigned int) * temporal_ids_length_); |
- temporal_pattern_length_ = 16; |
- temporal_pattern_[0] = kTemporalUpdateLast; |
- temporal_pattern_[1] = kTemporalUpdateNone; |
- temporal_pattern_[2] = kTemporalUpdateAltrefWithoutDependency; |
- temporal_pattern_[3] = kTemporalUpdateNone; |
- temporal_pattern_[4] = kTemporalUpdateGoldenWithoutDependency; |
- temporal_pattern_[5] = kTemporalUpdateNone; |
- temporal_pattern_[6] = kTemporalUpdateAltref; |
- temporal_pattern_[7] = kTemporalUpdateNone; |
- temporal_pattern_[8] = kTemporalUpdateLast; |
- temporal_pattern_[9] = kTemporalUpdateNone; |
- temporal_pattern_[10] = kTemporalUpdateAltref; |
- temporal_pattern_[11] = kTemporalUpdateNone; |
- temporal_pattern_[12] = kTemporalUpdateGolden; |
- temporal_pattern_[13] = kTemporalUpdateNone; |
- temporal_pattern_[14] = kTemporalUpdateAltref; |
- temporal_pattern_[15] = kTemporalUpdateNone; |
- break; |
- default: |
- RTC_NOTREACHED(); |
- return false; |
+ for (size_t i = 0; i < num_layers_; ++i) { |
+ cfg->ts_target_bitrate[i] = (*new_bitrates_kbps_)[i]; |
+ // ..., 4, 2, 1 |
+ cfg->ts_rate_decimator[i] = 1 << (num_layers_ - i - 1); |
} |
+ cfg->ts_number_layers = num_layers_; |
+ cfg->ts_periodicity = temporal_ids_.size(); |
+ memcpy(cfg->ts_layer_id, &temporal_ids_[0], |
+ sizeof(unsigned int) * temporal_ids_.size()); |
+ |
new_bitrates_kbps_ = rtc::Optional<std::vector<uint32_t>>(); |
return true; |
} |
int DefaultTemporalLayers::EncodeFlags(uint32_t timestamp) { |
- assert(number_of_temporal_layers_ > 0); |
- assert(kMaxTemporalPattern >= temporal_pattern_length_); |
- assert(0 < temporal_pattern_length_); |
+ assert(num_layers_ > 0); |
+ assert(0 < temporal_pattern_.size()); |
int flags = 0; |
- int patternIdx = ++pattern_idx_ % temporal_pattern_length_; |
- assert(kMaxTemporalPattern >= patternIdx); |
- switch (temporal_pattern_[patternIdx]) { |
- case kTemporalUpdateLast: |
- flags |= VP8_EFLAG_NO_UPD_GF; |
- flags |= VP8_EFLAG_NO_UPD_ARF; |
- flags |= VP8_EFLAG_NO_REF_GF; |
- flags |= VP8_EFLAG_NO_REF_ARF; |
- break; |
- case kTemporalUpdateGoldenWithoutDependency: |
- flags |= VP8_EFLAG_NO_REF_GF; |
- // Deliberately no break here. |
- FALLTHROUGH(); |
- case kTemporalUpdateGolden: |
- flags |= VP8_EFLAG_NO_REF_ARF; |
- flags |= VP8_EFLAG_NO_UPD_ARF; |
- flags |= VP8_EFLAG_NO_UPD_LAST; |
- break; |
- case kTemporalUpdateAltrefWithoutDependency: |
- flags |= VP8_EFLAG_NO_REF_ARF; |
- flags |= VP8_EFLAG_NO_REF_GF; |
- // Deliberately no break here. |
- FALLTHROUGH(); |
- case kTemporalUpdateAltref: |
- flags |= VP8_EFLAG_NO_UPD_GF; |
- flags |= VP8_EFLAG_NO_UPD_LAST; |
- break; |
- case kTemporalUpdateNoneNoRefAltref: |
- flags |= VP8_EFLAG_NO_REF_ARF; |
- // Deliberately no break here. |
- FALLTHROUGH(); |
- case kTemporalUpdateNone: |
- flags |= VP8_EFLAG_NO_UPD_GF; |
- flags |= VP8_EFLAG_NO_UPD_ARF; |
- flags |= VP8_EFLAG_NO_UPD_LAST; |
- flags |= VP8_EFLAG_NO_UPD_ENTROPY; |
- break; |
- case kTemporalUpdateNoneNoRefGoldenRefAltRef: |
- flags |= VP8_EFLAG_NO_REF_GF; |
- flags |= VP8_EFLAG_NO_UPD_GF; |
- flags |= VP8_EFLAG_NO_UPD_ARF; |
- flags |= VP8_EFLAG_NO_UPD_LAST; |
- flags |= VP8_EFLAG_NO_UPD_ENTROPY; |
- break; |
- case kTemporalUpdateGoldenWithoutDependencyRefAltRef: |
- flags |= VP8_EFLAG_NO_REF_GF; |
- flags |= VP8_EFLAG_NO_UPD_ARF; |
- flags |= VP8_EFLAG_NO_UPD_LAST; |
- break; |
- case kTemporalUpdateLastRefAltRef: |
- flags |= VP8_EFLAG_NO_UPD_GF; |
- flags |= VP8_EFLAG_NO_UPD_ARF; |
- flags |= VP8_EFLAG_NO_REF_GF; |
- break; |
- case kTemporalUpdateGoldenRefAltRef: |
- flags |= VP8_EFLAG_NO_UPD_ARF; |
- flags |= VP8_EFLAG_NO_UPD_LAST; |
- break; |
- case kTemporalUpdateLastAndGoldenRefAltRef: |
- flags |= VP8_EFLAG_NO_UPD_ARF; |
- flags |= VP8_EFLAG_NO_REF_GF; |
- break; |
- case kTemporalUpdateLastRefAll: |
- flags |= VP8_EFLAG_NO_UPD_ARF; |
- flags |= VP8_EFLAG_NO_UPD_GF; |
- break; |
- } |
+ int patternIdx = ++pattern_idx_ % temporal_pattern_.size(); |
sprang_webrtc
2017/03/21 13:50:38
Why not just update pattern_idx_ itself?
pbos-webrtc
2017/03/21 17:37:46
pattern_idx_ is used for both temporal_pattern_.si
|
+ const TemporalReferences& references = temporal_pattern_[patternIdx]; |
+ |
+ if (!references.reference_last) |
+ flags |= VP8_EFLAG_NO_REF_LAST; |
+ if (!references.update_last) |
+ flags |= VP8_EFLAG_NO_UPD_LAST; |
+ if (!references.reference_golden) |
+ flags |= VP8_EFLAG_NO_REF_GF; |
+ if (!references.update_golden) |
+ flags |= VP8_EFLAG_NO_UPD_GF; |
+ if (!references.reference_arf) |
+ flags |= VP8_EFLAG_NO_REF_ARF; |
+ if (!references.update_arf) |
+ flags |= VP8_EFLAG_NO_UPD_ARF; |
+ if (references.freeze_entropy) |
+ flags |= VP8_EFLAG_NO_UPD_ENTROPY; |
+ |
return flags; |
} |
@@ -281,10 +234,9 @@ void DefaultTemporalLayers::PopulateCodecSpecific( |
bool base_layer_sync, |
CodecSpecificInfoVP8* vp8_info, |
uint32_t timestamp) { |
- assert(number_of_temporal_layers_ > 0); |
- assert(0 < temporal_ids_length_); |
+ assert(num_layers_ > 0); |
sprang_webrtc
2017/03/21 13:50:38
nit: RTC_[D]CHECK_GT
pbos-webrtc
2017/03/21 17:37:46
Done.
|
- if (number_of_temporal_layers_ == 1) { |
+ if (num_layers_ == 1) { |
vp8_info->temporalIdx = kNoTemporalIdx; |
vp8_info->layerSync = false; |
vp8_info->tl0PicIdx = kNoTl0PicIdx; |
@@ -295,19 +247,9 @@ void DefaultTemporalLayers::PopulateCodecSpecific( |
} else { |
vp8_info->temporalIdx = CurrentLayerId(); |
TemporalReferences temporal_reference = |
- temporal_pattern_[pattern_idx_ % temporal_pattern_length_]; |
- |
- if (temporal_reference == kTemporalUpdateAltrefWithoutDependency || |
- temporal_reference == kTemporalUpdateGoldenWithoutDependency || |
- temporal_reference == |
- kTemporalUpdateGoldenWithoutDependencyRefAltRef || |
- temporal_reference == kTemporalUpdateNoneNoRefGoldenRefAltRef || |
- (temporal_reference == kTemporalUpdateNone && |
- number_of_temporal_layers_ == 4)) { |
- vp8_info->layerSync = true; |
- } else { |
- vp8_info->layerSync = false; |
- } |
+ temporal_pattern_[pattern_idx_ % temporal_pattern_.size()]; |
+ |
+ vp8_info->layerSync = temporal_reference.layer_sync; |
} |
if (last_base_layer_sync_ && vp8_info->temporalIdx != 0) { |
// Regardless of pattern the frame after a base layer sync will always |