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-Original-Commit-Position: refs/heads/master@{#19716}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: ff19d35bae3cb2567ad39f82dda9e1f9ded107c9
diff --git a/modules/video_coding/codecs/vp8/default_temporal_layers.cc b/modules/video_coding/codecs/vp8/default_temporal_layers.cc
index 979c64e..02c3f15 100644
--- a/modules/video_coding/codecs/vp8/default_temporal_layers.cc
+++ b/modules/video_coding/codecs/vp8/default_temporal_layers.cc
@@ -96,16 +96,19 @@
     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<TemporalLayers::FrameConfig> GetTemporalPattern(size_t num_layers) {
@@ -134,7 +137,7 @@
       // 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 @@
                   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 @@
                                           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/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc b/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc
index fd4bf52..0075fd5 100644
--- a/modules/video_coding/codecs/vp8/default_temporal_layers_unittest.cc
+++ b/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 @@
       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 @@
   tl.UpdateConfiguration(&cfg);
 
   int expected_flags[16] = {
-      kTemporalUpdateLastAndGoldenRefAltRef,
+      kTemporalUpdateLastRefAltRef,
       kTemporalUpdateGoldenWithoutDependencyRefAltRef,
       kTemporalUpdateLastRefAltRef,
       kTemporalUpdateGoldenRefAltRef,
@@ -66,7 +67,7 @@
       kTemporalUpdateGoldenRefAltRef,
       kTemporalUpdateLastRefAltRef,
       kTemporalUpdateNone,
-      kTemporalUpdateLastAndGoldenRefAltRef,
+      kTemporalUpdateLastRefAltRef,
       kTemporalUpdateGoldenWithoutDependencyRefAltRef,
       kTemporalUpdateLastRefAltRef,
       kTemporalUpdateGoldenRefAltRef,
@@ -85,7 +86,7 @@
   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 @@
   tl.UpdateConfiguration(&cfg);
 
   int expected_flags[16] = {
-      kTemporalUpdateLastAndGoldenRefAltRef,
+      kTemporalUpdateLastRefAltRef,
       kTemporalUpdateNoneNoRefGolden,
       kTemporalUpdateGoldenWithoutDependencyRefAltRef,
       kTemporalUpdateNone,
@@ -112,7 +113,7 @@
       kTemporalUpdateNone,
       kTemporalUpdateGoldenRefAltRef,
       kTemporalUpdateNone,
-      kTemporalUpdateLastAndGoldenRefAltRef,
+      kTemporalUpdateLastRefAltRef,
       kTemporalUpdateNoneNoRefGolden,
       kTemporalUpdateGoldenWithoutDependencyRefAltRef,
       kTemporalUpdateNone,
@@ -131,7 +132,42 @@
   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 @@
   tl.UpdateConfiguration(&cfg);
   int expected_flags[16] = {
       kTemporalUpdateLast,
-      kTemporalUpdateNone,
+      kTemporalUpdateNoneNoRefGoldenAltRef,
       kTemporalUpdateAltrefWithoutDependency,
-      kTemporalUpdateNone,
+      kTemporalUpdateNoneNoRefGolden,
       kTemporalUpdateGoldenWithoutDependency,
       kTemporalUpdateNone,
       kTemporalUpdateAltref,
@@ -169,14 +205,14 @@
   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 @@
   tl.UpdateConfiguration(&cfg);
 
   int expected_flags[8] = {
-      kTemporalUpdateLastAndGoldenRefAltRef,
+      kTemporalUpdateLastRefAltRef,
       kTemporalUpdateNoneNoRefGolden,
       kTemporalUpdateGoldenWithoutDependencyRefAltRef,
       kTemporalUpdateNone,
@@ -211,7 +247,7 @@
   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 @@
                                      "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
diff --git a/modules/video_coding/codecs/vp8/include/vp8_common_types.h b/modules/video_coding/codecs/vp8/include/vp8_common_types.h
index 7a27e44..67888f7 100644
--- a/modules/video_coding/codecs/vp8/include/vp8_common_types.h
+++ b/modules/video_coding/codecs/vp8/include/vp8_common_types.h
@@ -18,7 +18,7 @@
 // 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/video/full_stack_tests.cc b/video/full_stack_tests.cc
index 66253cc..24c3625 100644
--- a/video/full_stack_tests.cc
+++ b/video/full_stack_tests.cc
@@ -304,6 +304,7 @@
   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 @@
   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/video/replay.cc b/video/replay.cc
index 5a391d6..c93017a 100644
--- a/video/replay.cc
+++ b/video/replay.cc
@@ -198,8 +198,10 @@
 };
 
 void RtpReplay() {
+  std::stringstream window_title;
+  window_title << "Playback Video (" << flags::InputFile() << ")";
   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(),
                                          playback_video.get());
 
diff --git a/video/video_quality_test.cc b/video/video_quality_test.cc
index 8224377..92d7968 100644
--- a/video/video_quality_test.cc
+++ b/video/video_quality_test.cc
@@ -1297,6 +1297,12 @@
   } 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<int>(
+          stream.max_bitrate_bps * kVp8LayerRateAlloction[0][i] + 0.5));
+    }
   }
   return stream;
 }