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 979c64e243..02c3f1541f 100644 --- a/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc +++ b/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.cc @@ -96,16 +96,19 @@ std::vector GetTemporalLayerSync(size_t num_layers) { case 2: return {false, true, false, false, false, false, false, false}; case 3: - return {false, true, true, false, false, false, false, false}; + 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, true, true, true, false, true, - false, true, false, true, false, true, false, true}; + return {false, true, true, false, true, false, false, false, + false, false, false, false, false, false, false, false}; default: - RTC_NOTREACHED(); break; } - RTC_NOTREACHED(); - return {false}; + RTC_NOTREACHED() << num_layers; + return {}; } std::vector GetTemporalPattern(size_t num_layers) { @@ -134,7 +137,7 @@ std::vector GetTemporalPattern(size_t num_layers) { // TL0 also references and updates the 'last' buffer. // TL1 also references 'last' and references and updates 'golden'. return {TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, - TemporalLayers::kUpdate, + TemporalLayers::kNone, TemporalLayers::kReference), TemporalLayers::FrameConfig(TemporalLayers::kReference, TemporalLayers::kUpdate, @@ -158,35 +161,68 @@ std::vector GetTemporalPattern(size_t num_layers) { TemporalLayers::kReference, TemporalLayers::kReference, TemporalLayers::kReference, TemporalLayers::kFreezeEntropy)}; case 3: - // 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::kUpdate, - 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)}; + 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'. @@ -196,13 +232,13 @@ std::vector GetTemporalPattern(size_t num_layers) { TemporalLayers::kNone, TemporalLayers::kNone), TemporalLayers::FrameConfig( - TemporalLayers::kReference, TemporalLayers::kReference, - TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), + TemporalLayers::kReference, TemporalLayers::kNone, + TemporalLayers::kNone, TemporalLayers::kFreezeEntropy), TemporalLayers::FrameConfig(TemporalLayers::kReference, TemporalLayers::kNone, TemporalLayers::kUpdate), TemporalLayers::FrameConfig( - TemporalLayers::kReference, TemporalLayers::kReference, + TemporalLayers::kReference, TemporalLayers::kNone, TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), TemporalLayers::FrameConfig(TemporalLayers::kReference, TemporalLayers::kUpdate, 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 fd4bf527b4..0075fd51b1 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 { + 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 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 diff --git a/webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h b/webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h index 7a27e4429a..67888f7a21 100644 --- a/webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h +++ b/webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h @@ -18,7 +18,7 @@ namespace webrtc { // Ratio allocation between temporal streams: // Values as required for the VP8 codec (accumulating). static const float - kVp8LayerRateAlloction[kMaxTemporalStreams][kMaxTemporalStreams] = { + kVp8LayerRateAlloction[kMaxSimulcastStreams][kMaxTemporalStreams] = { {1.0f, 1.0f, 1.0f, 1.0f}, // 1 layer {0.6f, 1.0f, 1.0f, 1.0f}, // 2 layers {60%, 40%} {0.4f, 0.6f, 1.0f, 1.0f}, // 3 layers {40%, 20%, 40%} diff --git a/webrtc/video/full_stack_tests.cc b/webrtc/video/full_stack_tests.cc index 66253cc70e..24c362572e 100644 --- a/webrtc/video/full_stack_tests.cc +++ b/webrtc/video/full_stack_tests.cc @@ -304,6 +304,7 @@ TEST_F(FullStackTest, ForemanCif1000kbps100msLimitedQueue) { RunTest(foreman_cif); } +// TODO(sprang): Remove this if we have the similar ModerateLimits below? TEST_F(FullStackTest, ConferenceMotionHd2000kbps100msLimitedQueue) { VideoQualityTest::Params conf_motion_hd; conf_motion_hd.call.send_side_bwe = true; @@ -319,6 +320,87 @@ TEST_F(FullStackTest, ConferenceMotionHd2000kbps100msLimitedQueue) { RunTest(conf_motion_hd); } +TEST_F(FullStackTest, ConferenceMotionHd1TLModerateLimits) { + VideoQualityTest::Params conf_motion_hd; + conf_motion_hd.call.send_side_bwe = true; + conf_motion_hd.video = { + true, 1280, 720, 50, 30000, + 3000000, 3000000, false, "VP8", 1, + -1, 0, false, false, "ConferenceMotion_1280_720_50"}; + conf_motion_hd.analyzer = {"conference_motion_hd_1tl_moderate_limits", 0.0, + 0.0, kFullStackTestDurationSecs}; + conf_motion_hd.pipe.queue_length_packets = 50; + conf_motion_hd.pipe.loss_percent = 3; + conf_motion_hd.pipe.queue_delay_ms = 100; + conf_motion_hd.pipe.link_capacity_kbps = 2000; + RunTest(conf_motion_hd); +} + +TEST_F(FullStackTest, ConferenceMotionHd2TLModerateLimits) { + VideoQualityTest::Params conf_motion_hd; + conf_motion_hd.call.send_side_bwe = true; + conf_motion_hd.video = { + true, 1280, 720, 50, 30000, + 3000000, 3000000, false, "VP8", 2, + -1, 0, false, false, "ConferenceMotion_1280_720_50"}; + conf_motion_hd.analyzer = {"conference_motion_hd_2tl_moderate_limits", 0.0, + 0.0, kFullStackTestDurationSecs}; + conf_motion_hd.pipe.queue_length_packets = 50; + conf_motion_hd.pipe.loss_percent = 3; + conf_motion_hd.pipe.queue_delay_ms = 100; + conf_motion_hd.pipe.link_capacity_kbps = 2000; + RunTest(conf_motion_hd); +} + +TEST_F(FullStackTest, ConferenceMotionHd3TLModerateLimits) { + VideoQualityTest::Params conf_motion_hd; + conf_motion_hd.call.send_side_bwe = true; + conf_motion_hd.video = { + true, 1280, 720, 50, 30000, + 3000000, 3000000, false, "VP8", 3, + -1, 0, false, false, "ConferenceMotion_1280_720_50"}; + conf_motion_hd.analyzer = {"conference_motion_hd_3tl_moderate_limits", 0.0, + 0.0, kFullStackTestDurationSecs}; + conf_motion_hd.pipe.queue_length_packets = 50; + conf_motion_hd.pipe.loss_percent = 3; + conf_motion_hd.pipe.queue_delay_ms = 100; + conf_motion_hd.pipe.link_capacity_kbps = 2000; + RunTest(conf_motion_hd); +} + +TEST_F(FullStackTest, ConferenceMotionHd4TLModerateLimits) { + VideoQualityTest::Params conf_motion_hd; + conf_motion_hd.call.send_side_bwe = true; + conf_motion_hd.video = { + true, 1280, 720, 50, 30000, + 3000000, 3000000, false, "VP8", 4, + -1, 0, false, false, "ConferenceMotion_1280_720_50"}; + conf_motion_hd.analyzer = {"conference_motion_hd_4tl_moderate_limits", 0.0, + 0.0, kFullStackTestDurationSecs}; + conf_motion_hd.pipe.queue_length_packets = 50; + conf_motion_hd.pipe.loss_percent = 3; + conf_motion_hd.pipe.queue_delay_ms = 100; + conf_motion_hd.pipe.link_capacity_kbps = 2000; + RunTest(conf_motion_hd); +} + +TEST_F(FullStackTest, ConferenceMotionHd3TLModerateLimitsAltTLPattern) { + test::ScopedFieldTrials field_trial("WebRTC-UseShortVP8TL3Pattern/Enabled/"); + VideoQualityTest::Params conf_motion_hd; + conf_motion_hd.call.send_side_bwe = true; + conf_motion_hd.video = { + true, 1280, 720, 50, 30000, + 3000000, 3000000, false, "VP8", 3, + -1, 0, false, false, "ConferenceMotion_1280_720_50"}; + conf_motion_hd.analyzer = {"conference_motion_hd_3tl_alt_moderate_limits", + 0.0, 0.0, kFullStackTestDurationSecs}; + conf_motion_hd.pipe.queue_length_packets = 50; + conf_motion_hd.pipe.loss_percent = 3; + conf_motion_hd.pipe.queue_delay_ms = 100; + conf_motion_hd.pipe.link_capacity_kbps = 2000; + RunTest(conf_motion_hd); +} + #if !defined(RTC_DISABLE_VP9) TEST_F(FullStackTest, ConferenceMotionHd2000kbps100msLimitedQueueVP9) { VideoQualityTest::Params conf_motion_hd; diff --git a/webrtc/video/replay.cc b/webrtc/video/replay.cc index 5a391d69b0..c93017acd0 100644 --- a/webrtc/video/replay.cc +++ b/webrtc/video/replay.cc @@ -198,8 +198,10 @@ class DecoderBitstreamFileWriter : public EncodedFrameObserver { }; void RtpReplay() { + std::stringstream window_title; + window_title << "Playback Video (" << flags::InputFile() << ")"; std::unique_ptr playback_video( - test::VideoRenderer::Create("Playback Video", 640, 480)); + test::VideoRenderer::Create(window_title.str().c_str(), 640, 480)); FileRenderPassthrough file_passthrough(flags::OutBase(), playback_video.get()); diff --git a/webrtc/video/video_quality_test.cc b/webrtc/video/video_quality_test.cc index 8224377627..92d796822a 100644 --- a/webrtc/video/video_quality_test.cc +++ b/webrtc/video/video_quality_test.cc @@ -1297,6 +1297,12 @@ VideoStream VideoQualityTest::DefaultVideoStream(const Params& params) { } else if (params.video.num_temporal_layers == 3) { stream.temporal_layer_thresholds_bps.push_back(stream.max_bitrate_bps / 4); stream.temporal_layer_thresholds_bps.push_back(stream.target_bitrate_bps); + } else { + RTC_CHECK_LE(params.video.num_temporal_layers, kMaxTemporalStreams); + for (int i = 0; i < params.video.num_temporal_layers - 1; ++i) { + stream.temporal_layer_thresholds_bps.push_back(static_cast( + stream.max_bitrate_bps * kVp8LayerRateAlloction[0][i] + 0.5)); + } } return stream; }