Fix FrameConfigs used for VP8 with four temporal layers.

sync flag should only be true if:

* temporal layer > 0
* frame predics _only_ from tl0 (last)

Furthermore, flags should not predict from a buffer unless it has been
previously updated in the current iteration for the pattern.

I also added an experiment with an alternative pattern for a three
layer setup, where loose some efficieny by halving the pattern but
gain a little bit by updating arf for the top layer. The theory is that
this will cause fewer frame drops since we don't have as much
dependency on previous frames in the upper layer (which might not be
retransmitted).

BUG=webrtc:8162

Review-Url: https://codereview.webrtc.org/3003823003
Cr-Commit-Position: refs/heads/master@{#19716}
This commit is contained in:
sprang
2017-09-06 07:14:02 -07:00
committed by Commit Bot
parent 35713eaf56
commit ff19d35bae
6 changed files with 353 additions and 67 deletions

View File

@ -96,16 +96,19 @@ std::vector<bool> GetTemporalLayerSync(size_t num_layers) {
case 2: case 2:
return {false, true, false, false, false, false, false, false}; return {false, true, false, false, false, false, false, false};
case 3: case 3:
if (field_trial::IsEnabled("WebRTC-UseShortVP8TL3Pattern")) {
return {false, true, true, false};
} else {
return {false, true, true, false, false, false, false, false}; return {false, true, true, false, false, false, false, false};
}
case 4: case 4:
return {false, true, true, true, true, true, false, true, return {false, true, true, false, true, false, false, false,
false, true, false, true, false, true, false, true}; false, false, false, false, false, false, false, false};
default: default:
RTC_NOTREACHED();
break; break;
} }
RTC_NOTREACHED(); RTC_NOTREACHED() << num_layers;
return {false}; return {};
} }
std::vector<TemporalLayers::FrameConfig> GetTemporalPattern(size_t num_layers) { std::vector<TemporalLayers::FrameConfig> GetTemporalPattern(size_t num_layers) {
@ -134,7 +137,7 @@ std::vector<TemporalLayers::FrameConfig> GetTemporalPattern(size_t num_layers) {
// TL0 also references and updates the 'last' buffer. // TL0 also references and updates the 'last' buffer.
// TL1 also references 'last' and references and updates 'golden'. // TL1 also references 'last' and references and updates 'golden'.
return {TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, return {TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate,
TemporalLayers::kUpdate, TemporalLayers::kNone,
TemporalLayers::kReference), TemporalLayers::kReference),
TemporalLayers::FrameConfig(TemporalLayers::kReference, TemporalLayers::FrameConfig(TemporalLayers::kReference,
TemporalLayers::kUpdate, TemporalLayers::kUpdate,
@ -158,13 +161,44 @@ std::vector<TemporalLayers::FrameConfig> GetTemporalPattern(size_t num_layers) {
TemporalLayers::kReference, TemporalLayers::kReference, TemporalLayers::kReference, TemporalLayers::kReference,
TemporalLayers::kReference, TemporalLayers::kFreezeEntropy)}; TemporalLayers::kReference, TemporalLayers::kFreezeEntropy)};
case 3: 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 // All layers can reference but not update the 'alt' buffer, this means
// that the 'alt' buffer reference is effectively the last keyframe. // that the 'alt' buffer reference is effectively the last keyframe.
// TL0 also references and updates the 'last' buffer. // TL0 also references and updates the 'last' buffer.
// TL1 also references 'last' and references and updates 'golden'. // TL1 also references 'last' and references and updates 'golden'.
// TL2 references both 'last' and 'golden' but updates no buffer. // TL2 references both 'last' and 'golden' but updates no buffer.
return {TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate, return {TemporalLayers::FrameConfig(TemporalLayers::kReferenceAndUpdate,
TemporalLayers::kUpdate, TemporalLayers::kNone,
TemporalLayers::kReference), TemporalLayers::kReference),
TemporalLayers::FrameConfig( TemporalLayers::FrameConfig(
TemporalLayers::kReference, TemporalLayers::kNone, TemporalLayers::kReference, TemporalLayers::kNone,
@ -184,9 +218,11 @@ std::vector<TemporalLayers::FrameConfig> GetTemporalPattern(size_t num_layers) {
TemporalLayers::FrameConfig(TemporalLayers::kReference, TemporalLayers::FrameConfig(TemporalLayers::kReference,
TemporalLayers::kReferenceAndUpdate, TemporalLayers::kReferenceAndUpdate,
TemporalLayers::kReference), TemporalLayers::kReference),
TemporalLayers::FrameConfig( TemporalLayers::FrameConfig(TemporalLayers::kReference,
TemporalLayers::kReference, TemporalLayers::kReference, TemporalLayers::kReference,
TemporalLayers::kReference, TemporalLayers::kFreezeEntropy)}; TemporalLayers::kReference,
TemporalLayers::kFreezeEntropy)};
}
case 4: case 4:
// TL0 references and updates only the 'last' buffer. // TL0 references and updates only the 'last' buffer.
// TL1 references 'last' and updates and references 'golden'. // TL1 references 'last' and updates and references 'golden'.
@ -196,13 +232,13 @@ std::vector<TemporalLayers::FrameConfig> GetTemporalPattern(size_t num_layers) {
TemporalLayers::kNone, TemporalLayers::kNone,
TemporalLayers::kNone), TemporalLayers::kNone),
TemporalLayers::FrameConfig( TemporalLayers::FrameConfig(
TemporalLayers::kReference, TemporalLayers::kReference, TemporalLayers::kReference, TemporalLayers::kNone,
TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), TemporalLayers::kNone, TemporalLayers::kFreezeEntropy),
TemporalLayers::FrameConfig(TemporalLayers::kReference, TemporalLayers::FrameConfig(TemporalLayers::kReference,
TemporalLayers::kNone, TemporalLayers::kNone,
TemporalLayers::kUpdate), TemporalLayers::kUpdate),
TemporalLayers::FrameConfig( TemporalLayers::FrameConfig(
TemporalLayers::kReference, TemporalLayers::kReference, TemporalLayers::kReference, TemporalLayers::kNone,
TemporalLayers::kReference, TemporalLayers::kFreezeEntropy), TemporalLayers::kReference, TemporalLayers::kFreezeEntropy),
TemporalLayers::FrameConfig(TemporalLayers::kReference, TemporalLayers::FrameConfig(TemporalLayers::kReference,
TemporalLayers::kUpdate, TemporalLayers::kUpdate,

View File

@ -13,14 +13,15 @@
#include "vpx/vpx_encoder.h" #include "vpx/vpx_encoder.h"
#include "webrtc/modules/video_coding/codecs/vp8/vp8_impl.h" #include "webrtc/modules/video_coding/codecs/vp8/vp8_impl.h"
#include "webrtc/modules/video_coding/include/video_codec_interface.h" #include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/test/field_trial.h"
#include "webrtc/test/gtest.h" #include "webrtc/test/gtest.h"
namespace webrtc { namespace webrtc {
namespace test {
enum { enum {
kTemporalUpdateLast = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | kTemporalUpdateLast = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF,
VP8_EFLAG_NO_REF_ARF,
kTemporalUpdateGoldenWithoutDependency = kTemporalUpdateGoldenWithoutDependency =
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF |
VP8_EFLAG_NO_UPD_LAST, VP8_EFLAG_NO_UPD_LAST,
@ -31,16 +32,16 @@ enum {
VP8_EFLAG_NO_UPD_LAST, VP8_EFLAG_NO_UPD_LAST,
kTemporalUpdateAltref = VP8_EFLAG_NO_UPD_GF | 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 | kTemporalUpdateNone = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY,
VP8_EFLAG_NO_UPD_ENTROPY, kTemporalUpdateNoneNoRefAltRef =
kTemporalUpdateNoneNoRefAltRef = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY,
VP8_EFLAG_NO_UPD_LAST | kTemporalUpdateNoneNoRefGolden =
VP8_EFLAG_NO_UPD_ENTROPY, VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
kTemporalUpdateNoneNoRefGolden = VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY,
VP8_EFLAG_NO_UPD_ARF | kTemporalUpdateNoneNoRefGoldenAltRef =
VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_REF_ARF |
VP8_EFLAG_NO_UPD_ENTROPY, VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ENTROPY,
kTemporalUpdateGoldenWithoutDependencyRefAltRef = kTemporalUpdateGoldenWithoutDependencyRefAltRef =
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST, 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, kTemporalUpdateGoldenRefAltRef = VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST,
@ -58,7 +59,7 @@ TEST(TemporalLayersTest, 2Layers) {
tl.UpdateConfiguration(&cfg); tl.UpdateConfiguration(&cfg);
int expected_flags[16] = { int expected_flags[16] = {
kTemporalUpdateLastAndGoldenRefAltRef, kTemporalUpdateLastRefAltRef,
kTemporalUpdateGoldenWithoutDependencyRefAltRef, kTemporalUpdateGoldenWithoutDependencyRefAltRef,
kTemporalUpdateLastRefAltRef, kTemporalUpdateLastRefAltRef,
kTemporalUpdateGoldenRefAltRef, kTemporalUpdateGoldenRefAltRef,
@ -66,7 +67,7 @@ TEST(TemporalLayersTest, 2Layers) {
kTemporalUpdateGoldenRefAltRef, kTemporalUpdateGoldenRefAltRef,
kTemporalUpdateLastRefAltRef, kTemporalUpdateLastRefAltRef,
kTemporalUpdateNone, kTemporalUpdateNone,
kTemporalUpdateLastAndGoldenRefAltRef, kTemporalUpdateLastRefAltRef,
kTemporalUpdateGoldenWithoutDependencyRefAltRef, kTemporalUpdateGoldenWithoutDependencyRefAltRef,
kTemporalUpdateLastRefAltRef, kTemporalUpdateLastRefAltRef,
kTemporalUpdateGoldenRefAltRef, kTemporalUpdateGoldenRefAltRef,
@ -85,7 +86,7 @@ TEST(TemporalLayersTest, 2Layers) {
uint32_t timestamp = 0; uint32_t timestamp = 0;
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp); 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); tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0);
EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx); 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.packetizer_temporal_idx);
@ -104,7 +105,7 @@ TEST(TemporalLayersTest, 3Layers) {
tl.UpdateConfiguration(&cfg); tl.UpdateConfiguration(&cfg);
int expected_flags[16] = { int expected_flags[16] = {
kTemporalUpdateLastAndGoldenRefAltRef, kTemporalUpdateLastRefAltRef,
kTemporalUpdateNoneNoRefGolden, kTemporalUpdateNoneNoRefGolden,
kTemporalUpdateGoldenWithoutDependencyRefAltRef, kTemporalUpdateGoldenWithoutDependencyRefAltRef,
kTemporalUpdateNone, kTemporalUpdateNone,
@ -112,7 +113,7 @@ TEST(TemporalLayersTest, 3Layers) {
kTemporalUpdateNone, kTemporalUpdateNone,
kTemporalUpdateGoldenRefAltRef, kTemporalUpdateGoldenRefAltRef,
kTemporalUpdateNone, kTemporalUpdateNone,
kTemporalUpdateLastAndGoldenRefAltRef, kTemporalUpdateLastRefAltRef,
kTemporalUpdateNoneNoRefGolden, kTemporalUpdateNoneNoRefGolden,
kTemporalUpdateGoldenWithoutDependencyRefAltRef, kTemporalUpdateGoldenWithoutDependencyRefAltRef,
kTemporalUpdateNone, kTemporalUpdateNone,
@ -131,7 +132,42 @@ TEST(TemporalLayersTest, 3Layers) {
unsigned int timestamp = 0; unsigned int timestamp = 0;
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp); 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); tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0);
EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx); 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.packetizer_temporal_idx);
@ -150,9 +186,9 @@ TEST(TemporalLayersTest, 4Layers) {
tl.UpdateConfiguration(&cfg); tl.UpdateConfiguration(&cfg);
int expected_flags[16] = { int expected_flags[16] = {
kTemporalUpdateLast, kTemporalUpdateLast,
kTemporalUpdateNone, kTemporalUpdateNoneNoRefGoldenAltRef,
kTemporalUpdateAltrefWithoutDependency, kTemporalUpdateAltrefWithoutDependency,
kTemporalUpdateNone, kTemporalUpdateNoneNoRefGolden,
kTemporalUpdateGoldenWithoutDependency, kTemporalUpdateGoldenWithoutDependency,
kTemporalUpdateNone, kTemporalUpdateNone,
kTemporalUpdateAltref, kTemporalUpdateAltref,
@ -169,14 +205,14 @@ TEST(TemporalLayersTest, 4Layers) {
int expected_temporal_idx[16] = {0, 3, 2, 3, 1, 3, 2, 3, int expected_temporal_idx[16] = {0, 3, 2, 3, 1, 3, 2, 3,
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, bool expected_layer_sync[16] = {false, true, true, false, true, false,
false, true, false, true, false, true, false, false, false, false, false, false,
false, true, false, true}; false, false, false, false};
uint32_t timestamp = 0; uint32_t timestamp = 0;
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp); 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); tl.PopulateCodecSpecific(false, tl_config, &vp8_info, 0);
EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx); 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.packetizer_temporal_idx);
@ -195,7 +231,7 @@ TEST(TemporalLayersTest, KeyFrame) {
tl.UpdateConfiguration(&cfg); tl.UpdateConfiguration(&cfg);
int expected_flags[8] = { int expected_flags[8] = {
kTemporalUpdateLastAndGoldenRefAltRef, kTemporalUpdateLastRefAltRef,
kTemporalUpdateNoneNoRefGolden, kTemporalUpdateNoneNoRefGolden,
kTemporalUpdateGoldenWithoutDependencyRefAltRef, kTemporalUpdateGoldenWithoutDependencyRefAltRef,
kTemporalUpdateNone, kTemporalUpdateNone,
@ -211,7 +247,7 @@ TEST(TemporalLayersTest, KeyFrame) {
uint32_t timestamp = 0; uint32_t timestamp = 0;
for (int i = 0; i < 7; ++i) { for (int i = 0; i < 7; ++i) {
TemporalLayers::FrameConfig tl_config = tl.UpdateLayerConfig(timestamp); 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); 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.packetizer_temporal_idx);
EXPECT_EQ(expected_temporal_idx[i], tl_config.encoder_layer_id); 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 " "marked layer sync since it only depends "
"on the base layer."; "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 } // namespace webrtc

View File

@ -18,7 +18,7 @@ namespace webrtc {
// Ratio allocation between temporal streams: // Ratio allocation between temporal streams:
// Values as required for the VP8 codec (accumulating). // Values as required for the VP8 codec (accumulating).
static const float static const float
kVp8LayerRateAlloction[kMaxTemporalStreams][kMaxTemporalStreams] = { kVp8LayerRateAlloction[kMaxSimulcastStreams][kMaxTemporalStreams] = {
{1.0f, 1.0f, 1.0f, 1.0f}, // 1 layer {1.0f, 1.0f, 1.0f, 1.0f}, // 1 layer
{0.6f, 1.0f, 1.0f, 1.0f}, // 2 layers {60%, 40%} {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%} {0.4f, 0.6f, 1.0f, 1.0f}, // 3 layers {40%, 20%, 40%}

View File

@ -304,6 +304,7 @@ TEST_F(FullStackTest, ForemanCif1000kbps100msLimitedQueue) {
RunTest(foreman_cif); RunTest(foreman_cif);
} }
// TODO(sprang): Remove this if we have the similar ModerateLimits below?
TEST_F(FullStackTest, ConferenceMotionHd2000kbps100msLimitedQueue) { TEST_F(FullStackTest, ConferenceMotionHd2000kbps100msLimitedQueue) {
VideoQualityTest::Params conf_motion_hd; VideoQualityTest::Params conf_motion_hd;
conf_motion_hd.call.send_side_bwe = true; conf_motion_hd.call.send_side_bwe = true;
@ -319,6 +320,87 @@ TEST_F(FullStackTest, ConferenceMotionHd2000kbps100msLimitedQueue) {
RunTest(conf_motion_hd); 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) #if !defined(RTC_DISABLE_VP9)
TEST_F(FullStackTest, ConferenceMotionHd2000kbps100msLimitedQueueVP9) { TEST_F(FullStackTest, ConferenceMotionHd2000kbps100msLimitedQueueVP9) {
VideoQualityTest::Params conf_motion_hd; VideoQualityTest::Params conf_motion_hd;

View File

@ -198,8 +198,10 @@ class DecoderBitstreamFileWriter : public EncodedFrameObserver {
}; };
void RtpReplay() { void RtpReplay() {
std::stringstream window_title;
window_title << "Playback Video (" << flags::InputFile() << ")";
std::unique_ptr<test::VideoRenderer> playback_video( std::unique_ptr<test::VideoRenderer> playback_video(
test::VideoRenderer::Create("Playback Video", 640, 480)); test::VideoRenderer::Create(window_title.str().c_str(), 640, 480));
FileRenderPassthrough file_passthrough(flags::OutBase(), FileRenderPassthrough file_passthrough(flags::OutBase(),
playback_video.get()); playback_video.get());

View File

@ -1297,6 +1297,12 @@ VideoStream VideoQualityTest::DefaultVideoStream(const Params& params) {
} else if (params.video.num_temporal_layers == 3) { } 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.max_bitrate_bps / 4);
stream.temporal_layer_thresholds_bps.push_back(stream.target_bitrate_bps); 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<int>(
stream.max_bitrate_bps * kVp8LayerRateAlloction[0][i] + 0.5));
}
} }
return stream; return stream;
} }