| /* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "webrtc/modules/include/module_common_types.h" |
| #include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h" |
| #include "webrtc/modules/video_coding/include/video_codec_interface.h" |
| #include "webrtc/rtc_base/checks.h" |
| #include "webrtc/system_wrappers/include/field_trial.h" |
| |
| #include "vpx/vpx_encoder.h" |
| #include "vpx/vp8cx.h" |
| |
| namespace webrtc { |
| |
| TemporalLayers::FrameConfig::FrameConfig() |
| : FrameConfig(kNone, kNone, kNone, false) {} |
| |
| TemporalLayers::FrameConfig::FrameConfig(TemporalLayers::BufferFlags last, |
| TemporalLayers::BufferFlags golden, |
| TemporalLayers::BufferFlags arf) |
| : FrameConfig(last, golden, arf, false) {} |
| |
| TemporalLayers::FrameConfig::FrameConfig(TemporalLayers::BufferFlags last, |
| TemporalLayers::BufferFlags golden, |
| TemporalLayers::BufferFlags arf, |
| FreezeEntropy) |
| : FrameConfig(last, golden, arf, true) {} |
| |
| TemporalLayers::FrameConfig::FrameConfig(TemporalLayers::BufferFlags last, |
| TemporalLayers::BufferFlags golden, |
| TemporalLayers::BufferFlags arf, |
| bool freeze_entropy) |
| : drop_frame(last == TemporalLayers::kNone && |
| golden == TemporalLayers::kNone && |
| arf == TemporalLayers::kNone), |
| last_buffer_flags(last), |
| golden_buffer_flags(golden), |
| arf_buffer_flags(arf), |
| encoder_layer_id(0), |
| packetizer_temporal_idx(kNoTemporalIdx), |
| layer_sync(false), |
| freeze_entropy(freeze_entropy) {} |
| |
| namespace { |
| |
| std::vector<unsigned int> GetTemporalIds(size_t num_layers) { |
| switch (num_layers) { |
| case 1: |
| // Temporal layer structure (single layer): |
| // 0 0 0 0 ... |
| return {0}; |
| case 2: |
| // Temporal layer structure: |
| // 1 1 ... |
| // 0 0 ... |
| return {0, 1}; |
| case 3: |
| // Temporal layer structure: |
| // 2 2 2 2 ... |
| // 1 1 ... |
| // 0 0 ... |
| return {0, 2, 1, 2}; |
| case 4: |
| // Temporal layer structure: |
| // 3 3 3 3 3 3 3 3 ... |
| // 2 2 2 2 ... |
| // 1 1 ... |
| // 0 0 ... |
| return {0, 3, 2, 3, 1, 3, 2, 3}; |
| default: |
| RTC_NOTREACHED(); |
| break; |
| } |
| RTC_NOTREACHED(); |
| return {0}; |
| } |
| |
| std::vector<bool> GetTemporalLayerSync(size_t num_layers) { |
| switch (num_layers) { |
| case 1: |
| return {false}; |
| case 2: |
| return {false, true, false, false, false, false, false, false}; |
| case 3: |
| if (field_trial::IsEnabled("WebRTC-UseShortVP8TL3Pattern")) { |
| return {false, true, true, false}; |
| } else { |
| return {false, true, true, false, false, false, false, false}; |
| } |
| case 4: |
| return {false, true, true, false, true, false, false, false, |
| false, false, false, false, false, false, false, false}; |
| default: |
| break; |
| } |
| RTC_NOTREACHED() << num_layers; |
| return {}; |
| } |
| |
| std::vector<TemporalLayers::FrameConfig> GetTemporalPattern(size_t num_layers) { |
| // For indexing in the patterns described below (which temporal layers they |
| // belong to), see the diagram above. |
| // Layer sync is done similarly for all patterns (except single stream) and |
| // happens every 8 frames: |
| // TL1 layer syncs by periodically by only referencing TL0 ('last'), but still |
| // updating 'golden', so it can be used as a reference by future TL1 frames. |
| // TL2 layer syncs just before TL1 by only depending on TL0 (and not depending |
| // on TL1's buffer before TL1 has layer synced). |
| // TODO(pbos): Consider cyclically updating 'arf' (and 'golden' for 1TL) for |
| // the base layer in 1-3TL instead of 'last' periodically on long intervals, |
| // so that if scene changes occur (user walks between rooms or rotates webcam) |
| // the 'arf' (or 'golden' respectively) is not stuck on a no-longer relevant |
| // keyframe. |
| switch (num_layers) { |
| case 1: |
| // All frames reference all buffers and the 'last' buffer is updated. |
| return {TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kReference, |
| TemporalLayers::kReference)}; |
| case 2: |
| // All layers can reference but not update the 'alt' buffer, this means |
| // that the 'alt' buffer reference is effectively the last keyframe. |
| // TL0 also references and updates the 'last' buffer. |
| // TL1 also references 'last' and references and updates 'golden'. |
| return {TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kNone, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kUpdate, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kNone, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kNone, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kNone, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kReference, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy)}; |
| case 3: |
| if (field_trial::IsEnabled("WebRTC-UseShortVP8TL3Pattern")) { |
| // This field trial is intended to check if it is worth using a shorter |
| // temporal pattern, trading some coding efficiency for less risk of |
| // dropped frames. |
| // The coding efficiency will decrease somewhat since the higher layer |
| // state is more volatile, but it will be offset slightly by updating |
| // the altref buffer with TL2 frames, instead of just referencing lower |
| // layers. |
| // If a frame is dropped in a higher layer, the jitter |
| // buffer on the receive side won't be able to decode any higher layer |
| // frame until the next sync frame. So we expect a noticeable decrease |
| // in frame drops on links with high packet loss. |
| |
| // TL0 references and updates the 'last' buffer. |
| // TL1 references 'last' and references and updates 'golden'. |
| // TL2 references both 'last' & 'golden' and references and updates |
| // 'arf'. |
| return {TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kNone, |
| TemporalLayers::kNone), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kNone, |
| TemporalLayers::kUpdate), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kUpdate, |
| TemporalLayers::kNone), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kReference, |
| TemporalLayers::kReference, |
| TemporalLayers::kFreezeEntropy)}; |
| } else { |
| // All layers can reference but not update the 'alt' buffer, this means |
| // that the 'alt' buffer reference is effectively the last keyframe. |
| // TL0 also references and updates the 'last' buffer. |
| // TL1 also references 'last' and references and updates 'golden'. |
| // TL2 references both 'last' and 'golden' but updates no buffer. |
| return {TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kNone, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kNone, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kUpdate, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kReference, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), |
| TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kNone, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kReference, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kReference), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kReference, |
| TemporalLayers::kReference, |
| TemporalLayers::kFreezeEntropy)}; |
| } |
| case 4: |
| // TL0 references and updates only the 'last' buffer. |
| // TL1 references 'last' and updates and references 'golden'. |
| // TL2 references 'last' and 'golden', and references and updates 'arf'. |
| // TL3 references all buffers but update none of them. |
| return {TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kNone, |
| TemporalLayers::kNone), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kNone, |
| TemporalLayers::kNone, TemporalLayers::kFreezeEntropy), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kNone, |
| TemporalLayers::kUpdate), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kNone, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kUpdate, |
| TemporalLayers::kNone), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kReference, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kReference, |
| TemporalLayers::kReferenceAndUpdate), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kReference, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), |
| TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kNone, |
| TemporalLayers::kNone), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kReference, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kReference, |
| TemporalLayers::kReferenceAndUpdate), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kReference, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kReferenceAndUpdate, |
| TemporalLayers::kNone), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kReference, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), |
| TemporalLayers::FrameConfig(TemporalLayers::kReference, |
| TemporalLayers::kReference, |
| TemporalLayers::kReferenceAndUpdate), |
| TemporalLayers::FrameConfig( |
| TemporalLayers::kReference, TemporalLayers::kReference, |
| TemporalLayers::kReference, TemporalLayers::kFreezeEntropy)}; |
| default: |
| RTC_NOTREACHED(); |
| break; |
| } |
| RTC_NOTREACHED(); |
| return {TemporalLayers::FrameConfig( |
| TemporalLayers::kNone, TemporalLayers::kNone, TemporalLayers::kNone)}; |
| } |
| |
| // Temporary fix for forced SW fallback. |
| // For VP8 SW codec, |TemporalLayers| is created and reported to |
| // SimulcastRateAllocator::OnTemporalLayersCreated but not for VP8 HW. |
| // Causes an issue when going from forced SW -> HW as |TemporalLayers| is not |
| // deregistred when deleted by SW codec (tl factory might not exist, owned by |
| // SimulcastRateAllocator). |
| bool ExcludeOnTemporalLayersCreated(int num_temporal_layers) { |
| return webrtc::field_trial::IsEnabled("WebRTC-VP8-Forced-Fallback-Encoder") && |
| num_temporal_layers == 1; |
| } |
| } // namespace |
| |
| DefaultTemporalLayers::DefaultTemporalLayers(int number_of_temporal_layers, |
| uint8_t initial_tl0_pic_idx) |
| : num_layers_(std::max(1, number_of_temporal_layers)), |
| temporal_ids_(GetTemporalIds(num_layers_)), |
| temporal_layer_sync_(GetTemporalLayerSync(num_layers_)), |
| temporal_pattern_(GetTemporalPattern(num_layers_)), |
| tl0_pic_idx_(initial_tl0_pic_idx), |
| pattern_idx_(255), |
| last_base_layer_sync_(false) { |
| RTC_DCHECK_EQ(temporal_pattern_.size(), temporal_layer_sync_.size()); |
| RTC_CHECK_GE(kMaxTemporalStreams, number_of_temporal_layers); |
| RTC_CHECK_GE(number_of_temporal_layers, 0); |
| RTC_CHECK_LE(number_of_temporal_layers, 4); |
| // pattern_idx_ wraps around temporal_pattern_.size, this is incorrect if |
| // temporal_ids_ are ever longer. If this is no longer correct it needs to |
| // wrap at max(temporal_ids_.size(), temporal_pattern_.size()). |
| RTC_DCHECK_LE(temporal_ids_.size(), temporal_pattern_.size()); |
| } |
| |
| uint8_t DefaultTemporalLayers::Tl0PicIdx() const { |
| return tl0_pic_idx_; |
| } |
| |
| std::vector<uint32_t> DefaultTemporalLayers::OnRatesUpdated( |
| int bitrate_kbps, |
| int max_bitrate_kbps, |
| int framerate) { |
| std::vector<uint32_t> bitrates; |
| for (size_t 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 (size_t 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; |
| |
| 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; |
| } |
| |
| TemporalLayers::FrameConfig DefaultTemporalLayers::UpdateLayerConfig( |
| uint32_t timestamp) { |
| RTC_DCHECK_GT(num_layers_, 0); |
| RTC_DCHECK_LT(0, temporal_pattern_.size()); |
| pattern_idx_ = (pattern_idx_ + 1) % temporal_pattern_.size(); |
| TemporalLayers::FrameConfig tl_config = temporal_pattern_[pattern_idx_]; |
| tl_config.layer_sync = |
| temporal_layer_sync_[pattern_idx_ % temporal_layer_sync_.size()]; |
| tl_config.encoder_layer_id = tl_config.packetizer_temporal_idx = |
| temporal_ids_[pattern_idx_ % temporal_ids_.size()]; |
| return tl_config; |
| } |
| |
| void DefaultTemporalLayers::PopulateCodecSpecific( |
| bool frame_is_keyframe, |
| const TemporalLayers::FrameConfig& tl_config, |
| CodecSpecificInfoVP8* vp8_info, |
| uint32_t timestamp) { |
| RTC_DCHECK_GT(num_layers_, 0); |
| |
| if (num_layers_ == 1) { |
| vp8_info->temporalIdx = kNoTemporalIdx; |
| vp8_info->layerSync = false; |
| vp8_info->tl0PicIdx = kNoTl0PicIdx; |
| } else { |
| vp8_info->temporalIdx = tl_config.packetizer_temporal_idx; |
| vp8_info->layerSync = tl_config.layer_sync; |
| if (frame_is_keyframe) { |
| vp8_info->temporalIdx = 0; |
| vp8_info->layerSync = true; |
| } |
| if (last_base_layer_sync_ && vp8_info->temporalIdx != 0) { |
| // Regardless of pattern the frame after a base layer sync will always |
| // be a layer sync. |
| vp8_info->layerSync = true; |
| } |
| if (vp8_info->temporalIdx == 0) |
| tl0_pic_idx_++; |
| last_base_layer_sync_ = frame_is_keyframe; |
| vp8_info->tl0PicIdx = tl0_pic_idx_; |
| } |
| } |
| |
| TemporalLayers* TemporalLayersFactory::Create( |
| int simulcast_id, |
| int temporal_layers, |
| uint8_t initial_tl0_pic_idx) const { |
| TemporalLayers* tl = |
| new DefaultTemporalLayers(temporal_layers, initial_tl0_pic_idx); |
| if (listener_ && !ExcludeOnTemporalLayersCreated(temporal_layers)) |
| listener_->OnTemporalLayersCreated(simulcast_id, tl); |
| return tl; |
| } |
| |
| void TemporalLayersFactory::SetListener(TemporalLayersListener* listener) { |
| listener_ = listener; |
| } |
| |
| } // namespace webrtc |