Bug 1237023: Cherry-pick VP9 packetization/jitter-buffer/encoder code from Webrtc.org 48 r=pkerr
authorRandell Jesup <rjesup@jesup.org>
Sun, 24 Jan 2016 23:13:26 -0500
changeset 303684 f442638a278cc570046b9f18f23e6c60da159bc3
parent 303683 e9e0aaa18f4a1875532889c4a8033afa581ecfd4
child 303685 4bd447a3d7da016d2a3bb0f5de0511cfdf40113e
push id8978
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 14:05:32 +0000
treeherdermozilla-aurora@b9a803752a2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspkerr
bugs1237023
milestone46.0a1
Bug 1237023: Cherry-pick VP9 packetization/jitter-buffer/encoder code from Webrtc.org 48 r=pkerr
media/webrtc/trunk/webrtc/modules/interface/module_common_types.h
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.cc
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp9.cc
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp9_unittest.cc
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
media/webrtc/trunk/webrtc/modules/video_coding/codecs/interface/video_codec_interface.h
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/screenshare_layers.cc
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/screenshare_layers.h
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/screenshare_layers_unittest.cc
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9.gyp
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.cc
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.cc
media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.cc
media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.h
media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_encoder.cc
media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.cc
media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc
--- a/media/webrtc/trunk/webrtc/modules/interface/module_common_types.h
+++ b/media/webrtc/trunk/webrtc/modules/interface/module_common_types.h
@@ -10,34 +10,43 @@
 
 #ifndef MODULE_COMMON_TYPES_H
 #define MODULE_COMMON_TYPES_H
 
 #include <assert.h>
 #include <string.h>  // memcpy
 
 #include <algorithm>
+#include <limits>
 
 #include "webrtc/base/constructormagic.h"
 #include "webrtc/common_types.h"
 #include "webrtc/common_video/rotation.h"
 #include "webrtc/typedefs.h"
 
 namespace webrtc {
 
 struct RTPAudioHeader {
   uint8_t numEnergy;                  // number of valid entries in arrOfEnergy
   uint8_t arrOfEnergy[kRtpCsrcSize];  // one energy byte (0-9) per channel
   bool isCNG;                         // is this CNG
   uint8_t channel;                    // number of channels 2 = stereo
 };
 
 const int16_t kNoPictureId = -1;
+const int16_t kMaxOneBytePictureId = 0x7F;    // 7 bits
+const int16_t kMaxTwoBytePictureId = 0x7FFF;  // 15 bits
 const int16_t kNoTl0PicIdx = -1;
 const uint8_t kNoTemporalIdx = 0xFF;
+const uint8_t kNoSpatialIdx = 0xFF;
+const uint8_t kNoGofIdx = 0xFF;
+const uint8_t kNumVp9Buffers = 8;
+const size_t kMaxVp9RefPics = 3;
+const size_t kMaxVp9FramesInGof = 0xFF;  // 8 bits
+const size_t kMaxVp9NumberOfSpatialLayers = 8;
 const int kNoKeyIdx = -1;
 
 struct RTPVideoHeaderVP8 {
   void InitRTPVideoHeaderVP8() {
     nonReference = false;
     pictureId = kNoPictureId;
     tl0PicIdx = kNoTl0PicIdx;
     temporalIdx = kNoTemporalIdx;
@@ -56,47 +65,174 @@ struct RTPVideoHeaderVP8 {
   bool layerSync;             // This frame is a layer sync frame.
                               // Disabled if temporalIdx == kNoTemporalIdx.
   int keyIdx;                 // 5 bits; kNoKeyIdx means not used.
   int partitionId;            // VP8 partition ID
   bool beginningOfPartition;  // True if this packet is the first
                               // in a VP8 partition. Otherwise false
 };
 
+enum TemporalStructureMode {
+  kTemporalStructureMode1,    // 1 temporal layer structure - i.e., IPPP...
+  kTemporalStructureMode2,    // 2 temporal layers 0-1-0-1...
+  kTemporalStructureMode3     // 3 temporal layers 0-2-1-2-0-2-1-2...
+};
+
+struct GofInfoVP9 {
+  void SetGofInfoVP9(TemporalStructureMode tm) {
+    switch (tm) {
+      case kTemporalStructureMode1:
+        num_frames_in_gof = 1;
+        temporal_idx[0] = 0;
+        temporal_up_switch[0] = false;
+        num_ref_pics[0] = 1;
+        pid_diff[0][0] = 1;
+        break;
+      case kTemporalStructureMode2:
+        num_frames_in_gof = 2;
+        temporal_idx[0] = 0;
+        temporal_up_switch[0] = false;
+        num_ref_pics[0] = 1;
+        pid_diff[0][0] = 2;
+
+        temporal_idx[1] = 1;
+        temporal_up_switch[1] = true;
+        num_ref_pics[1] = 1;
+        pid_diff[1][0] = 1;
+        break;
+      case kTemporalStructureMode3:
+        num_frames_in_gof = 4;
+        temporal_idx[0] = 0;
+        temporal_up_switch[0] = false;
+        num_ref_pics[0] = 1;
+        pid_diff[0][0] = 4;
+
+        temporal_idx[1] = 2;
+        temporal_up_switch[1] = true;
+        num_ref_pics[1] = 1;
+        pid_diff[1][0] = 1;
+
+        temporal_idx[2] = 1;
+        temporal_up_switch[2] = true;
+        num_ref_pics[2] = 1;
+        pid_diff[2][0] = 2;
+
+        temporal_idx[3] = 2;
+        temporal_up_switch[3] = false;
+        num_ref_pics[3] = 2;
+        pid_diff[3][0] = 1;
+        pid_diff[3][1] = 2;
+        break;
+      default:
+        assert(false);
+    }
+  }
+
+  void CopyGofInfoVP9(const GofInfoVP9& src) {
+    num_frames_in_gof = src.num_frames_in_gof;
+    for (size_t i = 0; i < num_frames_in_gof; ++i) {
+      temporal_idx[i] = src.temporal_idx[i];
+      temporal_up_switch[i] = src.temporal_up_switch[i];
+      num_ref_pics[i] = src.num_ref_pics[i];
+      for (uint8_t r = 0; r < num_ref_pics[i]; ++r) {
+        pid_diff[i][r] = src.pid_diff[i][r];
+      }
+    }
+  }
+
+  size_t num_frames_in_gof;
+  uint8_t temporal_idx[kMaxVp9FramesInGof];
+  bool temporal_up_switch[kMaxVp9FramesInGof];
+  uint8_t num_ref_pics[kMaxVp9FramesInGof];
+  uint8_t pid_diff[kMaxVp9FramesInGof][kMaxVp9RefPics];
+};
+
+struct RTPVideoHeaderVP9 {
+  void InitRTPVideoHeaderVP9() {
+    inter_pic_predicted = false;
+    flexible_mode = false;
+    beginning_of_frame = false;
+    end_of_frame = false;
+    ss_data_available = false;
+    picture_id = kNoPictureId;
+    max_picture_id = kMaxTwoBytePictureId;
+    tl0_pic_idx = kNoTl0PicIdx;
+    temporal_idx = kNoTemporalIdx;
+    spatial_idx = kNoSpatialIdx;
+    temporal_up_switch = false;
+    inter_layer_predicted = false;
+    gof_idx = kNoGofIdx;
+    num_ref_pics = 0;
+    num_spatial_layers = 1;
+  }
+
+  bool inter_pic_predicted;  // This layer frame is dependent on previously
+                             // coded frame(s).
+  bool flexible_mode;        // This frame is in flexible mode.
+  bool beginning_of_frame;   // True if this packet is the first in a VP9 layer
+                             // frame.
+  bool end_of_frame;  // True if this packet is the last in a VP9 layer frame.
+  bool ss_data_available;  // True if SS data is available in this payload
+                           // descriptor.
+  int16_t picture_id;      // PictureID index, 15 bits;
+                              // kNoPictureId if PictureID does not exist.
+  int16_t max_picture_id;  // Maximum picture ID index; either 0x7F or 0x7FFF;
+  int16_t tl0_pic_idx;     // TL0PIC_IDX, 8 bits;
+                              // kNoTl0PicIdx means no value provided.
+  uint8_t temporal_idx;    // Temporal layer index, or kNoTemporalIdx.
+  uint8_t spatial_idx;     // Spatial layer index, or kNoSpatialIdx.
+  bool temporal_up_switch;  // True if upswitch to higher frame rate is possible
+                            // starting from this frame.
+  bool inter_layer_predicted;  // Frame is dependent on directly lower spatial
+                               // layer frame.
+
+  uint8_t gof_idx;  // Index to predefined temporal frame info in SS data.
+
+  uint8_t num_ref_pics;  // Number of reference pictures used by this layer
+                         // frame.
+  uint8_t pid_diff[kMaxVp9RefPics];  // P_DIFF signaled to derive the PictureID
+                                     // of the reference pictures.
+  int16_t ref_picture_id[kMaxVp9RefPics];  // PictureID of reference pictures.
+
+  // SS data.
+  size_t num_spatial_layers;  // Always populated.
+  bool spatial_layer_resolution_present;
+  uint16_t width[kMaxVp9NumberOfSpatialLayers];
+  uint16_t height[kMaxVp9NumberOfSpatialLayers];
+  GofInfoVP9 gof;
+};
+
+#if WEBRTC_48_H264_IMPL
+// The packetization types that we support: single, aggregated, and fragmented.
+enum H264PacketizationTypes {
+  kH264SingleNalu,  // This packet contains a single NAL unit.
+  kH264StapA,       // This packet contains STAP-A (single time
+                    // aggregation) packets. If this packet has an
+                    // associated NAL unit type, it'll be for the
+                    // first such aggregated packet.
+  kH264FuA,         // This packet contains a FU-A (fragmentation
+                    // unit) packet, meaning it is a part of a frame
+                    // that was too large to fit into a single packet.
+};
+
+struct RTPVideoHeaderH264 {
+  uint8_t nalu_type;  // The NAL unit type. If this is a header for a
+                      // fragmented packet, it's the NAL unit type of
+                      // the original data. If this is the header for an
+                      // aggregated packet, it's the NAL unit type of
+                      // the first NAL unit in the packet.
+  H264PacketizationTypes packetization_type;
+};
+#else
+// Mozilla's OpenH264 implementation
 struct RTPVideoHeaderH264 {
   bool stap_a;
   bool single_nalu;
 };
-
-// XXX fix vp9 (bug 1138629)
-struct RTPVideoHeaderVP9 {
-  void InitRTPVideoHeaderVP9() {
-    nonReference = false;
-    pictureId = kNoPictureId;
-    tl0PicIdx = kNoTl0PicIdx;
-    temporalIdx = kNoTemporalIdx;
-    layerSync = false;
-    keyIdx = kNoKeyIdx;
-    partitionId = 0;
-    beginningOfPartition = false;
-  }
-
-  bool nonReference;          // Frame is discardable.
-  int16_t pictureId;          // Picture ID index, 15 bits;
-                              // kNoPictureId if PictureID does not exist.
-  int16_t tl0PicIdx;          // TL0PIC_IDX, 8 bits;
-                              // kNoTl0PicIdx means no value provided.
-  uint8_t temporalIdx;        // Temporal layer index, or kNoTemporalIdx.
-  bool layerSync;             // This frame is a layer sync frame.
-                              // Disabled if temporalIdx == kNoTemporalIdx.
-  int keyIdx;                 // 5 bits; kNoKeyIdx means not used.
-  int partitionId;            // VP9 partition ID
-  bool beginningOfPartition;  // True if this packet is the first
-                              // in a VP9 partition. Otherwise false
-};
+#endif
 
 union RTPVideoTypeHeader {
   RTPVideoHeaderVP8 VP8;
   RTPVideoHeaderVP9 VP9;
   RTPVideoHeaderH264 H264;
 };
 
 enum RtpVideoCodecTypes {
@@ -606,16 +742,28 @@ inline AudioFrame& AudioFrame::Append(co
   int offset = samples_per_channel_ * num_channels_;
   for (int i = 0; i < rhs.samples_per_channel_ * rhs.num_channels_; i++) {
     data_[offset + i] = rhs.data_[i];
   }
   samples_per_channel_ += rhs.samples_per_channel_;
   return *this;
 }
 
+namespace {
+inline int16_t ClampToInt16(int32_t input) {
+  if (input < -0x00008000) {
+    return -0x8000;
+  } else if (input > 0x00007FFF) {
+    return 0x7FFF;
+  } else {
+    return static_cast<int16_t>(input);
+  }
+}
+}
+
 inline AudioFrame& AudioFrame::operator+=(const AudioFrame& rhs) {
   // Sanity check
   assert((num_channels_ > 0) && (num_channels_ < 3));
   assert(interleaved_ == rhs.interleaved_);
   if ((num_channels_ > 2) || (num_channels_ < 1)) return *this;
   if (num_channels_ != rhs.num_channels_) return *this;
 
   bool noPrevData = false;
@@ -638,25 +786,19 @@ inline AudioFrame& AudioFrame::operator+
   if (speech_type_ != rhs.speech_type_) speech_type_ = kUndefined;
 
   if (noPrevData) {
     memcpy(data_, rhs.data_,
            sizeof(int16_t) * rhs.samples_per_channel_ * num_channels_);
   } else {
     // IMPROVEMENT this can be done very fast in assembly
     for (int i = 0; i < samples_per_channel_ * num_channels_; i++) {
-      int32_t wrapGuard =
+      int32_t wrap_guard =
           static_cast<int32_t>(data_[i]) + static_cast<int32_t>(rhs.data_[i]);
-      if (wrapGuard < -32768) {
-        data_[i] = -32768;
-      } else if (wrapGuard > 32767) {
-        data_[i] = 32767;
-      } else {
-        data_[i] = (int16_t)wrapGuard;
-      }
+      data_[i] = ClampToInt16(wrap_guard);
     }
   }
   energy_ = 0xffffffff;
   return *this;
 }
 
 inline AudioFrame& AudioFrame::operator-=(const AudioFrame& rhs) {
   // Sanity check
@@ -669,37 +811,44 @@ inline AudioFrame& AudioFrame::operator-
     return *this;
   }
   if ((vad_activity_ != kVadPassive) || rhs.vad_activity_ != kVadPassive) {
     vad_activity_ = kVadUnknown;
   }
   speech_type_ = kUndefined;
 
   for (int i = 0; i < samples_per_channel_ * num_channels_; i++) {
-    int32_t wrapGuard =
+    int32_t wrap_guard =
         static_cast<int32_t>(data_[i]) - static_cast<int32_t>(rhs.data_[i]);
-    if (wrapGuard < -32768) {
-      data_[i] = -32768;
-    } else if (wrapGuard > 32767) {
-      data_[i] = 32767;
-    } else {
-      data_[i] = (int16_t)wrapGuard;
-    }
+    data_[i] = ClampToInt16(wrap_guard);
   }
   energy_ = 0xffffffff;
   return *this;
 }
 
 inline bool IsNewerSequenceNumber(uint16_t sequence_number,
                                   uint16_t prev_sequence_number) {
+  // Distinguish between elements that are exactly 0x8000 apart.
+  // If s1>s2 and |s1-s2| = 0x8000: IsNewer(s1,s2)=true, IsNewer(s2,s1)=false
+  // rather than having IsNewer(s1,s2) = IsNewer(s2,s1) = false.
+  if (static_cast<uint16_t>(sequence_number - prev_sequence_number) == 0x8000) {
+    return sequence_number > prev_sequence_number;
+  }
   return sequence_number != prev_sequence_number &&
          static_cast<uint16_t>(sequence_number - prev_sequence_number) < 0x8000;
 }
 
 inline bool IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) {
+  // Distinguish between elements that are exactly 0x80000000 apart.
+  // If t1>t2 and |t1-t2| = 0x80000000: IsNewer(t1,t2)=true,
+  // IsNewer(t2,t1)=false
+  // rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false.
+  if (static_cast<uint32_t>(timestamp - prev_timestamp) == 0x80000000) {
+    return timestamp > prev_timestamp;
+  }
   return timestamp != prev_timestamp &&
          static_cast<uint32_t>(timestamp - prev_timestamp) < 0x80000000;
 }
 
 inline bool IsNewerOrSameTimestamp(uint32_t timestamp, uint32_t prev_timestamp) {
   return timestamp == prev_timestamp ||
       static_cast<uint32_t>(timestamp - prev_timestamp) < 0x80000000;
 }
@@ -710,11 +859,51 @@ inline uint16_t LatestSequenceNumber(uin
              ? sequence_number1
              : sequence_number2;
 }
 
 inline uint32_t LatestTimestamp(uint32_t timestamp1, uint32_t timestamp2) {
   return IsNewerTimestamp(timestamp1, timestamp2) ? timestamp1 : timestamp2;
 }
 
+// Utility class to unwrap a sequence number to a larger type, for easier
+// handling large ranges. Note that sequence numbers will never be unwrapped
+// to a negative value.
+class SequenceNumberUnwrapper {
+ public:
+  SequenceNumberUnwrapper() : last_seq_(-1) {}
+
+  // Get the unwrapped sequence, but don't update the internal state.
+  int64_t UnwrapWithoutUpdate(uint16_t sequence_number) {
+    if (last_seq_ == -1)
+      return sequence_number;
+
+    uint16_t cropped_last = static_cast<uint16_t>(last_seq_);
+    int64_t delta = sequence_number - cropped_last;
+    if (IsNewerSequenceNumber(sequence_number, cropped_last)) {
+      if (delta < 0)
+        delta += (1 << 16);  // Wrap forwards.
+    } else if (delta > 0 && (last_seq_ + delta - (1 << 16)) >= 0) {
+      // If sequence_number is older but delta is positive, this is a backwards
+      // wrap-around. However, don't wrap backwards past 0 (unwrapped).
+      delta -= (1 << 16);
+    }
+
+    return last_seq_ + delta;
+  }
+
+  // Only update the internal state to the specified last (unwrapped) sequence.
+  void UpdateLast(int64_t last_sequence) { last_seq_ = last_sequence; }
+
+  // Unwrap the sequence number and update the internal state.
+  int64_t Unwrap(uint16_t sequence_number) {
+    int64_t unwrapped = UnwrapWithoutUpdate(sequence_number);
+    UpdateLast(unwrapped);
+    return unwrapped;
+  }
+
+ private:
+  int64_t last_seq_;
+};
+
 }  // namespace webrtc
 
 #endif  // MODULE_COMMON_TYPES_H
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.cc
@@ -8,44 +8,48 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
 #include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
 
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h"
 
 namespace webrtc {
 RtpPacketizer* RtpPacketizer::Create(RtpVideoCodecTypes type,
                                      size_t max_payload_len,
                                      const RTPVideoTypeHeader* rtp_type_header,
                                      FrameType frame_type) {
   switch (type) {
     case kRtpVideoH264:
       return new RtpPacketizerH264(frame_type, max_payload_len);
     case kRtpVideoVp8:
       assert(rtp_type_header != NULL);
       return new RtpPacketizerVp8(rtp_type_header->VP8, max_payload_len);
     case kRtpVideoVp9:
+      assert(rtp_type_header != NULL);
+      return new RtpPacketizerVp9(rtp_type_header->VP9, max_payload_len);
     case kRtpVideoGeneric:
       return new RtpPacketizerGeneric(frame_type, max_payload_len);
     case kRtpVideoNone:
       assert(false);
   }
   return NULL;
 }
 
 RtpDepacketizer* RtpDepacketizer::Create(RtpVideoCodecTypes type) {
   switch (type) {
     case kRtpVideoH264:
       return new RtpDepacketizerH264();
     case kRtpVideoVp8:
       return new RtpDepacketizerVp8();
-    case kRtpVideoVp9: // XXX fix vp9 packetization (bug 1138629)
+    case kRtpVideoVp9:
+      return new RtpDepacketizerVp9();
     case kRtpVideoGeneric:
       return new RtpDepacketizerGeneric();
     case kRtpVideoNone:
       assert(false);
   }
   return NULL;
 }
 }  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp9.cc
@@ -0,0 +1,743 @@
+/*
+ *  Copyright (c) 2015 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 "webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include <cmath>
+
+#include "webrtc/base/bitbuffer.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+
+#define RETURN_FALSE_ON_ERROR(x) \
+  if (!(x)) {                    \
+    return false;                \
+  }
+
+namespace webrtc {
+namespace {
+// Length of VP9 payload descriptors' fixed part.
+const size_t kFixedPayloadDescriptorBytes = 1;
+
+// Packet fragmentation mode. If true, packets are split into (almost) equal
+// sizes. Otherwise, as many bytes as possible are fit into one packet.
+const bool kBalancedMode = true;
+
+const uint32_t kReservedBitValue0 = 0;
+
+uint8_t TemporalIdxField(const RTPVideoHeaderVP9& hdr, uint8_t def) {
+  return (hdr.temporal_idx == kNoTemporalIdx) ? def : hdr.temporal_idx;
+}
+
+uint8_t SpatialIdxField(const RTPVideoHeaderVP9& hdr, uint8_t def) {
+  return (hdr.spatial_idx == kNoSpatialIdx) ? def : hdr.spatial_idx;
+}
+
+int16_t Tl0PicIdxField(const RTPVideoHeaderVP9& hdr, uint8_t def) {
+  return (hdr.tl0_pic_idx == kNoTl0PicIdx) ? def : hdr.tl0_pic_idx;
+}
+
+// Picture ID:
+//
+//      +-+-+-+-+-+-+-+-+
+// I:   |M| PICTURE ID  |   M:0 => picture id is 7 bits.
+//      +-+-+-+-+-+-+-+-+   M:1 => picture id is 15 bits.
+// M:   | EXTENDED PID  |
+//      +-+-+-+-+-+-+-+-+
+//
+size_t PictureIdLength(const RTPVideoHeaderVP9& hdr) {
+  if (hdr.picture_id == kNoPictureId)
+    return 0;
+  return (hdr.max_picture_id == kMaxOneBytePictureId) ? 1 : 2;
+}
+
+bool PictureIdPresent(const RTPVideoHeaderVP9& hdr) {
+  return PictureIdLength(hdr) > 0;
+}
+
+// Layer indices:
+//
+// Flexible mode (F=1):     Non-flexible mode (F=0):
+//
+//      +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D|   |  T  |U|  S  |D|
+//      +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
+//                          |   TL0PICIDX   |
+//                          +-+-+-+-+-+-+-+-+
+//
+size_t LayerInfoLength(const RTPVideoHeaderVP9& hdr) {
+  if (hdr.temporal_idx == kNoTemporalIdx &&
+      hdr.spatial_idx == kNoSpatialIdx) {
+    return 0;
+  }
+  return hdr.flexible_mode ? 1 : 2;
+}
+
+bool LayerInfoPresent(const RTPVideoHeaderVP9& hdr) {
+  return LayerInfoLength(hdr) > 0;
+}
+
+// Reference indices:
+//
+//      +-+-+-+-+-+-+-+-+                P=1,F=1: At least one reference index
+// P,F: | P_DIFF      |N|  up to 3 times          has to be specified.
+//      +-+-+-+-+-+-+-+-+                    N=1: An additional P_DIFF follows
+//                                                current P_DIFF.
+//
+size_t RefIndicesLength(const RTPVideoHeaderVP9& hdr) {
+  if (!hdr.inter_pic_predicted || !hdr.flexible_mode)
+    return 0;
+
+  RTC_DCHECK_GT(hdr.num_ref_pics, 0U);
+  RTC_DCHECK_LE(hdr.num_ref_pics, kMaxVp9RefPics);
+  return hdr.num_ref_pics;
+}
+
+// Scalability structure (SS).
+//
+//      +-+-+-+-+-+-+-+-+
+// V:   | N_S |Y|G|-|-|-|
+//      +-+-+-+-+-+-+-+-+              -|
+// Y:   |     WIDTH     | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+               . N_S + 1 times
+//      |     HEIGHT    | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+              -|
+// G:   |      N_G      | (OPTIONAL)
+//      +-+-+-+-+-+-+-+-+                           -|
+// N_G: |  T  |U| R |-|-| (OPTIONAL)                 .
+//      +-+-+-+-+-+-+-+-+              -|            . N_G times
+//      |    P_DIFF     | (OPTIONAL)    . R times    .
+//      +-+-+-+-+-+-+-+-+              -|           -|
+//
+size_t SsDataLength(const RTPVideoHeaderVP9& hdr) {
+  if (!hdr.ss_data_available)
+    return 0;
+
+  RTC_DCHECK_GT(hdr.num_spatial_layers, 0U);
+  RTC_DCHECK_LE(hdr.num_spatial_layers, kMaxVp9NumberOfSpatialLayers);
+  RTC_DCHECK_LE(hdr.gof.num_frames_in_gof, kMaxVp9FramesInGof);
+  size_t length = 1;                           // V
+  if (hdr.spatial_layer_resolution_present) {
+    length += 4 * hdr.num_spatial_layers;      // Y
+  }
+  if (hdr.gof.num_frames_in_gof > 0) {
+    ++length;                                  // G
+  }
+  // N_G
+  length += hdr.gof.num_frames_in_gof;  // T, U, R
+  for (size_t i = 0; i < hdr.gof.num_frames_in_gof; ++i) {
+    RTC_DCHECK_LE(hdr.gof.num_ref_pics[i], kMaxVp9RefPics);
+    length += hdr.gof.num_ref_pics[i];  // R times
+  }
+  return length;
+}
+
+size_t PayloadDescriptorLengthMinusSsData(const RTPVideoHeaderVP9& hdr) {
+  return kFixedPayloadDescriptorBytes + PictureIdLength(hdr) +
+         LayerInfoLength(hdr) + RefIndicesLength(hdr);
+}
+
+size_t PayloadDescriptorLength(const RTPVideoHeaderVP9& hdr) {
+  return PayloadDescriptorLengthMinusSsData(hdr) + SsDataLength(hdr);
+}
+
+void QueuePacket(size_t start_pos,
+                 size_t size,
+                 bool layer_begin,
+                 bool layer_end,
+                 RtpPacketizerVp9::PacketInfoQueue* packets) {
+  RtpPacketizerVp9::PacketInfo packet_info;
+  packet_info.payload_start_pos = start_pos;
+  packet_info.size = size;
+  packet_info.layer_begin = layer_begin;
+  packet_info.layer_end = layer_end;
+  packets->push(packet_info);
+}
+
+// Picture ID:
+//
+//      +-+-+-+-+-+-+-+-+
+// I:   |M| PICTURE ID  |   M:0 => picture id is 7 bits.
+//      +-+-+-+-+-+-+-+-+   M:1 => picture id is 15 bits.
+// M:   | EXTENDED PID  |
+//      +-+-+-+-+-+-+-+-+
+//
+bool WritePictureId(const RTPVideoHeaderVP9& vp9,
+                    rtc::BitBufferWriter* writer) {
+  bool m_bit = (PictureIdLength(vp9) == 2);
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(m_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.picture_id, m_bit ? 15 : 7));
+  return true;
+}
+
+// Layer indices:
+//
+// Flexible mode (F=1):
+//
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D|
+//      +-+-+-+-+-+-+-+-+
+//
+bool WriteLayerInfoCommon(const RTPVideoHeaderVP9& vp9,
+                          rtc::BitBufferWriter* writer) {
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(TemporalIdxField(vp9, 0), 3));
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.temporal_up_switch ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(SpatialIdxField(vp9, 0), 3));
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.inter_layer_predicted ? 1: 0, 1));
+  return true;
+}
+
+// Non-flexible mode (F=0):
+//
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D|
+//      +-+-+-+-+-+-+-+-+
+//      |   TL0PICIDX   |
+//      +-+-+-+-+-+-+-+-+
+//
+bool WriteLayerInfoNonFlexibleMode(const RTPVideoHeaderVP9& vp9,
+                                   rtc::BitBufferWriter* writer) {
+  RETURN_FALSE_ON_ERROR(writer->WriteUInt8(Tl0PicIdxField(vp9, 0)));
+  return true;
+}
+
+bool WriteLayerInfo(const RTPVideoHeaderVP9& vp9,
+                    rtc::BitBufferWriter* writer) {
+  if (!WriteLayerInfoCommon(vp9, writer))
+    return false;
+
+  if (vp9.flexible_mode)
+    return true;
+
+  return WriteLayerInfoNonFlexibleMode(vp9, writer);
+}
+
+// Reference indices:
+//
+//      +-+-+-+-+-+-+-+-+                P=1,F=1: At least one reference index
+// P,F: | P_DIFF      |N|  up to 3 times          has to be specified.
+//      +-+-+-+-+-+-+-+-+                    N=1: An additional P_DIFF follows
+//                                                current P_DIFF.
+//
+bool WriteRefIndices(const RTPVideoHeaderVP9& vp9,
+                     rtc::BitBufferWriter* writer) {
+  if (!PictureIdPresent(vp9) ||
+      vp9.num_ref_pics == 0 || vp9.num_ref_pics > kMaxVp9RefPics) {
+    return false;
+  }
+  for (uint8_t i = 0; i < vp9.num_ref_pics; ++i) {
+    bool n_bit = !(i == vp9.num_ref_pics - 1);
+    RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.pid_diff[i], 7));
+    RETURN_FALSE_ON_ERROR(writer->WriteBits(n_bit ? 1 : 0, 1));
+  }
+  return true;
+}
+
+// Scalability structure (SS).
+//
+//      +-+-+-+-+-+-+-+-+
+// V:   | N_S |Y|G|-|-|-|
+//      +-+-+-+-+-+-+-+-+              -|
+// Y:   |     WIDTH     | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+               . N_S + 1 times
+//      |     HEIGHT    | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+              -|
+// G:   |      N_G      | (OPTIONAL)
+//      +-+-+-+-+-+-+-+-+                           -|
+// N_G: |  T  |U| R |-|-| (OPTIONAL)                 .
+//      +-+-+-+-+-+-+-+-+              -|            . N_G times
+//      |    P_DIFF     | (OPTIONAL)    . R times    .
+//      +-+-+-+-+-+-+-+-+              -|           -|
+//
+bool WriteSsData(const RTPVideoHeaderVP9& vp9, rtc::BitBufferWriter* writer) {
+  RTC_DCHECK_GT(vp9.num_spatial_layers, 0U);
+  RTC_DCHECK_LE(vp9.num_spatial_layers, kMaxVp9NumberOfSpatialLayers);
+  RTC_DCHECK_LE(vp9.gof.num_frames_in_gof, kMaxVp9FramesInGof);
+  bool g_bit = vp9.gof.num_frames_in_gof > 0;
+
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.num_spatial_layers - 1, 3));
+  RETURN_FALSE_ON_ERROR(
+      writer->WriteBits(vp9.spatial_layer_resolution_present ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(g_bit ? 1 : 0, 1));  // G
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(kReservedBitValue0, 3));
+
+  if (vp9.spatial_layer_resolution_present) {
+    for (size_t i = 0; i < vp9.num_spatial_layers; ++i) {
+      RETURN_FALSE_ON_ERROR(writer->WriteUInt16(vp9.width[i]));
+      RETURN_FALSE_ON_ERROR(writer->WriteUInt16(vp9.height[i]));
+    }
+  }
+  if (g_bit) {
+    RETURN_FALSE_ON_ERROR(writer->WriteUInt8(vp9.gof.num_frames_in_gof));
+  }
+  for (size_t i = 0; i < vp9.gof.num_frames_in_gof; ++i) {
+    RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.gof.temporal_idx[i], 3));
+    RETURN_FALSE_ON_ERROR(
+        writer->WriteBits(vp9.gof.temporal_up_switch[i] ? 1 : 0, 1));
+    RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.gof.num_ref_pics[i], 2));
+    RETURN_FALSE_ON_ERROR(writer->WriteBits(kReservedBitValue0, 2));
+    for (uint8_t r = 0; r < vp9.gof.num_ref_pics[i]; ++r) {
+      RETURN_FALSE_ON_ERROR(writer->WriteUInt8(vp9.gof.pid_diff[i][r]));
+    }
+  }
+  return true;
+}
+
+// Picture ID:
+//
+//      +-+-+-+-+-+-+-+-+
+// I:   |M| PICTURE ID  |   M:0 => picture id is 7 bits.
+//      +-+-+-+-+-+-+-+-+   M:1 => picture id is 15 bits.
+// M:   | EXTENDED PID  |
+//      +-+-+-+-+-+-+-+-+
+//
+bool ParsePictureId(rtc::BitBuffer* parser, RTPVideoHeaderVP9* vp9) {
+  uint32_t picture_id;
+  uint32_t m_bit;
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&m_bit, 1));
+  if (m_bit) {
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&picture_id, 15));
+    vp9->max_picture_id = kMaxTwoBytePictureId;
+  } else {
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&picture_id, 7));
+    vp9->max_picture_id = kMaxOneBytePictureId;
+  }
+  vp9->picture_id = picture_id;
+  return true;
+}
+
+// Layer indices (flexible mode):
+//
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D|
+//      +-+-+-+-+-+-+-+-+
+//
+bool ParseLayerInfoCommon(rtc::BitBuffer* parser, RTPVideoHeaderVP9* vp9) {
+  uint32_t t, u_bit, s, d_bit;
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&t, 3));
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&u_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&s, 3));
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&d_bit, 1));
+  vp9->temporal_idx = t;
+  vp9->temporal_up_switch = u_bit ? true : false;
+  vp9->spatial_idx = s;
+  vp9->inter_layer_predicted = d_bit ? true : false;
+  return true;
+}
+
+// Layer indices (non-flexible mode):
+//
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D|
+//      +-+-+-+-+-+-+-+-+
+//      |   TL0PICIDX   |
+//      +-+-+-+-+-+-+-+-+
+//
+bool ParseLayerInfoNonFlexibleMode(rtc::BitBuffer* parser,
+                                   RTPVideoHeaderVP9* vp9) {
+  uint8_t tl0picidx;
+  RETURN_FALSE_ON_ERROR(parser->ReadUInt8(&tl0picidx));
+  vp9->tl0_pic_idx = tl0picidx;
+  return true;
+}
+
+bool ParseLayerInfo(rtc::BitBuffer* parser, RTPVideoHeaderVP9* vp9) {
+  if (!ParseLayerInfoCommon(parser, vp9))
+    return false;
+
+  if (vp9->flexible_mode)
+    return true;
+
+  return ParseLayerInfoNonFlexibleMode(parser, vp9);
+}
+
+// Reference indices:
+//
+//      +-+-+-+-+-+-+-+-+                P=1,F=1: At least one reference index
+// P,F: | P_DIFF      |N|  up to 3 times          has to be specified.
+//      +-+-+-+-+-+-+-+-+                    N=1: An additional P_DIFF follows
+//                                                current P_DIFF.
+//
+bool ParseRefIndices(rtc::BitBuffer* parser, RTPVideoHeaderVP9* vp9) {
+  if (vp9->picture_id == kNoPictureId)
+    return false;
+
+  vp9->num_ref_pics = 0;
+  uint32_t n_bit;
+  do {
+    if (vp9->num_ref_pics == kMaxVp9RefPics)
+      return false;
+
+    uint32_t p_diff;
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&p_diff, 7));
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&n_bit, 1));
+
+    vp9->pid_diff[vp9->num_ref_pics] = p_diff;
+    uint32_t scaled_pid = vp9->picture_id;
+    if (p_diff > scaled_pid) {
+      // TODO(asapersson): Max should correspond to the picture id of last wrap.
+      scaled_pid += vp9->max_picture_id + 1;
+    }
+    vp9->ref_picture_id[vp9->num_ref_pics++] = scaled_pid - p_diff;
+  } while (n_bit);
+
+  return true;
+}
+
+// Scalability structure (SS).
+//
+//      +-+-+-+-+-+-+-+-+
+// V:   | N_S |Y|G|-|-|-|
+//      +-+-+-+-+-+-+-+-+              -|
+// Y:   |     WIDTH     | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+               . N_S + 1 times
+//      |     HEIGHT    | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+              -|
+// G:   |      N_G      | (OPTIONAL)
+//      +-+-+-+-+-+-+-+-+                           -|
+// N_G: |  T  |U| R |-|-| (OPTIONAL)                 .
+//      +-+-+-+-+-+-+-+-+              -|            . N_G times
+//      |    P_DIFF     | (OPTIONAL)    . R times    .
+//      +-+-+-+-+-+-+-+-+              -|           -|
+//
+bool ParseSsData(rtc::BitBuffer* parser, RTPVideoHeaderVP9* vp9) {
+  uint32_t n_s, y_bit, g_bit;
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&n_s, 3));
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&y_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&g_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser->ConsumeBits(3));
+  vp9->num_spatial_layers = n_s + 1;
+  vp9->spatial_layer_resolution_present = y_bit ? true : false;
+  vp9->gof.num_frames_in_gof = 0;
+
+  if (y_bit) {
+    for (size_t i = 0; i < vp9->num_spatial_layers; ++i) {
+      RETURN_FALSE_ON_ERROR(parser->ReadUInt16(&vp9->width[i]));
+      RETURN_FALSE_ON_ERROR(parser->ReadUInt16(&vp9->height[i]));
+    }
+  }
+  if (g_bit) {
+    uint8_t n_g;
+    RETURN_FALSE_ON_ERROR(parser->ReadUInt8(&n_g));
+    vp9->gof.num_frames_in_gof = n_g;
+  }
+  for (size_t i = 0; i < vp9->gof.num_frames_in_gof; ++i) {
+    uint32_t t, u_bit, r;
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&t, 3));
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&u_bit, 1));
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&r, 2));
+    RETURN_FALSE_ON_ERROR(parser->ConsumeBits(2));
+    vp9->gof.temporal_idx[i] = t;
+    vp9->gof.temporal_up_switch[i] = u_bit ? true : false;
+    vp9->gof.num_ref_pics[i] = r;
+
+    for (uint8_t p = 0; p < vp9->gof.num_ref_pics[i]; ++p) {
+      uint8_t p_diff;
+      RETURN_FALSE_ON_ERROR(parser->ReadUInt8(&p_diff));
+      vp9->gof.pid_diff[i][p] = p_diff;
+    }
+  }
+  return true;
+}
+
+// Gets the size of next payload chunk to send. Returns 0 on error.
+size_t CalcNextSize(size_t max_length, size_t rem_bytes) {
+  if (max_length == 0 || rem_bytes == 0) {
+    return 0;
+  }
+  if (kBalancedMode) {
+    size_t num_frags = std::ceil(static_cast<double>(rem_bytes) / max_length);
+    return static_cast<size_t>(
+        static_cast<double>(rem_bytes) / num_frags + 0.5);
+  }
+  return max_length >= rem_bytes ? rem_bytes : max_length;
+}
+}  // namespace
+
+
+RtpPacketizerVp9::RtpPacketizerVp9(const RTPVideoHeaderVP9& hdr,
+                                   size_t max_payload_length)
+    : hdr_(hdr),
+      max_payload_length_(max_payload_length),
+      payload_(nullptr),
+      payload_size_(0) {
+}
+
+RtpPacketizerVp9::~RtpPacketizerVp9() {
+}
+
+ProtectionType RtpPacketizerVp9::GetProtectionType() {
+  bool protect =
+      hdr_.temporal_idx == 0 || hdr_.temporal_idx == kNoTemporalIdx;
+  return protect ? kProtectedPacket : kUnprotectedPacket;
+}
+
+StorageType RtpPacketizerVp9::GetStorageType(uint32_t retransmission_settings) {
+  StorageType storage = kAllowRetransmission;
+  if (hdr_.temporal_idx == 0 &&
+      !(retransmission_settings & kRetransmitBaseLayer)) {
+    storage = kDontRetransmit;
+  } else if (hdr_.temporal_idx != kNoTemporalIdx && hdr_.temporal_idx > 0 &&
+             !(retransmission_settings & kRetransmitHigherLayers)) {
+    storage = kDontRetransmit;
+  }
+  return storage;
+}
+
+std::string RtpPacketizerVp9::ToString() {
+  return "RtpPacketizerVp9";
+}
+
+void RtpPacketizerVp9::SetPayloadData(
+    const uint8_t* payload,
+    size_t payload_size,
+    const RTPFragmentationHeader* fragmentation) {
+  payload_ = payload;
+  payload_size_ = payload_size;
+  GeneratePackets();
+}
+
+void RtpPacketizerVp9::GeneratePackets() {
+  if (max_payload_length_ < PayloadDescriptorLength(hdr_) + 1) {
+    LOG(LS_ERROR) << "Payload header and one payload byte won't fit.";
+    return;
+  }
+  size_t bytes_processed = 0;
+  while (bytes_processed < payload_size_) {
+    size_t rem_bytes = payload_size_ - bytes_processed;
+    size_t rem_payload_len = max_payload_length_ -
+         (bytes_processed ? PayloadDescriptorLengthMinusSsData(hdr_)
+                          : PayloadDescriptorLength(hdr_));
+
+    size_t packet_bytes = CalcNextSize(rem_payload_len, rem_bytes);
+    if (packet_bytes == 0) {
+      LOG(LS_ERROR) << "Failed to generate VP9 packets.";
+      while (!packets_.empty())
+        packets_.pop();
+      return;
+    }
+    QueuePacket(bytes_processed, packet_bytes, bytes_processed == 0,
+                rem_bytes == packet_bytes, &packets_);
+    bytes_processed += packet_bytes;
+  }
+  assert(bytes_processed == payload_size_);
+}
+
+bool RtpPacketizerVp9::NextPacket(uint8_t* buffer,
+                                  size_t* bytes_to_send,
+                                  bool* last_packet) {
+  if (packets_.empty()) {
+    return false;
+  }
+  PacketInfo packet_info = packets_.front();
+  packets_.pop();
+
+  if (!WriteHeaderAndPayload(packet_info, buffer, bytes_to_send)) {
+    return false;
+  }
+  *last_packet =
+      packets_.empty() && (hdr_.spatial_idx == kNoSpatialIdx ||
+                           hdr_.spatial_idx == hdr_.num_spatial_layers - 1);
+  return true;
+}
+
+// VP9 format:
+//
+// Payload descriptor for F = 1 (flexible mode)
+//       0 1 2 3 4 5 6 7
+//      +-+-+-+-+-+-+-+-+
+//      |I|P|L|F|B|E|V|-| (REQUIRED)
+//      +-+-+-+-+-+-+-+-+
+// I:   |M| PICTURE ID  | (RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+
+// M:   | EXTENDED PID  | (RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D| (CONDITIONALLY RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+                             -|
+// P,F: | P_DIFF      |N| (CONDITIONALLY RECOMMENDED)  . up to 3 times
+//      +-+-+-+-+-+-+-+-+                             -|
+// V:   | SS            |
+//      | ..            |
+//      +-+-+-+-+-+-+-+-+
+//
+// Payload descriptor for F = 0 (non-flexible mode)
+//       0 1 2 3 4 5 6 7
+//      +-+-+-+-+-+-+-+-+
+//      |I|P|L|F|B|E|V|-| (REQUIRED)
+//      +-+-+-+-+-+-+-+-+
+// I:   |M| PICTURE ID  | (RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+
+// M:   | EXTENDED PID  | (RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D| (CONDITIONALLY RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+
+//      |   TL0PICIDX   | (CONDITIONALLY REQUIRED)
+//      +-+-+-+-+-+-+-+-+
+// V:   | SS            |
+//      | ..            |
+//      +-+-+-+-+-+-+-+-+
+
+bool RtpPacketizerVp9::WriteHeaderAndPayload(const PacketInfo& packet_info,
+                                             uint8_t* buffer,
+                                             size_t* bytes_to_send) const {
+  size_t header_length;
+  if (!WriteHeader(packet_info, buffer, &header_length))
+    return false;
+
+  // Copy payload data.
+  memcpy(&buffer[header_length],
+         &payload_[packet_info.payload_start_pos], packet_info.size);
+
+  *bytes_to_send = header_length + packet_info.size;
+  return true;
+}
+
+bool RtpPacketizerVp9::WriteHeader(const PacketInfo& packet_info,
+                                   uint8_t* buffer,
+                                   size_t* header_length) const {
+  // Required payload descriptor byte.
+  bool i_bit = PictureIdPresent(hdr_);
+  bool p_bit = hdr_.inter_pic_predicted;
+  bool l_bit = LayerInfoPresent(hdr_);
+  bool f_bit = hdr_.flexible_mode;
+  bool b_bit = packet_info.layer_begin;
+  bool e_bit = packet_info.layer_end;
+  bool v_bit = hdr_.ss_data_available && b_bit;
+
+  rtc::BitBufferWriter writer(buffer, max_payload_length_);
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(i_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(p_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(l_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(f_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(b_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(e_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(v_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(kReservedBitValue0, 1));
+
+  // Add fields that are present.
+  if (i_bit && !WritePictureId(hdr_, &writer)) {
+    LOG(LS_ERROR) << "Failed writing VP9 picture id.";
+    return false;
+  }
+  if (l_bit && !WriteLayerInfo(hdr_, &writer)) {
+    LOG(LS_ERROR) << "Failed writing VP9 layer info.";
+    return false;
+  }
+  if (p_bit && f_bit && !WriteRefIndices(hdr_, &writer)) {
+    LOG(LS_ERROR) << "Failed writing VP9 ref indices.";
+    return false;
+  }
+  if (v_bit && !WriteSsData(hdr_, &writer)) {
+    LOG(LS_ERROR) << "Failed writing VP9 SS data.";
+    return false;
+  }
+
+  size_t offset_bytes = 0;
+  size_t offset_bits = 0;
+  writer.GetCurrentOffset(&offset_bytes, &offset_bits);
+  assert(offset_bits == 0);
+
+  *header_length = offset_bytes;
+  return true;
+}
+
+bool RtpDepacketizerVp9::Parse(ParsedPayload* parsed_payload,
+                               const uint8_t* payload,
+                               size_t payload_length) {
+  assert(parsed_payload != nullptr);
+  if (payload_length == 0) {
+    LOG(LS_ERROR) << "Payload length is zero.";
+    return false;
+  }
+
+  // Parse mandatory first byte of payload descriptor.
+  rtc::BitBuffer parser(payload, payload_length);
+  uint32_t i_bit, p_bit, l_bit, f_bit, b_bit, e_bit, v_bit;
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&i_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&p_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&l_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&f_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&b_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&e_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&v_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ConsumeBits(1));
+
+  // Parsed payload.
+  parsed_payload->type.Video.width = 0;
+  parsed_payload->type.Video.height = 0;
+  parsed_payload->type.Video.simulcastIdx = 0;
+  parsed_payload->type.Video.codec = kRtpVideoVp9;
+
+  parsed_payload->frame_type = p_bit ? kVideoFrameDelta : kVideoFrameKey;
+
+  RTPVideoHeaderVP9* vp9 = &parsed_payload->type.Video.codecHeader.VP9;
+  vp9->InitRTPVideoHeaderVP9();
+  vp9->inter_pic_predicted = p_bit ? true : false;
+  vp9->flexible_mode = f_bit ? true : false;
+  vp9->beginning_of_frame = b_bit ? true : false;
+  vp9->end_of_frame = e_bit ? true : false;
+  vp9->ss_data_available = v_bit ? true : false;
+  vp9->spatial_idx = 0;
+
+  // Parse fields that are present.
+  if (i_bit && !ParsePictureId(&parser, vp9)) {
+    LOG(LS_ERROR) << "Failed parsing VP9 picture id.";
+    return false;
+  }
+  if (l_bit && !ParseLayerInfo(&parser, vp9)) {
+    LOG(LS_ERROR) << "Failed parsing VP9 layer info.";
+    return false;
+  }
+  if (p_bit && f_bit && !ParseRefIndices(&parser, vp9)) {
+    LOG(LS_ERROR) << "Failed parsing VP9 ref indices.";
+    return false;
+  }
+  if (v_bit) {
+    if (!ParseSsData(&parser, vp9)) {
+      LOG(LS_ERROR) << "Failed parsing VP9 SS data.";
+      return false;
+    }
+    if (vp9->spatial_layer_resolution_present) {
+      // TODO(asapersson): Add support for spatial layers.
+      parsed_payload->type.Video.width = vp9->width[0];
+      parsed_payload->type.Video.height = vp9->height[0];
+    }
+  }
+  parsed_payload->type.Video.isFirstPacket =
+      b_bit && (!l_bit || !vp9->inter_layer_predicted);
+
+  uint64_t rem_bits = parser.RemainingBitCount();
+  assert(rem_bits % 8 == 0);
+  parsed_payload->payload_length = rem_bits / 8;
+  if (parsed_payload->payload_length == 0) {
+    LOG(LS_ERROR) << "Failed parsing VP9 payload data.";
+    return false;
+  }
+  parsed_payload->payload =
+      payload + payload_length - parsed_payload->payload_length;
+
+  return true;
+}
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (c) 2015 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 contains the declaration of the VP9 packetizer class.
+// A packetizer object is created for each encoded video frame. The
+// constructor is called with the payload data and size.
+//
+// After creating the packetizer, the method NextPacket is called
+// repeatedly to get all packets for the frame. The method returns
+// false as long as there are more packets left to fetch.
+//
+
+#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP9_H_
+#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP9_H_
+
+#include <queue>
+#include <string>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/modules/include/module_common_types.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class RtpPacketizerVp9 : public RtpPacketizer {
+ public:
+  RtpPacketizerVp9(const RTPVideoHeaderVP9& hdr, size_t max_payload_length);
+
+  virtual ~RtpPacketizerVp9();
+
+  ProtectionType GetProtectionType() override;
+
+  StorageType GetStorageType(uint32_t retransmission_settings) override;
+
+  std::string ToString() override;
+
+  // The payload data must be one encoded VP9 frame.
+  void SetPayloadData(const uint8_t* payload,
+                      size_t payload_size,
+                      const RTPFragmentationHeader* fragmentation) override;
+
+  // Gets the next payload with VP9 payload header.
+  // |buffer| is a pointer to where the output will be written.
+  // |bytes_to_send| is an output variable that will contain number of bytes
+  // written to buffer.
+  // |last_packet| is true for the last packet of the frame, false otherwise
+  // (i.e. call the function again to get the next packet).
+  // Returns true on success, false otherwise.
+  bool NextPacket(uint8_t* buffer,
+                  size_t* bytes_to_send,
+                  bool* last_packet) override;
+
+  typedef struct {
+    size_t payload_start_pos;
+    size_t size;
+    bool layer_begin;
+    bool layer_end;
+  } PacketInfo;
+  typedef std::queue<PacketInfo> PacketInfoQueue;
+
+ private:
+  // Calculates all packet sizes and loads info to packet queue.
+  void GeneratePackets();
+
+  // Writes the payload descriptor header and copies payload to the |buffer|.
+  // |packet_info| determines which part of the payload to write.
+  // |bytes_to_send| contains the number of written bytes to the buffer.
+  // Returns true on success, false otherwise.
+  bool WriteHeaderAndPayload(const PacketInfo& packet_info,
+                             uint8_t* buffer,
+                             size_t* bytes_to_send) const;
+
+  // Writes payload descriptor header to |buffer|.
+  // Returns true on success, false otherwise.
+  bool WriteHeader(const PacketInfo& packet_info,
+                   uint8_t* buffer,
+                   size_t* header_length) const;
+
+  const RTPVideoHeaderVP9 hdr_;
+  const size_t max_payload_length_;  // The max length in bytes of one packet.
+  const uint8_t* payload_;           // The payload data to be packetized.
+  size_t payload_size_;              // The size in bytes of the payload data.
+  PacketInfoQueue packets_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(RtpPacketizerVp9);
+};
+
+
+class RtpDepacketizerVp9 : public RtpDepacketizer {
+ public:
+  virtual ~RtpDepacketizerVp9() {}
+
+  bool Parse(ParsedPayload* parsed_payload,
+             const uint8_t* payload,
+             size_t payload_length) override;
+};
+
+}  // namespace webrtc
+#endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP9_H_
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp9_unittest.cc
@@ -0,0 +1,690 @@
+/*
+ *  Copyright (c) 2015 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 <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace {
+void VerifyHeader(const RTPVideoHeaderVP9& expected,
+                  const RTPVideoHeaderVP9& actual) {
+  EXPECT_EQ(expected.inter_layer_predicted, actual.inter_layer_predicted);
+  EXPECT_EQ(expected.inter_pic_predicted, actual.inter_pic_predicted);
+  EXPECT_EQ(expected.flexible_mode, actual.flexible_mode);
+  EXPECT_EQ(expected.beginning_of_frame, actual.beginning_of_frame);
+  EXPECT_EQ(expected.end_of_frame, actual.end_of_frame);
+  EXPECT_EQ(expected.ss_data_available, actual.ss_data_available);
+  EXPECT_EQ(expected.picture_id, actual.picture_id);
+  EXPECT_EQ(expected.max_picture_id, actual.max_picture_id);
+  EXPECT_EQ(expected.temporal_idx, actual.temporal_idx);
+  EXPECT_EQ(expected.spatial_idx == kNoSpatialIdx ? 0 : expected.spatial_idx,
+      actual.spatial_idx);
+  EXPECT_EQ(expected.gof_idx, actual.gof_idx);
+  EXPECT_EQ(expected.tl0_pic_idx, actual.tl0_pic_idx);
+  EXPECT_EQ(expected.temporal_up_switch, actual.temporal_up_switch);
+
+  EXPECT_EQ(expected.num_ref_pics, actual.num_ref_pics);
+  for (uint8_t i = 0; i < expected.num_ref_pics; ++i) {
+    EXPECT_EQ(expected.pid_diff[i], actual.pid_diff[i]);
+    EXPECT_EQ(expected.ref_picture_id[i], actual.ref_picture_id[i]);
+  }
+  if (expected.ss_data_available) {
+    EXPECT_EQ(expected.spatial_layer_resolution_present,
+              actual.spatial_layer_resolution_present);
+    EXPECT_EQ(expected.num_spatial_layers, actual.num_spatial_layers);
+    if (expected.spatial_layer_resolution_present) {
+      for (size_t i = 0; i < expected.num_spatial_layers; i++) {
+        EXPECT_EQ(expected.width[i], actual.width[i]);
+        EXPECT_EQ(expected.height[i], actual.height[i]);
+      }
+    }
+    EXPECT_EQ(expected.gof.num_frames_in_gof, actual.gof.num_frames_in_gof);
+    for (size_t i = 0; i < expected.gof.num_frames_in_gof; i++) {
+      EXPECT_EQ(expected.gof.temporal_up_switch[i],
+                actual.gof.temporal_up_switch[i]);
+      EXPECT_EQ(expected.gof.temporal_idx[i], actual.gof.temporal_idx[i]);
+      EXPECT_EQ(expected.gof.num_ref_pics[i], actual.gof.num_ref_pics[i]);
+      for (uint8_t j = 0; j < expected.gof.num_ref_pics[i]; j++) {
+        EXPECT_EQ(expected.gof.pid_diff[i][j], actual.gof.pid_diff[i][j]);
+      }
+    }
+  }
+}
+
+void VerifyPayload(const RtpDepacketizer::ParsedPayload& parsed,
+                   const uint8_t* payload,
+                   size_t payload_length) {
+  EXPECT_EQ(payload, parsed.payload);
+  EXPECT_EQ(payload_length, parsed.payload_length);
+  EXPECT_THAT(std::vector<uint8_t>(parsed.payload,
+                                   parsed.payload + parsed.payload_length),
+              ::testing::ElementsAreArray(payload, payload_length));
+}
+
+void ParseAndCheckPacket(const uint8_t* packet,
+                         const RTPVideoHeaderVP9& expected,
+                         size_t expected_hdr_length,
+                         size_t expected_length) {
+  rtc::scoped_ptr<RtpDepacketizer> depacketizer(new RtpDepacketizerVp9());
+  RtpDepacketizer::ParsedPayload parsed;
+  ASSERT_TRUE(depacketizer->Parse(&parsed, packet, expected_length));
+  EXPECT_EQ(kRtpVideoVp9, parsed.type.Video.codec);
+  VerifyHeader(expected, parsed.type.Video.codecHeader.VP9);
+  const size_t kExpectedPayloadLength = expected_length - expected_hdr_length;
+  VerifyPayload(parsed, packet + expected_hdr_length, kExpectedPayloadLength);
+}
+}  // namespace
+
+// Payload descriptor for flexible mode
+//        0 1 2 3 4 5 6 7
+//        +-+-+-+-+-+-+-+-+
+//        |I|P|L|F|B|E|V|-| (REQUIRED)
+//        +-+-+-+-+-+-+-+-+
+//   I:   |M| PICTURE ID  | (RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+
+//   M:   | EXTENDED PID  | (RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+
+//   L:   |  T  |U|  S  |D| (CONDITIONALLY RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+                             -|
+//   P,F: | P_DIFF      |N| (CONDITIONALLY RECOMMENDED)  . up to 3 times
+//        +-+-+-+-+-+-+-+-+                             -|
+//   V:   | SS            |
+//        | ..            |
+//        +-+-+-+-+-+-+-+-+
+//
+// Payload descriptor for non-flexible mode
+//        0 1 2 3 4 5 6 7
+//        +-+-+-+-+-+-+-+-+
+//        |I|P|L|F|B|E|V|-| (REQUIRED)
+//        +-+-+-+-+-+-+-+-+
+//   I:   |M| PICTURE ID  | (RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+
+//   M:   | EXTENDED PID  | (RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+
+//   L:   |  T  |U|  S  |D| (CONDITIONALLY RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+
+//        |   TL0PICIDX   | (CONDITIONALLY REQUIRED)
+//        +-+-+-+-+-+-+-+-+
+//   V:   | SS            |
+//        | ..            |
+//        +-+-+-+-+-+-+-+-+
+
+class RtpPacketizerVp9Test : public ::testing::Test {
+ protected:
+  RtpPacketizerVp9Test() {}
+  virtual void SetUp() {
+    expected_.InitRTPVideoHeaderVP9();
+  }
+
+  rtc::scoped_ptr<uint8_t[]> packet_;
+  rtc::scoped_ptr<uint8_t[]> payload_;
+  size_t payload_size_;
+  size_t payload_pos_;
+  RTPVideoHeaderVP9 expected_;
+  rtc::scoped_ptr<RtpPacketizerVp9> packetizer_;
+
+  void Init(size_t payload_size, size_t packet_size) {
+    payload_.reset(new uint8_t[payload_size]);
+    memset(payload_.get(), 7, payload_size);
+    payload_size_ = payload_size;
+    payload_pos_ = 0;
+    packetizer_.reset(new RtpPacketizerVp9(expected_, packet_size));
+    packetizer_->SetPayloadData(payload_.get(), payload_size_, NULL);
+
+    const int kMaxPayloadDescriptorLength = 100;
+    packet_.reset(new uint8_t[payload_size_ + kMaxPayloadDescriptorLength]);
+  }
+
+  void CheckPayload(const uint8_t* packet,
+                    size_t start_pos,
+                    size_t end_pos,
+                    bool last) {
+    for (size_t i = start_pos; i < end_pos; ++i) {
+      EXPECT_EQ(packet[i], payload_[payload_pos_++]);
+    }
+    EXPECT_EQ(last, payload_pos_ == payload_size_);
+  }
+
+  void CreateParseAndCheckPackets(const size_t* expected_hdr_sizes,
+                                  const size_t* expected_sizes,
+                                  size_t expected_num_packets) {
+    ASSERT_TRUE(packetizer_.get() != NULL);
+    size_t length = 0;
+    bool last = false;
+    if (expected_num_packets == 0) {
+      EXPECT_FALSE(packetizer_->NextPacket(packet_.get(), &length, &last));
+      return;
+    }
+    for (size_t i = 0; i < expected_num_packets; ++i) {
+      EXPECT_TRUE(packetizer_->NextPacket(packet_.get(), &length, &last));
+      EXPECT_EQ(expected_sizes[i], length);
+      RTPVideoHeaderVP9 hdr = expected_;
+      hdr.beginning_of_frame = (i == 0);
+      hdr.end_of_frame = last;
+      ParseAndCheckPacket(packet_.get(), hdr, expected_hdr_sizes[i], length);
+      CheckPayload(packet_.get(), expected_hdr_sizes[i], length, last);
+    }
+    EXPECT_TRUE(last);
+  }
+};
+
+TEST_F(RtpPacketizerVp9Test, TestEqualSizedMode_OnePacket) {
+  const size_t kFrameSize = 25;
+  const size_t kPacketSize = 26;
+  Init(kFrameSize, kPacketSize);
+
+  // One packet:
+  // I:0, P:0, L:0, F:0, B:1, E:1, V:0  (1hdr + 25 payload)
+  const size_t kExpectedHdrSizes[] = {1};
+  const size_t kExpectedSizes[] = {26};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestEqualSizedMode_TwoPackets) {
+  const size_t kFrameSize = 27;
+  const size_t kPacketSize = 27;
+  Init(kFrameSize, kPacketSize);
+
+  // Two packets:
+  // I:0, P:0, L:0, F:0, B:1, E:0, V:0  (1hdr + 14 payload)
+  // I:0, P:0, L:0, F:0, B:0, E:1, V:0  (1hdr + 13 payload)
+  const size_t kExpectedHdrSizes[] = {1, 1};
+  const size_t kExpectedSizes[] = {15, 14};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestTooShortBufferToFitPayload) {
+  const size_t kFrameSize = 1;
+  const size_t kPacketSize = 1;
+  Init(kFrameSize, kPacketSize);  // 1hdr + 1 payload
+
+  const size_t kExpectedNum = 0;
+  CreateParseAndCheckPackets(NULL, NULL, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestOneBytePictureId) {
+  const size_t kFrameSize = 30;
+  const size_t kPacketSize = 12;
+
+  expected_.picture_id = kMaxOneBytePictureId;   // 2 byte payload descriptor
+  expected_.max_picture_id = kMaxOneBytePictureId;
+  Init(kFrameSize, kPacketSize);
+
+  // Three packets:
+  // I:1, P:0, L:0, F:0, B:1, E:0, V:0 (2hdr + 10 payload)
+  // I:1, P:0, L:0, F:0, B:0, E:0, V:0 (2hdr + 10 payload)
+  // I:1, P:0, L:0, F:0, B:0, E:1, V:0 (2hdr + 10 payload)
+  const size_t kExpectedHdrSizes[] = {2, 2, 2};
+  const size_t kExpectedSizes[] = {12, 12, 12};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestTwoBytePictureId) {
+  const size_t kFrameSize = 31;
+  const size_t kPacketSize = 13;
+
+  expected_.picture_id = kMaxTwoBytePictureId;  // 3 byte payload descriptor
+  Init(kFrameSize, kPacketSize);
+
+  // Four packets:
+  // I:1, P:0, L:0, F:0, B:1, E:0, V:0 (3hdr + 8 payload)
+  // I:1, P:0, L:0, F:0, B:0, E:0, V:0 (3hdr + 8 payload)
+  // I:1, P:0, L:0, F:0, B:0, E:0, V:0 (3hdr + 8 payload)
+  // I:1, P:0, L:0, F:0, B:0, E:1, V:0 (3hdr + 7 payload)
+  const size_t kExpectedHdrSizes[] = {3, 3, 3, 3};
+  const size_t kExpectedSizes[] = {11, 11, 11, 10};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestLayerInfoWithNonFlexibleMode) {
+  const size_t kFrameSize = 30;
+  const size_t kPacketSize = 25;
+
+  expected_.temporal_idx = 3;
+  expected_.temporal_up_switch = true;  // U
+  expected_.num_spatial_layers = 3;
+  expected_.spatial_idx = 2;
+  expected_.inter_layer_predicted = true;  // D
+  expected_.tl0_pic_idx = 117;
+  Init(kFrameSize, kPacketSize);
+
+  // Two packets:
+  //    | I:0, P:0, L:1, F:0, B:1, E:0, V:0 | (3hdr + 15 payload)
+  // L: | T:3, U:1, S:2, D:1 | TL0PICIDX:117 |
+  //    | I:0, P:0, L:1, F:0, B:0, E:1, V:0 | (3hdr + 15 payload)
+  // L: | T:3, U:1, S:2, D:1 | TL0PICIDX:117 |
+  const size_t kExpectedHdrSizes[] = {3, 3};
+  const size_t kExpectedSizes[] = {18, 18};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestLayerInfoWithFlexibleMode) {
+  const size_t kFrameSize = 21;
+  const size_t kPacketSize = 23;
+
+  expected_.flexible_mode = true;
+  expected_.temporal_idx = 3;
+  expected_.temporal_up_switch = true;  // U
+  expected_.num_spatial_layers = 3;
+  expected_.spatial_idx = 2;
+  expected_.inter_layer_predicted = false;  // D
+  Init(kFrameSize, kPacketSize);
+
+  // One packet:
+  // I:0, P:0, L:1, F:1, B:1, E:1, V:0 (2hdr + 21 payload)
+  // L:   T:3, U:1, S:2, D:0
+  const size_t kExpectedHdrSizes[] = {2};
+  const size_t kExpectedSizes[] = {23};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestRefIdx) {
+  const size_t kFrameSize = 16;
+  const size_t kPacketSize = 21;
+
+  expected_.inter_pic_predicted = true;  // P
+  expected_.flexible_mode = true;        // F
+  expected_.picture_id = 2;
+  expected_.max_picture_id = kMaxOneBytePictureId;
+
+  expected_.num_ref_pics = 3;
+  expected_.pid_diff[0] = 1;
+  expected_.pid_diff[1] = 3;
+  expected_.pid_diff[2] = 127;
+  expected_.ref_picture_id[0] = 1;    // 2 - 1 = 1
+  expected_.ref_picture_id[1] = 127;  // (kMaxPictureId + 1) + 2 - 3 = 127
+  expected_.ref_picture_id[2] = 3;    // (kMaxPictureId + 1) + 2 - 127 = 3
+  Init(kFrameSize, kPacketSize);
+
+  // Two packets:
+  // I:1, P:1, L:0, F:1, B:1, E:1, V:0 (5hdr + 16 payload)
+  // I:   2
+  // P,F: P_DIFF:1,   N:1
+  //      P_DIFF:3,   N:1
+  //      P_DIFF:127, N:0
+  const size_t kExpectedHdrSizes[] = {5};
+  const size_t kExpectedSizes[] = {21};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestRefIdxFailsWithoutPictureId) {
+  const size_t kFrameSize = 16;
+  const size_t kPacketSize = 21;
+
+  expected_.inter_pic_predicted = true;
+  expected_.flexible_mode = true;
+  expected_.num_ref_pics = 1;
+  expected_.pid_diff[0] = 3;
+  Init(kFrameSize, kPacketSize);
+
+  const size_t kExpectedNum = 0;
+  CreateParseAndCheckPackets(NULL, NULL, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestSsDataWithoutSpatialResolutionPresent) {
+  const size_t kFrameSize = 21;
+  const size_t kPacketSize = 26;
+
+  expected_.ss_data_available = true;
+  expected_.num_spatial_layers = 1;
+  expected_.spatial_layer_resolution_present = false;
+  expected_.gof.num_frames_in_gof = 1;
+  expected_.gof.temporal_idx[0] = 0;
+  expected_.gof.temporal_up_switch[0] = true;
+  expected_.gof.num_ref_pics[0] = 1;
+  expected_.gof.pid_diff[0][0] = 4;
+  Init(kFrameSize, kPacketSize);
+
+  // One packet:
+  // I:0, P:0, L:0, F:0, B:1, E:1, V:1 (5hdr + 21 payload)
+  // N_S:0, Y:0, G:1
+  // N_G:1
+  // T:0, U:1, R:1 | P_DIFF[0][0]:4
+  const size_t kExpectedHdrSizes[] = {5};
+  const size_t kExpectedSizes[] = {26};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestSsDataWithoutGbitPresent) {
+  const size_t kFrameSize = 21;
+  const size_t kPacketSize = 23;
+
+  expected_.ss_data_available = true;
+  expected_.num_spatial_layers = 1;
+  expected_.spatial_layer_resolution_present = false;
+  expected_.gof.num_frames_in_gof = 0;
+  Init(kFrameSize, kPacketSize);
+
+  // One packet:
+  // I:0, P:0, L:0, F:0, B:1, E:1, V:1 (2hdr + 21 payload)
+  // N_S:0, Y:0, G:0
+  const size_t kExpectedHdrSizes[] = {2};
+  const size_t kExpectedSizes[] = {23};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestSsData) {
+  const size_t kFrameSize = 21;
+  const size_t kPacketSize = 40;
+
+  expected_.ss_data_available = true;
+  expected_.num_spatial_layers = 2;
+  expected_.spatial_layer_resolution_present = true;
+  expected_.width[0] = 640;
+  expected_.width[1] = 1280;
+  expected_.height[0] = 360;
+  expected_.height[1] = 720;
+  expected_.gof.num_frames_in_gof = 3;
+  expected_.gof.temporal_idx[0] = 0;
+  expected_.gof.temporal_idx[1] = 1;
+  expected_.gof.temporal_idx[2] = 2;
+  expected_.gof.temporal_up_switch[0] = true;
+  expected_.gof.temporal_up_switch[1] = true;
+  expected_.gof.temporal_up_switch[2] = false;
+  expected_.gof.num_ref_pics[0] = 0;
+  expected_.gof.num_ref_pics[1] = 3;
+  expected_.gof.num_ref_pics[2] = 2;
+  expected_.gof.pid_diff[1][0] = 5;
+  expected_.gof.pid_diff[1][1] = 6;
+  expected_.gof.pid_diff[1][2] = 7;
+  expected_.gof.pid_diff[2][0] = 8;
+  expected_.gof.pid_diff[2][1] = 9;
+  Init(kFrameSize, kPacketSize);
+
+  // One packet:
+  // I:0, P:0, L:0, F:0, B:1, E:1, V:1 (19hdr + 21 payload)
+  // N_S:1, Y:1, G:1
+  // WIDTH:640   // 2 bytes
+  // HEIGHT:360  // 2 bytes
+  // WIDTH:1280  // 2 bytes
+  // HEIGHT:720  // 2 bytes
+  // N_G:3
+  // T:0, U:1, R:0
+  // T:1, U:1, R:3 | P_DIFF[1][0]:5 | P_DIFF[1][1]:6 | P_DIFF[1][2]:7
+  // T:2, U:0, R:2 | P_DIFF[2][0]:8 | P_DIFF[2][0]:9
+  const size_t kExpectedHdrSizes[] = {19};
+  const size_t kExpectedSizes[] = {40};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestBaseLayerProtectionAndStorageType) {
+  const size_t kFrameSize = 10;
+  const size_t kPacketSize = 12;
+
+  // I:0, P:0, L:1, F:1, B:1, E:1, V:0 (2hdr + 10 payload)
+  // L:   T:0, U:0, S:0, D:0
+  expected_.flexible_mode = true;
+  expected_.temporal_idx = 0;
+  Init(kFrameSize, kPacketSize);
+  EXPECT_EQ(kProtectedPacket, packetizer_->GetProtectionType());
+  EXPECT_EQ(kAllowRetransmission,
+            packetizer_->GetStorageType(kRetransmitBaseLayer));
+  EXPECT_EQ(kDontRetransmit, packetizer_->GetStorageType(kRetransmitOff));
+}
+
+TEST_F(RtpPacketizerVp9Test, TestHigherLayerProtectionAndStorageType) {
+  const size_t kFrameSize = 10;
+  const size_t kPacketSize = 12;
+
+  // I:0, P:0, L:1, F:1, B:1, E:1, V:0 (2hdr + 10 payload)
+  // L:   T:1, U:0, S:0, D:0
+  expected_.flexible_mode = true;
+  expected_.temporal_idx = 1;
+  Init(kFrameSize, kPacketSize);
+  EXPECT_EQ(kUnprotectedPacket, packetizer_->GetProtectionType());
+  EXPECT_EQ(kDontRetransmit, packetizer_->GetStorageType(kRetransmitBaseLayer));
+  EXPECT_EQ(kAllowRetransmission,
+            packetizer_->GetStorageType(kRetransmitHigherLayers));
+}
+
+
+class RtpDepacketizerVp9Test : public ::testing::Test {
+ protected:
+  RtpDepacketizerVp9Test()
+      : depacketizer_(new RtpDepacketizerVp9()) {}
+
+  virtual void SetUp() {
+    expected_.InitRTPVideoHeaderVP9();
+  }
+
+  RTPVideoHeaderVP9 expected_;
+  rtc::scoped_ptr<RtpDepacketizer> depacketizer_;
+};
+
+TEST_F(RtpDepacketizerVp9Test, ParseBasicHeader) {
+  const uint8_t kHeaderLength = 1;
+  uint8_t packet[4] = {0};
+  packet[0] = 0x0C;  // I:0 P:0 L:0 F:0 B:1 E:1 V:0 R:0
+  expected_.beginning_of_frame = true;
+  expected_.end_of_frame = true;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseOneBytePictureId) {
+  const uint8_t kHeaderLength = 2;
+  uint8_t packet[10] = {0};
+  packet[0] = 0x80;  // I:1 P:0 L:0 F:0 B:0 E:0 V:0 R:0
+  packet[1] = kMaxOneBytePictureId;
+
+  expected_.picture_id = kMaxOneBytePictureId;
+  expected_.max_picture_id = kMaxOneBytePictureId;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseTwoBytePictureId) {
+  const uint8_t kHeaderLength = 3;
+  uint8_t packet[10] = {0};
+  packet[0] = 0x80;  // I:1 P:0 L:0 F:0 B:0 E:0 V:0 R:0
+  packet[1] = 0x80 | ((kMaxTwoBytePictureId >> 8) & 0x7F);
+  packet[2] = kMaxTwoBytePictureId & 0xFF;
+
+  expected_.picture_id = kMaxTwoBytePictureId;
+  expected_.max_picture_id = kMaxTwoBytePictureId;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseLayerInfoWithNonFlexibleMode) {
+  const uint8_t kHeaderLength = 3;
+  const uint8_t kTemporalIdx = 2;
+  const uint8_t kUbit = 1;
+  const uint8_t kSpatialIdx = 1;
+  const uint8_t kDbit = 1;
+  const uint8_t kTl0PicIdx = 17;
+  uint8_t packet[13] = {0};
+  packet[0] = 0x20;  // I:0 P:0 L:1 F:0 B:0 E:0 V:0 R:0
+  packet[1] = (kTemporalIdx << 5) | (kUbit << 4) | (kSpatialIdx << 1) | kDbit;
+  packet[2] = kTl0PicIdx;
+
+  // T:2 U:1 S:1 D:1
+  // TL0PICIDX:17
+  expected_.temporal_idx = kTemporalIdx;
+  expected_.temporal_up_switch = kUbit ? true : false;
+  expected_.spatial_idx = kSpatialIdx;
+  expected_.inter_layer_predicted = kDbit ? true : false;
+  expected_.tl0_pic_idx = kTl0PicIdx;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseLayerInfoWithFlexibleMode) {
+  const uint8_t kHeaderLength = 2;
+  const uint8_t kTemporalIdx = 2;
+  const uint8_t kUbit = 1;
+  const uint8_t kSpatialIdx = 0;
+  const uint8_t kDbit = 0;
+  uint8_t packet[13] = {0};
+  packet[0] = 0x38;  // I:0 P:0 L:1 F:1 B:1 E:0 V:0 R:0
+  packet[1] = (kTemporalIdx << 5) | (kUbit << 4) | (kSpatialIdx << 1) | kDbit;
+
+  // I:0 P:0 L:1 F:1 B:1 E:0 V:0
+  // L:   T:2 U:1 S:0 D:0
+  expected_.beginning_of_frame = true;
+  expected_.flexible_mode = true;
+  expected_.temporal_idx = kTemporalIdx;
+  expected_.temporal_up_switch = kUbit ? true : false;
+  expected_.spatial_idx = kSpatialIdx;
+  expected_.inter_layer_predicted = kDbit ? true : false;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseRefIdx) {
+  const uint8_t kHeaderLength = 6;
+  const int16_t kPictureId = 17;
+  const uint8_t kPdiff1 = 17;
+  const uint8_t kPdiff2 = 18;
+  const uint8_t kPdiff3 = 127;
+  uint8_t packet[13] = {0};
+  packet[0] = 0xD8;  // I:1 P:1 L:0 F:1 B:1 E:0 V:0 R:0
+  packet[1] = 0x80 | ((kPictureId >> 8) & 0x7F);  // Two byte pictureID.
+  packet[2] = kPictureId;
+  packet[3] = (kPdiff1 << 1) | 1;  // P_DIFF N:1
+  packet[4] = (kPdiff2 << 1) | 1;  // P_DIFF N:1
+  packet[5] = (kPdiff3 << 1) | 0;  // P_DIFF N:0
+
+  // I:1 P:1 L:0 F:1 B:1 E:0 V:0
+  // I:    PICTURE ID:17
+  // I:
+  // P,F:  P_DIFF:17  N:1 => refPicId = 17 - 17 = 0
+  // P,F:  P_DIFF:18  N:1 => refPicId = (kMaxPictureId + 1) + 17 - 18 = 0x7FFF
+  // P,F:  P_DIFF:127 N:0 => refPicId = (kMaxPictureId + 1) + 17 - 127 = 32658
+  expected_.beginning_of_frame = true;
+  expected_.inter_pic_predicted = true;
+  expected_.flexible_mode = true;
+  expected_.picture_id = kPictureId;
+  expected_.num_ref_pics = 3;
+  expected_.pid_diff[0] = kPdiff1;
+  expected_.pid_diff[1] = kPdiff2;
+  expected_.pid_diff[2] = kPdiff3;
+  expected_.ref_picture_id[0] = 0;
+  expected_.ref_picture_id[1] = 0x7FFF;
+  expected_.ref_picture_id[2] = 32658;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseRefIdxFailsWithNoPictureId) {
+  const uint8_t kPdiff = 3;
+  uint8_t packet[13] = {0};
+  packet[0] = 0x58;            // I:0 P:1 L:0 F:1 B:1 E:0 V:0 R:0
+  packet[1] = (kPdiff << 1);   // P,F:  P_DIFF:3 N:0
+
+  RtpDepacketizer::ParsedPayload parsed;
+  EXPECT_FALSE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseRefIdxFailsWithTooManyRefPics) {
+  const uint8_t kPdiff = 3;
+  uint8_t packet[13] = {0};
+  packet[0] = 0xD8;                  // I:1 P:1 L:0 F:1 B:1 E:0 V:0 R:0
+  packet[1] = kMaxOneBytePictureId;  // I:    PICTURE ID:127
+  packet[2] = (kPdiff << 1) | 1;     // P,F:  P_DIFF:3 N:1
+  packet[3] = (kPdiff << 1) | 1;     // P,F:  P_DIFF:3 N:1
+  packet[4] = (kPdiff << 1) | 1;     // P,F:  P_DIFF:3 N:1
+  packet[5] = (kPdiff << 1) | 0;     // P,F:  P_DIFF:3 N:0
+
+  RtpDepacketizer::ParsedPayload parsed;
+  EXPECT_FALSE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseSsData) {
+  const uint8_t kHeaderLength = 6;
+  const uint8_t kYbit = 0;
+  const size_t kNs = 2;
+  const size_t kNg = 2;
+  uint8_t packet[23] = {0};
+  packet[0] = 0x0A;  // I:0 P:0 L:0 F:0 B:1 E:0 V:1 R:0
+  packet[1] = ((kNs - 1) << 5) | (kYbit << 4) | (1 << 3);  // N_S Y G:1 -
+  packet[2] = kNg;                                         // N_G
+  packet[3] = (0 << 5) | (1 << 4) | (0 << 2) | 0;          // T:0 U:1 R:0 -
+  packet[4] = (2 << 5) | (0 << 4) | (1 << 2) | 0;          // T:2 U:0 R:1 -
+  packet[5] = 33;
+
+  expected_.beginning_of_frame = true;
+  expected_.ss_data_available = true;
+  expected_.num_spatial_layers = kNs;
+  expected_.spatial_layer_resolution_present = kYbit ? true : false;
+  expected_.gof.num_frames_in_gof = kNg;
+  expected_.gof.temporal_idx[0] = 0;
+  expected_.gof.temporal_idx[1] = 2;
+  expected_.gof.temporal_up_switch[0] = true;
+  expected_.gof.temporal_up_switch[1] = false;
+  expected_.gof.num_ref_pics[0] = 0;
+  expected_.gof.num_ref_pics[1] = 1;
+  expected_.gof.pid_diff[1][0] = 33;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseFirstPacketInKeyFrame) {
+  uint8_t packet[2] = {0};
+  packet[0] = 0x08;  // I:0 P:0 L:0 F:0 B:1 E:0 V:0 R:0
+
+  RtpDepacketizer::ParsedPayload parsed;
+  ASSERT_TRUE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+  EXPECT_EQ(kVideoFrameKey, parsed.frame_type);
+  EXPECT_TRUE(parsed.type.Video.isFirstPacket);
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseLastPacketInDeltaFrame) {
+  uint8_t packet[2] = {0};
+  packet[0] = 0x44;  // I:0 P:1 L:0 F:0 B:0 E:1 V:0 R:0
+
+  RtpDepacketizer::ParsedPayload parsed;
+  ASSERT_TRUE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+  EXPECT_EQ(kVideoFrameDelta, parsed.frame_type);
+  EXPECT_FALSE(parsed.type.Video.isFirstPacket);
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseResolution) {
+  const uint16_t kWidth[2] = {640, 1280};
+  const uint16_t kHeight[2] = {360, 720};
+  uint8_t packet[20] = {0};
+  packet[0] = 0x0A;  // I:0 P:0 L:0 F:0 B:1 E:0 V:1 R:0
+  packet[1] = (1 << 5) | (1 << 4) | 0;  // N_S:1 Y:1 G:0
+  packet[2] = kWidth[0] >> 8;
+  packet[3] = kWidth[0] & 0xFF;
+  packet[4] = kHeight[0] >> 8;
+  packet[5] = kHeight[0] & 0xFF;
+  packet[6] = kWidth[1] >> 8;
+  packet[7] = kWidth[1] & 0xFF;
+  packet[8] = kHeight[1] >> 8;
+  packet[9] = kHeight[1] & 0xFF;
+
+  RtpDepacketizer::ParsedPayload parsed;
+  ASSERT_TRUE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+  EXPECT_EQ(kWidth[0], parsed.type.Video.width);
+  EXPECT_EQ(kHeight[0], parsed.type.Video.height);
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseFailsForNoPayloadLength) {
+  uint8_t packet[1] = {0};
+  RtpDepacketizer::ParsedPayload parsed;
+  EXPECT_FALSE(depacketizer_->Parse(&parsed, packet, 0));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseFailsForTooShortBufferToFitPayload) {
+  const uint8_t kHeaderLength = 1;
+  uint8_t packet[kHeaderLength] = {0};
+  RtpDepacketizer::ParsedPayload parsed;
+  EXPECT_FALSE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+}
+
+}  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
@@ -9,21 +9,25 @@
  */
 
 #include "webrtc/modules/rtp_rtcp/source/rtp_sender_video.h"
 
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/trace_event.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
 #include "webrtc/modules/rtp_rtcp/source/byte_io.h"
 #include "webrtc/modules/rtp_rtcp/source/producer_fec.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/trace_event.h"
 
 namespace webrtc {
 enum { REDForFECHeaderLength = 1 };
 
@@ -318,17 +322,17 @@ bool RTPSenderVideo::Send(const RtpVideo
 
   rtc::scoped_ptr<RtpPacketizer> packetizer(RtpPacketizer::Create(
       videoType, max_payload_length, &(rtpHdr->codecHeader), frameType));
 
   // TODO(changbin): we currently don't support to configure the codec to
   // output multiple partitions for VP8. Should remove below check after the
   // issue is fixed.
   const RTPFragmentationHeader* frag =
-      (videoType == kRtpVideoVp8 || videoType == kRtpVideoVp9) ? NULL : fragmentation;
+      (videoType == kRtpVideoVp8) ? NULL : fragmentation;
 
   packetizer->SetPayloadData(data, payload_bytes_to_send, frag);
 
   bool last = false;
   while (!last) {
     uint8_t dataBuffer[IP_PACKET_SIZE] = {0};
     size_t payload_bytes_in_packet = 0;
     if (!packetizer->NextPacket(
@@ -355,17 +359,17 @@ bool RTPSenderVideo::Send(const RtpVideo
     if (!rtpHdr) {
       assert(!_rtpSender.IsRtpHeaderExtensionRegistered(
           kRtpExtensionVideoRotation));
     } else if (cvo_mode == RTPSenderInterface::kCVOActivated) {
       // Checking whether CVO header extension is registered will require taking
       // a lock. It'll be a no-op if it's not registered.
       // TODO(guoweis): For now, all packets sent will carry the CVO such that
       // the RTP header length is consistent, although the receiver side will
-      // only exam the packets with market bit set.
+      // only exam the packets with marker bit set.
       size_t packetSize = payloadSize + rtp_header_length;
       RtpUtility::RtpHeaderParser rtp_parser(dataBuffer, packetSize);
       RTPHeader rtp_header;
       rtp_parser.Parse(rtp_header);
       _rtpSender.UpdateVideoRotation(dataBuffer, packetSize, rtp_header,
                                      rtpHdr->rotation);
     }
     if (SendVideoPacket(dataBuffer,
--- a/media/webrtc/trunk/webrtc/modules/video_coding/codecs/interface/video_codec_interface.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/interface/video_codec_interface.h
@@ -43,21 +43,40 @@ struct CodecSpecificInfoVP8 {
 };
 
 struct CodecSpecificInfoVP9 {
   bool hasReceivedSLI;
   uint8_t pictureIdSLI;
   bool hasReceivedRPSI;
   uint64_t pictureIdRPSI;
   int16_t pictureId;  // Negative value to skip pictureId.
-  bool nonReference;
-  uint8_t temporalIdx;
-  bool layerSync;
-  int tl0PicIdx;  // Negative value to skip tl0PicIdx.
-  int8_t keyIdx;  // Negative value to skip keyIdx.
+
+  bool inter_pic_predicted;  // This layer frame is dependent on previously
+                             // coded frame(s).
+  bool flexible_mode;
+  bool ss_data_available;
+
+  int tl0_pic_idx;  // Negative value to skip tl0PicIdx.
+  uint8_t temporal_idx;
+  uint8_t spatial_idx;
+  bool temporal_up_switch;
+  bool inter_layer_predicted;  // Frame is dependent on directly lower spatial
+                               // layer frame.
+  uint8_t gof_idx;
+
+  // SS data.
+  size_t num_spatial_layers;  // Always populated.
+  bool spatial_layer_resolution_present;
+  uint16_t width[kMaxVp9NumberOfSpatialLayers];
+  uint16_t height[kMaxVp9NumberOfSpatialLayers];
+  GofInfoVP9 gof;
+
+  // Frame reference data.
+  uint8_t num_ref_pics;
+  uint8_t p_diff[kMaxVp9RefPics];
 };
 
 struct CodecSpecificInfoGeneric {
   uint8_t simulcast_idx;
 };
 
 struct CodecSpecificInfoH264 {
   bool    single_nalu;
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/screenshare_layers.cc
@@ -0,0 +1,93 @@
+/* Copyright (c) 2015 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 <algorithm>
+#include "webrtc/modules/video_coding/codecs/vp9/screenshare_layers.h"
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+
+ScreenshareLayersVP9::ScreenshareLayersVP9(uint8_t num_layers)
+    : num_layers_(num_layers),
+      start_layer_(0),
+      last_timestamp_(0),
+      timestamp_initialized_(false) {
+  RTC_DCHECK_GT(num_layers, 0);
+  RTC_DCHECK_LE(num_layers, kMaxVp9NumberOfSpatialLayers);
+  memset(bits_used_, 0, sizeof(bits_used_));
+  memset(threshold_kbps_, 0, sizeof(threshold_kbps_));
+}
+
+uint8_t ScreenshareLayersVP9::GetStartLayer() const {
+  return start_layer_;
+}
+
+void ScreenshareLayersVP9::ConfigureBitrate(int threshold_kbps,
+                                            uint8_t layer_id) {
+  // The upper layer is always the layer we spill frames
+  // to when the bitrate becomes to high, therefore setting
+  // a max limit is not allowed. The top layer bitrate is
+  // never used either so configuring it makes no difference.
+  RTC_DCHECK_LT(layer_id, num_layers_ - 1);
+  threshold_kbps_[layer_id] = threshold_kbps;
+}
+
+void ScreenshareLayersVP9::LayerFrameEncoded(unsigned int size_bytes,
+                                             uint8_t layer_id) {
+  RTC_DCHECK_LT(layer_id, num_layers_);
+  bits_used_[layer_id] += size_bytes * 8;
+}
+
+VP9EncoderImpl::SuperFrameRefSettings
+ScreenshareLayersVP9::GetSuperFrameSettings(uint32_t timestamp,
+                                            bool is_keyframe) {
+  VP9EncoderImpl::SuperFrameRefSettings settings;
+  if (!timestamp_initialized_) {
+    last_timestamp_ = timestamp;
+    timestamp_initialized_ = true;
+  }
+  float time_diff = (timestamp - last_timestamp_) / 90.f;
+  float total_bits_used = 0;
+  float total_threshold_kbps = 0;
+  start_layer_ = 0;
+
+  // Up to (num_layers - 1) because we only have
+  // (num_layers - 1) thresholds to check.
+  for (int layer_id = 0; layer_id < num_layers_ - 1; ++layer_id) {
+    bits_used_[layer_id] = std::max(
+        0.f, bits_used_[layer_id] - time_diff * threshold_kbps_[layer_id]);
+    total_bits_used += bits_used_[layer_id];
+    total_threshold_kbps += threshold_kbps_[layer_id];
+
+    // If this is a keyframe then there should be no
+    // references to any previous frames.
+    if (!is_keyframe) {
+      settings.layer[layer_id].ref_buf1 = layer_id;
+      if (total_bits_used > total_threshold_kbps * 1000)
+        start_layer_ = layer_id + 1;
+    }
+
+    settings.layer[layer_id].upd_buf = layer_id;
+  }
+  // Since the above loop does not iterate over the last layer
+  // the reference of the last layer has to be set after the loop,
+  // and if this is a keyframe there should be no references to
+  // any previous frames.
+  if (!is_keyframe)
+    settings.layer[num_layers_ - 1].ref_buf1 = num_layers_ - 1;
+
+  settings.layer[num_layers_ - 1].upd_buf = num_layers_ - 1;
+  settings.is_keyframe = is_keyframe;
+  settings.start_layer = start_layer_;
+  settings.stop_layer = num_layers_ - 1;
+  last_timestamp_ = timestamp;
+  return settings;
+}
+
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/screenshare_layers.h
@@ -0,0 +1,66 @@
+/* Copyright (c) 2015 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.
+*/
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_SCREENSHARE_LAYERS_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_SCREENSHARE_LAYERS_H_
+
+#include "webrtc/modules/video_coding/codecs/vp9/vp9_impl.h"
+
+namespace webrtc {
+
+class ScreenshareLayersVP9 {
+ public:
+  explicit ScreenshareLayersVP9(uint8_t num_layers);
+
+  // The target bitrate for layer with id layer_id.
+  void ConfigureBitrate(int threshold_kbps, uint8_t layer_id);
+
+  // The current start layer.
+  uint8_t GetStartLayer() const;
+
+  // Update the layer with the size of the layer frame.
+  void LayerFrameEncoded(unsigned int size_bytes, uint8_t layer_id);
+
+  // Get the layer settings for the next superframe.
+  //
+  // In short, each time the GetSuperFrameSettings is called the
+  // bitrate of every layer is calculated and if the cummulative
+  // bitrate exceeds the configured cummulative bitrates
+  // (ConfigureBitrate to configure) up to and including that
+  // layer then the resulting encoding settings for the
+  // superframe will only encode layers above that layer.
+  VP9EncoderImpl::SuperFrameRefSettings GetSuperFrameSettings(
+      uint32_t timestamp,
+      bool is_keyframe);
+
+ private:
+  // How many layers that are used.
+  uint8_t num_layers_;
+
+  // The index of the first layer to encode.
+  uint8_t start_layer_;
+
+  // Cummulative target kbps for the different layers.
+  float threshold_kbps_[kMaxVp9NumberOfSpatialLayers - 1];
+
+  // How many bits that has been used for a certain layer. Increased in
+  // FrameEncoded() by the size of the encoded frame and decreased in
+  // GetSuperFrameSettings() depending on the time between frames.
+  float bits_used_[kMaxVp9NumberOfSpatialLayers];
+
+  // Timestamp of last frame.
+  uint32_t last_timestamp_;
+
+  // If the last_timestamp_ has been set.
+  bool timestamp_initialized_;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_SCREENSHARE_LAYERS_H_
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/screenshare_layers_unittest.cc
@@ -0,0 +1,323 @@
+/*
+ *  Copyright (c) 2015 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 <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "vpx/vp8cx.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/modules/video_coding/codecs/vp9/screenshare_layers.h"
+#include "webrtc/modules/video_coding/codecs/vp9/vp9_impl.h"
+#include "webrtc/system_wrappers/include/clock.h"
+
+namespace webrtc {
+
+typedef VP9EncoderImpl::SuperFrameRefSettings Settings;
+
+const uint32_t kTickFrequency = 90000;
+
+class ScreenshareLayerTestVP9 : public ::testing::Test {
+ protected:
+  ScreenshareLayerTestVP9() : clock_(0) {}
+  virtual ~ScreenshareLayerTestVP9() {}
+
+  void InitScreenshareLayers(int layers) {
+    layers_.reset(new ScreenshareLayersVP9(layers));
+  }
+
+  void ConfigureBitrateForLayer(int kbps, uint8_t layer_id) {
+    layers_->ConfigureBitrate(kbps, layer_id);
+  }
+
+  void AdvanceTime(int64_t milliseconds) {
+    clock_.AdvanceTimeMilliseconds(milliseconds);
+  }
+
+  void AddKilobitsToLayer(int kilobits, uint8_t layer_id) {
+    layers_->LayerFrameEncoded(kilobits * 1000 / 8, layer_id);
+  }
+
+  void EqualRefsForLayer(const Settings& actual, uint8_t layer_id) {
+    EXPECT_EQ(expected_.layer[layer_id].upd_buf,
+              actual.layer[layer_id].upd_buf);
+    EXPECT_EQ(expected_.layer[layer_id].ref_buf1,
+              actual.layer[layer_id].ref_buf1);
+    EXPECT_EQ(expected_.layer[layer_id].ref_buf2,
+              actual.layer[layer_id].ref_buf2);
+    EXPECT_EQ(expected_.layer[layer_id].ref_buf3,
+              actual.layer[layer_id].ref_buf3);
+  }
+
+  void EqualRefs(const Settings& actual) {
+    for (unsigned int layer_id = 0; layer_id < kMaxVp9NumberOfSpatialLayers;
+         ++layer_id) {
+      EqualRefsForLayer(actual, layer_id);
+    }
+  }
+
+  void EqualStartStopKeyframe(const Settings& actual) {
+    EXPECT_EQ(expected_.start_layer, actual.start_layer);
+    EXPECT_EQ(expected_.stop_layer, actual.stop_layer);
+    EXPECT_EQ(expected_.is_keyframe, actual.is_keyframe);
+  }
+
+  // Check that the settings returned by GetSuperFrameSettings() is
+  // equal to the expected_ settings.
+  void EqualToExpected() {
+    uint32_t frame_timestamp_ =
+        clock_.TimeInMilliseconds() * (kTickFrequency / 1000);
+    Settings actual =
+        layers_->GetSuperFrameSettings(frame_timestamp_, expected_.is_keyframe);
+    EqualRefs(actual);
+    EqualStartStopKeyframe(actual);
+  }
+
+  Settings expected_;
+  SimulatedClock clock_;
+  rtc::scoped_ptr<ScreenshareLayersVP9> layers_;
+};
+
+TEST_F(ScreenshareLayerTestVP9, NoRefsOnKeyFrame) {
+  const int kNumLayers = kMaxVp9NumberOfSpatialLayers;
+  InitScreenshareLayers(kNumLayers);
+  expected_.start_layer = 0;
+  expected_.stop_layer = kNumLayers - 1;
+
+  for (int l = 0; l < kNumLayers; ++l) {
+    expected_.layer[l].upd_buf = l;
+  }
+  expected_.is_keyframe = true;
+  EqualToExpected();
+
+  for (int l = 0; l < kNumLayers; ++l) {
+    expected_.layer[l].ref_buf1 = l;
+  }
+  expected_.is_keyframe = false;
+  EqualToExpected();
+}
+
+// Test if it is possible to send at a high bitrate (over the threshold)
+// after a longer period of low bitrate. This should not be possible.
+TEST_F(ScreenshareLayerTestVP9, DontAccumelateAvailableBitsOverTime) {
+  InitScreenshareLayers(2);
+  ConfigureBitrateForLayer(100, 0);
+
+  expected_.layer[0].upd_buf = 0;
+  expected_.layer[0].ref_buf1 = 0;
+  expected_.layer[1].upd_buf = 1;
+  expected_.layer[1].ref_buf1 = 1;
+  expected_.start_layer = 0;
+  expected_.stop_layer = 1;
+
+  // Send 10 frames at a low bitrate (50 kbps)
+  for (int i = 0; i < 10; ++i) {
+    AdvanceTime(200);
+    EqualToExpected();
+    AddKilobitsToLayer(10, 0);
+  }
+
+  AdvanceTime(200);
+  EqualToExpected();
+  AddKilobitsToLayer(301, 0);
+
+  // Send 10 frames at a high bitrate (200 kbps)
+  expected_.start_layer = 1;
+  for (int i = 0; i < 10; ++i) {
+    AdvanceTime(200);
+    EqualToExpected();
+    AddKilobitsToLayer(40, 1);
+  }
+}
+
+// Test if used bits are accumelated over layers, as they should;
+TEST_F(ScreenshareLayerTestVP9, AccumelateUsedBitsOverLayers) {
+  const int kNumLayers = kMaxVp9NumberOfSpatialLayers;
+  InitScreenshareLayers(kNumLayers);
+  for (int l = 0; l < kNumLayers - 1; ++l)
+    ConfigureBitrateForLayer(100, l);
+  for (int l = 0; l < kNumLayers; ++l) {
+    expected_.layer[l].upd_buf = l;
+    expected_.layer[l].ref_buf1 = l;
+  }
+
+  expected_.start_layer = 0;
+  expected_.stop_layer = kNumLayers - 1;
+  EqualToExpected();
+
+  for (int layer = 0; layer < kNumLayers - 1; ++layer) {
+    expected_.start_layer = layer;
+    EqualToExpected();
+    AddKilobitsToLayer(101, layer);
+  }
+}
+
+// General testing of the bitrate controller.
+TEST_F(ScreenshareLayerTestVP9, 2LayerBitrate) {
+  InitScreenshareLayers(2);
+  ConfigureBitrateForLayer(100, 0);
+
+  expected_.layer[0].upd_buf = 0;
+  expected_.layer[1].upd_buf = 1;
+  expected_.layer[0].ref_buf1 = -1;
+  expected_.layer[1].ref_buf1 = -1;
+  expected_.start_layer = 0;
+  expected_.stop_layer = 1;
+
+  expected_.is_keyframe = true;
+  EqualToExpected();
+  AddKilobitsToLayer(100, 0);
+
+  expected_.layer[0].ref_buf1 = 0;
+  expected_.layer[1].ref_buf1 = 1;
+  expected_.is_keyframe = false;
+  AdvanceTime(199);
+  EqualToExpected();
+  AddKilobitsToLayer(100, 0);
+
+  expected_.start_layer = 1;
+  for (int frame = 0; frame < 3; ++frame) {
+    AdvanceTime(200);
+    EqualToExpected();
+    AddKilobitsToLayer(100, 1);
+  }
+
+  // Just before enough bits become available for L0 @0.999 seconds.
+  AdvanceTime(199);
+  EqualToExpected();
+  AddKilobitsToLayer(100, 1);
+
+  // Just after enough bits become available for L0 @1.0001 seconds.
+  expected_.start_layer = 0;
+  AdvanceTime(2);
+  EqualToExpected();
+  AddKilobitsToLayer(100, 0);
+
+  // Keyframes always encode all layers, even if it is over budget.
+  expected_.layer[0].ref_buf1 = -1;
+  expected_.layer[1].ref_buf1 = -1;
+  expected_.is_keyframe = true;
+  AdvanceTime(499);
+  EqualToExpected();
+  expected_.layer[0].ref_buf1 = 0;
+  expected_.layer[1].ref_buf1 = 1;
+  expected_.start_layer = 1;
+  expected_.is_keyframe = false;
+  EqualToExpected();
+  AddKilobitsToLayer(100, 0);
+
+  // 400 kb in L0 --> @3 second mark to fall below the threshold..
+  // just before @2.999 seconds.
+  expected_.is_keyframe = false;
+  AdvanceTime(1499);
+  EqualToExpected();
+  AddKilobitsToLayer(100, 1);
+
+  // just after @3.001 seconds.
+  expected_.start_layer = 0;
+  AdvanceTime(2);
+  EqualToExpected();
+  AddKilobitsToLayer(100, 0);
+}
+
+// General testing of the bitrate controller.
+TEST_F(ScreenshareLayerTestVP9, 3LayerBitrate) {
+  InitScreenshareLayers(3);
+  ConfigureBitrateForLayer(100, 0);
+  ConfigureBitrateForLayer(100, 1);
+
+  for (int l = 0; l < 3; ++l) {
+    expected_.layer[l].upd_buf = l;
+    expected_.layer[l].ref_buf1 = l;
+  }
+  expected_.start_layer = 0;
+  expected_.stop_layer = 2;
+
+  EqualToExpected();
+  AddKilobitsToLayer(105, 0);
+  AddKilobitsToLayer(30, 1);
+
+  AdvanceTime(199);
+  EqualToExpected();
+  AddKilobitsToLayer(105, 0);
+  AddKilobitsToLayer(30, 1);
+
+  expected_.start_layer = 1;
+  AdvanceTime(200);
+  EqualToExpected();
+  AddKilobitsToLayer(130, 1);
+
+  expected_.start_layer = 2;
+  AdvanceTime(200);
+  EqualToExpected();
+
+  // 400 kb in L1 --> @1.0 second mark to fall below threshold.
+  // 210 kb in L0 --> @1.1 second mark to fall below threshold.
+  // Just before L1 @0.999 seconds.
+  AdvanceTime(399);
+  EqualToExpected();
+
+  // Just after L1 @1.001 seconds.
+  expected_.start_layer = 1;
+  AdvanceTime(2);
+  EqualToExpected();
+
+  // Just before L0 @1.099 seconds.
+  AdvanceTime(99);
+  EqualToExpected();
+
+  // Just after L0 @1.101 seconds.
+  expected_.start_layer = 0;
+  AdvanceTime(2);
+  EqualToExpected();
+
+  // @1.1 seconds
+  AdvanceTime(99);
+  EqualToExpected();
+  AddKilobitsToLayer(200, 1);
+
+  expected_.is_keyframe = true;
+  for (int l = 0; l < 3; ++l)
+    expected_.layer[l].ref_buf1 = -1;
+  AdvanceTime(200);
+  EqualToExpected();
+
+  expected_.is_keyframe = false;
+  expected_.start_layer = 2;
+  for (int l = 0; l < 3; ++l)
+    expected_.layer[l].ref_buf1 = l;
+  AdvanceTime(200);
+  EqualToExpected();
+}
+
+// Test that the bitrate calculations are
+// correct when the timestamp wrap.
+TEST_F(ScreenshareLayerTestVP9, TimestampWrap) {
+  InitScreenshareLayers(2);
+  ConfigureBitrateForLayer(100, 0);
+
+  expected_.layer[0].upd_buf = 0;
+  expected_.layer[0].ref_buf1 = 0;
+  expected_.layer[1].upd_buf = 1;
+  expected_.layer[1].ref_buf1 = 1;
+  expected_.start_layer = 0;
+  expected_.stop_layer = 1;
+
+  // Advance time to just before the timestamp wraps.
+  AdvanceTime(std::numeric_limits<uint32_t>::max() / (kTickFrequency / 1000));
+  EqualToExpected();
+  AddKilobitsToLayer(200, 0);
+
+  // Wrap
+  expected_.start_layer = 1;
+  AdvanceTime(1);
+  EqualToExpected();
+}
+
+}  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9.gyp
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9.gyp
@@ -17,22 +17,26 @@
       'dependencies': [
         '<(webrtc_root)/common_video/common_video.gyp:common_video',
         '<(webrtc_root)/modules/video_coding/utility/video_coding_utility.gyp:video_coding_utility',
         '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
       ],
       'conditions': [
         ['build_libvpx==1', {
           'dependencies': [
-            '<(libvpx_dir)/libvpx.gyp:libvpx',
+            '<(libvpx_dir)/libvpx.gyp:libvpx_new',
           ],
         }],
         ['build_vp9==1', {
           'sources': [
             'include/vp9.h',
+            'screenshare_layers.cc',
+            'screenshare_layers.h',
+            'vp9_frame_buffer_pool.cc',
+            'vp9_frame_buffer_pool.h',
             'vp9_impl.cc',
             'vp9_impl.h',
           ],
         }, {
           'sources': [
             'vp9_dummy_impl.cc',
           ],
         }],
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.cc
@@ -0,0 +1,136 @@
+/*
+ *  Copyright (c) 2015 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 "webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
+
+#include "vpx/vpx_codec.h"
+#include "vpx/vpx_decoder.h"
+#include "vpx/vpx_frame_buffer.h"
+
+#include "webrtc/base/checks.h"
+#include "webrtc/system_wrappers/include/logging.h"
+
+namespace webrtc {
+
+uint8_t* Vp9FrameBufferPool::Vp9FrameBuffer::GetData() {
+  return data_.data<uint8_t>();
+}
+
+size_t Vp9FrameBufferPool::Vp9FrameBuffer::GetDataSize() const {
+  return data_.size();
+}
+
+void Vp9FrameBufferPool::Vp9FrameBuffer::SetSize(size_t size) {
+  data_.SetSize(size);
+}
+
+bool Vp9FrameBufferPool::InitializeVpxUsePool(
+    vpx_codec_ctx* vpx_codec_context) {
+  RTC_DCHECK(vpx_codec_context);
+  // Tell libvpx to use this pool.
+  if (vpx_codec_set_frame_buffer_functions(
+          // In which context to use these callback functions.
+          vpx_codec_context,
+          // Called by libvpx when it needs another frame buffer.
+          &Vp9FrameBufferPool::VpxGetFrameBuffer,
+          // Called by libvpx when it no longer uses a frame buffer.
+          &Vp9FrameBufferPool::VpxReleaseFrameBuffer,
+          // |this| will be passed as |user_priv| to VpxGetFrameBuffer.
+          this)) {
+    // Failed to configure libvpx to use Vp9FrameBufferPool.
+    return false;
+  }
+  return true;
+}
+
+rtc::scoped_refptr<Vp9FrameBufferPool::Vp9FrameBuffer>
+Vp9FrameBufferPool::GetFrameBuffer(size_t min_size) {
+  RTC_DCHECK_GT(min_size, 0u);
+  rtc::scoped_refptr<Vp9FrameBuffer> available_buffer = nullptr;
+  {
+    rtc::CritScope cs(&buffers_lock_);
+    // Do we have a buffer we can recycle?
+    for (const auto& buffer : allocated_buffers_) {
+      if (buffer->HasOneRef()) {
+        available_buffer = buffer;
+        break;
+      }
+    }
+    // Otherwise create one.
+    if (available_buffer == nullptr) {
+      available_buffer = new rtc::RefCountedObject<Vp9FrameBuffer>();
+      allocated_buffers_.push_back(available_buffer);
+      if (allocated_buffers_.size() > max_num_buffers_) {
+        LOG(LS_WARNING)
+            << allocated_buffers_.size() << " Vp9FrameBuffers have been "
+            << "allocated by a Vp9FrameBufferPool (exceeding what is "
+            << "considered reasonable, " << max_num_buffers_ << ").";
+        RTC_NOTREACHED();
+      }
+    }
+  }
+
+  available_buffer->SetSize(min_size);
+  return available_buffer;
+}
+
+int Vp9FrameBufferPool::GetNumBuffersInUse() const {
+  int num_buffers_in_use = 0;
+  rtc::CritScope cs(&buffers_lock_);
+  for (const auto& buffer : allocated_buffers_) {
+    if (!buffer->HasOneRef())
+      ++num_buffers_in_use;
+  }
+  return num_buffers_in_use;
+}
+
+void Vp9FrameBufferPool::ClearPool() {
+  rtc::CritScope cs(&buffers_lock_);
+  allocated_buffers_.clear();
+}
+
+// static
+int32_t Vp9FrameBufferPool::VpxGetFrameBuffer(void* user_priv,
+                                              size_t min_size,
+                                              vpx_codec_frame_buffer* fb) {
+  RTC_DCHECK(user_priv);
+  RTC_DCHECK(fb);
+  Vp9FrameBufferPool* pool = static_cast<Vp9FrameBufferPool*>(user_priv);
+
+  rtc::scoped_refptr<Vp9FrameBuffer> buffer = pool->GetFrameBuffer(min_size);
+  fb->data = buffer->GetData();
+  fb->size = buffer->GetDataSize();
+  // Store Vp9FrameBuffer* in |priv| for use in VpxReleaseFrameBuffer.
+  // This also makes vpx_codec_get_frame return images with their |fb_priv| set
+  // to |buffer| which is important for external reference counting.
+  // Release from refptr so that the buffer's |ref_count_| remains 1 when
+  // |buffer| goes out of scope.
+  fb->priv = static_cast<void*>(buffer.release());
+  return 0;
+}
+
+// static
+int32_t Vp9FrameBufferPool::VpxReleaseFrameBuffer(void* user_priv,
+                                                  vpx_codec_frame_buffer* fb) {
+  RTC_DCHECK(user_priv);
+  RTC_DCHECK(fb);
+  Vp9FrameBuffer* buffer = static_cast<Vp9FrameBuffer*>(fb->priv);
+  if (buffer != nullptr) {
+    buffer->Release();
+    // When libvpx fails to decode and you continue to try to decode (and fail)
+    // libvpx can for some reason try to release the same buffer multiple times.
+    // Setting |priv| to null protects against trying to Release multiple times.
+    fb->priv = nullptr;
+  }
+  return 0;
+}
+
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h
@@ -0,0 +1,117 @@
+/*
+ *  Copyright (c) 2015 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.
+ *
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_FRAME_BUFFER_POOL_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_FRAME_BUFFER_POOL_H_
+
+#include <vector>
+
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/refcount.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+
+struct vpx_codec_ctx;
+struct vpx_codec_frame_buffer;
+
+namespace webrtc {
+
+// This memory pool is used to serve buffers to libvpx for decoding purposes in
+// VP9, which is set up in InitializeVPXUsePool. After the initialization any
+// time libvpx wants to decode a frame it will use buffers provided and released
+// through VpxGetFrameBuffer and VpxReleaseFrameBuffer.
+// The benefit of owning the pool that libvpx relies on for decoding is that the
+// decoded frames returned by libvpx (from vpx_codec_get_frame) use parts of our
+// buffers for the decoded image data. By retaining ownership of this buffer
+// using scoped_refptr, the image buffer can be reused by VideoFrames and no
+// frame copy has to occur during decoding and frame delivery.
+//
+// Pseudo example usage case:
+//    Vp9FrameBufferPool pool;
+//    pool.InitializeVpxUsePool(decoder_ctx);
+//    ...
+//
+//    // During decoding, libvpx will get and release buffers from the pool.
+//    vpx_codec_decode(decoder_ctx, ...);
+//
+//    vpx_image_t* img = vpx_codec_get_frame(decoder_ctx, &iter);
+//    // Important to use scoped_refptr to protect it against being recycled by
+//    // the pool.
+//    scoped_refptr<Vp9FrameBuffer> img_buffer = (Vp9FrameBuffer*)img->fb_priv;
+//    ...
+//
+//    // Destroying the codec will make libvpx release any buffers it was using.
+//    vpx_codec_destroy(decoder_ctx);
+class Vp9FrameBufferPool {
+ public:
+  class Vp9FrameBuffer : public rtc::RefCountInterface {
+   public:
+    uint8_t* GetData();
+    size_t GetDataSize() const;
+    void SetSize(size_t size);
+
+    virtual bool HasOneRef() const = 0;
+
+   private:
+    // Data as an easily resizable buffer.
+    rtc::Buffer data_;
+  };
+
+  // Configures libvpx to, in the specified context, use this memory pool for
+  // buffers used to decompress frames. This is only supported for VP9.
+  bool InitializeVpxUsePool(vpx_codec_ctx* vpx_codec_context);
+
+  // Gets a frame buffer of at least |min_size|, recycling an available one or
+  // creating a new one. When no longer referenced from the outside the buffer
+  // becomes recyclable.
+  rtc::scoped_refptr<Vp9FrameBuffer> GetFrameBuffer(size_t min_size);
+  // Gets the number of buffers currently in use (not ready to be recycled).
+  int GetNumBuffersInUse() const;
+  // Releases allocated buffers, deleting available buffers. Buffers in use are
+  // not deleted until they are no longer referenced.
+  void ClearPool();
+
+  // InitializeVpxUsePool configures libvpx to call this function when it needs
+  // a new frame buffer. Parameters:
+  // |user_priv| Private data passed to libvpx, InitializeVpxUsePool sets it up
+  //             to be a pointer to the pool.
+  // |min_size|  Minimum size needed by libvpx (to decompress a frame).
+  // |fb|        Pointer to the libvpx frame buffer object, this is updated to
+  //             use the pool's buffer.
+  // Returns 0 on success. Returns < 0 on failure.
+  static int32_t VpxGetFrameBuffer(void* user_priv,
+                                   size_t min_size,
+                                   vpx_codec_frame_buffer* fb);
+
+  // InitializeVpxUsePool configures libvpx to call this function when it has
+  // finished using one of the pool's frame buffer. Parameters:
+  // |user_priv| Private data passed to libvpx, InitializeVpxUsePool sets it up
+  //             to be a pointer to the pool.
+  // |fb|        Pointer to the libvpx frame buffer object, its |priv| will be
+  //             a pointer to one of the pool's Vp9FrameBuffer.
+  static int32_t VpxReleaseFrameBuffer(void* user_priv,
+                                       vpx_codec_frame_buffer* fb);
+
+ private:
+  // Protects |allocated_buffers_|.
+  mutable rtc::CriticalSection buffers_lock_;
+  // All buffers, in use or ready to be recycled.
+  std::vector<rtc::scoped_refptr<Vp9FrameBuffer>> allocated_buffers_
+      GUARDED_BY(buffers_lock_);
+  // If more buffers than this are allocated we print warnings, and crash if
+  // in debug mode.
+  static const size_t max_num_buffers_ = 10;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_FRAME_BUFFER_POOL_H_
--- a/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
@@ -16,40 +16,78 @@
 #include <time.h>
 #include <vector>
 
 #include "vpx/vpx_encoder.h"
 #include "vpx/vpx_decoder.h"
 #include "vpx/vp8cx.h"
 #include "vpx/vp8dx.h"
 
+#include "webrtc/base/bind.h"
 #include "webrtc/base/checks.h"
+#include "webrtc/base/trace_event.h"
 #include "webrtc/common.h"
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
-#include "webrtc/modules/interface/module_common_types.h"
-#include "webrtc/system_wrappers/interface/tick_util.h"
-#include "webrtc/system_wrappers/interface/trace_event.h"
+#include "webrtc/modules/include/module_common_types.h"
+#include "webrtc/modules/video_coding/codecs/vp9/screenshare_layers.h"
+#include "webrtc/system_wrappers/include/logging.h"
+#include "webrtc/system_wrappers/include/tick_util.h"
+
+namespace {
+
+// VP9DecoderImpl::ReturnFrame helper function used with WrappedI420Buffer.
+static void WrappedI420BufferNoLongerUsedCb(
+    webrtc::Vp9FrameBufferPool::Vp9FrameBuffer* img_buffer) {
+  img_buffer->Release();
+}
+
+}  // anonymous namespace
 
 namespace webrtc {
 
+// Only positive speeds, range for real-time coding currently is: 5 - 8.
+// Lower means slower/better quality, higher means fastest/lower quality.
+int GetCpuSpeed(int width, int height) {
+  // For smaller resolutions, use lower speed setting (get some coding gain at
+  // the cost of increased encoding complexity).
+  if (width * height <= 352 * 288)
+    return 5;
+  else
+    return 7;
+}
+
 VP9Encoder* VP9Encoder::Create() {
   return new VP9EncoderImpl();
 }
 
+void VP9EncoderImpl::EncoderOutputCodedPacketCallback(vpx_codec_cx_pkt* pkt,
+                                                      void* user_data) {
+  VP9EncoderImpl* enc = (VP9EncoderImpl*)(user_data);
+  enc->GetEncodedLayerFrame(pkt);
+}
+
 VP9EncoderImpl::VP9EncoderImpl()
     : encoded_image_(),
       encoded_complete_callback_(NULL),
       inited_(false),
       timestamp_(0),
       picture_id_(0),
       cpu_speed_(3),
       rc_max_intra_target_(0),
       encoder_(NULL),
       config_(NULL),
-      raw_(NULL) {
+      raw_(NULL),
+      input_image_(NULL),
+      tl0_pic_idx_(0),
+      frames_since_kf_(0),
+      num_temporal_layers_(0),
+      num_spatial_layers_(0),
+      frames_encoded_(0),
+      // Use two spatial when screensharing with flexible mode.
+      spatial_layer_(new ScreenshareLayersVP9(2)) {
   memset(&codec_, 0, sizeof(codec_));
   uint32_t seed = static_cast<uint32_t>(TickTime::MillisecondTimestamp());
   srand(seed);
 }
 
 VP9EncoderImpl::~VP9EncoderImpl() {
   Release();
 }
@@ -73,16 +111,95 @@ int VP9EncoderImpl::Release() {
   if (raw_ != NULL) {
     vpx_img_free(raw_);
     raw_ = NULL;
   }
   inited_ = false;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+bool VP9EncoderImpl::ExplicitlyConfiguredSpatialLayers() const {
+  // We check target_bitrate_bps of the 0th layer to see if the spatial layers
+  // (i.e. bitrates) were explicitly configured.
+  return num_spatial_layers_ > 1 &&
+         codec_.spatialLayers[0].target_bitrate_bps > 0;
+}
+
+bool VP9EncoderImpl::SetSvcRates() {
+  uint8_t i = 0;
+
+  if (ExplicitlyConfiguredSpatialLayers()) {
+    if (num_temporal_layers_ > 1) {
+      LOG(LS_ERROR) << "Multiple temporal layers when manually specifying "
+                       "spatial layers not implemented yet!";
+      return false;
+    }
+    int total_bitrate_bps = 0;
+    for (i = 0; i < num_spatial_layers_; ++i)
+      total_bitrate_bps += codec_.spatialLayers[i].target_bitrate_bps;
+    // If total bitrate differs now from what has been specified at the
+    // beginning, update the bitrates in the same ratio as before.
+    for (i = 0; i < num_spatial_layers_; ++i) {
+      config_->ss_target_bitrate[i] = config_->layer_target_bitrate[i] =
+          static_cast<int>(static_cast<int64_t>(config_->rc_target_bitrate) *
+                           codec_.spatialLayers[i].target_bitrate_bps /
+                           total_bitrate_bps);
+    }
+  } else {
+    float rate_ratio[VPX_MAX_LAYERS] = {0};
+    float total = 0;
+
+    for (i = 0; i < num_spatial_layers_; ++i) {
+      if (svc_internal_.svc_params.scaling_factor_num[i] <= 0 ||
+          svc_internal_.svc_params.scaling_factor_den[i] <= 0) {
+        LOG(LS_ERROR) << "Scaling factors not specified!";
+        return false;
+      }
+      rate_ratio[i] =
+          static_cast<float>(svc_internal_.svc_params.scaling_factor_num[i]) /
+          svc_internal_.svc_params.scaling_factor_den[i];
+      total += rate_ratio[i];
+    }
+
+    for (i = 0; i < num_spatial_layers_; ++i) {
+      config_->ss_target_bitrate[i] = static_cast<unsigned int>(
+          config_->rc_target_bitrate * rate_ratio[i] / total);
+      if (num_temporal_layers_ == 1) {
+        config_->layer_target_bitrate[i] = config_->ss_target_bitrate[i];
+      } else if (num_temporal_layers_ == 2) {
+        config_->layer_target_bitrate[i * num_temporal_layers_] =
+            config_->ss_target_bitrate[i] * 2 / 3;
+        config_->layer_target_bitrate[i * num_temporal_layers_ + 1] =
+            config_->ss_target_bitrate[i];
+      } else if (num_temporal_layers_ == 3) {
+        config_->layer_target_bitrate[i * num_temporal_layers_] =
+            config_->ss_target_bitrate[i] / 2;
+        config_->layer_target_bitrate[i * num_temporal_layers_ + 1] =
+            config_->layer_target_bitrate[i * num_temporal_layers_] +
+            (config_->ss_target_bitrate[i] / 4);
+        config_->layer_target_bitrate[i * num_temporal_layers_ + 2] =
+            config_->ss_target_bitrate[i];
+      } else {
+        LOG(LS_ERROR) << "Unsupported number of temporal layers: "
+                      << num_temporal_layers_;
+        return false;
+      }
+    }
+  }
+
+  // For now, temporal layers only supported when having one spatial layer.
+  if (num_spatial_layers_ == 1) {
+    for (i = 0; i < num_temporal_layers_; ++i) {
+      config_->ts_target_bitrate[i] = config_->layer_target_bitrate[i];
+    }
+  }
+
+  return true;
+}
+
 int VP9EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
                              uint32_t new_framerate) {
   if (!inited_) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
   if (encoder_->err) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
@@ -90,16 +207,22 @@ int VP9EncoderImpl::SetRates(uint32_t ne
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
   // Update bit rate
   if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) {
     new_bitrate_kbit = codec_.maxBitrate;
   }
   config_->rc_target_bitrate = new_bitrate_kbit;
   codec_.maxFramerate = new_framerate;
+  spatial_layer_->ConfigureBitrate(new_bitrate_kbit, 0);
+
+  if (!SetSvcRates()) {
+    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  }
+
   // Update encoder context
   if (vpx_codec_enc_config_set(encoder_, config_)) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9EncoderImpl::InitEncode(const VideoCodec* inst,
@@ -116,30 +239,44 @@ int VP9EncoderImpl::InitEncode(const Vid
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
   if (inst->width < 1 || inst->height < 1) {
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
   if (number_of_cores < 1) {
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
+  if (inst->codecSpecific.VP9.numberOfTemporalLayers > 3) {
+    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  }
+  // libvpx currently supports only one or two spatial layers.
+  if (inst->codecSpecific.VP9.numberOfSpatialLayers > 2) {
+    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  }
+
   int retVal = Release();
   if (retVal < 0) {
     return retVal;
   }
   if (encoder_ == NULL) {
     encoder_ = new vpx_codec_ctx_t;
   }
   if (config_ == NULL) {
     config_ = new vpx_codec_enc_cfg_t;
   }
   timestamp_ = 0;
   if (&codec_ != inst) {
     codec_ = *inst;
   }
+
+  num_spatial_layers_ = inst->codecSpecific.VP9.numberOfSpatialLayers;
+  num_temporal_layers_ = inst->codecSpecific.VP9.numberOfTemporalLayers;
+  if (num_temporal_layers_ == 0)
+    num_temporal_layers_ = 1;
+
   // Random start 16 bits is enough.
   picture_id_ = static_cast<uint16_t>(rand()) & 0x7FFF;
   // Allocate memory for encoded image
   if (encoded_image_._buffer != NULL) {
     delete [] encoded_image_._buffer;
   }
   encoded_image_._size = CalcBufferSize(kI420, codec_.width, codec_.height);
   encoded_image_._buffer = new uint8_t[encoded_image_._size];
@@ -177,21 +314,67 @@ int VP9EncoderImpl::InitEncode(const Vid
   // Set the maximum target size of any key-frame.
   rc_max_intra_target_ = MaxIntraTarget(config_->rc_buf_optimal_sz);
   if (inst->codecSpecific.VP9.keyFrameInterval  > 0) {
     config_->kf_mode = VPX_KF_AUTO;
     config_->kf_max_dist = inst->codecSpecific.VP9.keyFrameInterval;
   } else {
     config_->kf_mode = VPX_KF_DISABLED;
   }
-
+  config_->rc_resize_allowed = inst->codecSpecific.VP9.automaticResizeOn ?
+      1 : 0;
   // Determine number of threads based on the image size and #cores.
   config_->g_threads = NumberOfThreads(config_->g_w,
                                        config_->g_h,
                                        number_of_cores);
+
+  cpu_speed_ = GetCpuSpeed(config_->g_w, config_->g_h);
+
+  // TODO(asapersson): Check configuration of temporal switch up and increase
+  // pattern length.
+  is_flexible_mode_ = inst->codecSpecific.VP9.flexibleMode;
+  if (is_flexible_mode_) {
+    config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_BYPASS;
+    config_->ts_number_layers = num_temporal_layers_;
+    if (codec_.mode == kScreensharing)
+      spatial_layer_->ConfigureBitrate(inst->startBitrate, 0);
+  } else if (num_temporal_layers_ == 1) {
+    gof_.SetGofInfoVP9(kTemporalStructureMode1);
+    config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_NOLAYERING;
+    config_->ts_number_layers = 1;
+    config_->ts_rate_decimator[0] = 1;
+    config_->ts_periodicity = 1;
+    config_->ts_layer_id[0] = 0;
+  } else if (num_temporal_layers_ == 2) {
+    gof_.SetGofInfoVP9(kTemporalStructureMode2);
+    config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_0101;
+    config_->ts_number_layers = 2;
+    config_->ts_rate_decimator[0] = 2;
+    config_->ts_rate_decimator[1] = 1;
+    config_->ts_periodicity = 2;
+    config_->ts_layer_id[0] = 0;
+    config_->ts_layer_id[1] = 1;
+  } else if (num_temporal_layers_ == 3) {
+    gof_.SetGofInfoVP9(kTemporalStructureMode3);
+    config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_0212;
+    config_->ts_number_layers = 3;
+    config_->ts_rate_decimator[0] = 4;
+    config_->ts_rate_decimator[1] = 2;
+    config_->ts_rate_decimator[2] = 1;
+    config_->ts_periodicity = 4;
+    config_->ts_layer_id[0] = 0;
+    config_->ts_layer_id[1] = 2;
+    config_->ts_layer_id[2] = 1;
+    config_->ts_layer_id[3] = 2;
+  } else {
+    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  }
+
+  tl0_pic_idx_ = static_cast<uint8_t>(rand());
+
   return InitAndSetControlSettings(inst);
 }
 
 int VP9EncoderImpl::NumberOfThreads(int width,
                                     int height,
                                     int number_of_cores) {
   // Keep the number of encoder threads equal to the possible number of column
   // tiles, which is (1, 2, 4, 8). See comments below for VP9E_SET_TILE_COLUMNS.
@@ -201,40 +384,81 @@ int VP9EncoderImpl::NumberOfThreads(int 
     return 2;
   } else {
     // 1 thread less than VGA.
     return 1;
   }
 }
 
 int VP9EncoderImpl::InitAndSetControlSettings(const VideoCodec* inst) {
+  config_->ss_number_layers = num_spatial_layers_;
+
+  if (ExplicitlyConfiguredSpatialLayers()) {
+    for (int i = 0; i < num_spatial_layers_; ++i) {
+      const auto& layer = codec_.spatialLayers[i];
+      svc_internal_.svc_params.max_quantizers[i] = config_->rc_max_quantizer;
+      svc_internal_.svc_params.min_quantizers[i] = config_->rc_min_quantizer;
+      svc_internal_.svc_params.scaling_factor_num[i] = layer.scaling_factor_num;
+      svc_internal_.svc_params.scaling_factor_den[i] = layer.scaling_factor_den;
+    }
+  } else {
+    int scaling_factor_num = 256;
+    for (int i = num_spatial_layers_ - 1; i >= 0; --i) {
+      svc_internal_.svc_params.max_quantizers[i] = config_->rc_max_quantizer;
+      svc_internal_.svc_params.min_quantizers[i] = config_->rc_min_quantizer;
+      // 1:2 scaling in each dimension.
+      svc_internal_.svc_params.scaling_factor_num[i] = scaling_factor_num;
+      svc_internal_.svc_params.scaling_factor_den[i] = 256;
+      if (codec_.mode != kScreensharing)
+        scaling_factor_num /= 2;
+    }
+  }
+
+  if (!SetSvcRates()) {
+    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  }
+
   if (vpx_codec_enc_init(encoder_, vpx_codec_vp9_cx(), config_, 0)) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
-  // Only positive speeds, currently: 0 - 8.
-  // O means slowest/best quality, 8 means fastest/lower quality.
-  cpu_speed_ = 7;
-  // Note: some of these codec controls still use "VP8" in the control name.
-  // TODO(marpan): Update this in the next/future libvpx version.
   vpx_codec_control(encoder_, VP8E_SET_CPUUSED, cpu_speed_);
   vpx_codec_control(encoder_, VP8E_SET_MAX_INTRA_BITRATE_PCT,
                     rc_max_intra_target_);
   vpx_codec_control(encoder_, VP9E_SET_AQ_MODE,
                     inst->codecSpecific.VP9.adaptiveQpMode ? 3 : 0);
+
+  vpx_codec_control(
+      encoder_, VP9E_SET_SVC,
+      (num_temporal_layers_ > 1 || num_spatial_layers_ > 1) ? 1 : 0);
+  if (num_temporal_layers_ > 1 || num_spatial_layers_ > 1) {
+    vpx_codec_control(encoder_, VP9E_SET_SVC_PARAMETERS,
+                      &svc_internal_.svc_params);
+  }
+  // Register callback for getting each spatial layer.
+  vpx_codec_priv_output_cx_pkt_cb_pair_t cbp = {
+      VP9EncoderImpl::EncoderOutputCodedPacketCallback, (void*)(this)};
+  vpx_codec_control(encoder_, VP9E_REGISTER_CX_CALLBACK, (void*)(&cbp));
+
   // Control function to set the number of column tiles in encoding a frame, in
   // log2 unit: e.g., 0 = 1 tile column, 1 = 2 tile columns, 2 = 4 tile columns.
   // The number tile columns will be capped by the encoder based on image size
   // (minimum width of tile column is 256 pixels, maximum is 4096).
   vpx_codec_control(encoder_, VP9E_SET_TILE_COLUMNS, (config_->g_threads >> 1));
-#if !defined(WEBRTC_ARCH_ARM)
+#if !defined(WEBRTC_ARCH_ARM) && !defined(WEBRTC_ARCH_ARM64)
   // Note denoiser is still off by default until further testing/optimization,
   // i.e., codecSpecific.VP9.denoisingOn == 0.
   vpx_codec_control(encoder_, VP9E_SET_NOISE_SENSITIVITY,
                     inst->codecSpecific.VP9.denoisingOn ? 1 : 0);
 #endif
+  if (codec_.mode == kScreensharing) {
+    // Adjust internal parameters to screen content.
+    vpx_codec_control(encoder_, VP9E_SET_TUNE_CONTENT, 1);
+  }
+  // Enable encoder skip of static/low content blocks.
+  vpx_codec_control(encoder_, VP8E_SET_STATIC_THRESHOLD, 1);
   inited_ = true;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 uint32_t VP9EncoderImpl::MaxIntraTarget(uint32_t optimal_buffer_size) {
   // Set max to the optimal buffer level (normalized by target BR),
   // and scaled by a scale_par.
   // Max target size = scale_par * optimal_buffer_size * targetBR[Kbps].
@@ -244,130 +468,344 @@ uint32_t VP9EncoderImpl::MaxIntraTarget(
   float scale_par = 0.5;
   uint32_t target_pct =
       optimal_buffer_size * scale_par * codec_.maxFramerate / 10;
   // Don't go below 3 times the per frame bandwidth.
   const uint32_t min_intra_size = 300;
   return (target_pct < min_intra_size) ? min_intra_size: target_pct;
 }
 
-int VP9EncoderImpl::Encode(const I420VideoFrame& input_image,
+int VP9EncoderImpl::Encode(const VideoFrame& input_image,
                            const CodecSpecificInfo* codec_specific_info,
-                           const std::vector<VideoFrameType>* frame_types) {
+                           const std::vector<FrameType>* frame_types) {
   if (!inited_) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
   if (input_image.IsZeroSize()) {
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
   if (encoded_complete_callback_ == NULL) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
-  VideoFrameType frame_type = kDeltaFrame;
+  FrameType frame_type = kVideoFrameDelta;
   // We only support one stream at the moment.
   if (frame_types && frame_types->size() > 0) {
     frame_type = (*frame_types)[0];
   }
-  DCHECK_EQ(input_image.width(), static_cast<int>(raw_->d_w));
-  DCHECK_EQ(input_image.height(), static_cast<int>(raw_->d_h));
+  RTC_DCHECK_EQ(input_image.width(), static_cast<int>(raw_->d_w));
+  RTC_DCHECK_EQ(input_image.height(), static_cast<int>(raw_->d_h));
+
+  // Set input image for use in the callback.
+  // This was necessary since you need some information from input_image.
+  // You can save only the necessary information (such as timestamp) instead of
+  // doing this.
+  input_image_ = &input_image;
+
   // Image in vpx_image_t format.
   // Input image is const. VPX's raw image is not defined as const.
   raw_->planes[VPX_PLANE_Y] = const_cast<uint8_t*>(input_image.buffer(kYPlane));
   raw_->planes[VPX_PLANE_U] = const_cast<uint8_t*>(input_image.buffer(kUPlane));
   raw_->planes[VPX_PLANE_V] = const_cast<uint8_t*>(input_image.buffer(kVPlane));
   raw_->stride[VPX_PLANE_Y] = input_image.stride(kYPlane);
   raw_->stride[VPX_PLANE_U] = input_image.stride(kUPlane);
   raw_->stride[VPX_PLANE_V] = input_image.stride(kVPlane);
 
-  int flags = 0;
-  bool send_keyframe = (frame_type == kKeyFrame);
+  vpx_enc_frame_flags_t flags = 0;
+  bool send_keyframe = (frame_type == kVideoFrameKey);
   if (send_keyframe) {
     // Key frame request from caller.
     flags = VPX_EFLAG_FORCE_KF;
   }
+
+  if (is_flexible_mode_) {
+    SuperFrameRefSettings settings;
+
+    // These structs are copied when calling vpx_codec_control,
+    // therefore it is ok for them to go out of scope.
+    vpx_svc_ref_frame_config enc_layer_conf;
+    vpx_svc_layer_id layer_id;
+
+    if (codec_.mode == kRealtimeVideo) {
+      // Real time video not yet implemented in flexible mode.
+      RTC_NOTREACHED();
+    } else {
+      settings = spatial_layer_->GetSuperFrameSettings(input_image.timestamp(),
+                                                       send_keyframe);
+    }
+    enc_layer_conf = GenerateRefsAndFlags(settings);
+    layer_id.temporal_layer_id = 0;
+    layer_id.spatial_layer_id = settings.start_layer;
+    vpx_codec_control(encoder_, VP9E_SET_SVC_LAYER_ID, &layer_id);
+    vpx_codec_control(encoder_, VP9E_SET_SVC_REF_FRAME_CONFIG, &enc_layer_conf);
+  }
+
   assert(codec_.maxFramerate > 0);
   uint32_t duration = 90000 / codec_.maxFramerate;
   if (vpx_codec_encode(encoder_, raw_, timestamp_, duration, flags,
                        VPX_DL_REALTIME)) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
   timestamp_ += duration;
-  return GetEncodedPartitions(input_image);
+
+  return WEBRTC_VIDEO_CODEC_OK;
 }
 
 void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
                                        const vpx_codec_cx_pkt& pkt,
                                        uint32_t timestamp) {
   assert(codec_specific != NULL);
   codec_specific->codecType = kVideoCodecVP9;
   CodecSpecificInfoVP9 *vp9_info = &(codec_specific->codecSpecific.VP9);
-  vp9_info->pictureId = picture_id_;
-  vp9_info->keyIdx = kNoKeyIdx;
-  vp9_info->nonReference = (pkt.data.frame.flags & VPX_FRAME_IS_DROPPABLE) != 0;
-  // TODO(marpan): Temporal layers are supported in the current VP9 version,
-  // but for now use 1 temporal layer encoding. Will update this when temporal
-  // layer support for VP9 is added in webrtc.
-  vp9_info->temporalIdx = kNoTemporalIdx;
-  vp9_info->layerSync = false;
-  vp9_info->tl0PicIdx = kNoTl0PicIdx;
-  picture_id_ = (picture_id_ + 1) & 0x7FFF;
+  // TODO(asapersson): Set correct values.
+  vp9_info->inter_pic_predicted =
+      (pkt.data.frame.flags & VPX_FRAME_IS_KEY) ? false : true;
+  vp9_info->flexible_mode = codec_.codecSpecific.VP9.flexibleMode;
+  vp9_info->ss_data_available = ((pkt.data.frame.flags & VPX_FRAME_IS_KEY) &&
+                                 !codec_.codecSpecific.VP9.flexibleMode)
+                                    ? true
+                                    : false;
+
+  vpx_svc_layer_id_t layer_id = {0};
+  vpx_codec_control(encoder_, VP9E_GET_SVC_LAYER_ID, &layer_id);
+
+  assert(num_temporal_layers_ > 0);
+  assert(num_spatial_layers_ > 0);
+  if (num_temporal_layers_ == 1) {
+    assert(layer_id.temporal_layer_id == 0);
+    vp9_info->temporal_idx = kNoTemporalIdx;
+  } else {
+    vp9_info->temporal_idx = layer_id.temporal_layer_id;
+  }
+  if (num_spatial_layers_ == 1) {
+    assert(layer_id.spatial_layer_id == 0);
+    vp9_info->spatial_idx = kNoSpatialIdx;
+  } else {
+    vp9_info->spatial_idx = layer_id.spatial_layer_id;
+  }
+  if (layer_id.spatial_layer_id != 0) {
+    vp9_info->ss_data_available = false;
+  }
+
+  // TODO(asapersson): this info has to be obtained from the encoder.
+  vp9_info->temporal_up_switch = true;
+
+  bool is_first_frame = false;
+  if (is_flexible_mode_) {
+    is_first_frame =
+        layer_id.spatial_layer_id == spatial_layer_->GetStartLayer();
+  } else {
+    is_first_frame = layer_id.spatial_layer_id == 0;
+  }
+
+  if (is_first_frame) {
+    picture_id_ = (picture_id_ + 1) & 0x7FFF;
+    // TODO(asapersson): this info has to be obtained from the encoder.
+    vp9_info->inter_layer_predicted = false;
+    ++frames_since_kf_;
+  } else {
+    // TODO(asapersson): this info has to be obtained from the encoder.
+    vp9_info->inter_layer_predicted = true;
+  }
+
+  if (pkt.data.frame.flags & VPX_FRAME_IS_KEY) {
+    frames_since_kf_ = 0;
+  }
+
+  vp9_info->picture_id = picture_id_;
+
+  if (!vp9_info->flexible_mode) {
+    if (layer_id.temporal_layer_id == 0 && layer_id.spatial_layer_id == 0) {
+      tl0_pic_idx_++;
+    }
+    vp9_info->tl0_pic_idx = tl0_pic_idx_;
+  }
+
+  // Always populate this, so that the packetizer can properly set the marker
+  // bit.
+  vp9_info->num_spatial_layers = num_spatial_layers_;
+
+  vp9_info->num_ref_pics = 0;
+  if (vp9_info->flexible_mode) {
+    vp9_info->gof_idx = kNoGofIdx;
+    vp9_info->num_ref_pics = num_ref_pics_[layer_id.spatial_layer_id];
+    for (int i = 0; i < num_ref_pics_[layer_id.spatial_layer_id]; ++i) {
+      vp9_info->p_diff[i] = p_diff_[layer_id.spatial_layer_id][i];
+    }
+  } else {
+    vp9_info->gof_idx =
+        static_cast<uint8_t>(frames_since_kf_ % gof_.num_frames_in_gof);
+    vp9_info->temporal_up_switch = gof_.temporal_up_switch[vp9_info->gof_idx];
+  }
+
+  if (vp9_info->ss_data_available) {
+    vp9_info->spatial_layer_resolution_present = true;
+    for (size_t i = 0; i < vp9_info->num_spatial_layers; ++i) {
+      vp9_info->width[i] = codec_.width *
+                           svc_internal_.svc_params.scaling_factor_num[i] /
+                           svc_internal_.svc_params.scaling_factor_den[i];
+      vp9_info->height[i] = codec_.height *
+                            svc_internal_.svc_params.scaling_factor_num[i] /
+                            svc_internal_.svc_params.scaling_factor_den[i];
+    }
+    if (!vp9_info->flexible_mode) {
+      vp9_info->gof.CopyGofInfoVP9(gof_);
+    }
+  }
 }
 
-int VP9EncoderImpl::GetEncodedPartitions(const I420VideoFrame& input_image) {
-  vpx_codec_iter_t iter = NULL;
+int VP9EncoderImpl::GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt) {
   encoded_image_._length = 0;
-  encoded_image_._frameType = kDeltaFrame;
+  encoded_image_._frameType = kVideoFrameDelta;
   RTPFragmentationHeader frag_info;
   // Note: no data partitioning in VP9, so 1 partition only. We keep this
   // fragmentation data for now, until VP9 packetizer is implemented.
   frag_info.VerifyAndAllocateFragmentationHeader(1);
   int part_idx = 0;
   CodecSpecificInfo codec_specific;
-  const vpx_codec_cx_pkt_t *pkt = NULL;
-  while ((pkt = vpx_codec_get_cx_data(encoder_, &iter)) != NULL) {
-    switch (pkt->kind) {
-      case VPX_CODEC_CX_FRAME_PKT: {
-        memcpy(&encoded_image_._buffer[encoded_image_._length],
-               pkt->data.frame.buf,
-               pkt->data.frame.sz);
-        frag_info.fragmentationOffset[part_idx] = encoded_image_._length;
-        frag_info.fragmentationLength[part_idx] =
-            static_cast<uint32_t>(pkt->data.frame.sz);
-        frag_info.fragmentationPlType[part_idx] = 0;
-        frag_info.fragmentationTimeDiff[part_idx] = 0;
-        encoded_image_._length += static_cast<uint32_t>(pkt->data.frame.sz);
-        assert(encoded_image_._length <= encoded_image_._size);
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-    // End of frame.
-    if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) {
-      // Check if encoded frame is a key frame.
-      if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
-        encoded_image_._frameType = kKeyFrame;
-      }
-      PopulateCodecSpecific(&codec_specific, *pkt, input_image.timestamp());
-      break;
-    }
+
+  assert(pkt->kind == VPX_CODEC_CX_FRAME_PKT);
+  memcpy(&encoded_image_._buffer[encoded_image_._length], pkt->data.frame.buf,
+         pkt->data.frame.sz);
+  frag_info.fragmentationOffset[part_idx] = encoded_image_._length;
+  frag_info.fragmentationLength[part_idx] =
+      static_cast<uint32_t>(pkt->data.frame.sz);
+  frag_info.fragmentationPlType[part_idx] = 0;
+  frag_info.fragmentationTimeDiff[part_idx] = 0;
+  encoded_image_._length += static_cast<uint32_t>(pkt->data.frame.sz);
+
+  vpx_svc_layer_id_t layer_id = {0};
+  vpx_codec_control(encoder_, VP9E_GET_SVC_LAYER_ID, &layer_id);
+  if (is_flexible_mode_ && codec_.mode == kScreensharing)
+    spatial_layer_->LayerFrameEncoded(
+        static_cast<unsigned int>(encoded_image_._length),
+        layer_id.spatial_layer_id);
+
+  assert(encoded_image_._length <= encoded_image_._size);
+
+  // End of frame.
+  // Check if encoded frame is a key frame.
+  if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
+    encoded_image_._frameType = kVideoFrameKey;
   }
+  PopulateCodecSpecific(&codec_specific, *pkt, input_image_->timestamp());
+
   if (encoded_image_._length > 0) {
     TRACE_COUNTER1("webrtc", "EncodedFrameSize", encoded_image_._length);
-    encoded_image_._timeStamp = input_image.timestamp();
-    encoded_image_.capture_time_ms_ = input_image.render_time_ms();
+    encoded_image_._timeStamp = input_image_->timestamp();
+    encoded_image_.capture_time_ms_ = input_image_->render_time_ms();
     encoded_image_._encodedHeight = raw_->d_h;
     encoded_image_._encodedWidth = raw_->d_w;
     encoded_complete_callback_->Encoded(encoded_image_, &codec_specific,
-                                      &frag_info);
+                                        &frag_info);
   }
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+vpx_svc_ref_frame_config VP9EncoderImpl::GenerateRefsAndFlags(
+    const SuperFrameRefSettings& settings) {
+  static const vpx_enc_frame_flags_t kAllFlags =
+      VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_LAST |
+      VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_GF;
+  vpx_svc_ref_frame_config sf_conf = {};
+  if (settings.is_keyframe) {
+    // Used later on to make sure we don't make any invalid references.
+    memset(buffer_updated_at_frame_, -1, sizeof(buffer_updated_at_frame_));
+    for (int layer = settings.start_layer; layer <= settings.stop_layer;
+         ++layer) {
+      num_ref_pics_[layer] = 0;
+      buffer_updated_at_frame_[settings.layer[layer].upd_buf] = frames_encoded_;
+      // When encoding a keyframe only the alt_fb_idx is used
+      // to specify which layer ends up in which buffer.
+      sf_conf.alt_fb_idx[layer] = settings.layer[layer].upd_buf;
+    }
+  } else {
+    for (int layer_idx = settings.start_layer; layer_idx <= settings.stop_layer;
+         ++layer_idx) {
+      vpx_enc_frame_flags_t layer_flags = kAllFlags;
+      num_ref_pics_[layer_idx] = 0;
+      int8_t refs[3] = {settings.layer[layer_idx].ref_buf1,
+                        settings.layer[layer_idx].ref_buf2,
+                        settings.layer[layer_idx].ref_buf3};
+
+      for (unsigned int ref_idx = 0; ref_idx < kMaxVp9RefPics; ++ref_idx) {
+        if (refs[ref_idx] == -1)
+          continue;
+
+        RTC_DCHECK_GE(refs[ref_idx], 0);
+        RTC_DCHECK_LE(refs[ref_idx], 7);
+        // Easier to remove flags from all flags rather than having to
+        // build the flags from 0.
+        switch (num_ref_pics_[layer_idx]) {
+          case 0: {
+            sf_conf.lst_fb_idx[layer_idx] = refs[ref_idx];
+            layer_flags &= ~VP8_EFLAG_NO_REF_LAST;
+            break;
+          }
+          case 1: {
+            sf_conf.gld_fb_idx[layer_idx] = refs[ref_idx];
+            layer_flags &= ~VP8_EFLAG_NO_REF_GF;
+            break;
+          }
+          case 2: {
+            sf_conf.alt_fb_idx[layer_idx] = refs[ref_idx];
+            layer_flags &= ~VP8_EFLAG_NO_REF_ARF;
+            break;
+          }
+        }
+        // Make sure we don't reference a buffer that hasn't been
+        // used at all or hasn't been used since a keyframe.
+        RTC_DCHECK_NE(buffer_updated_at_frame_[refs[ref_idx]], -1);
+
+        p_diff_[layer_idx][num_ref_pics_[layer_idx]] =
+            frames_encoded_ - buffer_updated_at_frame_[refs[ref_idx]];
+        num_ref_pics_[layer_idx]++;
+      }
+
+      bool upd_buf_same_as_a_ref = false;
+      if (settings.layer[layer_idx].upd_buf != -1) {
+        for (unsigned int ref_idx = 0; ref_idx < kMaxVp9RefPics; ++ref_idx) {
+          if (settings.layer[layer_idx].upd_buf == refs[ref_idx]) {
+            switch (ref_idx) {
+              case 0: {
+                layer_flags &= ~VP8_EFLAG_NO_UPD_LAST;
+                break;
+              }
+              case 1: {
+                layer_flags &= ~VP8_EFLAG_NO_UPD_GF;
+                break;
+              }
+              case 2: {
+                layer_flags &= ~VP8_EFLAG_NO_UPD_ARF;
+                break;
+              }
+            }
+            upd_buf_same_as_a_ref = true;
+            break;
+          }
+        }
+        if (!upd_buf_same_as_a_ref) {
+          // If we have three references and a buffer is specified to be
+          // updated, then that buffer must be the same as one of the
+          // three references.
+          RTC_CHECK_LT(num_ref_pics_[layer_idx], kMaxVp9RefPics);
+
+          sf_conf.alt_fb_idx[layer_idx] = settings.layer[layer_idx].upd_buf;
+          layer_flags ^= VP8_EFLAG_NO_UPD_ARF;
+        }
+
+        int updated_buffer = settings.layer[layer_idx].upd_buf;
+        buffer_updated_at_frame_[updated_buffer] = frames_encoded_;
+        sf_conf.frame_flags[layer_idx] = layer_flags;
+      }
+    }
+  }
+  ++frames_encoded_;
+  return sf_conf;
+}
+
 int VP9EncoderImpl::SetChannelParameters(uint32_t packet_loss, int64_t rtt) {
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9EncoderImpl::RegisterEncodeCompleteCallback(
     EncodedImageCallback* callback) {
   encoded_complete_callback_ = callback;
   return WEBRTC_VIDEO_CODEC_OK;
@@ -383,16 +821,24 @@ VP9DecoderImpl::VP9DecoderImpl()
       decoder_(NULL),
       key_frame_required_(true) {
   memset(&codec_, 0, sizeof(codec_));
 }
 
 VP9DecoderImpl::~VP9DecoderImpl() {
   inited_ = true;  // in order to do the actual release
   Release();
+  int num_buffers_in_use = frame_buffer_pool_.GetNumBuffersInUse();
+  if (num_buffers_in_use > 0) {
+    // The frame buffers are reference counted and frames are exposed after
+    // decoding. There may be valid usage cases where previous frames are still
+    // referenced after ~VP9DecoderImpl that is not a leak.
+    LOG(LS_INFO) << num_buffers_in_use << " Vp9FrameBuffers are still "
+                 << "referenced during ~VP9DecoderImpl.";
+  }
 }
 
 int VP9DecoderImpl::Reset() {
   if (!inited_) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
   InitDecode(&codec_, 1);
   return WEBRTC_VIDEO_CODEC_OK;
@@ -416,16 +862,21 @@ int VP9DecoderImpl::InitDecode(const Vid
   vpx_codec_flags_t flags = 0;
   if (vpx_codec_dec_init(decoder_, vpx_codec_vp9_dx(), &cfg, flags)) {
     return WEBRTC_VIDEO_CODEC_MEMORY;
   }
   if (&codec_ != inst) {
     // Save VideoCodec instance for later; mainly for duplicating the decoder.
     codec_ = *inst;
   }
+
+  if (!frame_buffer_pool_.InitializeVpxUsePool(decoder_)) {
+    return WEBRTC_VIDEO_CODEC_MEMORY;
+  }
+
   inited_ = true;
   // Always start with a complete key frame.
   key_frame_required_ = true;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9DecoderImpl::Decode(const EncodedImage& input_image,
                            bool missing_frames,
@@ -435,75 +886,102 @@ int VP9DecoderImpl::Decode(const Encoded
   if (!inited_) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
   if (decode_complete_callback_ == NULL) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
   // Always start with a complete key frame.
   if (key_frame_required_) {
-    if (input_image._frameType != kKeyFrame)
+    if (input_image._frameType != kVideoFrameKey)
       return WEBRTC_VIDEO_CODEC_ERROR;
     // We have a key frame - is it complete?
     if (input_image._completeFrame) {
       key_frame_required_ = false;
     } else {
       return WEBRTC_VIDEO_CODEC_ERROR;
     }
   }
   vpx_codec_iter_t iter = NULL;
   vpx_image_t* img;
   uint8_t* buffer = input_image._buffer;
   if (input_image._length == 0) {
     buffer = NULL;  // Triggers full frame concealment.
   }
+  // During decode libvpx may get and release buffers from |frame_buffer_pool_|.
+  // In practice libvpx keeps a few (~3-4) buffers alive at a time.
   if (vpx_codec_decode(decoder_,
                        buffer,
                        static_cast<unsigned int>(input_image._length),
                        0,
                        VPX_DL_REALTIME)) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
+  // |img->fb_priv| contains the image data, a reference counted Vp9FrameBuffer.
+  // It may be released by libvpx during future vpx_codec_decode or
+  // vpx_codec_destroy calls.
   img = vpx_codec_get_frame(decoder_, &iter);
   int ret = ReturnFrame(img, input_image._timeStamp);
   if (ret != 0) {
     return ret;
   }
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9DecoderImpl::ReturnFrame(const vpx_image_t* img, uint32_t timestamp) {
   if (img == NULL) {
     // Decoder OK and NULL image => No show frame.
     return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
   }
-  decoded_image_.CreateFrame(img->planes[VPX_PLANE_Y],
-                             img->planes[VPX_PLANE_U],
-                             img->planes[VPX_PLANE_V],
-                             img->d_w, img->d_h,
-                             img->stride[VPX_PLANE_Y],
-                             img->stride[VPX_PLANE_U],
-                             img->stride[VPX_PLANE_V]);
-  decoded_image_.set_timestamp(timestamp);
-  int ret = decode_complete_callback_->Decoded(decoded_image_);
+
+  // This buffer contains all of |img|'s image data, a reference counted
+  // Vp9FrameBuffer. Performing AddRef/Release ensures it is not released and
+  // recycled during use (libvpx is done with the buffers after a few
+  // vpx_codec_decode calls or vpx_codec_destroy).
+  Vp9FrameBufferPool::Vp9FrameBuffer* img_buffer =
+      static_cast<Vp9FrameBufferPool::Vp9FrameBuffer*>(img->fb_priv);
+  img_buffer->AddRef();
+  // The buffer can be used directly by the VideoFrame (without copy) by
+  // using a WrappedI420Buffer.
+  rtc::scoped_refptr<WrappedI420Buffer> img_wrapped_buffer(
+      new rtc::RefCountedObject<webrtc::WrappedI420Buffer>(
+          img->d_w, img->d_h,
+          img->planes[VPX_PLANE_Y], img->stride[VPX_PLANE_Y],
+          img->planes[VPX_PLANE_U], img->stride[VPX_PLANE_U],
+          img->planes[VPX_PLANE_V], img->stride[VPX_PLANE_V],
+          // WrappedI420Buffer's mechanism for allowing the release of its frame
+          // buffer is through a callback function. This is where we should
+          // release |img_buffer|.
+          rtc::Bind(&WrappedI420BufferNoLongerUsedCb, img_buffer)));
+
+  VideoFrame decoded_image;
+  decoded_image.set_video_frame_buffer(img_wrapped_buffer);
+  decoded_image.set_timestamp(timestamp);
+  int ret = decode_complete_callback_->Decoded(decoded_image);
   if (ret != 0)
     return ret;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9DecoderImpl::RegisterDecodeCompleteCallback(
     DecodedImageCallback* callback) {
   decode_complete_callback_ = callback;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9DecoderImpl::Release() {
   if (decoder_ != NULL) {
+    // When a codec is destroyed libvpx will release any buffers of
+    // |frame_buffer_pool_| it is currently using.
     if (vpx_codec_destroy(decoder_)) {
       return WEBRTC_VIDEO_CODEC_MEMORY;
     }
     delete decoder_;
     decoder_ = NULL;
   }
+  // Releases buffers from the pool. Any buffers not in use are deleted. Buffers
+  // still referenced externally are deleted once fully released, not returning
+  // to the pool.
+  frame_buffer_pool_.ClearPool();
   inited_ = false;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 }  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
@@ -8,56 +8,92 @@
  *  be found in the AUTHORS file in the root of the source tree.
  *
  */
 
 #ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_IMPL_H_
 #define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_IMPL_H_
 
 #include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h"
+#include "webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
 
+#include "vpx/svc_context.h"
 #include "vpx/vpx_decoder.h"
 #include "vpx/vpx_encoder.h"
 
 namespace webrtc {
 
+class ScreenshareLayersVP9;
+
 class VP9EncoderImpl : public VP9Encoder {
  public:
   VP9EncoderImpl();
 
   virtual ~VP9EncoderImpl();
 
   int Release() override;
 
   int InitEncode(const VideoCodec* codec_settings,
                  int number_of_cores,
                  size_t max_payload_size) override;
 
-  int Encode(const I420VideoFrame& input_image,
+  int Encode(const VideoFrame& input_image,
              const CodecSpecificInfo* codec_specific_info,
-             const std::vector<VideoFrameType>* frame_types) override;
+             const std::vector<FrameType>* frame_types) override;
 
   int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) override;
 
   int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
 
   int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate) override;
 
+  void OnDroppedFrame() override {}
+
+  struct LayerFrameRefSettings {
+    int8_t upd_buf = -1;   // -1 - no update,    0..7 - update buffer 0..7
+    int8_t ref_buf1 = -1;  // -1 - no reference, 0..7 - reference buffer 0..7
+    int8_t ref_buf2 = -1;  // -1 - no reference, 0..7 - reference buffer 0..7
+    int8_t ref_buf3 = -1;  // -1 - no reference, 0..7 - reference buffer 0..7
+  };
+
+  struct SuperFrameRefSettings {
+    LayerFrameRefSettings layer[kMaxVp9NumberOfSpatialLayers];
+    uint8_t start_layer = 0;  // The first spatial layer to be encoded.
+    uint8_t stop_layer = 0;   // The last spatial layer to be encoded.
+    bool is_keyframe = false;
+  };
+
  private:
   // Determine number of encoder threads to use.
   int NumberOfThreads(int width, int height, int number_of_cores);
 
   // Call encoder initialize function and set control settings.
   int InitAndSetControlSettings(const VideoCodec* inst);
 
   void PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
                              const vpx_codec_cx_pkt& pkt,
                              uint32_t timestamp);
 
-  int GetEncodedPartitions(const I420VideoFrame& input_image);
+  bool ExplicitlyConfiguredSpatialLayers() const;
+  bool SetSvcRates();
+
+  // Used for flexible mode to set the flags and buffer references used
+  // by the encoder. Also calculates the references used by the RTP
+  // packetizer.
+  //
+  // Has to be called for every frame (keyframes included) to update the
+  // state used to calculate references.
+  vpx_svc_ref_frame_config GenerateRefsAndFlags(
+      const SuperFrameRefSettings& settings);
+
+  virtual int GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt);
+
+  // Callback function for outputting packets per spatial layer.
+  static void EncoderOutputCodedPacketCallback(vpx_codec_cx_pkt* pkt,
+                                               void* user_data);
 
   // Determine maximum target for Intra frames
   //
   // Input:
   //    - optimal_buffer_size : Optimal buffer size
   // Return Value             : Max target size for Intra frames represented as
   //                            percentage of the per frame bandwidth
   uint32_t MaxIntraTarget(uint32_t optimal_buffer_size);
@@ -68,16 +104,32 @@ class VP9EncoderImpl : public VP9Encoder
   bool inited_;
   int64_t timestamp_;
   uint16_t picture_id_;
   int cpu_speed_;
   uint32_t rc_max_intra_target_;
   vpx_codec_ctx_t* encoder_;
   vpx_codec_enc_cfg_t* config_;
   vpx_image_t* raw_;
+  SvcInternal_t svc_internal_;
+  const VideoFrame* input_image_;
+  GofInfoVP9 gof_;       // Contains each frame's temporal information for
+                         // non-flexible mode.
+  uint8_t tl0_pic_idx_;  // Only used in non-flexible mode.
+  size_t frames_since_kf_;
+  uint8_t num_temporal_layers_;
+  uint8_t num_spatial_layers_;
+
+  // Used for flexible mode.
+  bool is_flexible_mode_;
+  int64_t buffer_updated_at_frame_[kNumVp9Buffers];
+  int64_t frames_encoded_;
+  uint8_t num_ref_pics_[kMaxVp9NumberOfSpatialLayers];
+  uint8_t p_diff_[kMaxVp9NumberOfSpatialLayers][kMaxVp9RefPics];
+  rtc::scoped_ptr<ScreenshareLayersVP9> spatial_layer_;
 };
 
 
 class VP9DecoderImpl : public VP9Decoder {
  public:
   VP9DecoderImpl();
 
   virtual ~VP9DecoderImpl();
@@ -94,17 +146,18 @@ class VP9DecoderImpl : public VP9Decoder
 
   int Release() override;
 
   int Reset() override;
 
  private:
   int ReturnFrame(const vpx_image_t* img, uint32_t timeStamp);
 
-  I420VideoFrame decoded_image_;
+  // Memory pool used to share buffers between libvpx and webrtc.
+  Vp9FrameBufferPool frame_buffer_pool_;
   DecodedImageCallback* decode_complete_callback_;
   bool inited_;
   vpx_codec_ctx_t* decoder_;
   VideoCodec codec_;
   bool key_frame_required_;
 };
 }  // namespace webrtc
 
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.cc
@@ -52,17 +52,19 @@ VideoCodecVP9 VideoEncoder::GetDefaultVp
   memset(&vp9_settings, 0, sizeof(vp9_settings));
 
   vp9_settings.resilience = 1;
   vp9_settings.numberOfTemporalLayers = 1;
   vp9_settings.denoisingOn = false;
   vp9_settings.frameDroppingOn = true;
   vp9_settings.keyFrameInterval = 3000;
   vp9_settings.adaptiveQpMode = true;
-
+  vp9_settings.automaticResizeOn = true;
+  vp9_settings.numberOfSpatialLayers = 1;
+  vp9_settings.flexibleMode = false;
   return vp9_settings;
 }
 
 VideoCodecH264 VideoEncoder::GetDefaultH264Settings() {
   VideoCodecH264 h264_settings;
   memset(&h264_settings, 0, sizeof(h264_settings));
 
   h264_settings.profile = kProfileBase;
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.cc
@@ -136,37 +136,77 @@ void VCMEncodedFrame::CopyCodecSpecific(
         _codecSpecificInfo.codecSpecific.H264.single_nalu =
             header->codecHeader.H264.single_nalu;
         _codecSpecificInfo.codecType = kVideoCodecH264;
         break;
       }
       case kRtpVideoVp9: {
         if (_codecSpecificInfo.codecType != kVideoCodecVP9) {
           // This is the first packet for this frame.
-          _codecSpecificInfo.codecSpecific.VP9.pictureId = -1;
-          _codecSpecificInfo.codecSpecific.VP9.temporalIdx = 0;
-          _codecSpecificInfo.codecSpecific.VP9.layerSync = false;
-          _codecSpecificInfo.codecSpecific.VP9.keyIdx = -1;
+          _codecSpecificInfo.codecSpecific.VP9.picture_id = -1;
+          _codecSpecificInfo.codecSpecific.VP9.temporal_idx = 0;
+          _codecSpecificInfo.codecSpecific.VP9.spatial_idx = 0;
+          _codecSpecificInfo.codecSpecific.VP9.gof_idx = 0;
+          _codecSpecificInfo.codecSpecific.VP9.inter_layer_predicted = false;
+          _codecSpecificInfo.codecSpecific.VP9.tl0_pic_idx = -1;
           _codecSpecificInfo.codecType = kVideoCodecVP9;
         }
-        _codecSpecificInfo.codecSpecific.VP9.nonReference =
-            header->codecHeader.VP9.nonReference;
-        if (header->codecHeader.VP9.pictureId != kNoPictureId) {
-          _codecSpecificInfo.codecSpecific.VP9.pictureId =
-              header->codecHeader.VP9.pictureId;
+        _codecSpecificInfo.codecSpecific.VP9.inter_pic_predicted =
+            header->codecHeader.VP9.inter_pic_predicted;
+        _codecSpecificInfo.codecSpecific.VP9.flexible_mode =
+            header->codecHeader.VP9.flexible_mode;
+        _codecSpecificInfo.codecSpecific.VP9.num_ref_pics =
+            header->codecHeader.VP9.num_ref_pics;
+        for (uint8_t r = 0; r < header->codecHeader.VP9.num_ref_pics; ++r) {
+          _codecSpecificInfo.codecSpecific.VP9.p_diff[r] =
+              header->codecHeader.VP9.pid_diff[r];
+        }
+        _codecSpecificInfo.codecSpecific.VP9.ss_data_available =
+            header->codecHeader.VP9.ss_data_available;
+        if (header->codecHeader.VP9.picture_id != kNoPictureId) {
+          _codecSpecificInfo.codecSpecific.VP9.picture_id =
+              header->codecHeader.VP9.picture_id;
+        }
+        if (header->codecHeader.VP9.tl0_pic_idx != kNoTl0PicIdx) {
+          _codecSpecificInfo.codecSpecific.VP9.tl0_pic_idx =
+              header->codecHeader.VP9.tl0_pic_idx;
+        }
+        if (header->codecHeader.VP9.temporal_idx != kNoTemporalIdx) {
+          _codecSpecificInfo.codecSpecific.VP9.temporal_idx =
+              header->codecHeader.VP9.temporal_idx;
+          _codecSpecificInfo.codecSpecific.VP9.temporal_up_switch =
+              header->codecHeader.VP9.temporal_up_switch;
         }
-        if (header->codecHeader.VP9.temporalIdx != kNoTemporalIdx) {
-          _codecSpecificInfo.codecSpecific.VP9.temporalIdx =
-              header->codecHeader.VP9.temporalIdx;
-          _codecSpecificInfo.codecSpecific.VP9.layerSync =
-              header->codecHeader.VP9.layerSync;
+        if (header->codecHeader.VP9.spatial_idx != kNoSpatialIdx) {
+          _codecSpecificInfo.codecSpecific.VP9.spatial_idx =
+              header->codecHeader.VP9.spatial_idx;
+          _codecSpecificInfo.codecSpecific.VP9.inter_layer_predicted =
+              header->codecHeader.VP9.inter_layer_predicted;
+        }
+        if (header->codecHeader.VP9.gof_idx != kNoGofIdx) {
+          _codecSpecificInfo.codecSpecific.VP9.gof_idx =
+              header->codecHeader.VP9.gof_idx;
         }
-        if (header->codecHeader.VP9.keyIdx != kNoKeyIdx) {
-          _codecSpecificInfo.codecSpecific.VP9.keyIdx =
-              header->codecHeader.VP9.keyIdx;
+        if (header->codecHeader.VP9.ss_data_available) {
+          _codecSpecificInfo.codecSpecific.VP9.num_spatial_layers =
+              header->codecHeader.VP9.num_spatial_layers;
+          _codecSpecificInfo.codecSpecific.VP9
+              .spatial_layer_resolution_present =
+              header->codecHeader.VP9.spatial_layer_resolution_present;
+          if (header->codecHeader.VP9.spatial_layer_resolution_present) {
+            for (size_t i = 0; i < header->codecHeader.VP9.num_spatial_layers;
+                 ++i) {
+              _codecSpecificInfo.codecSpecific.VP9.width[i] =
+                  header->codecHeader.VP9.width[i];
+              _codecSpecificInfo.codecSpecific.VP9.height[i] =
+                  header->codecHeader.VP9.height[i];
+            }
+          }
+          _codecSpecificInfo.codecSpecific.VP9.gof.CopyGofInfoVP9(
+              header->codecHeader.VP9.gof);
         }
         break;
       }
       default: {
         _codecSpecificInfo.codecType = kVideoCodecUnknown;
         break;
       }
     }
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.h
@@ -56,16 +56,18 @@ class VCMFrameBuffer : public VCMEncoded
   int32_t GetHighSeqNum() const;
 
   int PictureId() const;
   int TemporalId() const;
   bool LayerSync() const;
   int Tl0PicId() const;
   bool NonReference() const;
 
+  void SetGofInfo(const GofInfoVP9& gof_info, size_t idx);
+
   // Increments a counter to keep track of the number of packets of this frame
   // which were NACKed before they arrived.
   void IncrementNackCount();
   // Returns the number of packets of this frame which were NACKed before they
   // arrived.
   int16_t GetNackCount() const;
 
   int64_t LatestPacketTimeMs() const;
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_encoder.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_encoder.cc
@@ -31,31 +31,59 @@ void CopyCodecSpecific(const CodecSpecif
           info->codecSpecific.VP8.nonReference;
       rtp->codecHeader.VP8.temporalIdx = info->codecSpecific.VP8.temporalIdx;
       rtp->codecHeader.VP8.layerSync = info->codecSpecific.VP8.layerSync;
       rtp->codecHeader.VP8.tl0PicIdx = info->codecSpecific.VP8.tl0PicIdx;
       rtp->codecHeader.VP8.keyIdx = info->codecSpecific.VP8.keyIdx;
       rtp->simulcastIdx = info->codecSpecific.VP8.simulcastIdx;
       return;
     }
+    case kVideoCodecVP9: {
+      rtp->codec = kRtpVideoVp9;
+      rtp->codecHeader.VP9.InitRTPVideoHeaderVP9();
+      rtp->codecHeader.VP9.inter_pic_predicted =
+          info->codecSpecific.VP9.inter_pic_predicted;
+      rtp->codecHeader.VP9.flexible_mode =
+          info->codecSpecific.VP9.flexible_mode;
+      rtp->codecHeader.VP9.ss_data_available =
+          info->codecSpecific.VP9.ss_data_available;
+      rtp->codecHeader.VP9.picture_id = info->codecSpecific.VP9.picture_id;
+      rtp->codecHeader.VP9.tl0_pic_idx = info->codecSpecific.VP9.tl0_pic_idx;
+      rtp->codecHeader.VP9.temporal_idx = info->codecSpecific.VP9.temporal_idx;
+      rtp->codecHeader.VP9.spatial_idx = info->codecSpecific.VP9.spatial_idx;
+      rtp->codecHeader.VP9.temporal_up_switch =
+          info->codecSpecific.VP9.temporal_up_switch;
+      rtp->codecHeader.VP9.inter_layer_predicted =
+          info->codecSpecific.VP9.inter_layer_predicted;
+      rtp->codecHeader.VP9.gof_idx = info->codecSpecific.VP9.gof_idx;
+      rtp->codecHeader.VP9.num_spatial_layers =
+          info->codecSpecific.VP9.num_spatial_layers;
+
+      if (info->codecSpecific.VP9.ss_data_available) {
+        rtp->codecHeader.VP9.spatial_layer_resolution_present =
+            info->codecSpecific.VP9.spatial_layer_resolution_present;
+        if (info->codecSpecific.VP9.spatial_layer_resolution_present) {
+          for (size_t i = 0; i < info->codecSpecific.VP9.num_spatial_layers;
+               ++i) {
+            rtp->codecHeader.VP9.width[i] = info->codecSpecific.VP9.width[i];
+            rtp->codecHeader.VP9.height[i] = info->codecSpecific.VP9.height[i];
+          }
+        }
+        rtp->codecHeader.VP9.gof.CopyGofInfoVP9(info->codecSpecific.VP9.gof);
+      }
+
+      rtp->codecHeader.VP9.num_ref_pics = info->codecSpecific.VP9.num_ref_pics;
+      for (int i = 0; i < info->codecSpecific.VP9.num_ref_pics; ++i)
+        rtp->codecHeader.VP9.pid_diff[i] = info->codecSpecific.VP9.p_diff[i];
+      return;
+    }
     case kVideoCodecH264:
       rtp->codec = kRtpVideoH264;
       rtp->simulcastIdx = info->codecSpecific.H264.simulcastIdx;
       return;
-    case kVideoCodecVP9:
-      rtp->codec = kRtpVideoVp9;
-      rtp->codecHeader.VP9.InitRTPVideoHeaderVP9();
-      rtp->codecHeader.VP9.pictureId = info->codecSpecific.VP9.pictureId;
-      rtp->codecHeader.VP9.nonReference =
-          info->codecSpecific.VP9.nonReference;
-      rtp->codecHeader.VP9.temporalIdx = info->codecSpecific.VP9.temporalIdx;
-      rtp->codecHeader.VP9.layerSync = info->codecSpecific.VP9.layerSync;
-      rtp->codecHeader.VP9.tl0PicIdx = info->codecSpecific.VP9.tl0PicIdx;
-      rtp->codecHeader.VP9.keyIdx = info->codecSpecific.VP9.keyIdx;
-      return;
     case kVideoCodecGeneric:
       rtp->codec = kRtpVideoGeneric;
       rtp->simulcastIdx = info->codecSpecific.generic.simulcast_idx;
       return;
     default:
       return;
   }
 }
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.cc
@@ -9,32 +9,37 @@
  */
 #include "webrtc/modules/video_coding/main/source/jitter_buffer.h"
 
 #include <assert.h>
 
 #include <algorithm>
 #include <utility>
 
+#include "webrtc/base/checks.h"
+#include "webrtc/base/trace_event.h"
+#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
 #include "webrtc/modules/video_coding/main/interface/video_coding.h"
 #include "webrtc/modules/video_coding/main/source/frame_buffer.h"
 #include "webrtc/modules/video_coding/main/source/inter_frame_delay.h"
 #include "webrtc/modules/video_coding/main/source/internal_defines.h"
 #include "webrtc/modules/video_coding/main/source/jitter_buffer_common.h"
 #include "webrtc/modules/video_coding/main/source/jitter_estimator.h"
 #include "webrtc/modules/video_coding/main/source/packet.h"
 #include "webrtc/system_wrappers/interface/clock.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/event_wrapper.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/metrics.h"
-#include "webrtc/system_wrappers/interface/trace_event.h"
 
 namespace webrtc {
 
+// Interval for updating SS data.
+static const uint32_t kSsCleanupIntervalSec = 60;
+
 // Use this rtt if no value has been reported.
 static const int64_t kDefaultRtt = 200;
 
 typedef std::pair<uint32_t, VCMFrameBuffer*> FrameListPair;
 
 bool IsKeyFrame(FrameListPair pair) {
   return pair.second->FrameType() == kVideoFrameKey;
 }
@@ -141,16 +146,108 @@ void FrameList::CleanUpOldOrEmptyFrames(
 void FrameList::Reset(UnorderedFrameList* free_frames) {
   while (!empty()) {
     begin()->second->Reset();
     free_frames->push_back(begin()->second);
     erase(begin());
   }
 }
 
+bool Vp9SsMap::Insert(const VCMPacket& packet) {
+  if (!packet.codecSpecificHeader.codecHeader.VP9.ss_data_available)
+    return false;
+
+  ss_map_[packet.timestamp] = packet.codecSpecificHeader.codecHeader.VP9.gof;
+  return true;
+}
+
+void Vp9SsMap::Reset() {
+  ss_map_.clear();
+}
+
+bool Vp9SsMap::Find(uint32_t timestamp, SsMap::iterator* it_out) {
+  bool found = false;
+  for (SsMap::iterator it = ss_map_.begin(); it != ss_map_.end(); ++it) {
+    if (it->first == timestamp || IsNewerTimestamp(timestamp, it->first)) {
+      *it_out = it;
+      found = true;
+    }
+  }
+  return found;
+}
+
+void Vp9SsMap::RemoveOld(uint32_t timestamp) {
+  if (!TimeForCleanup(timestamp))
+    return;
+
+  SsMap::iterator it;
+  if (!Find(timestamp, &it))
+    return;
+
+  ss_map_.erase(ss_map_.begin(), it);
+  AdvanceFront(timestamp);
+}
+
+bool Vp9SsMap::TimeForCleanup(uint32_t timestamp) const {
+  if (ss_map_.empty() || !IsNewerTimestamp(timestamp, ss_map_.begin()->first))
+    return false;
+
+  uint32_t diff = timestamp - ss_map_.begin()->first;
+  return diff / kVideoPayloadTypeFrequency >= kSsCleanupIntervalSec;
+}
+
+void Vp9SsMap::AdvanceFront(uint32_t timestamp) {
+  RTC_DCHECK(!ss_map_.empty());
+  GofInfoVP9 gof = ss_map_.begin()->second;
+  ss_map_.erase(ss_map_.begin());
+  ss_map_[timestamp] = gof;
+}
+
+// TODO(asapersson): Update according to updates in RTP payload profile.
+bool Vp9SsMap::UpdatePacket(VCMPacket* packet) {
+  uint8_t gof_idx = packet->codecSpecificHeader.codecHeader.VP9.gof_idx;
+  if (gof_idx == kNoGofIdx)
+    return false;  // No update needed.
+
+  SsMap::iterator it;
+  if (!Find(packet->timestamp, &it))
+    return false;  // Corresponding SS not yet received.
+
+  if (gof_idx >= it->second.num_frames_in_gof)
+    return false;  // Assume corresponding SS not yet received.
+
+  RTPVideoHeaderVP9* vp9 = &packet->codecSpecificHeader.codecHeader.VP9;
+  vp9->temporal_idx = it->second.temporal_idx[gof_idx];
+  vp9->temporal_up_switch = it->second.temporal_up_switch[gof_idx];
+
+  // TODO(asapersson): Set vp9.ref_picture_id[i] and add usage.
+  vp9->num_ref_pics = it->second.num_ref_pics[gof_idx];
+  for (uint8_t i = 0; i < it->second.num_ref_pics[gof_idx]; ++i) {
+    vp9->pid_diff[i] = it->second.pid_diff[gof_idx][i];
+  }
+  return true;
+}
+
+void Vp9SsMap::UpdateFrames(FrameList* frames) {
+  for (const auto& frame_it : *frames) {
+    uint8_t gof_idx =
+        frame_it.second->CodecSpecific()->codecSpecific.VP9.gof_idx;
+    if (gof_idx == kNoGofIdx) {
+      continue;
+    }
+    SsMap::iterator ss_it;
+    if (Find(frame_it.second->TimeStamp(), &ss_it)) {
+      if (gof_idx >= ss_it->second.num_frames_in_gof) {
+        continue;  // Assume corresponding SS not yet received.
+      }
+      frame_it.second->SetGofInfo(ss_it->second, gof_idx);
+    }
+  }
+}
+
 VCMJitterBuffer::VCMJitterBuffer(Clock* clock, EventFactory* event_factory)
     : clock_(clock),
       running_(false),
       crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
       frame_event_(event_factory->CreateEvent()),
       max_number_of_frames_(kStartNumberOfFrames),
       free_frames_(),
       decodable_frames_(),
@@ -199,17 +296,17 @@ VCMJitterBuffer::~VCMJitterBuffer() {
   for (FrameList::iterator it = decodable_frames_.begin();
        it != decodable_frames_.end(); ++it) {
     delete it->second;
   }
   delete crit_sect_;
 }
 
 void VCMJitterBuffer::UpdateHistograms() {
-  if (num_packets_ <= 0) {
+  if (num_packets_ <= 0 || !running_) {
     return;
   }
   int64_t elapsed_sec =
       (clock_->TimeInMilliseconds() - time_first_packet_ms_) / 1000;
   if (elapsed_sec < metrics::kMinRunTimeInSeconds) {
     return;
   }
 
@@ -619,16 +716,19 @@ VCMFrameBufferEnum VCMJitterBuffer::Inse
         stats_callback_->OnDiscardedPacketsUpdated(num_discarded_packets_);
     }
     // Update last decoded sequence number if the packet arrived late and
     // belongs to a frame with a timestamp equal to the last decoded
     // timestamp.
     last_decoded_state_.UpdateOldPacket(&packet);
     DropPacketsFromNackList(last_decoded_state_.sequence_num());
 
+    // Also see if this old packet made more incomplete frames continuous.
+    FindAndInsertContinuousFramesWithState(last_decoded_state_);
+
     if (num_consecutive_old_packets_ > kMaxConsecutiveOldPackets) {
       LOG(LS_WARNING)
           << num_consecutive_old_packets_
           << " consecutive old packets received. Flushing the jitter buffer.";
       Flush();
       return kFlushIndicator;
     }
     return kOldPacket;
@@ -795,24 +895,35 @@ bool VCMJitterBuffer::IsContinuous(const
   return false;
 }
 
 void VCMJitterBuffer::FindAndInsertContinuousFrames(
     const VCMFrameBuffer& new_frame) {
   VCMDecodingState decoding_state;
   decoding_state.CopyFrom(last_decoded_state_);
   decoding_state.SetState(&new_frame);
+  FindAndInsertContinuousFramesWithState(decoding_state);
+}
+
+void VCMJitterBuffer::FindAndInsertContinuousFramesWithState(
+    const VCMDecodingState& original_decoded_state) {
+  // Copy original_decoded_state so we can move the state forward with each
+  // decodable frame we find.
+  VCMDecodingState decoding_state;
+  decoding_state.CopyFrom(original_decoded_state);
+
   // When temporal layers are available, we search for a complete or decodable
   // frame until we hit one of the following:
   // 1. Continuous base or sync layer.
   // 2. The end of the list was reached.
   for (FrameList::iterator it = incomplete_frames_.begin();
        it != incomplete_frames_.end();)  {
     VCMFrameBuffer* frame = it->second;
-    if (IsNewerTimestamp(new_frame.TimeStamp(), frame->TimeStamp())) {
+    if (IsNewerTimestamp(original_decoded_state.time_stamp(),
+                         frame->TimeStamp())) {
       ++it;
       continue;
     }
     if (IsContinuousInState(*frame, decoding_state)) {
       decodable_frames_.InsertFrame(frame);
       incomplete_frames_.erase(it++);
       decoding_state.SetState(frame);
     } else if (frame->TemporalId() <= 0) {
@@ -853,17 +964,17 @@ void VCMJitterBuffer::SetNackMode(VCMNac
   }
   assert(low_rtt_nack_threshold_ms >= -1 && high_rtt_nack_threshold_ms >= -1);
   assert(high_rtt_nack_threshold_ms == -1 ||
          low_rtt_nack_threshold_ms <= high_rtt_nack_threshold_ms);
   assert(low_rtt_nack_threshold_ms > -1 || high_rtt_nack_threshold_ms == -1);
   low_rtt_nack_threshold_ms_ = low_rtt_nack_threshold_ms;
   high_rtt_nack_threshold_ms_ = high_rtt_nack_threshold_ms;
   // Don't set a high start rtt if high_rtt_nack_threshold_ms_ is used, to not
-  // disable NACK in hybrid mode.
+  // disable NACK in |kNack| mode.
   if (rtt_ms_ == kDefaultRtt && high_rtt_nack_threshold_ms_ != -1) {
     rtt_ms_ = 0;
   }
   if (!WaitForRetransmissions()) {
     jitter_estimate_.ResetNackCount();
   }
 }
 
--- a/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc
@@ -84,16 +84,17 @@ static void LogCodec(const VideoCodec& c
                  << ", keyFrameInterval: "
                  << codec.codecSpecific.H264.keyFrameInterval
                  << ", spslen: "
                  << codec.codecSpecific.H264.spsLen
                  << ", ppslen: "
                  << codec.codecSpecific.H264.ppsLen;
   } else if (codec.codecType == kVideoCodecVP9) {
     LOG(LS_INFO) << "VP9 specific settings";
+    // XXX FIX!! log VP9 specific settings
   }
 }
 
 
 ViECodec* ViECodec::GetInterface(VideoEngine* video_engine) {
 #ifdef WEBRTC_VIDEO_ENGINE_CODEC_API
   if (!video_engine) {
     return NULL;