Index: webrtc/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc |
diff --git a/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc b/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc |
index fd4bf527b4a1327a5697f04bc88099e224723035..0075fd51b14d86ff64a0a0009342a3c1c125f653 100644 |
--- a/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc |
+++ b/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc |
@@ -13,14 +13,15 @@ |
#include "vpx/vpx_encoder.h" |
#include "webrtc/modules/video_coding/codecs/vp8/vp8_impl.h" |
#include "webrtc/modules/video_coding/include/video_codec_interface.h" |
+#include "webrtc/test/field_trial.h" |
#include "webrtc/test/gtest.h" |
namespace webrtc { |
+namespace test { |
enum { |
kTemporalUpdateLast = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | |
- VP8_EFLAG_NO_REF_GF | |
- VP8_EFLAG_NO_REF_ARF, |
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF, |
kTemporalUpdateGoldenWithoutDependency = |
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF | |
VP8_EFLAG_NO_UPD_LAST, |
@@ -31,16 +32,16 @@ enum { |
VP8_EFLAG_NO_UPD_LAST, |
kTemporalUpdateAltref = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_LAST, |
kTemporalUpdateNone = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | |
- VP8_EFLAG_NO_UPD_LAST | |
- VP8_EFLAG_NO_UPD_ENTROPY, |
- kTemporalUpdateNoneNoRefAltRef = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_GF | |
- VP8_EFLAG_NO_UPD_ARF | |
- VP8_EFLAG_NO_UPD_LAST | |
- VP8_EFLAG_NO_UPD_ENTROPY, |
- kTemporalUpdateNoneNoRefGolden = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF | |
- VP8_EFLAG_NO_UPD_ARF | |
- VP8_EFLAG_NO_UPD_LAST | |
- VP8_EFLAG_NO_UPD_ENTROPY, |
+ VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY, |
+ kTemporalUpdateNoneNoRefAltRef = |
+ VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | |
+ VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY, |
+ kTemporalUpdateNoneNoRefGolden = |
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | |
+ VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY, |
+ kTemporalUpdateNoneNoRefGoldenAltRef = |
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_REF_ARF | |
+ VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY, |
kTemporalUpdateGoldenWithoutDependencyRefAltRef = |
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST, |
kTemporalUpdateGoldenRefAltRef = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST, |
@@ -58,7 +59,7 @@ TEST(TemporalLayersTest, 2Layers) { |
tl.UpdateConfiguration(&cfg); |
int expected_flags[16] = { |
- kTemporalUpdateLastAndGoldenRefAltRef, |
+ kTemporalUpdateLastRefAltRef, |
kTemporalUpdateGoldenWithoutDependencyRefAltRef, |
kTemporalUpdateLastRefAltRef, |
kTemporalUpdateGoldenRefAltRef, |
@@ -66,7 +67,7 @@ TEST(TemporalLayersTest, 2Layers) { |
kTemporalUpdateGoldenRefAltRef, |
kTemporalUpdateLastRefAltRef, |
kTemporalUpdateNone, |
- kTemporalUpdateLastAndGoldenRefAltRef, |
+ kTemporalUpdateLastRefAltRef, |
kTemporalUpdateGoldenWithoutDependencyRefAltRef, |
kTemporalUpdateLastRefAltRef, |
kTemporalUpdateGoldenRefAltRef, |
@@ -85,7 +86,7 @@ TEST(TemporalLayersTest, 2Layers) { |
uint32_t timestamp = 0; |
for (int i = 0; i < 16; ++i) { |
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp); |
- EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)); |
+ EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)) << i; |
tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0); |
EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx); |
EXPECT_EQ(expected_temporal_idx[i], tl_config.packetizer_temporal_idx); |
@@ -104,7 +105,7 @@ TEST(TemporalLayersTest, 3Layers) { |
tl.UpdateConfiguration(&cfg); |
int expected_flags[16] = { |
- kTemporalUpdateLastAndGoldenRefAltRef, |
+ kTemporalUpdateLastRefAltRef, |
kTemporalUpdateNoneNoRefGolden, |
kTemporalUpdateGoldenWithoutDependencyRefAltRef, |
kTemporalUpdateNone, |
@@ -112,7 +113,7 @@ TEST(TemporalLayersTest, 3Layers) { |
kTemporalUpdateNone, |
kTemporalUpdateGoldenRefAltRef, |
kTemporalUpdateNone, |
- kTemporalUpdateLastAndGoldenRefAltRef, |
+ kTemporalUpdateLastRefAltRef, |
kTemporalUpdateNoneNoRefGolden, |
kTemporalUpdateGoldenWithoutDependencyRefAltRef, |
kTemporalUpdateNone, |
@@ -131,7 +132,42 @@ TEST(TemporalLayersTest, 3Layers) { |
unsigned int timestamp = 0; |
for (int i = 0; i < 16; ++i) { |
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp); |
- EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)); |
+ EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)) << i; |
+ tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0); |
+ EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx); |
+ EXPECT_EQ(expected_temporal_idx[i], tl_config.packetizer_temporal_idx); |
+ EXPECT_EQ(expected_temporal_idx[i], tl_config.encoder_layer_id); |
+ EXPECT_EQ(expected_layer_sync[i], vp8_info.layerSync); |
+ EXPECT_EQ(expected_layer_sync[i], tl_config.layer_sync); |
+ timestamp += 3000; |
+ } |
+} |
+ |
+TEST(TemporalLayersTest, Alternative3Layers) { |
+ ScopedFieldTrials field_trial("WebRTC-UseShortVP8TL3Pattern/Enabled/"); |
+ DefaultTemporalLayers tl(3, 0); |
+ vpx_codec_enc_cfg_t cfg; |
+ CodecSpecificInfoVP8 vp8_info; |
+ tl.OnRatesUpdated(500, 500, 30); |
+ tl.UpdateConfiguration(&cfg); |
+ |
+ int expected_flags[8] = {kTemporalUpdateLast, |
+ kTemporalUpdateAltrefWithoutDependency, |
+ kTemporalUpdateGoldenWithoutDependency, |
+ kTemporalUpdateNone, |
+ kTemporalUpdateLast, |
+ kTemporalUpdateAltrefWithoutDependency, |
+ kTemporalUpdateGoldenWithoutDependency, |
+ kTemporalUpdateNone}; |
+ int expected_temporal_idx[8] = {0, 2, 1, 2, 0, 2, 1, 2}; |
+ |
+ bool expected_layer_sync[8] = {false, true, true, false, |
+ false, true, true, false}; |
+ |
+ unsigned int timestamp = 0; |
+ for (int i = 0; i < 8; ++i) { |
+ TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp); |
+ EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)) << i; |
tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0); |
EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx); |
EXPECT_EQ(expected_temporal_idx[i], tl_config.packetizer_temporal_idx); |
@@ -150,9 +186,9 @@ TEST(TemporalLayersTest, 4Layers) { |
tl.UpdateConfiguration(&cfg); |
int expected_flags[16] = { |
kTemporalUpdateLast, |
- kTemporalUpdateNone, |
+ kTemporalUpdateNoneNoRefGoldenAltRef, |
kTemporalUpdateAltrefWithoutDependency, |
- kTemporalUpdateNone, |
+ kTemporalUpdateNoneNoRefGolden, |
kTemporalUpdateGoldenWithoutDependency, |
kTemporalUpdateNone, |
kTemporalUpdateAltref, |
@@ -169,14 +205,14 @@ TEST(TemporalLayersTest, 4Layers) { |
int expected_temporal_idx[16] = {0, 3, 2, 3, 1, 3, 2, 3, |
0, 3, 2, 3, 1, 3, 2, 3}; |
- bool expected_layer_sync[16] = {false, true, true, true, true, true, |
- false, true, false, true, false, true, |
- false, true, false, true}; |
+ bool expected_layer_sync[16] = {false, true, true, false, true, false, |
+ false, false, false, false, false, false, |
+ false, false, false, false}; |
uint32_t timestamp = 0; |
for (int i = 0; i < 16; ++i) { |
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp); |
- EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)); |
+ EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)) << i; |
tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0); |
EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx); |
EXPECT_EQ(expected_temporal_idx[i], tl_config.packetizer_temporal_idx); |
@@ -195,7 +231,7 @@ TEST(TemporalLayersTest, KeyFrame) { |
tl.UpdateConfiguration(&cfg); |
int expected_flags[8] = { |
- kTemporalUpdateLastAndGoldenRefAltRef, |
+ kTemporalUpdateLastRefAltRef, |
kTemporalUpdateNoneNoRefGolden, |
kTemporalUpdateGoldenWithoutDependencyRefAltRef, |
kTemporalUpdateNone, |
@@ -211,7 +247,7 @@ TEST(TemporalLayersTest, KeyFrame) { |
uint32_t timestamp = 0; |
for (int i = 0; i < 7; ++i) { |
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp); |
- EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)); |
+ EXPECT_EQ(expected_flags[i], VP8EncoderImpl::EncodeFlags(tl_config)) << i; |
tl.PopulateCodecSpecific(true, tl_config, &vp8_info, 0); |
EXPECT_EQ(expected_temporal_idx[i], tl_config.packetizer_temporal_idx); |
EXPECT_EQ(expected_temporal_idx[i], tl_config.encoder_layer_id); |
@@ -235,4 +271,128 @@ TEST(TemporalLayersTest, KeyFrame) { |
"marked layer sync since it only depends " |
"on the base layer."; |
} |
+ |
+class TemporalLayersReferenceTest : public ::testing::TestWithParam<int> { |
+ public: |
+ TemporalLayersReferenceTest() |
+ : timestamp_(1), |
+ last_sync_timestamp_(timestamp_), |
+ tl0_reference_(nullptr) {} |
+ virtual ~TemporalLayersReferenceTest() {} |
+ |
+ protected: |
+ static const int kMaxPatternLength = 32; |
+ |
+ struct BufferState { |
+ BufferState() : BufferState(-1, 0, false) {} |
+ BufferState(int temporal_idx, uint32_t timestamp, bool sync) |
+ : temporal_idx(temporal_idx), timestamp(timestamp), sync(sync) {} |
+ int temporal_idx; |
+ uint32_t timestamp; |
+ bool sync; |
+ }; |
+ |
+ bool UpdateSyncRefState(const TemporalLayers::BufferFlags& flags, |
+ BufferState* buffer_state) { |
+ if (flags & TemporalLayers::kReference) { |
+ if (buffer_state->temporal_idx == -1) |
+ return true; // References key-frame. |
+ if (buffer_state->temporal_idx == 0) { |
+ // No more than one reference to TL0 frame. |
+ EXPECT_EQ(nullptr, tl0_reference_); |
+ tl0_reference_ = buffer_state; |
+ return true; |
+ } |
+ return false; // References higher layer. |
+ } |
+ return true; // No reference, does not affect sync frame status. |
+ } |
+ |
+ void ValidateReference(const TemporalLayers::BufferFlags& flags, |
+ const BufferState& buffer_state, |
+ int temporal_layer) { |
+ if (flags & TemporalLayers::kReference) { |
+ if (temporal_layer > 0 && buffer_state.timestamp > 0) { |
+ // Check that high layer reference does not go past last sync frame. |
+ EXPECT_GE(buffer_state.timestamp, last_sync_timestamp_); |
+ } |
+ // No reference to buffer in higher layer. |
+ EXPECT_LE(buffer_state.temporal_idx, temporal_layer); |
+ } |
+ } |
+ |
+ uint32_t timestamp_ = 1; |
+ uint32_t last_sync_timestamp_ = timestamp_; |
+ BufferState* tl0_reference_; |
+ |
+ BufferState last_state; |
+ BufferState golden_state; |
+ BufferState altref_state; |
+}; |
+ |
+INSTANTIATE_TEST_CASE_P(DefaultTemporalLayersTest, |
+ TemporalLayersReferenceTest, |
+ ::testing::Range(1, kMaxTemporalStreams + 1)); |
+ |
+TEST_P(TemporalLayersReferenceTest, ValidFrameConfigs) { |
+ const int num_layers = GetParam(); |
+ DefaultTemporalLayers tl(num_layers, 0); |
+ vpx_codec_enc_cfg_t cfg; |
+ tl.OnRatesUpdated(500, 500, 30); |
+ tl.UpdateConfiguration(&cfg); |
+ |
+ // Run through the pattern and store the frame dependencies, plus keep track |
+ // of the buffer state; which buffers references which temporal layers (if |
+ // (any). If a given buffer is never updated, it is legal to reference it |
+ // even for sync frames. In order to be general, don't assume TL0 always |
+ // updates |last|. |
+ std::vector<TemporalLayers::FrameConfig> tl_configs(kMaxPatternLength); |
+ for (int i = 0; i < kMaxPatternLength; ++i) { |
+ TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp_++); |
+ EXPECT_FALSE(tl_config.drop_frame); |
+ tl_configs.push_back(tl_config); |
+ int temporal_idx = tl_config.encoder_layer_id; |
+ // For the default layers, always keep encoder and rtp layers in sync. |
+ EXPECT_EQ(tl_config.packetizer_temporal_idx, temporal_idx); |
+ |
+ // Determine if this frame is in a higher layer but references only TL0 |
+ // or untouched buffers, if so verify it is marked as a layer sync. |
+ bool is_sync_frame = true; |
+ tl0_reference_ = nullptr; |
+ if (temporal_idx <= 0) { |
+ is_sync_frame = false; // TL0 by definition not a sync frame. |
+ } else if (!UpdateSyncRefState(tl_config.last_buffer_flags, &last_state)) { |
+ is_sync_frame = false; |
+ } else if (!UpdateSyncRefState(tl_config.golden_buffer_flags, |
+ &golden_state)) { |
+ is_sync_frame = false; |
+ } else if (!UpdateSyncRefState(tl_config.arf_buffer_flags, &altref_state)) { |
+ is_sync_frame = false; |
+ } |
+ if (is_sync_frame) { |
+ // Cache timestamp for last found sync frame, so that we can verify no |
+ // references back past this frame. |
+ ASSERT_TRUE(tl0_reference_); |
+ last_sync_timestamp_ = tl0_reference_->timestamp; |
+ } |
+ EXPECT_EQ(tl_config.layer_sync, is_sync_frame); |
+ |
+ // Validate no reference from lower to high temporal layer, or backwards |
+ // past last reference frame. |
+ ValidateReference(tl_config.last_buffer_flags, last_state, temporal_idx); |
+ ValidateReference(tl_config.golden_buffer_flags, golden_state, |
+ temporal_idx); |
+ ValidateReference(tl_config.arf_buffer_flags, altref_state, temporal_idx); |
+ |
+ // Update the current layer state. |
+ BufferState state = {temporal_idx, timestamp_, is_sync_frame}; |
+ if (tl_config.last_buffer_flags & TemporalLayers::kUpdate) |
+ last_state = state; |
+ if (tl_config.golden_buffer_flags & TemporalLayers::kUpdate) |
+ golden_state = state; |
+ if (tl_config.arf_buffer_flags & TemporalLayers::kUpdate) |
+ altref_state = state; |
+ } |
+} |
+} // namespace test |
} // namespace webrtc |