Fixes to temporal layers, Henrika please review src/common_types.h
Review URL: http://webrtc-codereview.appspot.com/286001

git-svn-id: http://webrtc.googlecode.com/svn/trunk@1091 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/src/common_types.h b/src/common_types.h
index e59b985..5ce4542 100644
--- a/src/common_types.h
+++ b/src/common_types.h
@@ -434,6 +434,7 @@
 enum { kConfigParameterSize = 128};
 enum { kPayloadNameSize = 32};
 enum { kMaxSimulcastStreams = 4};
+enum { kMaxTemporalStreams = 4};
 
 // H.263 specific
 struct VideoCodecH263
diff --git a/src/modules/video_coding/codecs/interface/video_codec_interface.h b/src/modules/video_coding/codecs/interface/video_codec_interface.h
index 9a96a23..87d34bf 100644
--- a/src/modules/video_coding/codecs/interface/video_codec_interface.h
+++ b/src/modules/video_coding/codecs/interface/video_codec_interface.h
@@ -33,6 +33,7 @@
     bool             nonReference;
     WebRtc_UWord8    simulcastIdx;
     WebRtc_UWord8    temporalIdx;
+    int              tl0PicIdx;         // Negative value to skip tl0PicIdx
     WebRtc_Word8     keyIdx;            // negative value to skip keyIdx
 };
 
diff --git a/src/modules/video_coding/codecs/vp8/main/interface/vp8.h b/src/modules/video_coding/codecs/vp8/main/interface/vp8.h
index 24b1a26..944fa2f 100644
--- a/src/modules/video_coding/codecs/vp8/main/interface/vp8.h
+++ b/src/modules/video_coding/codecs/vp8/main/interface/vp8.h
@@ -29,6 +29,7 @@
 
 namespace webrtc
 {
+class TemporalLayers;
 
 class ReferencePictureSelection;
 
@@ -139,7 +140,7 @@
                                         WebRtc_Word32 length);
 
 private:
-// Call encoder initialize function and set control settings.
+    // Call encoder initialize function and set control settings.
     WebRtc_Word32 InitAndSetControlSettings();
 
     void PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
@@ -174,6 +175,7 @@
     WebRtc_UWord32             _rcMaxIntraTarget;
     int                        _tokenPartitions;
     ReferencePictureSelection* _rps;
+    TemporalLayers*            _temporalLayers;
 
     vpx_codec_ctx_t*            _encoder;
     vpx_codec_enc_cfg_t*        _cfg;
@@ -270,9 +272,7 @@
     vpx_ref_frame_t*           _refFrame;
     int                        _propagationCnt;
     bool                       _latestKeyFrameComplete;
-
 };// end of VP8Decoder class
-
 } // namespace webrtc
 
 #endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_H_
diff --git a/src/modules/video_coding/codecs/vp8/main/source/temporal_layers.cc b/src/modules/video_coding/codecs/vp8/main/source/temporal_layers.cc
new file mode 100644
index 0000000..b5d59f8
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/source/temporal_layers.cc
@@ -0,0 +1,202 @@
+/* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+*
+*  Use of this source code is governed by a BSD-style license
+*  that can be found in the LICENSE file in the root of the source
+*  tree. An additional intellectual property rights grant can be found
+*  in the file PATENTS.  All contributing project authors may
+*  be found in the AUTHORS file in the root of the source tree.
+*/
+
+#include "temporal_layers.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <cassert>
+
+#include "module_common_types.h"
+#include "video_codec_interface.h"
+
+#include "vpx/vpx_encoder.h"
+#include "vpx/vp8cx.h"
+
+namespace webrtc {
+
+TemporalLayers::TemporalLayers(int numberOfTemporalLayers)
+    : number_of_temporal_layers_(numberOfTemporalLayers),
+      temporal_ids_length_(0),
+      temporal_pattern_length_(0),
+      tl0_pic_idx_(rand()),
+      pattern_idx_(255) {
+  assert(kMaxTemporalStreams >= numberOfTemporalLayers);
+  memset(temporal_ids_, 0, sizeof(temporal_ids_));
+  memset(temporal_pattern_, 0, sizeof(temporal_pattern_));
+}
+
+bool TemporalLayers::ConfigureBitrates(int bitrateKbit,
+                                       vpx_codec_enc_cfg_t* cfg) {
+  switch (number_of_temporal_layers_) {
+    case 0:
+    case 1:
+      // Do nothing.
+      break;
+    case 2:
+      temporal_ids_length_ = 2;
+      temporal_ids_[0] = 0;
+      temporal_ids_[1] = 1;
+      cfg->ts_number_layers = number_of_temporal_layers_;
+      cfg->ts_periodicity = temporal_ids_length_;
+      // Split stream 60% 40%.
+      // Bitrate API for VP8 is the agregated bitrate for all lower layers.
+      cfg->ts_target_bitrate[0] = bitrateKbit * 3 / 5;
+      cfg->ts_target_bitrate[1] = bitrateKbit;
+      cfg->ts_rate_decimator[0] = 2;
+      cfg->ts_rate_decimator[1] = 1;
+      memcpy(cfg->ts_layer_id,
+             temporal_ids_,
+             sizeof(unsigned int) * temporal_ids_length_);
+      temporal_pattern_length_ = 8;
+      temporal_pattern_[0] = kTemporalUpdateLast;
+      temporal_pattern_[1] = kTemporalUpdateGoldenWithoutDependency;
+      temporal_pattern_[2] = kTemporalUpdateLast;
+      temporal_pattern_[3] = kTemporalUpdateGolden;
+      temporal_pattern_[4] = kTemporalUpdateLast;
+      temporal_pattern_[5] = kTemporalUpdateGolden;
+      temporal_pattern_[6] = kTemporalUpdateLast;
+      temporal_pattern_[7] = kTemporalUpdateNone;
+      break;
+    case 3:
+      temporal_ids_length_ = 4;
+      temporal_ids_[0] = 0;
+      temporal_ids_[1] = 2;
+      temporal_ids_[2] = 1;
+      temporal_ids_[3] = 2;
+      cfg->ts_number_layers = number_of_temporal_layers_;
+      cfg->ts_periodicity = temporal_ids_length_;
+      // Split stream 40% 20% 40%.
+      // Bitrate API for VP8 is the agregated bitrate for all lower layers.
+      cfg->ts_target_bitrate[0] = bitrateKbit * 2 / 5;
+      cfg->ts_target_bitrate[1] = bitrateKbit * 3 / 5;
+      cfg->ts_target_bitrate[2] = bitrateKbit;
+      cfg->ts_rate_decimator[0] = 4;
+      cfg->ts_rate_decimator[1] = 2;
+      cfg->ts_rate_decimator[2] = 1;
+      memcpy(cfg->ts_layer_id,
+             temporal_ids_,
+             sizeof(unsigned int) * temporal_ids_length_);
+      temporal_pattern_length_ = 8;
+      temporal_pattern_[0] = kTemporalUpdateLast;
+      temporal_pattern_[1] = kTemporalUpdateAltrefWithoutDependency;
+      temporal_pattern_[2] = kTemporalUpdateGoldenWithoutDependency;
+      temporal_pattern_[3] = kTemporalUpdateAltref;
+      temporal_pattern_[4] = kTemporalUpdateLast;
+      temporal_pattern_[5] = kTemporalUpdateAltref;
+      temporal_pattern_[6] = kTemporalUpdateGolden;
+      temporal_pattern_[7] = kTemporalUpdateNone;
+      break;
+    case 4:
+      temporal_ids_length_ = 8;
+      temporal_ids_[0] = 0;
+      temporal_ids_[1] = 3;
+      temporal_ids_[2] = 2;
+      temporal_ids_[3] = 3;
+      temporal_ids_[4] = 1;
+      temporal_ids_[5] = 3;
+      temporal_ids_[6] = 2;
+      temporal_ids_[7] = 3;
+      // Split stream 25% 15% 20% 40%.
+      // Bitrate API for VP8 is the agregated bitrate for all lower layers.
+      cfg->ts_number_layers = 4;
+      cfg->ts_periodicity = temporal_ids_length_;
+      cfg->ts_target_bitrate[0] = bitrateKbit / 4;
+      cfg->ts_target_bitrate[1] = bitrateKbit * 2 / 5;
+      cfg->ts_target_bitrate[2] = bitrateKbit * 3 / 5;
+      cfg->ts_target_bitrate[3] = bitrateKbit;
+      cfg->ts_rate_decimator[0] = 8;
+      cfg->ts_rate_decimator[1] = 4;
+      cfg->ts_rate_decimator[2] = 2;
+      cfg->ts_rate_decimator[3] = 1;
+      memcpy(cfg->ts_layer_id,
+             temporal_ids_,
+             sizeof(unsigned int) * temporal_ids_length_);
+      temporal_pattern_length_ = 16;
+      temporal_pattern_[0] = kTemporalUpdateLast;
+      temporal_pattern_[1] = kTemporalUpdateNone;
+      temporal_pattern_[2] = kTemporalUpdateAltrefWithoutDependency;
+      temporal_pattern_[3] = kTemporalUpdateNone;
+      temporal_pattern_[4] = kTemporalUpdateGoldenWithoutDependency;
+      temporal_pattern_[5] = kTemporalUpdateNone;
+      temporal_pattern_[6] = kTemporalUpdateAltref;
+      temporal_pattern_[7] = kTemporalUpdateNone;
+      temporal_pattern_[8] = kTemporalUpdateLast;
+      temporal_pattern_[9] = kTemporalUpdateNone;
+      temporal_pattern_[10] = kTemporalUpdateAltref;
+      temporal_pattern_[11] = kTemporalUpdateNone;
+      temporal_pattern_[12] = kTemporalUpdateGolden;
+      temporal_pattern_[13] = kTemporalUpdateNone;
+      temporal_pattern_[14] = kTemporalUpdateAltref;
+      temporal_pattern_[15] = kTemporalUpdateNone;
+      break;
+    default:
+      assert(false);
+      return false;
+  }
+  return true;
+}
+
+int TemporalLayers::EncodeFlags() {
+  assert(number_of_temporal_layers_ > 1);
+  assert(kMaxTemporalPattern >= temporal_pattern_length_);
+  assert(0 < temporal_pattern_length_);
+
+  int flags = 0;
+  int patternIdx = ++pattern_idx_ % temporal_pattern_length_;
+  assert(kMaxTemporalPattern >= patternIdx);
+  switch (temporal_pattern_[patternIdx]) {
+    case kTemporalUpdateLast:
+      flags |= VP8_EFLAG_NO_UPD_GF;
+      flags |= VP8_EFLAG_NO_UPD_ARF;
+      flags |= VP8_EFLAG_NO_REF_GF;
+      flags |= VP8_EFLAG_NO_REF_ARF;
+      break;
+    case kTemporalUpdateGoldenWithoutDependency:
+      flags |= VP8_EFLAG_NO_REF_GF;
+      // Deliberetely no break here.
+    case kTemporalUpdateGolden:
+      flags |= VP8_EFLAG_NO_REF_ARF;
+      flags |= VP8_EFLAG_NO_UPD_ARF;
+      flags |= VP8_EFLAG_NO_UPD_LAST;
+      break;
+    case kTemporalUpdateAltrefWithoutDependency:
+      flags |= VP8_EFLAG_NO_REF_ARF;
+      // Deliberetely no break here.
+    case kTemporalUpdateAltref:
+      flags |= VP8_EFLAG_NO_UPD_GF;
+      flags |= VP8_EFLAG_NO_UPD_LAST;
+      break;
+    case kTemporalUpdateNone:
+      flags |= VP8_EFLAG_NO_UPD_GF;
+      flags |= VP8_EFLAG_NO_UPD_ARF;
+      flags |= VP8_EFLAG_NO_UPD_LAST;
+      flags |= VP8_EFLAG_NO_UPD_ENTROPY;
+      break;
+  }
+  return flags;
+}
+
+void TemporalLayers::PopulateCodecSpecific(bool key_frame,
+                                           CodecSpecificInfoVP8 *vp8_info) {
+  assert(number_of_temporal_layers_ > 1);
+  assert(0 < temporal_ids_length_);
+
+  if (key_frame) {
+    // Keyframe is always temporal layer 0
+    vp8_info->temporalIdx = 0;
+  } else {
+    vp8_info->temporalIdx = temporal_ids_[pattern_idx_ % temporal_ids_length_];
+  }
+  if (vp8_info->temporalIdx == 0) {
+    tl0_pic_idx_++;
+  }
+  vp8_info->tl0PicIdx = tl0_pic_idx_;
+}
+}  // namespace webrtc
diff --git a/src/modules/video_coding/codecs/vp8/main/source/temporal_layers.h b/src/modules/video_coding/codecs/vp8/main/source/temporal_layers.h
new file mode 100644
index 0000000..6be0582
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/source/temporal_layers.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+*
+*  Use of this source code is governed by a BSD-style license
+*  that can be found in the LICENSE file in the root of the source
+*  tree. An additional intellectual property rights grant can be found
+*  in the file PATENTS.  All contributing project authors may
+*  be found in the AUTHORS file in the root of the source tree.
+*/
+/*
+* This file defines classes for doing temporal layers with VP8.
+*/
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
+
+#include <typedefs.h>
+
+ // VPX forward declaration
+typedef struct vpx_codec_enc_cfg vpx_codec_enc_cfg_t;
+
+namespace webrtc {
+
+struct CodecSpecificInfoVP8;
+
+class TemporalLayers {
+ public:
+  TemporalLayers(int number_of_temporal_layers);
+
+  // Returns the recommended VP8 encode flags needed. May refresh the decoder
+  // and/or update the reference buffers.
+  int EncodeFlags();
+
+  bool ConfigureBitrates(int bitrate_kbit, vpx_codec_enc_cfg_t* cfg);
+
+  void PopulateCodecSpecific(bool key_frame, CodecSpecificInfoVP8 *vp8_info);
+
+ private:
+  enum TemporalReferences {
+    // Highest enhancement layer.
+    kTemporalUpdateNone = 5,
+    // Second enhancement layer.
+    kTemporalUpdateAltref = 4,
+    // Second enhancement layer without dependency on previous frames in
+    // the second enhancement layer.
+    kTemporalUpdateAltrefWithoutDependency = 3,
+    // First enhancement layer.
+    kTemporalUpdateGolden = 2,
+    // First enhancement layer without dependency on previous frames in
+    // the first enhancement layer.
+    kTemporalUpdateGoldenWithoutDependency = 1,
+    // Base layer.
+    kTemporalUpdateLast = 0,
+  };
+  enum { kMaxTemporalPattern = 16 };
+
+  int number_of_temporal_layers_;
+  int temporal_ids_length_;
+  int temporal_ids_[kMaxTemporalPattern];
+  int temporal_pattern_length_;
+  TemporalReferences temporal_pattern_[kMaxTemporalPattern];
+  uint8_t tl0_pic_idx_;
+  uint8_t pattern_idx_;
+};
+}  // namespace webrtc
+#endif  // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP8_TEMPORAL_LAYERS_H_
+
diff --git a/src/modules/video_coding/codecs/vp8/main/source/temporal_layers_unittest.cc b/src/modules/video_coding/codecs/vp8/main/source/temporal_layers_unittest.cc
new file mode 100644
index 0000000..45d9ede
--- /dev/null
+++ b/src/modules/video_coding/codecs/vp8/main/source/temporal_layers_unittest.cc
@@ -0,0 +1,171 @@
+/*
+ *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+
+#include "gtest/gtest.h"
+#include "temporal_layers.h"
+#include "video_codec_interface.h"
+
+#include "vpx/vpx_encoder.h"
+#include "vpx/vp8cx.h"
+
+namespace webrtc {
+
+enum {
+  kTemporalUpdateLast = VP8_EFLAG_NO_UPD_GF |
+                        VP8_EFLAG_NO_UPD_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,
+  kTemporalUpdateGolden = VP8_EFLAG_NO_REF_ARF |
+                          VP8_EFLAG_NO_UPD_ARF |
+                          VP8_EFLAG_NO_UPD_LAST,
+  kTemporalUpdateAltrefWithoutDependency = VP8_EFLAG_NO_REF_ARF |
+                                           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 |
+                        VP8_EFLAG_NO_UPD_LAST |
+                        VP8_EFLAG_NO_UPD_ENTROPY,
+};
+
+TEST(TemporalLayersTest, 2Layers) {
+  TemporalLayers tl(2);
+  vpx_codec_enc_cfg_t cfg;
+  CodecSpecificInfoVP8 vp8_info;
+  tl.ConfigureBitrates(500, &cfg);
+
+  int expected_flags[16] = { kTemporalUpdateLast,
+                             kTemporalUpdateGoldenWithoutDependency,
+                             kTemporalUpdateLast,
+                             kTemporalUpdateGolden,
+                             kTemporalUpdateLast,
+                             kTemporalUpdateGolden,
+                             kTemporalUpdateLast,
+                             kTemporalUpdateNone,
+                             kTemporalUpdateLast,
+                             kTemporalUpdateGoldenWithoutDependency,
+                             kTemporalUpdateLast,
+                             kTemporalUpdateGolden,
+                             kTemporalUpdateLast,
+                             kTemporalUpdateGolden,
+                             kTemporalUpdateLast,
+                             kTemporalUpdateNone
+  };
+  int expected_temporal_idx[16] =
+      { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 };
+
+  for (int i = 0; i < 16; ++i) {
+    EXPECT_EQ(expected_flags[i], tl.EncodeFlags());
+    tl.PopulateCodecSpecific(false, &vp8_info);
+    EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx);
+  }
+}
+
+TEST(TemporalLayersTest, 3Layers) {
+  TemporalLayers tl(3);
+  vpx_codec_enc_cfg_t cfg;
+  CodecSpecificInfoVP8 vp8_info;
+  tl.ConfigureBitrates(500, &cfg);
+
+  int expected_flags[16] = { kTemporalUpdateLast,
+                             kTemporalUpdateAltrefWithoutDependency,
+                             kTemporalUpdateGoldenWithoutDependency,
+                             kTemporalUpdateAltref,
+                             kTemporalUpdateLast,
+                             kTemporalUpdateAltref,
+                             kTemporalUpdateGolden,
+                             kTemporalUpdateNone,
+                             kTemporalUpdateLast,
+                             kTemporalUpdateAltrefWithoutDependency,
+                             kTemporalUpdateGoldenWithoutDependency,
+                             kTemporalUpdateAltref,
+                             kTemporalUpdateLast,
+                             kTemporalUpdateAltref,
+                             kTemporalUpdateGolden,
+                             kTemporalUpdateNone,
+  };
+  int expected_temporal_idx[16] =
+      { 0, 2, 1, 2, 0, 2, 1, 2, 0, 2, 1, 2, 0, 2, 1, 2 };
+
+  for (int i = 0; i < 16; ++i) {
+    EXPECT_EQ(expected_flags[i], tl.EncodeFlags());
+    tl.PopulateCodecSpecific(false, &vp8_info);
+    EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx);
+  }
+}
+
+TEST(TemporalLayersTest, 4Layers) {
+  TemporalLayers tl(4);
+  vpx_codec_enc_cfg_t cfg;
+  CodecSpecificInfoVP8 vp8_info;
+  tl.ConfigureBitrates(500, &cfg);
+  int expected_flags[16] = {
+      kTemporalUpdateLast,
+      kTemporalUpdateNone,
+      kTemporalUpdateAltrefWithoutDependency,
+      kTemporalUpdateNone,
+      kTemporalUpdateGoldenWithoutDependency,
+      kTemporalUpdateNone,
+      kTemporalUpdateAltref,
+      kTemporalUpdateNone,
+      kTemporalUpdateLast,
+      kTemporalUpdateNone,
+      kTemporalUpdateAltref,
+      kTemporalUpdateNone,
+      kTemporalUpdateGolden,
+      kTemporalUpdateNone,
+      kTemporalUpdateAltref,
+      kTemporalUpdateNone,
+  };
+  int expected_temporal_idx[16] =
+      { 0, 3, 2, 3, 1, 3, 2, 3, 0, 3, 2, 3, 1, 3, 2, 3 };
+
+  for (int i = 0; i < 16; ++i) {
+    EXPECT_EQ(expected_flags[i], tl.EncodeFlags());
+    tl.PopulateCodecSpecific(false, &vp8_info);
+    EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx);
+  }
+}
+
+TEST(TemporalLayersTest, KeyFrame) {
+  TemporalLayers tl(3);
+  vpx_codec_enc_cfg_t cfg;
+  CodecSpecificInfoVP8 vp8_info;
+  tl.ConfigureBitrates(500, &cfg);
+
+  int expected_flags[8] = {
+      kTemporalUpdateLast,
+      kTemporalUpdateAltrefWithoutDependency,
+      kTemporalUpdateGoldenWithoutDependency,
+      kTemporalUpdateAltref,
+      kTemporalUpdateLast,
+      kTemporalUpdateAltref,
+      kTemporalUpdateGolden,
+      kTemporalUpdateNone,
+  };
+  int expected_temporal_idx[8] =
+      { 0, 0, 0, 0, 0, 0, 0, 2};
+
+  for (int i = 0; i < 7; ++i) {
+    EXPECT_EQ(expected_flags[i], tl.EncodeFlags());
+    tl.PopulateCodecSpecific(true, &vp8_info);
+    EXPECT_EQ(expected_temporal_idx[i], vp8_info.temporalIdx);
+  }
+  EXPECT_EQ(expected_flags[7], tl.EncodeFlags());
+  tl.PopulateCodecSpecific(false, &vp8_info);
+  EXPECT_EQ(expected_temporal_idx[7], vp8_info.temporalIdx);
+}
+}  // namespace webrtc
diff --git a/src/modules/video_coding/codecs/vp8/main/source/vp8.cc b/src/modules/video_coding/codecs/vp8/main/source/vp8.cc
index 50c3e08..ffa64c9 100644
--- a/src/modules/video_coding/codecs/vp8/main/source/vp8.cc
+++ b/src/modules/video_coding/codecs/vp8/main/source/vp8.cc
@@ -22,6 +22,7 @@
 
 #include "module_common_types.h"
 #include "reference_picture_selection.h"
+#include "temporal_layers.h"
 #include "tick_util.h"
 #include "vpx/vpx_encoder.h"
 #include "vpx/vpx_decoder.h"
@@ -48,6 +49,7 @@
     _rcMaxIntraTarget(0),
     _tokenPartitions(VP8_ONE_TOKENPARTITION),
     _rps(new ReferencePictureSelection),
+    _temporalLayers(NULL),
     _encoder(NULL),
     _cfg(NULL),
     _raw(NULL)
@@ -111,6 +113,11 @@
         delete _raw;
         _raw = NULL;
     }
+    if (_temporalLayers != NULL)
+    {
+        delete _temporalLayers;
+        _temporalLayers = NULL;
+    }
     _inited = false;
 
     return WEBRTC_VIDEO_CODEC_OK;
@@ -163,21 +170,12 @@
     {
         newBitRateKbit = _maxBitRateKbit;
     }
-
     _cfg->rc_target_bitrate = newBitRateKbit; // in kbit/s
-/*  TODO(pwestin) use number of temoral layers config
-    int ids[3] = {0,1,2};                                
-    _cfg->ts_number_layers     = 3;
-    _cfg->ts_periodicity       = 3;
-    _cfg->ts_target_bitrate[0] = (newBitRateKbit*2/5);
-    _cfg->ts_target_bitrate[1] = (newBitRateKbit*3/5);
-    _cfg->ts_target_bitrate[2] = (newBitRateKbit);
-    _cfg->ts_rate_decimator[0] = 4;
-    _cfg->ts_rate_decimator[1] = 2;
-    _cfg->ts_rate_decimator[2] = 1;
-    memcpy(_cfg->ts_layer_id, ids, sizeof(ids));
-*/
 
+    if (_temporalLayers)
+    {
+        _temporalLayers->ConfigureBitrates(newBitRateKbit, _cfg);
+    }
     _maxFrameRate = newFrameRate;
 
     // update encoder context
@@ -240,8 +238,15 @@
     _width = inst->width;
     _height = inst->height;
 
-    // random start 16 bits is enough
-    _pictureID = ((WebRtc_UWord16)rand()) % 0x7FFF;
+    if (inst->codecSpecific.VP8.numberOfTemporalLayers > 1)
+    {
+      assert(_temporalLayers == NULL);
+      _temporalLayers =
+          new TemporalLayers(inst->codecSpecific.VP8.numberOfTemporalLayers);
+    }
+
+    // random start 16 bits is enough.
+    _pictureID = ((WebRtc_UWord16)rand()) & 0x7FFF;
 
     // allocate memory for encoded image
     if (_encodedImage._buffer != NULL)
@@ -258,7 +263,6 @@
     {
          return WEBRTC_VIDEO_CODEC_ERROR;
     }
-
     _cfg->g_w = inst->width;
     _cfg->g_h = inst->height;
     if (_maxBitRateKbit > 0 &&
@@ -270,19 +274,10 @@
     {
       _cfg->rc_target_bitrate = inst->startBitrate;  // in kbit/s
     }
-/*  TODO(pwestin) use number of temoral layers config
-    int ids[3] = {0,1,2};                                
-    _cfg->ts_number_layers     = 3;
-    _cfg->ts_periodicity       = 3;
-    _cfg->ts_target_bitrate[0] = (inst->startBitrate*2/5);
-    _cfg->ts_target_bitrate[1] = (inst->startBitrate*3/5);
-    _cfg->ts_target_bitrate[2] = (inst->startBitrate);
-    _cfg->ts_rate_decimator[0] = 4;
-    _cfg->ts_rate_decimator[1] = 2;
-    _cfg->ts_rate_decimator[2] = 1;
-    memcpy(_cfg->ts_layer_id, ids, sizeof(ids));
-*/
-
+    if (_temporalLayers)
+    {
+        _temporalLayers->ConfigureBitrates(inst->startBitrate, _cfg);
+    }
     // setting the time base of the codec
     _cfg->g_timebase.num = 1;
     _cfg->g_timebase.den = 90000;
@@ -328,7 +323,6 @@
         _cfg->kf_mode = VPX_KF_AUTO;
         _cfg->kf_max_dist = 3000;
     }
-
     switch (inst->codecSpecific.VP8.complexity)
     {
         case kComplexityHigh:
@@ -352,14 +346,12 @@
             break;
         }
     }
-
     _rps->Init();
 
     return InitAndSetControlSettings();
-
-
 }
 
+
 WebRtc_Word32
 VP8Encoder::InitAndSetControlSettings()
 {
@@ -369,7 +361,7 @@
     // TODO(holmer): We should make a smarter decision on the number of
     // partitions. Eight is probably not the optimal number for low resolution
     // video.
-    _tokenPartitions = VP8_EIGHT_TOKENPARTITION;
+
 #if WEBRTC_LIBVPX_VERSION >= 971
     flags |= VPX_CODEC_USE_OUTPUT_PARTITION;
 #endif
@@ -444,10 +436,15 @@
     _raw->planes[PLANE_V] =  &inputImage._buffer[_height * _width * 5 >> 2];
 
     int flags = 0;
-    if (frameTypes && *frameTypes == kKeyFrame) {
+    if (_temporalLayers) {
+      flags |= _temporalLayers->EncodeFlags();
+    }
+    bool sendKeyFrame = frameTypes && (*frameTypes == kKeyFrame);
+    if (sendKeyFrame)
+    {
       // Key frame request from caller.
       // Will update both golden and alt-ref.
-      flags |= VPX_EFLAG_FORCE_KF;
+      flags = VPX_EFLAG_FORCE_KF;
     } else if (_feedbackModeOn && codecSpecificInfo) {
       // Handle RPSI and SLI messages and set up the appropriate encode flags.
       bool sendRefresh = false;
@@ -490,10 +487,16 @@
   codec_specific->codecType = kVideoCodecVP8;
   CodecSpecificInfoVP8 *vp8Info = &(codec_specific->codecSpecific.VP8);
   vp8Info->pictureId = _pictureID;
-  vp8Info->simulcastIdx = _simulcastIdx;;
-  vp8Info->temporalIdx = kNoTemporalIdx; // TODO(pwestin) need to populate this
+  vp8Info->simulcastIdx = _simulcastIdx;
   vp8Info->keyIdx = kNoKeyIdx;  // TODO(hlundin) populate this
   vp8Info->nonReference = (pkt.data.frame.flags & VPX_FRAME_IS_DROPPABLE);
+  if (_temporalLayers) {
+    _temporalLayers->PopulateCodecSpecific(
+        (pkt.data.frame.flags & VPX_FRAME_IS_KEY) ? true : false, vp8Info);
+  } else {
+    vp8Info->temporalIdx = kNoTemporalIdx;
+    vp8Info->tl0PicIdx = kNoTl0PicIdx;
+  }
   _pictureID = (_pictureID + 1) % 0x7FFF; // prepare next
 }
 
@@ -568,6 +571,7 @@
   RTPFragmentationHeader frag_info;
   frag_info.VerifyAndAllocateFragmentationHeader((1 << _tokenPartitions) + 1);
   CodecSpecificInfo codecSpecific;
+
   const vpx_codec_cx_pkt_t *pkt = NULL;
   while ((pkt = vpx_codec_get_cx_data(_encoder, &iter)) != NULL) {
     switch(pkt->kind) {
diff --git a/src/modules/video_coding/codecs/vp8/main/source/vp8.gypi b/src/modules/video_coding/codecs/vp8/main/source/vp8.gypi
index 481a454..4ffefcd 100644
--- a/src/modules/video_coding/codecs/vp8/main/source/vp8.gypi
+++ b/src/modules/video_coding/codecs/vp8/main/source/vp8.gypi
@@ -50,6 +50,8 @@
         'reference_picture_selection.cc',
         '../interface/vp8.h',
         '../interface/vp8_simulcast.h',
+        'temporal_layers.h',
+        'temporal_layers.cc',
         'vp8.cc',
         'vp8_simulcast.cc',
       ],
@@ -103,6 +105,7 @@
           ],
           'sources': [
             'reference_picture_selection_unittest.cc',
+            'temporal_layers_unittest.cc',
           ],
         },
       ], # targets
diff --git a/src/modules/video_coding/main/source/generic_encoder.cc b/src/modules/video_coding/main/source/generic_encoder.cc
index 8c29e97..2838a86 100644
--- a/src/modules/video_coding/main/source/generic_encoder.cc
+++ b/src/modules/video_coding/main/source/generic_encoder.cc
@@ -267,6 +267,8 @@
                  info.codecSpecific.VP8.nonReference;
             (*rtp)->codecHeader.VP8.temporalIdx =
                  info.codecSpecific.VP8.temporalIdx;
+            (*rtp)->codecHeader.VP8.tl0PicIdx =
+                info.codecSpecific.VP8.tl0PicIdx;
             (*rtp)->codecHeader.VP8.keyIdx =
                  info.codecSpecific.VP8.keyIdx;
             (*rtp)->simulcastIdx = info.codecSpecific.VP8.simulcastIdx;
diff --git a/src/video_engine/test/auto_test/interface/tb_external_transport.h b/src/video_engine/test/auto_test/interface/tb_external_transport.h
index ce4b407..1a71b43 100644
--- a/src/video_engine/test/auto_test/interface/tb_external_transport.h
+++ b/src/video_engine/test/auto_test/interface/tb_external_transport.h
@@ -44,6 +44,7 @@
                   WebRtc_Word32& numDroppedPackets,
                   WebRtc_Word32& numRtcpPackets);
 
+    void SetTemporalToggle(unsigned char layers);
     void EnableSSRCCheck();
     unsigned int ReceivedSSRC();
 
@@ -87,6 +88,14 @@
     webrtc::ListWrapper _rtpPackets;
     webrtc::ListWrapper _rtcpPackets;
 
+    unsigned char _temporalLayers;
+    unsigned short _seqNum;
+    unsigned short _sendPID;
+    unsigned char _receivedPID;
+    bool _switchLayer;
+    unsigned char _currentRelayLayer;
+    unsigned int _lastTimeMs;
+
     bool _checkSSRC;
     WebRtc_UWord32 _lastSSRC;
     bool _filterSSRC;
diff --git a/src/video_engine/test/auto_test/source/tb_external_transport.cc b/src/video_engine/test/auto_test/source/tb_external_transport.cc
index 5ac18f7..f50f2eb 100644
--- a/src/video_engine/test/auto_test/source/tb_external_transport.cc
+++ b/src/video_engine/test/auto_test/source/tb_external_transport.cc
@@ -14,9 +14,11 @@
 
 #include "tb_external_transport.h"
 
+#include <stdio.h> // printf
 #include <stdlib.h> // rand
+#include <cassert>
+
 #if defined(WEBRTC_LINUX) || defined(__linux__)
-#include <stdlib.h>
 #include <string.h>
 #endif
 #if defined(WEBRTC_MAC)
@@ -48,6 +50,13 @@
         _dropCount(0),
         _rtpPackets(),
         _rtcpPackets(),
+        _temporalLayers(0),
+        _seqNum(0),
+        _sendPID(0),
+        _receivedPID(0),
+        _switchLayer(false),
+        _currentRelayLayer(0),
+        _lastTimeMs(webrtc::TickTime::MillisecondTimestamp()),
         _checkSSRC(false),
         _lastSSRC(0),
         _filterSSRC(false),
@@ -84,7 +93,68 @@
         ssrc += ptr[11];
         if (ssrc != _SSRC)
         {  
-            return len; // return len to avoif error in trace file
+            return len; // return len to avoid error in trace file
+        }
+    }
+    if (_temporalLayers) {
+        // parse out vp8 temporal layers
+        // 12 bytes RTP
+        WebRtc_UWord8* ptr = (WebRtc_UWord8*)data;
+
+        if (ptr[12] & 0x80 &&  // X-bit
+            ptr[13] & 0x20)  // T-bit
+        {
+            int offset = 1;
+            if (ptr[13] & 0x80) // PID-bit
+            {
+                offset++;
+                if (ptr[14] & 0x80) // 2 byte PID
+                {
+                    offset++;
+                }
+            }
+            if (ptr[13] & 0x40)
+            {
+                offset++;
+            }
+            unsigned char TID = (ptr[13 + offset] >> 5);
+            unsigned int timeMs = NowMs();
+
+            // Every 5 second switch layer
+            if (_lastTimeMs + 5000 < timeMs)
+            {
+                _lastTimeMs = timeMs;
+                _switchLayer = true;
+            }
+            // Switch at the non ref frame
+            if (_switchLayer && (ptr[12] & 0x20))
+            {   // N-bit
+              _currentRelayLayer++;
+                if (_currentRelayLayer >= _temporalLayers)
+                  _currentRelayLayer = 0;
+
+                _switchLayer = false;
+                printf("\t Switching to layer:%d\n", _currentRelayLayer);
+            }
+            if (_currentRelayLayer < TID)
+            {
+                return len; // return len to avoid error in trace file
+            }
+            if (ptr[14] & 0x80) // 2 byte PID
+            {
+                if(_receivedPID != ptr[15])
+                {
+                    _sendPID++;
+                    _receivedPID = ptr[15];
+                }
+            } else
+            {
+              if(_receivedPID != ptr[14])
+              {
+                _sendPID++;
+                _receivedPID = ptr[14];
+              }
+            }
         }
     }
     _statCrit.Enter();
@@ -103,6 +173,24 @@
 
     VideoPacket* newPacket = new VideoPacket();
     memcpy(newPacket->packetBuffer, data, len);
+
+    if (_temporalLayers)
+    {
+        // rewrite seqNum
+        newPacket->packetBuffer[2] = _seqNum >> 8;
+        newPacket->packetBuffer[3] = _seqNum;
+        _seqNum++;
+
+        // rewrite PID
+        if (newPacket->packetBuffer[14] & 0x80) // 2 byte PID
+        {
+            newPacket->packetBuffer[14] = (_sendPID >> 8) | 0x80;
+            newPacket->packetBuffer[15] = _sendPID;
+        } else
+        {
+            newPacket->packetBuffer[14] = (_sendPID & 0x7f);
+        }
+    }
     newPacket->length = len;
     newPacket->channel = channel;
 
@@ -114,6 +202,12 @@
     return len;
 }
 
+// Set to 0 to disable.
+void TbExternalTransport::SetTemporalToggle(unsigned char layers)
+{
+    _temporalLayers = layers;
+}
+
 int TbExternalTransport::SendRTCPPacket(int channel, const void *data, int len)
 {
     _statCrit.Enter();
diff --git a/src/video_engine/test/auto_test/source/vie_autotest_loopback.cc b/src/video_engine/test/auto_test/source/vie_autotest_loopback.cc
index 4b7e055..ed4883c 100644
--- a/src/video_engine/test/auto_test/source/vie_autotest_loopback.cc
+++ b/src/video_engine/test/auto_test/source/vie_autotest_loopback.cc
@@ -212,7 +212,6 @@
         printf("ERROR in ViERTP_RTCP::SetKeyFrameRequestMethod\n");
         return -1;
     }
-
     error = ptrViERtpRtcp->SetTMMBRStatus(videoChannel, true);
     if (error == -1)
     {
@@ -343,29 +342,36 @@
     switch (resolnOption)
     {
         case 1:
-        videoCodec.width = 176;
-        videoCodec.height = 144;
-          break;
-
+            videoCodec.width = 176;
+            videoCodec.height = 144;
+            break;
         case 2:
-        videoCodec.width = 352;
-        videoCodec.height = 288;
-          break;
-
+            videoCodec.width = 352;
+            videoCodec.height = 288;
+            break;
         case 3:
-        videoCodec.width = 640;
-        videoCodec.height = 480;
-          break;
-
+            videoCodec.width = 640;
+            videoCodec.height = 480;
+            break;
         case 4:
-        videoCodec.width = 704;
-        videoCodec.height = 576;
-          break;
-
+            videoCodec.width = 704;
+            videoCodec.height = 576;
+            break;
         case 5:
-        videoCodec.width = 1280;
-        videoCodec.height = 720;
-          break;
+            videoCodec.width = 1280;
+            videoCodec.height = 720;
+            break;
+    }
+
+    // Set number of temporal layers.
+    std::cout << std::endl;
+    std::cout << "Choose number of temporal layers (1 to 4).";
+    std::cout << "Press enter for default: \n";
+    std::getline(std::cin, str);
+    int numTemporalLayers = atoi(str.c_str());
+    if(numTemporalLayers != 0)
+    {
+        videoCodec.codecSpecific.VP8.numberOfTemporalLayers = numTemporalLayers;
     }
 
     // Set start bit rate
@@ -439,6 +445,12 @@
 
     // Setting External transport
     TbExternalTransport extTransport(*(ptrViENetwork));
+    if (numTemporalLayers > 1) {
+      extTransport.SetTemporalToggle(numTemporalLayers);
+    } else {
+      // Disabled
+      extTransport.SetTemporalToggle(0);
+    }
 
     int testMode = 0;
     std::cout << std::endl;
@@ -449,8 +461,11 @@
     testMode = atoi(test_str.c_str());
     if (testMode == 1)
     {
+        // Avoid changing SSRC due to collision.
+        error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, 1);
+
         error = ptrViENetwork->RegisterSendTransport(videoChannel,
-                                                      extTransport);
+                                                     extTransport);
         if (error == -1)
         {
             printf("ERROR in ViECodec::RegisterSendTransport \n");