Merge remote-tracking branch 'goog/stage-aosp-master' into HEAD
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..3208f8c
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,4 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+include $(LOCAL_PATH)/vda/Android.mk
diff --git a/MODULE_LICENSE_BSD b/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_BSD
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..c892933
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vda/Android.mk b/vda/Android.mk
new file mode 100644
index 0000000..c005941
--- /dev/null
+++ b/vda/Android.mk
@@ -0,0 +1,39 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION:= .cc
+LOCAL_SRC_FILES:= \
+        bit_reader.cc       \
+        bit_reader_core.cc  \
+        h264_bit_reader.cc  \
+        h264_decoder.cc     \
+        h264_dpb.cc         \
+        h264_parser.cc      \
+        ranges.cc           \
+        vp8_bool_decoder.cc \
+        vp8_decoder.cc      \
+        vp8_parser.cc       \
+        vp8_picture.cc      \
+        vp9_bool_decoder.cc \
+        vp9_compressed_header_parser.cc \
+        vp9_decoder.cc      \
+        vp9_parser.cc       \
+        vp9_picture.cc      \
+        vp9_raw_bits_reader.cc \
+        vp9_uncompressed_header_parser.cc \
+
+LOCAL_C_INCLUDES += \
+        $(TOP)/external/libchrome \
+
+LOCAL_MODULE:= libv4l2_codec2_vda
+
+LOCAL_SHARED_LIBRARIES := libchrome \
+
+# -Wno-unused-parameter is needed for libchrome/base codes
+LOCAL_CFLAGS += -Werror -Wall -Wno-unused-parameter
+LOCAL_CLANG := true
+LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow
+
+LOCAL_LDFLAGS := -Wl,-Bsymbolic
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/vda/accelerated_video_decoder.h b/vda/accelerated_video_decoder.h
new file mode 100644
index 0000000..fe1c711
--- /dev/null
+++ b/vda/accelerated_video_decoder.h
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ACCELERATED_VIDEO_DECODER_H_
+#define ACCELERATED_VIDEO_DECODER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "size.h"
+
+namespace media {
+
+// An AcceleratedVideoDecoder is a video decoder that requires support from an
+// external accelerator (typically a hardware accelerator) to partially offload
+// the decode process after parsing stream headers, and performing reference
+// frame and state management.
+class AcceleratedVideoDecoder {
+ public:
+  AcceleratedVideoDecoder() {}
+  virtual ~AcceleratedVideoDecoder() {}
+
+  virtual void SetStream(const uint8_t* ptr, size_t size) = 0;
+
+  // Have the decoder flush its state and trigger output of all previously
+  // decoded surfaces. Return false on failure.
+  virtual bool Flush() WARN_UNUSED_RESULT = 0;
+
+  // Stop (pause) decoding, discarding all remaining inputs and outputs,
+  // but do not flush decoder state, so that playback can be resumed later,
+  // possibly from a different location.
+  // To be called during decoding.
+  virtual void Reset() = 0;
+
+  enum DecodeResult {
+    kDecodeError,  // Error while decoding.
+    // TODO(posciak): unsupported streams are currently treated as error
+    // in decoding; in future it could perhaps be possible to fall back
+    // to software decoding instead.
+    // kStreamError,  // Error in stream.
+    kAllocateNewSurfaces,  // Need a new set of surfaces to be allocated.
+    kRanOutOfStreamData,   // Need more stream data to proceed.
+    kRanOutOfSurfaces,     // Waiting for the client to free up output surfaces.
+    kNeedContextUpdate,    // Waiting for the client to update decoding context
+                           // with data acquired from the accelerator.
+  };
+
+  // Try to decode more of the stream, returning decoded frames asynchronously.
+  // Return when more stream is needed, when we run out of free surfaces, when
+  // we need a new set of them, or when an error occurs.
+  virtual DecodeResult Decode() WARN_UNUSED_RESULT = 0;
+
+  // Return dimensions/required number of output surfaces that client should
+  // be ready to provide for the decoder to function properly.
+  // To be used after Decode() returns kAllocateNewSurfaces.
+  virtual Size GetPicSize() const = 0;
+  virtual size_t GetRequiredNumOfPictures() const = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AcceleratedVideoDecoder);
+};
+
+}  //  namespace media
+
+#endif  // ACCELERATED_VIDEO_DECODER_H_
diff --git a/vda/bit_reader.cc b/vda/bit_reader.cc
new file mode 100644
index 0000000..953d144
--- /dev/null
+++ b/vda/bit_reader.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "bit_reader.h"
+
+namespace media {
+
+BitReader::BitReader(const uint8_t* data, int size)
+    : initial_size_(size),
+      data_(data),
+      bytes_left_(size),
+      bit_reader_core_(this) {
+  DCHECK(data != NULL);
+  DCHECK_GE(size, 0);
+}
+
+BitReader::~BitReader() {}
+
+bool BitReader::ReadString(int num_bits, std::string* str) {
+  DCHECK_EQ(num_bits % 8, 0);
+  DCHECK_GT(num_bits, 0);
+  DCHECK(str);
+  int num_bytes = num_bits / 8;
+  str->resize(num_bytes);
+  char* ptr = &str->front();
+  while (num_bytes--) {
+    if (!ReadBits(8, ptr++))
+      return false;
+  }
+  return true;
+}
+
+int BitReader::GetBytes(int max_nbytes, const uint8_t** out) {
+  DCHECK_GE(max_nbytes, 0);
+  DCHECK(out);
+
+  int nbytes = max_nbytes;
+  if (nbytes > bytes_left_)
+    nbytes = bytes_left_;
+
+  *out = data_;
+  data_ += nbytes;
+  bytes_left_ -= nbytes;
+  return nbytes;
+}
+
+}  // namespace media
diff --git a/vda/bit_reader.h b/vda/bit_reader.h
new file mode 100644
index 0000000..2b3fad0
--- /dev/null
+++ b/vda/bit_reader.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BIT_READER_H_
+#define BIT_READER_H_
+
+#include <stdint.h>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "bit_reader_core.h"
+
+namespace media {
+
+class BitReader
+    : NON_EXPORTED_BASE(private BitReaderCore::ByteStreamProvider)  {
+ public:
+  // Initialize the reader to start reading at |data|, |size| being size
+  // of |data| in bytes.
+  BitReader(const uint8_t* data, int size);
+  ~BitReader() override;
+
+  template<typename T> bool ReadBits(int num_bits, T* out) {
+    return bit_reader_core_.ReadBits(num_bits, out);
+  }
+
+  bool ReadFlag(bool* flag) {
+    return bit_reader_core_.ReadFlag(flag);
+  }
+
+  // Read |num_bits| of binary data into |str|. |num_bits| must be a positive
+  // multiple of 8. This is not efficient for extracting large strings.
+  // If false is returned, |str| may not be valid.
+  bool ReadString(int num_bits, std::string* str);
+
+  bool SkipBits(int num_bits) {
+    return bit_reader_core_.SkipBits(num_bits);
+  }
+
+  int bits_available() const {
+    return initial_size_ * 8 - bits_read();
+  }
+
+  int bits_read() const {
+    return bit_reader_core_.bits_read();
+  }
+
+ private:
+  // BitReaderCore::ByteStreamProvider implementation.
+  int GetBytes(int max_n, const uint8_t** out) override;
+
+  // Total number of bytes that was initially passed to BitReader.
+  const int initial_size_;
+
+  // Pointer to the next unread byte in the stream.
+  const uint8_t* data_;
+
+  // Bytes left in the stream.
+  int bytes_left_;
+
+  BitReaderCore bit_reader_core_;
+
+  DISALLOW_COPY_AND_ASSIGN(BitReader);
+};
+
+}  // namespace media
+
+#endif  // BIT_READER_H_
diff --git a/vda/bit_reader_core.cc b/vda/bit_reader_core.cc
new file mode 100644
index 0000000..220ea03
--- /dev/null
+++ b/vda/bit_reader_core.cc
@@ -0,0 +1,193 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "bit_reader_core.h"
+
+#include <stdint.h>
+
+#include "base/sys_byteorder.h"
+
+namespace {
+const int kRegWidthInBits = sizeof(uint64_t) * 8;
+}
+
+namespace media {
+
+BitReaderCore::ByteStreamProvider::ByteStreamProvider() {
+}
+
+BitReaderCore::ByteStreamProvider::~ByteStreamProvider() {
+}
+
+BitReaderCore::BitReaderCore(ByteStreamProvider* byte_stream_provider)
+    : byte_stream_provider_(byte_stream_provider),
+      bits_read_(0),
+      nbits_(0),
+      reg_(0),
+      nbits_next_(0),
+      reg_next_(0) {
+}
+
+BitReaderCore::~BitReaderCore() {
+}
+
+bool BitReaderCore::ReadFlag(bool* flag) {
+  if (nbits_ == 0 && !Refill(1))
+    return false;
+
+  *flag = (reg_ & (UINT64_C(1) << (kRegWidthInBits - 1))) != 0;
+  reg_ <<= 1;
+  nbits_--;
+  bits_read_++;
+  return true;
+}
+
+int BitReaderCore::PeekBitsMsbAligned(int num_bits, uint64_t* out) {
+  // Try to have at least |num_bits| in the bit register.
+  if (nbits_ < num_bits)
+    Refill(num_bits);
+
+  *out = reg_;
+  return nbits_;
+}
+
+bool BitReaderCore::SkipBitsSmall(int num_bits) {
+  DCHECK_GE(num_bits, 0);
+  uint64_t dummy;
+  while (num_bits >= kRegWidthInBits) {
+    if (!ReadBitsInternal(kRegWidthInBits, &dummy))
+      return false;
+    num_bits -= kRegWidthInBits;
+  }
+  return ReadBitsInternal(num_bits, &dummy);
+}
+
+bool BitReaderCore::SkipBits(int num_bits) {
+  DCHECK_GE(num_bits, 0);
+
+  const int remaining_bits = nbits_ + nbits_next_;
+  if (remaining_bits >= num_bits)
+    return SkipBitsSmall(num_bits);
+
+  // Skip first the remaining available bits.
+  num_bits -= remaining_bits;
+  bits_read_ += remaining_bits;
+  nbits_ = 0;
+  reg_ = 0;
+  nbits_next_ = 0;
+  reg_next_ = 0;
+
+  // Next, skip an integer number of bytes.
+  const int nbytes = num_bits / 8;
+  if (nbytes > 0) {
+    const uint8_t* byte_stream_window;
+    const int window_size =
+        byte_stream_provider_->GetBytes(nbytes, &byte_stream_window);
+    DCHECK_GE(window_size, 0);
+    DCHECK_LE(window_size, nbytes);
+    if (window_size < nbytes) {
+      // Note that some bytes were consumed.
+      bits_read_ += 8 * window_size;
+      return false;
+    }
+    num_bits -= 8 * nbytes;
+    bits_read_ += 8 * nbytes;
+  }
+
+  // Skip the remaining bits.
+  return SkipBitsSmall(num_bits);
+}
+
+int BitReaderCore::bits_read() const {
+  return bits_read_;
+}
+
+bool BitReaderCore::ReadBitsInternal(int num_bits, uint64_t* out) {
+  DCHECK_GE(num_bits, 0);
+
+  if (num_bits == 0) {
+    *out = 0;
+    return true;
+  }
+
+  if (num_bits > nbits_ && !Refill(num_bits)) {
+    // Any subsequent ReadBits should fail:
+    // empty the current bit register for that purpose.
+    nbits_ = 0;
+    reg_ = 0;
+    return false;
+  }
+
+  bits_read_ += num_bits;
+
+  if (num_bits == kRegWidthInBits) {
+    // Special case needed since for example for a 64 bit integer "a"
+    // "a << 64" is not defined by the C/C++ standard.
+    *out = reg_;
+    reg_ = 0;
+    nbits_ = 0;
+    return true;
+  }
+
+  *out = reg_ >> (kRegWidthInBits - num_bits);
+  reg_ <<= num_bits;
+  nbits_ -= num_bits;
+  return true;
+}
+
+bool BitReaderCore::Refill(int min_nbits) {
+  DCHECK_LE(min_nbits, kRegWidthInBits);
+
+  // Transfer from the next to the current register.
+  RefillCurrentRegister();
+  if (min_nbits <= nbits_)
+    return true;
+  DCHECK_EQ(nbits_next_, 0);
+  DCHECK_EQ(reg_next_, 0u);
+
+  // Max number of bytes to refill.
+  int max_nbytes = sizeof(reg_next_);
+
+  // Refill.
+  const uint8_t* byte_stream_window;
+  int window_size =
+      byte_stream_provider_->GetBytes(max_nbytes, &byte_stream_window);
+  DCHECK_GE(window_size, 0);
+  DCHECK_LE(window_size, max_nbytes);
+  if (window_size == 0)
+    return false;
+
+  reg_next_ = 0;
+  memcpy(&reg_next_, byte_stream_window, window_size);
+  reg_next_ = base::NetToHost64(reg_next_);
+  nbits_next_ = window_size * 8;
+
+  // Transfer from the next to the current register.
+  RefillCurrentRegister();
+
+  return (nbits_ >= min_nbits);
+}
+
+void BitReaderCore::RefillCurrentRegister() {
+  // No refill possible if the destination register is full
+  // or the source register is empty.
+  if (nbits_ == kRegWidthInBits || nbits_next_ == 0)
+    return;
+
+  reg_ |= (reg_next_ >> nbits_);
+
+  int free_nbits = kRegWidthInBits - nbits_;
+  if (free_nbits >= nbits_next_) {
+    nbits_ += nbits_next_;
+    reg_next_ = 0;
+    nbits_next_ = 0;
+    return;
+  }
+
+  nbits_ += free_nbits;
+  reg_next_ <<= free_nbits;
+  nbits_next_ -= free_nbits;
+}
+
+}  // namespace media
diff --git a/vda/bit_reader_core.h b/vda/bit_reader_core.h
new file mode 100644
index 0000000..9e73018
--- /dev/null
+++ b/vda/bit_reader_core.h
@@ -0,0 +1,124 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BIT_READER_CORE_H_
+#define BIT_READER_CORE_H_
+
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace media {
+
+class BitReaderCore {
+ public:
+  class ByteStreamProvider {
+   public:
+    ByteStreamProvider();
+    virtual ~ByteStreamProvider();
+
+    // Consume at most the following |max_n| bytes of the stream
+    // and return the number n of bytes actually consumed.
+    // Set |*array| to point to a memory buffer containing those n bytes.
+    // Note: |*array| must be valid until the next call to GetBytes
+    // but there is no guarantee it is valid after.
+    virtual int GetBytes(int max_n, const uint8_t** array) = 0;
+  };
+
+  // Lifetime of |byte_stream_provider| must be longer than BitReaderCore.
+  explicit BitReaderCore(ByteStreamProvider* byte_stream_provider);
+  ~BitReaderCore();
+
+  // Read one bit from the stream and return it as a boolean in |*out|.
+  // Remark: we do not use the template version for reading a bool
+  // since it generates some optimization warnings during compilation
+  // on Windows platforms.
+  bool ReadBits(int num_bits, bool* out) {
+    DCHECK_EQ(num_bits, 1);
+    return ReadFlag(out);
+  }
+
+  // Read |num_bits| next bits from stream and return in |*out|, first bit
+  // from the stream starting at |num_bits| position in |*out|,
+  // bits of |*out| whose position is strictly greater than |num_bits|
+  // are all set to zero.
+  // Notes:
+  // - |num_bits| cannot be larger than the bits the type can hold.
+  // - From the above description, passing a signed type in |T| does not
+  //   mean the first bit read from the stream gives the sign of the value.
+  // Return false if the given number of bits cannot be read (not enough
+  // bits in the stream), true otherwise. When return false, the stream will
+  // enter a state where further ReadBits/SkipBits operations will always
+  // return false unless |num_bits| is 0. The type |T| has to be a primitive
+  // integer type.
+  template<typename T> bool ReadBits(int num_bits, T* out) {
+    DCHECK_LE(num_bits, static_cast<int>(sizeof(T) * 8));
+    uint64_t temp;
+    bool ret = ReadBitsInternal(num_bits, &temp);
+    *out = static_cast<T>(temp);
+    return ret;
+  }
+
+  // Read one bit from the stream and return it as a boolean in |*flag|.
+  bool ReadFlag(bool* flag);
+
+  // Retrieve some bits without actually consuming them.
+  // Bits returned in |*out| are shifted so the most significant bit contains
+  // the next bit that can be read from the stream.
+  // Return the number of bits actually written in |out|.
+  // Note: |num_bits| is just a suggestion of how many bits the caller
+  // wish to get in |*out| and must be less than 64:
+  // - The number of bits returned can be more than |num_bits|.
+  // - However, it will be strictly less than |num_bits|
+  //   if and only if there are not enough bits left in the stream.
+  int PeekBitsMsbAligned(int num_bits, uint64_t* out);
+
+  // Skip |num_bits| next bits from stream. Return false if the given number of
+  // bits cannot be skipped (not enough bits in the stream), true otherwise.
+  // When return false, the stream will enter a state where further
+  // ReadBits/ReadFlag/SkipBits operations
+  // will always return false unless |num_bits| is 0.
+  bool SkipBits(int num_bits);
+
+  // Returns the number of bits read so far.
+  int bits_read() const;
+
+ private:
+  // This function can skip any number of bits but is more efficient
+  // for small numbers. Return false if the given number of bits cannot be
+  // skipped (not enough bits in the stream), true otherwise.
+  bool SkipBitsSmall(int num_bits);
+
+  // Help function used by ReadBits to avoid inlining the bit reading logic.
+  bool ReadBitsInternal(int num_bits, uint64_t* out);
+
+  // Refill bit registers to have at least |min_nbits| bits available.
+  // Return true if the mininimum bit count condition is met after the refill.
+  bool Refill(int min_nbits);
+
+  // Refill the current bit register from the next bit register.
+  void RefillCurrentRegister();
+
+  ByteStreamProvider* const byte_stream_provider_;
+
+  // Number of bits read so far.
+  int bits_read_;
+
+  // Number of bits in |reg_| that have not been consumed yet.
+  // Note: bits are consumed from MSB to LSB.
+  int nbits_;
+  uint64_t reg_;
+
+  // Number of bits in |reg_next_| that have not been consumed yet.
+  // Note: bits are consumed from MSB to LSB.
+  int nbits_next_;
+  uint64_t reg_next_;
+
+  DISALLOW_COPY_AND_ASSIGN(BitReaderCore);
+};
+
+}  // namespace media
+
+#endif  // BIT_READER_CORE_H_
diff --git a/vda/h264_bit_reader.cc b/vda/h264_bit_reader.cc
new file mode 100644
index 0000000..7c536b3
--- /dev/null
+++ b/vda/h264_bit_reader.cc
@@ -0,0 +1,122 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "h264_bit_reader.h"
+
+namespace media {
+
+H264BitReader::H264BitReader()
+    : data_(NULL),
+      bytes_left_(0),
+      curr_byte_(0),
+      num_remaining_bits_in_curr_byte_(0),
+      prev_two_bytes_(0),
+      emulation_prevention_bytes_(0) {}
+
+H264BitReader::~H264BitReader() {}
+
+bool H264BitReader::Initialize(const uint8_t* data, off_t size) {
+  DCHECK(data);
+
+  if (size < 1)
+    return false;
+
+  data_ = data;
+  bytes_left_ = size;
+  num_remaining_bits_in_curr_byte_ = 0;
+  // Initially set to 0xffff to accept all initial two-byte sequences.
+  prev_two_bytes_ = 0xffff;
+  emulation_prevention_bytes_ = 0;
+
+  return true;
+}
+
+bool H264BitReader::UpdateCurrByte() {
+  if (bytes_left_ < 1)
+    return false;
+
+  // Emulation prevention three-byte detection.
+  // If a sequence of 0x000003 is found, skip (ignore) the last byte (0x03).
+  if (*data_ == 0x03 && (prev_two_bytes_ & 0xffff) == 0) {
+    // Detected 0x000003, skip last byte.
+    ++data_;
+    --bytes_left_;
+    ++emulation_prevention_bytes_;
+    // Need another full three bytes before we can detect the sequence again.
+    prev_two_bytes_ = 0xffff;
+
+    if (bytes_left_ < 1)
+      return false;
+  }
+
+  // Load a new byte and advance pointers.
+  curr_byte_ = *data_++ & 0xff;
+  --bytes_left_;
+  num_remaining_bits_in_curr_byte_ = 8;
+
+  prev_two_bytes_ = ((prev_two_bytes_ & 0xff) << 8) | curr_byte_;
+
+  return true;
+}
+
+// Read |num_bits| (1 to 31 inclusive) from the stream and return them
+// in |out|, with first bit in the stream as MSB in |out| at position
+// (|num_bits| - 1).
+bool H264BitReader::ReadBits(int num_bits, int* out) {
+  int bits_left = num_bits;
+  *out = 0;
+  DCHECK(num_bits <= 31);
+
+  while (num_remaining_bits_in_curr_byte_ < bits_left) {
+    // Take all that's left in current byte, shift to make space for the rest.
+    *out |= (curr_byte_ << (bits_left - num_remaining_bits_in_curr_byte_));
+    bits_left -= num_remaining_bits_in_curr_byte_;
+
+    if (!UpdateCurrByte())
+      return false;
+  }
+
+  *out |= (curr_byte_ >> (num_remaining_bits_in_curr_byte_ - bits_left));
+  *out &= ((1u << num_bits) - 1u);
+  num_remaining_bits_in_curr_byte_ -= bits_left;
+
+  return true;
+}
+
+off_t H264BitReader::NumBitsLeft() {
+  return (num_remaining_bits_in_curr_byte_ + bytes_left_ * 8);
+}
+
+bool H264BitReader::HasMoreRBSPData() {
+  // Make sure we have more bits, if we are at 0 bits in current byte and
+  // updating current byte fails, we don't have more data anyway.
+  if (num_remaining_bits_in_curr_byte_ == 0 && !UpdateCurrByte())
+    return false;
+
+  // If there is no more RBSP data, then |curr_byte_| contains the stop bit and
+  // zero padding. Check to see if there is other data instead.
+  // (We don't actually check for the stop bit itself, instead treating the
+  // invalid case of all trailing zeros identically).
+  if ((curr_byte_ & ((1 << (num_remaining_bits_in_curr_byte_ - 1)) - 1)) != 0)
+    return true;
+
+  // While the spec disallows it (7.4.1: "The last byte of the NAL unit shall
+  // not be equal to 0x00"), some streams have trailing null bytes anyway. We
+  // don't handle emulation prevention sequences because HasMoreRBSPData() is
+  // not used when parsing slices (where cabac_zero_word elements are legal).
+  for (off_t i = 0; i < bytes_left_; i++) {
+    if (data_[i] != 0)
+      return true;
+  }
+
+  bytes_left_ = 0;
+  return false;
+}
+
+size_t H264BitReader::NumEmulationPreventionBytesRead() {
+  return emulation_prevention_bytes_;
+}
+
+}  // namespace media
diff --git a/vda/h264_bit_reader.h b/vda/h264_bit_reader.h
new file mode 100644
index 0000000..156b524
--- /dev/null
+++ b/vda/h264_bit_reader.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains an implementation of an H264 Annex-B video stream parser.
+
+#ifndef H264_BIT_READER_H_
+#define H264_BIT_READER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "base/macros.h"
+
+namespace media {
+
+// A class to provide bit-granularity reading of H.264 streams.
+// This is not a generic bit reader class, as it takes into account
+// H.264 stream-specific constraints, such as skipping emulation-prevention
+// bytes and stop bits. See spec for more details.
+class H264BitReader {
+ public:
+  H264BitReader();
+  ~H264BitReader();
+
+  // Initialize the reader to start reading at |data|, |size| being size
+  // of |data| in bytes.
+  // Return false on insufficient size of stream..
+  // TODO(posciak,fischman): consider replacing Initialize() with
+  // heap-allocating and creating bit readers on demand instead.
+  bool Initialize(const uint8_t* data, off_t size);
+
+  // Read |num_bits| next bits from stream and return in |*out|, first bit
+  // from the stream starting at |num_bits| position in |*out|.
+  // |num_bits| may be 1-32, inclusive.
+  // Return false if the given number of bits cannot be read (not enough
+  // bits in the stream), true otherwise.
+  bool ReadBits(int num_bits, int* out);
+
+  // Return the number of bits left in the stream.
+  off_t NumBitsLeft();
+
+  // See the definition of more_rbsp_data() in spec.
+  bool HasMoreRBSPData();
+
+  // Return the number of emulation prevention bytes already read.
+  size_t NumEmulationPreventionBytesRead();
+
+ private:
+  // Advance to the next byte, loading it into curr_byte_.
+  // Return false on end of stream.
+  bool UpdateCurrByte();
+
+  // Pointer to the next unread (not in curr_byte_) byte in the stream.
+  const uint8_t* data_;
+
+  // Bytes left in the stream (without the curr_byte_).
+  off_t bytes_left_;
+
+  // Contents of the current byte; first unread bit starting at position
+  // 8 - num_remaining_bits_in_curr_byte_ from MSB.
+  int curr_byte_;
+
+  // Number of bits remaining in curr_byte_
+  int num_remaining_bits_in_curr_byte_;
+
+  // Used in emulation prevention three byte detection (see spec).
+  // Initially set to 0xffff to accept all initial two-byte sequences.
+  int prev_two_bytes_;
+
+  // Number of emulation preventation bytes (0x000003) we met.
+  size_t emulation_prevention_bytes_;
+
+  DISALLOW_COPY_AND_ASSIGN(H264BitReader);
+};
+
+}  // namespace media
+
+#endif  // H264_BIT_READER_H_
diff --git a/vda/h264_decoder.cc b/vda/h264_decoder.cc
new file mode 100644
index 0000000..3964059
--- /dev/null
+++ b/vda/h264_decoder.cc
@@ -0,0 +1,1436 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <limits>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/optional.h"
+#include "base/stl_util.h"
+#include "h264_decoder.h"
+
+namespace media {
+
+H264Decoder::H264Accelerator::H264Accelerator() {}
+
+H264Decoder::H264Accelerator::~H264Accelerator() {}
+
+H264Decoder::H264Decoder(H264Accelerator* accelerator)
+    : max_frame_num_(0),
+      max_pic_num_(0),
+      max_long_term_frame_idx_(0),
+      max_num_reorder_frames_(0),
+      accelerator_(accelerator) {
+  DCHECK(accelerator_);
+  Reset();
+  state_ = kNeedStreamMetadata;
+}
+
+H264Decoder::~H264Decoder() {}
+
+void H264Decoder::Reset() {
+  curr_pic_ = nullptr;
+  curr_nalu_ = nullptr;
+  curr_slice_hdr_ = nullptr;
+  curr_sps_id_ = -1;
+  curr_pps_id_ = -1;
+
+  prev_frame_num_ = -1;
+  prev_ref_frame_num_ = -1;
+  prev_frame_num_offset_ = -1;
+  prev_has_memmgmnt5_ = false;
+
+  prev_ref_has_memmgmnt5_ = false;
+  prev_ref_top_field_order_cnt_ = -1;
+  prev_ref_pic_order_cnt_msb_ = -1;
+  prev_ref_pic_order_cnt_lsb_ = -1;
+  prev_ref_field_ = H264Picture::FIELD_NONE;
+
+  ref_pic_list_p0_.clear();
+  ref_pic_list_b0_.clear();
+  ref_pic_list_b1_.clear();
+  dpb_.Clear();
+  parser_.Reset();
+  accelerator_->Reset();
+  last_output_poc_ = std::numeric_limits<int>::min();
+
+  // If we are in kDecoding, we can resume without processing an SPS.
+  if (state_ == kDecoding)
+    state_ = kAfterReset;
+}
+
+void H264Decoder::PrepareRefPicLists(const H264SliceHeader* slice_hdr) {
+  ConstructReferencePicListsP(slice_hdr);
+  ConstructReferencePicListsB(slice_hdr);
+}
+
+bool H264Decoder::ModifyReferencePicLists(const H264SliceHeader* slice_hdr,
+                                          H264Picture::Vector* ref_pic_list0,
+                                          H264Picture::Vector* ref_pic_list1) {
+  ref_pic_list0->clear();
+  ref_pic_list1->clear();
+
+  // Fill reference picture lists for B and S/SP slices.
+  if (slice_hdr->IsPSlice() || slice_hdr->IsSPSlice()) {
+    *ref_pic_list0 = ref_pic_list_p0_;
+    return ModifyReferencePicList(slice_hdr, 0, ref_pic_list0);
+  } else if (slice_hdr->IsBSlice()) {
+    *ref_pic_list0 = ref_pic_list_b0_;
+    *ref_pic_list1 = ref_pic_list_b1_;
+    return ModifyReferencePicList(slice_hdr, 0, ref_pic_list0) &&
+           ModifyReferencePicList(slice_hdr, 1, ref_pic_list1);
+  }
+
+  return true;
+}
+
+bool H264Decoder::DecodePicture() {
+  DCHECK(curr_pic_.get());
+
+  DVLOG(4) << "Decoding POC " << curr_pic_->pic_order_cnt;
+  return accelerator_->SubmitDecode(curr_pic_);
+}
+
+bool H264Decoder::InitNonexistingPicture(scoped_refptr<H264Picture> pic,
+                                         int frame_num) {
+  pic->nonexisting = true;
+  pic->nal_ref_idc = 1;
+  pic->frame_num = pic->pic_num = frame_num;
+  pic->adaptive_ref_pic_marking_mode_flag = false;
+  pic->ref = true;
+  pic->long_term_reference_flag = false;
+  pic->field = H264Picture::FIELD_NONE;
+
+  return CalculatePicOrderCounts(pic);
+}
+
+bool H264Decoder::InitCurrPicture(const H264SliceHeader* slice_hdr) {
+  DCHECK(curr_pic_.get());
+
+  curr_pic_->idr = slice_hdr->idr_pic_flag;
+  if (curr_pic_->idr)
+    curr_pic_->idr_pic_id = slice_hdr->idr_pic_id;
+
+  if (slice_hdr->field_pic_flag) {
+    curr_pic_->field = slice_hdr->bottom_field_flag ? H264Picture::FIELD_BOTTOM
+                                                    : H264Picture::FIELD_TOP;
+  } else {
+    curr_pic_->field = H264Picture::FIELD_NONE;
+  }
+
+  if (curr_pic_->field != H264Picture::FIELD_NONE) {
+    DVLOG(1) << "Interlaced video not supported.";
+    return false;
+  }
+
+  curr_pic_->nal_ref_idc = slice_hdr->nal_ref_idc;
+  curr_pic_->ref = slice_hdr->nal_ref_idc != 0;
+  // This assumes non-interlaced stream.
+  curr_pic_->frame_num = curr_pic_->pic_num = slice_hdr->frame_num;
+
+  DCHECK_NE(curr_sps_id_, -1);
+  const H264SPS* sps = parser_.GetSPS(curr_sps_id_);
+  if (!sps)
+    return false;
+
+  curr_pic_->pic_order_cnt_type = sps->pic_order_cnt_type;
+  switch (curr_pic_->pic_order_cnt_type) {
+    case 0:
+      curr_pic_->pic_order_cnt_lsb = slice_hdr->pic_order_cnt_lsb;
+      curr_pic_->delta_pic_order_cnt_bottom =
+          slice_hdr->delta_pic_order_cnt_bottom;
+      break;
+
+    case 1:
+      curr_pic_->delta_pic_order_cnt0 = slice_hdr->delta_pic_order_cnt0;
+      curr_pic_->delta_pic_order_cnt1 = slice_hdr->delta_pic_order_cnt1;
+      break;
+
+    case 2:
+      break;
+
+    default:
+      NOTREACHED();
+      return false;
+  }
+
+  if (!CalculatePicOrderCounts(curr_pic_))
+    return false;
+
+  curr_pic_->long_term_reference_flag = slice_hdr->long_term_reference_flag;
+  curr_pic_->adaptive_ref_pic_marking_mode_flag =
+      slice_hdr->adaptive_ref_pic_marking_mode_flag;
+
+  // If the slice header indicates we will have to perform reference marking
+  // process after this picture is decoded, store required data for that
+  // purpose.
+  if (slice_hdr->adaptive_ref_pic_marking_mode_flag) {
+    static_assert(sizeof(curr_pic_->ref_pic_marking) ==
+                      sizeof(slice_hdr->ref_pic_marking),
+                  "Array sizes of ref pic marking do not match.");
+    memcpy(curr_pic_->ref_pic_marking, slice_hdr->ref_pic_marking,
+           sizeof(curr_pic_->ref_pic_marking));
+  }
+
+  return true;
+}
+
+bool H264Decoder::CalculatePicOrderCounts(scoped_refptr<H264Picture> pic) {
+  const H264SPS* sps = parser_.GetSPS(curr_sps_id_);
+  if (!sps)
+    return false;
+
+  switch (pic->pic_order_cnt_type) {
+    case 0: {
+      // See spec 8.2.1.1.
+      int prev_pic_order_cnt_msb, prev_pic_order_cnt_lsb;
+
+      if (pic->idr) {
+        prev_pic_order_cnt_msb = prev_pic_order_cnt_lsb = 0;
+      } else {
+        if (prev_ref_has_memmgmnt5_) {
+          if (prev_ref_field_ != H264Picture::FIELD_BOTTOM) {
+            prev_pic_order_cnt_msb = 0;
+            prev_pic_order_cnt_lsb = prev_ref_top_field_order_cnt_;
+          } else {
+            prev_pic_order_cnt_msb = 0;
+            prev_pic_order_cnt_lsb = 0;
+          }
+        } else {
+          prev_pic_order_cnt_msb = prev_ref_pic_order_cnt_msb_;
+          prev_pic_order_cnt_lsb = prev_ref_pic_order_cnt_lsb_;
+        }
+      }
+
+      int max_pic_order_cnt_lsb =
+          1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
+      DCHECK_NE(max_pic_order_cnt_lsb, 0);
+      if ((pic->pic_order_cnt_lsb < prev_pic_order_cnt_lsb) &&
+          (prev_pic_order_cnt_lsb - pic->pic_order_cnt_lsb >=
+           max_pic_order_cnt_lsb / 2)) {
+        pic->pic_order_cnt_msb = prev_pic_order_cnt_msb + max_pic_order_cnt_lsb;
+      } else if ((pic->pic_order_cnt_lsb > prev_pic_order_cnt_lsb) &&
+                 (pic->pic_order_cnt_lsb - prev_pic_order_cnt_lsb >
+                  max_pic_order_cnt_lsb / 2)) {
+        pic->pic_order_cnt_msb = prev_pic_order_cnt_msb - max_pic_order_cnt_lsb;
+      } else {
+        pic->pic_order_cnt_msb = prev_pic_order_cnt_msb;
+      }
+
+      if (pic->field != H264Picture::FIELD_BOTTOM) {
+        pic->top_field_order_cnt =
+            pic->pic_order_cnt_msb + pic->pic_order_cnt_lsb;
+      }
+
+      if (pic->field != H264Picture::FIELD_TOP) {
+        if (pic->field == H264Picture::FIELD_NONE) {
+          pic->bottom_field_order_cnt =
+              pic->top_field_order_cnt + pic->delta_pic_order_cnt_bottom;
+        } else {
+          pic->bottom_field_order_cnt =
+              pic->pic_order_cnt_msb + pic->pic_order_cnt_lsb;
+        }
+      }
+      break;
+    }
+
+    case 1: {
+      // See spec 8.2.1.2.
+      if (prev_has_memmgmnt5_)
+        prev_frame_num_offset_ = 0;
+
+      if (pic->idr)
+        pic->frame_num_offset = 0;
+      else if (prev_frame_num_ > pic->frame_num)
+        pic->frame_num_offset = prev_frame_num_offset_ + max_frame_num_;
+      else
+        pic->frame_num_offset = prev_frame_num_offset_;
+
+      int abs_frame_num = 0;
+      if (sps->num_ref_frames_in_pic_order_cnt_cycle != 0)
+        abs_frame_num = pic->frame_num_offset + pic->frame_num;
+      else
+        abs_frame_num = 0;
+
+      if (pic->nal_ref_idc == 0 && abs_frame_num > 0)
+        --abs_frame_num;
+
+      int expected_pic_order_cnt = 0;
+      if (abs_frame_num > 0) {
+        if (sps->num_ref_frames_in_pic_order_cnt_cycle == 0) {
+          DVLOG(1) << "Invalid num_ref_frames_in_pic_order_cnt_cycle "
+                   << "in stream";
+          return false;
+        }
+
+        int pic_order_cnt_cycle_cnt =
+            (abs_frame_num - 1) / sps->num_ref_frames_in_pic_order_cnt_cycle;
+        int frame_num_in_pic_order_cnt_cycle =
+            (abs_frame_num - 1) % sps->num_ref_frames_in_pic_order_cnt_cycle;
+
+        expected_pic_order_cnt = pic_order_cnt_cycle_cnt *
+                                 sps->expected_delta_per_pic_order_cnt_cycle;
+        // frame_num_in_pic_order_cnt_cycle is verified < 255 in parser
+        for (int i = 0; i <= frame_num_in_pic_order_cnt_cycle; ++i)
+          expected_pic_order_cnt += sps->offset_for_ref_frame[i];
+      }
+
+      if (!pic->nal_ref_idc)
+        expected_pic_order_cnt += sps->offset_for_non_ref_pic;
+
+      if (pic->field == H264Picture::FIELD_NONE) {
+        pic->top_field_order_cnt =
+            expected_pic_order_cnt + pic->delta_pic_order_cnt0;
+        pic->bottom_field_order_cnt = pic->top_field_order_cnt +
+                                      sps->offset_for_top_to_bottom_field +
+                                      pic->delta_pic_order_cnt1;
+      } else if (pic->field != H264Picture::FIELD_BOTTOM) {
+        pic->top_field_order_cnt =
+            expected_pic_order_cnt + pic->delta_pic_order_cnt0;
+      } else {
+        pic->bottom_field_order_cnt = expected_pic_order_cnt +
+                                      sps->offset_for_top_to_bottom_field +
+                                      pic->delta_pic_order_cnt0;
+      }
+      break;
+    }
+
+    case 2: {
+      // See spec 8.2.1.3.
+      if (prev_has_memmgmnt5_)
+        prev_frame_num_offset_ = 0;
+
+      if (pic->idr)
+        pic->frame_num_offset = 0;
+      else if (prev_frame_num_ > pic->frame_num)
+        pic->frame_num_offset = prev_frame_num_offset_ + max_frame_num_;
+      else
+        pic->frame_num_offset = prev_frame_num_offset_;
+
+      int temp_pic_order_cnt;
+      if (pic->idr) {
+        temp_pic_order_cnt = 0;
+      } else if (!pic->nal_ref_idc) {
+        temp_pic_order_cnt = 2 * (pic->frame_num_offset + pic->frame_num) - 1;
+      } else {
+        temp_pic_order_cnt = 2 * (pic->frame_num_offset + pic->frame_num);
+      }
+
+      if (pic->field == H264Picture::FIELD_NONE) {
+        pic->top_field_order_cnt = temp_pic_order_cnt;
+        pic->bottom_field_order_cnt = temp_pic_order_cnt;
+      } else if (pic->field == H264Picture::FIELD_BOTTOM) {
+        pic->bottom_field_order_cnt = temp_pic_order_cnt;
+      } else {
+        pic->top_field_order_cnt = temp_pic_order_cnt;
+      }
+      break;
+    }
+
+    default:
+      DVLOG(1) << "Invalid pic_order_cnt_type: " << sps->pic_order_cnt_type;
+      return false;
+  }
+
+  switch (pic->field) {
+    case H264Picture::FIELD_NONE:
+      pic->pic_order_cnt =
+          std::min(pic->top_field_order_cnt, pic->bottom_field_order_cnt);
+      break;
+    case H264Picture::FIELD_TOP:
+      pic->pic_order_cnt = pic->top_field_order_cnt;
+      break;
+    case H264Picture::FIELD_BOTTOM:
+      pic->pic_order_cnt = pic->bottom_field_order_cnt;
+      break;
+  }
+
+  return true;
+}
+
+void H264Decoder::UpdatePicNums(int frame_num) {
+  for (auto& pic : dpb_) {
+    if (!pic->ref)
+      continue;
+
+    // 8.2.4.1. Assumes non-interlaced stream.
+    DCHECK_EQ(pic->field, H264Picture::FIELD_NONE);
+    if (pic->long_term) {
+      pic->long_term_pic_num = pic->long_term_frame_idx;
+    } else {
+      if (pic->frame_num > frame_num)
+        pic->frame_num_wrap = pic->frame_num - max_frame_num_;
+      else
+        pic->frame_num_wrap = pic->frame_num;
+
+      pic->pic_num = pic->frame_num_wrap;
+    }
+  }
+}
+
+struct PicNumDescCompare {
+  bool operator()(const scoped_refptr<H264Picture>& a,
+                  const scoped_refptr<H264Picture>& b) const {
+    return a->pic_num > b->pic_num;
+  }
+};
+
+struct LongTermPicNumAscCompare {
+  bool operator()(const scoped_refptr<H264Picture>& a,
+                  const scoped_refptr<H264Picture>& b) const {
+    return a->long_term_pic_num < b->long_term_pic_num;
+  }
+};
+
+void H264Decoder::ConstructReferencePicListsP(
+    const H264SliceHeader* slice_hdr) {
+  // RefPicList0 (8.2.4.2.1) [[1] [2]], where:
+  // [1] shortterm ref pics sorted by descending pic_num,
+  // [2] longterm ref pics by ascending long_term_pic_num.
+  ref_pic_list_p0_.clear();
+
+  // First get the short ref pics...
+  dpb_.GetShortTermRefPicsAppending(&ref_pic_list_p0_);
+  size_t num_short_refs = ref_pic_list_p0_.size();
+
+  // and sort them to get [1].
+  std::sort(ref_pic_list_p0_.begin(), ref_pic_list_p0_.end(),
+            PicNumDescCompare());
+
+  // Now get long term pics and sort them by long_term_pic_num to get [2].
+  dpb_.GetLongTermRefPicsAppending(&ref_pic_list_p0_);
+  std::sort(ref_pic_list_p0_.begin() + num_short_refs, ref_pic_list_p0_.end(),
+            LongTermPicNumAscCompare());
+}
+
+struct POCAscCompare {
+  bool operator()(const scoped_refptr<H264Picture>& a,
+                  const scoped_refptr<H264Picture>& b) const {
+    return a->pic_order_cnt < b->pic_order_cnt;
+  }
+};
+
+struct POCDescCompare {
+  bool operator()(const scoped_refptr<H264Picture>& a,
+                  const scoped_refptr<H264Picture>& b) const {
+    return a->pic_order_cnt > b->pic_order_cnt;
+  }
+};
+
+void H264Decoder::ConstructReferencePicListsB(
+    const H264SliceHeader* slice_hdr) {
+  // RefPicList0 (8.2.4.2.3) [[1] [2] [3]], where:
+  // [1] shortterm ref pics with POC < curr_pic's POC sorted by descending POC,
+  // [2] shortterm ref pics with POC > curr_pic's POC by ascending POC,
+  // [3] longterm ref pics by ascending long_term_pic_num.
+  ref_pic_list_b0_.clear();
+  ref_pic_list_b1_.clear();
+  dpb_.GetShortTermRefPicsAppending(&ref_pic_list_b0_);
+  size_t num_short_refs = ref_pic_list_b0_.size();
+
+  // First sort ascending, this will put [1] in right place and finish [2].
+  std::sort(ref_pic_list_b0_.begin(), ref_pic_list_b0_.end(), POCAscCompare());
+
+  // Find first with POC > curr_pic's POC to get first element in [2]...
+  H264Picture::Vector::iterator iter;
+  iter = std::upper_bound(ref_pic_list_b0_.begin(), ref_pic_list_b0_.end(),
+                          curr_pic_.get(), POCAscCompare());
+
+  // and sort [1] descending, thus finishing sequence [1] [2].
+  std::sort(ref_pic_list_b0_.begin(), iter, POCDescCompare());
+
+  // Now add [3] and sort by ascending long_term_pic_num.
+  dpb_.GetLongTermRefPicsAppending(&ref_pic_list_b0_);
+  std::sort(ref_pic_list_b0_.begin() + num_short_refs, ref_pic_list_b0_.end(),
+            LongTermPicNumAscCompare());
+
+  // RefPicList1 (8.2.4.2.4) [[1] [2] [3]], where:
+  // [1] shortterm ref pics with POC > curr_pic's POC sorted by ascending POC,
+  // [2] shortterm ref pics with POC < curr_pic's POC by descending POC,
+  // [3] longterm ref pics by ascending long_term_pic_num.
+
+  dpb_.GetShortTermRefPicsAppending(&ref_pic_list_b1_);
+  num_short_refs = ref_pic_list_b1_.size();
+
+  // First sort by descending POC.
+  std::sort(ref_pic_list_b1_.begin(), ref_pic_list_b1_.end(), POCDescCompare());
+
+  // Find first with POC < curr_pic's POC to get first element in [2]...
+  iter = std::upper_bound(ref_pic_list_b1_.begin(), ref_pic_list_b1_.end(),
+                          curr_pic_.get(), POCDescCompare());
+
+  // and sort [1] ascending.
+  std::sort(ref_pic_list_b1_.begin(), iter, POCAscCompare());
+
+  // Now add [3] and sort by ascending long_term_pic_num
+  dpb_.GetShortTermRefPicsAppending(&ref_pic_list_b1_);
+  std::sort(ref_pic_list_b1_.begin() + num_short_refs, ref_pic_list_b1_.end(),
+            LongTermPicNumAscCompare());
+
+  // If lists identical, swap first two entries in RefPicList1 (spec 8.2.4.2.3)
+  if (ref_pic_list_b1_.size() > 1 &&
+      std::equal(ref_pic_list_b0_.begin(), ref_pic_list_b0_.end(),
+                 ref_pic_list_b1_.begin()))
+    std::swap(ref_pic_list_b1_[0], ref_pic_list_b1_[1]);
+}
+
+// See 8.2.4
+int H264Decoder::PicNumF(const scoped_refptr<H264Picture>& pic) {
+  if (!pic)
+    return -1;
+
+  if (!pic->long_term)
+    return pic->pic_num;
+  else
+    return max_pic_num_;
+}
+
+// See 8.2.4
+int H264Decoder::LongTermPicNumF(const scoped_refptr<H264Picture>& pic) {
+  if (pic->ref && pic->long_term)
+    return pic->long_term_pic_num;
+  else
+    return 2 * (max_long_term_frame_idx_ + 1);
+}
+
+// Shift elements on the |v| starting from |from| to |to|, inclusive,
+// one position to the right and insert pic at |from|.
+static void ShiftRightAndInsert(H264Picture::Vector* v,
+                                int from,
+                                int to,
+                                const scoped_refptr<H264Picture>& pic) {
+  // Security checks, do not disable in Debug mode.
+  CHECK(from <= to);
+  CHECK(to <= std::numeric_limits<int>::max() - 2);
+  // Additional checks. Debug mode ok.
+  DCHECK(v);
+  DCHECK(pic);
+  DCHECK((to + 1 == static_cast<int>(v->size())) ||
+         (to + 2 == static_cast<int>(v->size())));
+
+  v->resize(to + 2);
+
+  for (int i = to + 1; i > from; --i)
+    (*v)[i] = (*v)[i - 1];
+
+  (*v)[from] = pic;
+}
+
+bool H264Decoder::ModifyReferencePicList(const H264SliceHeader* slice_hdr,
+                                         int list,
+                                         H264Picture::Vector* ref_pic_listx) {
+  bool ref_pic_list_modification_flag_lX;
+  int num_ref_idx_lX_active_minus1;
+  const H264ModificationOfPicNum* list_mod;
+
+  // This can process either ref_pic_list0 or ref_pic_list1, depending on
+  // the list argument. Set up pointers to proper list to be processed here.
+  if (list == 0) {
+    ref_pic_list_modification_flag_lX =
+        slice_hdr->ref_pic_list_modification_flag_l0;
+    num_ref_idx_lX_active_minus1 = slice_hdr->num_ref_idx_l0_active_minus1;
+    list_mod = slice_hdr->ref_list_l0_modifications;
+  } else {
+    ref_pic_list_modification_flag_lX =
+        slice_hdr->ref_pic_list_modification_flag_l1;
+    num_ref_idx_lX_active_minus1 = slice_hdr->num_ref_idx_l1_active_minus1;
+    list_mod = slice_hdr->ref_list_l1_modifications;
+  }
+
+  // Resize the list to the size requested in the slice header.
+  // Note that per 8.2.4.2 it's possible for num_ref_idx_lX_active_minus1 to
+  // indicate there should be more ref pics on list than we constructed.
+  // Those superfluous ones should be treated as non-reference and will be
+  // initialized to nullptr, which must be handled by clients.
+  DCHECK_GE(num_ref_idx_lX_active_minus1, 0);
+  ref_pic_listx->resize(num_ref_idx_lX_active_minus1 + 1);
+
+  if (!ref_pic_list_modification_flag_lX)
+    return true;
+
+  // Spec 8.2.4.3:
+  // Reorder pictures on the list in a way specified in the stream.
+  int pic_num_lx_pred = curr_pic_->pic_num;
+  int ref_idx_lx = 0;
+  int pic_num_lx_no_wrap;
+  int pic_num_lx;
+  bool done = false;
+  scoped_refptr<H264Picture> pic;
+  for (int i = 0; i < H264SliceHeader::kRefListModSize && !done; ++i) {
+    switch (list_mod->modification_of_pic_nums_idc) {
+      case 0:
+      case 1:
+        // Modify short reference picture position.
+        if (list_mod->modification_of_pic_nums_idc == 0) {
+          // Subtract given value from predicted PicNum.
+          pic_num_lx_no_wrap =
+              pic_num_lx_pred -
+              (static_cast<int>(list_mod->abs_diff_pic_num_minus1) + 1);
+          // Wrap around max_pic_num_ if it becomes < 0 as result
+          // of subtraction.
+          if (pic_num_lx_no_wrap < 0)
+            pic_num_lx_no_wrap += max_pic_num_;
+        } else {
+          // Add given value to predicted PicNum.
+          pic_num_lx_no_wrap =
+              pic_num_lx_pred +
+              (static_cast<int>(list_mod->abs_diff_pic_num_minus1) + 1);
+          // Wrap around max_pic_num_ if it becomes >= max_pic_num_ as result
+          // of the addition.
+          if (pic_num_lx_no_wrap >= max_pic_num_)
+            pic_num_lx_no_wrap -= max_pic_num_;
+        }
+
+        // For use in next iteration.
+        pic_num_lx_pred = pic_num_lx_no_wrap;
+
+        if (pic_num_lx_no_wrap > curr_pic_->pic_num)
+          pic_num_lx = pic_num_lx_no_wrap - max_pic_num_;
+        else
+          pic_num_lx = pic_num_lx_no_wrap;
+
+        DCHECK_LT(num_ref_idx_lX_active_minus1 + 1,
+                  H264SliceHeader::kRefListModSize);
+        pic = dpb_.GetShortRefPicByPicNum(pic_num_lx);
+        if (!pic) {
+          DVLOG(1) << "Malformed stream, no pic num " << pic_num_lx;
+          return false;
+        }
+        ShiftRightAndInsert(ref_pic_listx, ref_idx_lx,
+                            num_ref_idx_lX_active_minus1, pic);
+        ref_idx_lx++;
+
+        for (int src = ref_idx_lx, dst = ref_idx_lx;
+             src <= num_ref_idx_lX_active_minus1 + 1; ++src) {
+          if (PicNumF((*ref_pic_listx)[src]) != pic_num_lx)
+            (*ref_pic_listx)[dst++] = (*ref_pic_listx)[src];
+        }
+        break;
+
+      case 2:
+        // Modify long term reference picture position.
+        DCHECK_LT(num_ref_idx_lX_active_minus1 + 1,
+                  H264SliceHeader::kRefListModSize);
+        pic = dpb_.GetLongRefPicByLongTermPicNum(list_mod->long_term_pic_num);
+        if (!pic) {
+          DVLOG(1) << "Malformed stream, no pic num "
+                   << list_mod->long_term_pic_num;
+          return false;
+        }
+        ShiftRightAndInsert(ref_pic_listx, ref_idx_lx,
+                            num_ref_idx_lX_active_minus1, pic);
+        ref_idx_lx++;
+
+        for (int src = ref_idx_lx, dst = ref_idx_lx;
+             src <= num_ref_idx_lX_active_minus1 + 1; ++src) {
+          if (LongTermPicNumF((*ref_pic_listx)[src]) !=
+              static_cast<int>(list_mod->long_term_pic_num))
+            (*ref_pic_listx)[dst++] = (*ref_pic_listx)[src];
+        }
+        break;
+
+      case 3:
+        // End of modification list.
+        done = true;
+        break;
+
+      default:
+        // May be recoverable.
+        DVLOG(1) << "Invalid modification_of_pic_nums_idc="
+                 << list_mod->modification_of_pic_nums_idc
+                 << " in position " << i;
+        break;
+    }
+
+    ++list_mod;
+  }
+
+  // Per NOTE 2 in 8.2.4.3.2, the ref_pic_listx size in the above loop is
+  // temporarily made one element longer than the required final list.
+  // Resize the list back to its required size.
+  ref_pic_listx->resize(num_ref_idx_lX_active_minus1 + 1);
+
+  return true;
+}
+
+void H264Decoder::OutputPic(scoped_refptr<H264Picture> pic) {
+  DCHECK(!pic->outputted);
+  pic->outputted = true;
+
+  if (pic->nonexisting) {
+    DVLOG(4) << "Skipping output, non-existing frame_num: " << pic->frame_num;
+    return;
+  }
+
+  DVLOG_IF(1, pic->pic_order_cnt < last_output_poc_)
+      << "Outputting out of order, likely a broken stream: "
+      << last_output_poc_ << " -> " << pic->pic_order_cnt;
+  last_output_poc_ = pic->pic_order_cnt;
+
+  DVLOG(4) << "Posting output task for POC: " << pic->pic_order_cnt;
+  accelerator_->OutputPicture(pic);
+}
+
+void H264Decoder::ClearDPB() {
+  // Clear DPB contents, marking the pictures as unused first.
+  dpb_.Clear();
+  last_output_poc_ = std::numeric_limits<int>::min();
+}
+
+bool H264Decoder::OutputAllRemainingPics() {
+  // Output all pictures that are waiting to be outputted.
+  FinishPrevFrameIfPresent();
+  H264Picture::Vector to_output;
+  dpb_.GetNotOutputtedPicsAppending(&to_output);
+  // Sort them by ascending POC to output in order.
+  std::sort(to_output.begin(), to_output.end(), POCAscCompare());
+
+  for (auto& pic : to_output)
+    OutputPic(pic);
+
+  return true;
+}
+
+bool H264Decoder::Flush() {
+  DVLOG(2) << "Decoder flush";
+
+  if (!OutputAllRemainingPics())
+    return false;
+
+  ClearDPB();
+  DVLOG(2) << "Decoder flush finished";
+  return true;
+}
+
+bool H264Decoder::StartNewFrame(const H264SliceHeader* slice_hdr) {
+  // TODO posciak: add handling of max_num_ref_frames per spec.
+  CHECK(curr_pic_.get());
+  DCHECK(slice_hdr);
+
+  curr_pps_id_ = slice_hdr->pic_parameter_set_id;
+  const H264PPS* pps = parser_.GetPPS(curr_pps_id_);
+  if (!pps)
+    return false;
+
+  curr_sps_id_ = pps->seq_parameter_set_id;
+  const H264SPS* sps = parser_.GetSPS(curr_sps_id_);
+  if (!sps)
+    return false;
+
+  max_frame_num_ = 1 << (sps->log2_max_frame_num_minus4 + 4);
+  int frame_num = slice_hdr->frame_num;
+  if (slice_hdr->idr_pic_flag)
+    prev_ref_frame_num_ = 0;
+
+  // 7.4.3
+  if (frame_num != prev_ref_frame_num_ &&
+      frame_num != (prev_ref_frame_num_ + 1) % max_frame_num_) {
+    if (!HandleFrameNumGap(frame_num))
+      return false;
+  }
+
+  if (!InitCurrPicture(slice_hdr))
+    return false;
+
+  UpdatePicNums(frame_num);
+  PrepareRefPicLists(slice_hdr);
+
+  if (!accelerator_->SubmitFrameMetadata(sps, pps, dpb_, ref_pic_list_p0_,
+                                         ref_pic_list_b0_, ref_pic_list_b1_,
+                                         curr_pic_.get()))
+    return false;
+
+  return true;
+}
+
+bool H264Decoder::HandleMemoryManagementOps(scoped_refptr<H264Picture> pic) {
+  // 8.2.5.4
+  for (size_t i = 0; i < arraysize(pic->ref_pic_marking); ++i) {
+    // Code below does not support interlaced stream (per-field pictures).
+    H264DecRefPicMarking* ref_pic_marking = &pic->ref_pic_marking[i];
+    scoped_refptr<H264Picture> to_mark;
+    int pic_num_x;
+
+    switch (ref_pic_marking->memory_mgmnt_control_operation) {
+      case 0:
+        // Normal end of operations' specification.
+        return true;
+
+      case 1:
+        // Mark a short term reference picture as unused so it can be removed
+        // if outputted.
+        pic_num_x =
+            pic->pic_num - (ref_pic_marking->difference_of_pic_nums_minus1 + 1);
+        to_mark = dpb_.GetShortRefPicByPicNum(pic_num_x);
+        if (to_mark) {
+          to_mark->ref = false;
+        } else {
+          DVLOG(1) << "Invalid short ref pic num to unmark";
+          return false;
+        }
+        break;
+
+      case 2:
+        // Mark a long term reference picture as unused so it can be removed
+        // if outputted.
+        to_mark = dpb_.GetLongRefPicByLongTermPicNum(
+            ref_pic_marking->long_term_pic_num);
+        if (to_mark) {
+          to_mark->ref = false;
+        } else {
+          DVLOG(1) << "Invalid long term ref pic num to unmark";
+          return false;
+        }
+        break;
+
+      case 3:
+        // Mark a short term reference picture as long term reference.
+        pic_num_x =
+            pic->pic_num - (ref_pic_marking->difference_of_pic_nums_minus1 + 1);
+        to_mark = dpb_.GetShortRefPicByPicNum(pic_num_x);
+        if (to_mark) {
+          DCHECK(to_mark->ref && !to_mark->long_term);
+          to_mark->long_term = true;
+          to_mark->long_term_frame_idx = ref_pic_marking->long_term_frame_idx;
+        } else {
+          DVLOG(1) << "Invalid short term ref pic num to mark as long ref";
+          return false;
+        }
+        break;
+
+      case 4: {
+        // Unmark all reference pictures with long_term_frame_idx over new max.
+        max_long_term_frame_idx_ =
+            ref_pic_marking->max_long_term_frame_idx_plus1 - 1;
+        H264Picture::Vector long_terms;
+        dpb_.GetLongTermRefPicsAppending(&long_terms);
+        for (size_t i = 0; i < long_terms.size(); ++i) {
+          scoped_refptr<H264Picture>& long_term_pic = long_terms[i];
+          DCHECK(long_term_pic->ref && long_term_pic->long_term);
+          // Ok to cast, max_long_term_frame_idx is much smaller than 16bit.
+          if (long_term_pic->long_term_frame_idx >
+              static_cast<int>(max_long_term_frame_idx_))
+            long_term_pic->ref = false;
+        }
+        break;
+      }
+
+      case 5:
+        // Unmark all reference pictures.
+        dpb_.MarkAllUnusedForRef();
+        max_long_term_frame_idx_ = -1;
+        pic->mem_mgmt_5 = true;
+        break;
+
+      case 6: {
+        // Replace long term reference pictures with current picture.
+        // First unmark if any existing with this long_term_frame_idx...
+        H264Picture::Vector long_terms;
+        dpb_.GetLongTermRefPicsAppending(&long_terms);
+        for (size_t i = 0; i < long_terms.size(); ++i) {
+          scoped_refptr<H264Picture>& long_term_pic = long_terms[i];
+          DCHECK(long_term_pic->ref && long_term_pic->long_term);
+          // Ok to cast, long_term_frame_idx is much smaller than 16bit.
+          if (long_term_pic->long_term_frame_idx ==
+              static_cast<int>(ref_pic_marking->long_term_frame_idx))
+            long_term_pic->ref = false;
+        }
+
+        // and mark the current one instead.
+        pic->ref = true;
+        pic->long_term = true;
+        pic->long_term_frame_idx = ref_pic_marking->long_term_frame_idx;
+        break;
+      }
+
+      default:
+        // Would indicate a bug in parser.
+        NOTREACHED();
+    }
+  }
+
+  return true;
+}
+
+// This method ensures that DPB does not overflow, either by removing
+// reference pictures as specified in the stream, or using a sliding window
+// procedure to remove the oldest one.
+// It also performs marking and unmarking pictures as reference.
+// See spac 8.2.5.1.
+bool H264Decoder::ReferencePictureMarking(scoped_refptr<H264Picture> pic) {
+  // If the current picture is an IDR, all reference pictures are unmarked.
+  if (pic->idr) {
+    dpb_.MarkAllUnusedForRef();
+
+    if (pic->long_term_reference_flag) {
+      pic->long_term = true;
+      pic->long_term_frame_idx = 0;
+      max_long_term_frame_idx_ = 0;
+    } else {
+      pic->long_term = false;
+      max_long_term_frame_idx_ = -1;
+    }
+
+    return true;
+  }
+
+  // Not an IDR. If the stream contains instructions on how to discard pictures
+  // from DPB and how to mark/unmark existing reference pictures, do so.
+  // Otherwise, fall back to default sliding window process.
+  if (pic->adaptive_ref_pic_marking_mode_flag) {
+    DCHECK(!pic->nonexisting);
+    return HandleMemoryManagementOps(pic);
+  } else {
+    return SlidingWindowPictureMarking();
+  }
+}
+
+bool H264Decoder::SlidingWindowPictureMarking() {
+  const H264SPS* sps = parser_.GetSPS(curr_sps_id_);
+  if (!sps)
+    return false;
+
+  // 8.2.5.3. Ensure the DPB doesn't overflow by discarding the oldest picture.
+  int num_ref_pics = dpb_.CountRefPics();
+  DCHECK_LE(num_ref_pics, std::max<int>(sps->max_num_ref_frames, 1));
+  if (num_ref_pics == std::max<int>(sps->max_num_ref_frames, 1)) {
+    // Max number of reference pics reached, need to remove one of the short
+    // term ones. Find smallest frame_num_wrap short reference picture and mark
+    // it as unused.
+    scoped_refptr<H264Picture> to_unmark =
+        dpb_.GetLowestFrameNumWrapShortRefPic();
+    if (!to_unmark) {
+      DVLOG(1) << "Couldn't find a short ref picture to unmark";
+      return false;
+    }
+
+    to_unmark->ref = false;
+  }
+
+  return true;
+}
+
+bool H264Decoder::FinishPicture(scoped_refptr<H264Picture> pic) {
+  // Finish processing the picture.
+  // Start by storing previous picture data for later use.
+  if (pic->ref) {
+    ReferencePictureMarking(pic);
+    prev_ref_has_memmgmnt5_ = pic->mem_mgmt_5;
+    prev_ref_top_field_order_cnt_ = pic->top_field_order_cnt;
+    prev_ref_pic_order_cnt_msb_ = pic->pic_order_cnt_msb;
+    prev_ref_pic_order_cnt_lsb_ = pic->pic_order_cnt_lsb;
+    prev_ref_field_ = pic->field;
+    prev_ref_frame_num_ = pic->frame_num;
+  }
+  prev_frame_num_ = pic->frame_num;
+  prev_has_memmgmnt5_ = pic->mem_mgmt_5;
+  prev_frame_num_offset_ = pic->frame_num_offset;
+
+  // Remove unused (for reference or later output) pictures from DPB, marking
+  // them as such.
+  dpb_.DeleteUnused();
+
+  DVLOG(4) << "Finishing picture frame_num: " << pic->frame_num
+           << ", entries in DPB: " << dpb_.size();
+
+  // The ownership of pic will either be transferred to DPB - if the picture is
+  // still needed (for output and/or reference) - or we will release it
+  // immediately if we manage to output it here and won't have to store it for
+  // future reference.
+
+  // Get all pictures that haven't been outputted yet.
+  H264Picture::Vector not_outputted;
+  dpb_.GetNotOutputtedPicsAppending(&not_outputted);
+  // Include the one we've just decoded.
+  not_outputted.push_back(pic);
+
+  // Sort in output order.
+  std::sort(not_outputted.begin(), not_outputted.end(), POCAscCompare());
+
+  // Try to output as many pictures as we can. A picture can be output,
+  // if the number of decoded and not yet outputted pictures that would remain
+  // in DPB afterwards would at least be equal to max_num_reorder_frames.
+  // If the outputted picture is not a reference picture, it doesn't have
+  // to remain in the DPB and can be removed.
+  H264Picture::Vector::iterator output_candidate = not_outputted.begin();
+  size_t num_remaining = not_outputted.size();
+  while (num_remaining > max_num_reorder_frames_ ||
+         // If the condition below is used, this is an invalid stream. We should
+         // not be forced to output beyond max_num_reorder_frames in order to
+         // make room in DPB to store the current picture (if we need to do so).
+         // However, if this happens, ignore max_num_reorder_frames and try
+         // to output more. This may cause out-of-order output, but is not
+         // fatal, and better than failing instead.
+         ((dpb_.IsFull() && (!pic->outputted || pic->ref)) && num_remaining)) {
+    DVLOG_IF(1, num_remaining <= max_num_reorder_frames_)
+        << "Invalid stream: max_num_reorder_frames not preserved";
+
+    OutputPic(*output_candidate);
+
+    if (!(*output_candidate)->ref) {
+      // Current picture hasn't been inserted into DPB yet, so don't remove it
+      // if we managed to output it immediately.
+      int outputted_poc = (*output_candidate)->pic_order_cnt;
+      if (outputted_poc != pic->pic_order_cnt)
+        dpb_.DeleteByPOC(outputted_poc);
+    }
+
+    ++output_candidate;
+    --num_remaining;
+  }
+
+  // If we haven't managed to output the picture that we just decoded, or if
+  // it's a reference picture, we have to store it in DPB.
+  if (!pic->outputted || pic->ref) {
+    if (dpb_.IsFull()) {
+      // If we haven't managed to output anything to free up space in DPB
+      // to store this picture, it's an error in the stream.
+      DVLOG(1) << "Could not free up space in DPB!";
+      return false;
+    }
+
+    dpb_.StorePic(pic);
+  }
+
+  return true;
+}
+
+static int LevelToMaxDpbMbs(int level) {
+  // See table A-1 in spec.
+  switch (level) {
+    case 10:
+      return 396;
+    case 11:
+      return 900;
+    case 12:  //  fallthrough
+    case 13:  //  fallthrough
+    case 20:
+      return 2376;
+    case 21:
+      return 4752;
+    case 22:  //  fallthrough
+    case 30:
+      return 8100;
+    case 31:
+      return 18000;
+    case 32:
+      return 20480;
+    case 40:  //  fallthrough
+    case 41:
+      return 32768;
+    case 42:
+      return 34816;
+    case 50:
+      return 110400;
+    case 51:  //  fallthrough
+    case 52:
+      return 184320;
+    default:
+      DVLOG(1) << "Invalid codec level (" << level << ")";
+      return 0;
+  }
+}
+
+bool H264Decoder::UpdateMaxNumReorderFrames(const H264SPS* sps) {
+  if (sps->vui_parameters_present_flag && sps->bitstream_restriction_flag) {
+    max_num_reorder_frames_ =
+        base::checked_cast<size_t>(sps->max_num_reorder_frames);
+    if (max_num_reorder_frames_ > dpb_.max_num_pics()) {
+      DVLOG(1)
+          << "max_num_reorder_frames present, but larger than MaxDpbFrames ("
+          << max_num_reorder_frames_ << " > " << dpb_.max_num_pics() << ")";
+      max_num_reorder_frames_ = 0;
+      return false;
+    }
+    return true;
+  }
+
+  // max_num_reorder_frames not present, infer from profile/constraints
+  // (see VUI semantics in spec).
+  if (sps->constraint_set3_flag) {
+    switch (sps->profile_idc) {
+      case 44:
+      case 86:
+      case 100:
+      case 110:
+      case 122:
+      case 244:
+        max_num_reorder_frames_ = 0;
+        break;
+      default:
+        max_num_reorder_frames_ = dpb_.max_num_pics();
+        break;
+    }
+  } else {
+    max_num_reorder_frames_ = dpb_.max_num_pics();
+  }
+
+  return true;
+}
+
+bool H264Decoder::ProcessSPS(int sps_id, bool* need_new_buffers) {
+  DVLOG(4) << "Processing SPS id:" << sps_id;
+
+  const H264SPS* sps = parser_.GetSPS(sps_id);
+  if (!sps)
+    return false;
+
+  *need_new_buffers = false;
+
+  if (sps->frame_mbs_only_flag == 0) {
+    DVLOG(1) << "frame_mbs_only_flag != 1 not supported";
+    return false;
+  }
+
+  Size new_pic_size = sps->GetCodedSize().value_or(Size());
+  if (new_pic_size.IsEmpty()) {
+    DVLOG(1) << "Invalid picture size";
+    return false;
+  }
+
+  int width_mb = new_pic_size.width() / 16;
+  int height_mb = new_pic_size.height() / 16;
+
+  // Verify that the values are not too large before multiplying.
+  if (std::numeric_limits<int>::max() / width_mb < height_mb) {
+    DVLOG(1) << "Picture size is too big: " << new_pic_size.ToString();
+    return false;
+  }
+
+  int level = sps->level_idc;
+  int max_dpb_mbs = LevelToMaxDpbMbs(level);
+  if (max_dpb_mbs == 0)
+    return false;
+
+  size_t max_dpb_size = std::min(max_dpb_mbs / (width_mb * height_mb),
+                                 static_cast<int>(H264DPB::kDPBMaxSize));
+  if (max_dpb_size == 0) {
+    DVLOG(1) << "Invalid DPB Size";
+    return false;
+  }
+
+  if ((pic_size_ != new_pic_size) || (dpb_.max_num_pics() != max_dpb_size)) {
+    if (!Flush())
+      return false;
+    DVLOG(1) << "Codec level: " << level << ", DPB size: " << max_dpb_size
+             << ", Picture size: " << new_pic_size.ToString();
+    *need_new_buffers = true;
+    pic_size_ = new_pic_size;
+    dpb_.set_max_num_pics(max_dpb_size);
+  }
+
+  if (!UpdateMaxNumReorderFrames(sps))
+    return false;
+  DVLOG(1) << "max_num_reorder_frames: " << max_num_reorder_frames_;
+
+  return true;
+}
+
+bool H264Decoder::FinishPrevFrameIfPresent() {
+  // If we already have a frame waiting to be decoded, decode it and finish.
+  if (curr_pic_) {
+    if (!DecodePicture())
+      return false;
+
+    scoped_refptr<H264Picture> pic = curr_pic_;
+    curr_pic_ = nullptr;
+    return FinishPicture(pic);
+  }
+
+  return true;
+}
+
+bool H264Decoder::HandleFrameNumGap(int frame_num) {
+  const H264SPS* sps = parser_.GetSPS(curr_sps_id_);
+  if (!sps)
+    return false;
+
+  if (!sps->gaps_in_frame_num_value_allowed_flag) {
+    DVLOG(1) << "Invalid frame_num: " << frame_num;
+    return false;
+  }
+
+  DVLOG(2) << "Handling frame_num gap: " << prev_ref_frame_num_ << "->"
+           << frame_num;
+
+  // 7.4.3/7-23
+  int unused_short_term_frame_num = (prev_ref_frame_num_ + 1) % max_frame_num_;
+  while (unused_short_term_frame_num != frame_num) {
+    scoped_refptr<H264Picture> pic = new H264Picture();
+    if (!InitNonexistingPicture(pic, unused_short_term_frame_num))
+      return false;
+
+    UpdatePicNums(unused_short_term_frame_num);
+
+    if (!FinishPicture(pic))
+      return false;
+
+    unused_short_term_frame_num++;
+    unused_short_term_frame_num %= max_frame_num_;
+  }
+
+  return true;
+}
+
+bool H264Decoder::IsNewPrimaryCodedPicture(
+    const H264SliceHeader* slice_hdr) const {
+  if (!curr_pic_)
+    return true;
+
+  // 7.4.1.2.4, assumes non-interlaced.
+  if (slice_hdr->frame_num != curr_pic_->frame_num ||
+      slice_hdr->pic_parameter_set_id != curr_pps_id_ ||
+      slice_hdr->nal_ref_idc != curr_pic_->nal_ref_idc ||
+      slice_hdr->idr_pic_flag != curr_pic_->idr ||
+      (slice_hdr->idr_pic_flag &&
+       (slice_hdr->idr_pic_id != curr_pic_->idr_pic_id ||
+        // If we have two consecutive IDR slices, and the second one has
+        // first_mb_in_slice == 0, treat it as a new picture.
+        // Per spec, idr_pic_id should not be equal in this case (and we should
+        // have hit the condition above instead, see spec 7.4.3 on idr_pic_id),
+        // but some encoders neglect changing idr_pic_id for two consecutive
+        // IDRs. Work around this by checking if the next slice contains the
+        // zeroth macroblock, i.e. data that belongs to the next picture.
+        slice_hdr->first_mb_in_slice == 0)))
+    return true;
+
+  const H264SPS* sps = parser_.GetSPS(curr_sps_id_);
+  if (!sps)
+    return false;
+
+  if (sps->pic_order_cnt_type == curr_pic_->pic_order_cnt_type) {
+    if (curr_pic_->pic_order_cnt_type == 0) {
+      if (slice_hdr->pic_order_cnt_lsb != curr_pic_->pic_order_cnt_lsb ||
+          slice_hdr->delta_pic_order_cnt_bottom !=
+              curr_pic_->delta_pic_order_cnt_bottom)
+        return true;
+    } else if (curr_pic_->pic_order_cnt_type == 1) {
+      if (slice_hdr->delta_pic_order_cnt0 != curr_pic_->delta_pic_order_cnt0 ||
+          slice_hdr->delta_pic_order_cnt1 != curr_pic_->delta_pic_order_cnt1)
+        return true;
+    }
+  }
+
+  return false;
+}
+
+bool H264Decoder::PreprocessCurrentSlice() {
+  const H264SliceHeader* slice_hdr = curr_slice_hdr_.get();
+  DCHECK(slice_hdr);
+
+  if (IsNewPrimaryCodedPicture(slice_hdr)) {
+    // New picture, so first finish the previous one before processing it.
+    if (!FinishPrevFrameIfPresent())
+      return false;
+
+    DCHECK(!curr_pic_);
+
+    if (slice_hdr->first_mb_in_slice != 0) {
+      DVLOG(1) << "ASO/invalid stream, first_mb_in_slice: "
+               << slice_hdr->first_mb_in_slice;
+      return false;
+    }
+
+    // If the new picture is an IDR, flush DPB.
+    if (slice_hdr->idr_pic_flag) {
+      // Output all remaining pictures, unless we are explicitly instructed
+      // not to do so.
+      if (!slice_hdr->no_output_of_prior_pics_flag) {
+        if (!Flush())
+          return false;
+      }
+      dpb_.Clear();
+      last_output_poc_ = std::numeric_limits<int>::min();
+    }
+  }
+
+  return true;
+}
+
+bool H264Decoder::ProcessCurrentSlice() {
+  DCHECK(curr_pic_);
+
+  const H264SliceHeader* slice_hdr = curr_slice_hdr_.get();
+  DCHECK(slice_hdr);
+
+  if (slice_hdr->field_pic_flag == 0)
+    max_pic_num_ = max_frame_num_;
+  else
+    max_pic_num_ = 2 * max_frame_num_;
+
+  H264Picture::Vector ref_pic_list0, ref_pic_list1;
+  if (!ModifyReferencePicLists(slice_hdr, &ref_pic_list0, &ref_pic_list1))
+    return false;
+
+  const H264PPS* pps = parser_.GetPPS(curr_pps_id_);
+  if (!pps)
+    return false;
+
+  if (!accelerator_->SubmitSlice(pps, slice_hdr, ref_pic_list0, ref_pic_list1,
+                                 curr_pic_.get(), slice_hdr->nalu_data,
+                                 slice_hdr->nalu_size))
+    return false;
+
+  return true;
+}
+
+#define SET_ERROR_AND_RETURN()         \
+  do {                                 \
+    DVLOG(1) << "Error during decode"; \
+    state_ = kError;                   \
+    return H264Decoder::kDecodeError;  \
+  } while (0)
+
+void H264Decoder::SetStream(const uint8_t* ptr, size_t size) {
+  DCHECK(ptr);
+  DCHECK(size);
+
+  DVLOG(4) << "New input stream at: " << (void*)ptr << " size: " << size;
+  parser_.SetStream(ptr, size);
+}
+
+H264Decoder::DecodeResult H264Decoder::Decode() {
+  if (state_ == kError) {
+    DVLOG(1) << "Decoder in error state";
+    return kDecodeError;
+  }
+
+  while (1) {
+    H264Parser::Result par_res;
+
+    if (!curr_nalu_) {
+      curr_nalu_.reset(new H264NALU());
+      par_res = parser_.AdvanceToNextNALU(curr_nalu_.get());
+      if (par_res == H264Parser::kEOStream)
+        return kRanOutOfStreamData;
+      else if (par_res != H264Parser::kOk)
+        SET_ERROR_AND_RETURN();
+
+      DVLOG(4) << "New NALU: " << static_cast<int>(curr_nalu_->nal_unit_type);
+    }
+
+    switch (curr_nalu_->nal_unit_type) {
+      case H264NALU::kNonIDRSlice:
+        // We can't resume from a non-IDR slice.
+        if (state_ != kDecoding)
+          break;
+
+      // else fallthrough
+      case H264NALU::kIDRSlice: {
+        // TODO(posciak): the IDR may require an SPS that we don't have
+        // available. For now we'd fail if that happens, but ideally we'd like
+        // to keep going until the next SPS in the stream.
+        if (state_ == kNeedStreamMetadata) {
+          // We need an SPS, skip this IDR and keep looking.
+          break;
+        }
+
+        // If after reset, we should be able to recover from an IDR.
+        state_ = kDecoding;
+
+        if (!curr_slice_hdr_) {
+          curr_slice_hdr_.reset(new H264SliceHeader());
+          par_res =
+              parser_.ParseSliceHeader(*curr_nalu_, curr_slice_hdr_.get());
+          if (par_res != H264Parser::kOk)
+            SET_ERROR_AND_RETURN();
+
+          if (!PreprocessCurrentSlice())
+            SET_ERROR_AND_RETURN();
+        }
+
+        if (!curr_pic_) {
+          // New picture/finished previous one, try to start a new one
+          // or tell the client we need more surfaces.
+          curr_pic_ = accelerator_->CreateH264Picture();
+          if (!curr_pic_)
+            return kRanOutOfSurfaces;
+
+          if (!StartNewFrame(curr_slice_hdr_.get()))
+            SET_ERROR_AND_RETURN();
+        }
+
+        if (!ProcessCurrentSlice())
+          SET_ERROR_AND_RETURN();
+
+        curr_slice_hdr_.reset();
+        break;
+      }
+
+      case H264NALU::kSPS: {
+        int sps_id;
+
+        if (!FinishPrevFrameIfPresent())
+          SET_ERROR_AND_RETURN();
+
+        par_res = parser_.ParseSPS(&sps_id);
+        if (par_res != H264Parser::kOk)
+          SET_ERROR_AND_RETURN();
+
+        bool need_new_buffers = false;
+        if (!ProcessSPS(sps_id, &need_new_buffers))
+          SET_ERROR_AND_RETURN();
+
+        if (state_ == kNeedStreamMetadata)
+          state_ = kAfterReset;
+
+        if (need_new_buffers) {
+          curr_pic_ = nullptr;
+          curr_nalu_ = nullptr;
+          ref_pic_list_p0_.clear();
+          ref_pic_list_b0_.clear();
+          ref_pic_list_b1_.clear();
+
+          return kAllocateNewSurfaces;
+        }
+        break;
+      }
+
+      case H264NALU::kPPS: {
+        int pps_id;
+
+        if (!FinishPrevFrameIfPresent())
+          SET_ERROR_AND_RETURN();
+
+        par_res = parser_.ParsePPS(&pps_id);
+        if (par_res != H264Parser::kOk)
+          SET_ERROR_AND_RETURN();
+
+        break;
+      }
+
+      case H264NALU::kAUD:
+      case H264NALU::kEOSeq:
+      case H264NALU::kEOStream:
+        if (state_ != kDecoding)
+          break;
+
+        if (!FinishPrevFrameIfPresent())
+          SET_ERROR_AND_RETURN();
+
+        break;
+
+      default:
+        DVLOG(4) << "Skipping NALU type: " << curr_nalu_->nal_unit_type;
+        break;
+    }
+
+    DVLOG(4) << "NALU done";
+    curr_nalu_.reset();
+  }
+}
+
+Size H264Decoder::GetPicSize() const {
+  return pic_size_;
+}
+
+size_t H264Decoder::GetRequiredNumOfPictures() const {
+  return dpb_.max_num_pics() + kPicsInPipeline;
+}
+
+}  // namespace media
diff --git a/vda/h264_decoder.h b/vda/h264_decoder.h
new file mode 100644
index 0000000..27a4c10
--- /dev/null
+++ b/vda/h264_decoder.h
@@ -0,0 +1,280 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef H264_DECODER_H_
+#define H264_DECODER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "accelerated_video_decoder.h"
+#include "h264_dpb.h"
+#include "h264_parser.h"
+#include "size.h"
+
+namespace media {
+
+// Clients of this class are expected to pass H264 Annex-B byte stream
+// and are expected to provide an implementation of H264Accelerator for
+// offloading final steps of the decoding process.
+//
+// This class must be created, called and destroyed on a single thread, and
+// does nothing internally on any other thread.
+class H264Decoder : public AcceleratedVideoDecoder {
+ public:
+  class H264Accelerator {
+   public:
+    H264Accelerator();
+    virtual ~H264Accelerator();
+
+    // Create a new H264Picture that the decoder client can use for decoding
+    // and pass back to this accelerator for decoding or reference.
+    // When the picture is no longer needed by decoder, it will just drop
+    // its reference to it, and it may do so at any time.
+    // Note that this may return nullptr if accelerator is not able to provide
+    // any new pictures at given time. The decoder is expected to handle
+    // this situation as normal and return from Decode() with kRanOutOfSurfaces.
+    virtual scoped_refptr<H264Picture> CreateH264Picture() = 0;
+
+    // Submit metadata for the current frame, providing the current |sps| and
+    // |pps| for it, |dpb| has to contain all the pictures in DPB for current
+    // frame, and |ref_pic_p0/b0/b1| as specified in the H264 spec. Note that
+    // depending on the frame type, either p0, or b0 and b1 are used. |pic|
+    // contains information about the picture for the current frame.
+    // Note that this does not run decode in the accelerator and the decoder
+    // is expected to follow this call with one or more SubmitSlice() calls
+    // before calling SubmitDecode().
+    // Return true if successful.
+    virtual bool SubmitFrameMetadata(const H264SPS* sps,
+                                     const H264PPS* pps,
+                                     const H264DPB& dpb,
+                                     const H264Picture::Vector& ref_pic_listp0,
+                                     const H264Picture::Vector& ref_pic_listb0,
+                                     const H264Picture::Vector& ref_pic_listb1,
+                                     const scoped_refptr<H264Picture>& pic) = 0;
+
+    // Submit one slice for the current frame, passing the current |pps| and
+    // |pic| (same as in SubmitFrameMetadata()), the parsed header for the
+    // current slice in |slice_hdr|, and the reordered |ref_pic_listX|,
+    // as per H264 spec.
+    // |data| pointing to the full slice (including the unparsed header| of
+    // |size| in bytes.
+    // This must be called one or more times per frame, before SubmitDecode().
+    // Note that |data| does not have to remain valid after this call returns.
+    // Return true if successful.
+    virtual bool SubmitSlice(const H264PPS* pps,
+                             const H264SliceHeader* slice_hdr,
+                             const H264Picture::Vector& ref_pic_list0,
+                             const H264Picture::Vector& ref_pic_list1,
+                             const scoped_refptr<H264Picture>& pic,
+                             const uint8_t* data,
+                             size_t size) = 0;
+
+    // Execute the decode in hardware for |pic|, using all the slices and
+    // metadata submitted via SubmitFrameMetadata() and SubmitSlice() since
+    // the previous call to SubmitDecode().
+    // Return true if successful.
+    virtual bool SubmitDecode(const scoped_refptr<H264Picture>& pic) = 0;
+
+    // Schedule output (display) of |pic|. Note that returning from this
+    // method does not mean that |pic| has already been outputted (displayed),
+    // but guarantees that all pictures will be outputted in the same order
+    // as this method was called for them. Decoder may drop its reference
+    // to |pic| after calling this method.
+    // Return true if successful.
+    virtual bool OutputPicture(const scoped_refptr<H264Picture>& pic) = 0;
+
+    // Reset any current state that may be cached in the accelerator, dropping
+    // any cached parameters/slices that have not been committed yet.
+    virtual void Reset() = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(H264Accelerator);
+  };
+
+  H264Decoder(H264Accelerator* accelerator);
+  ~H264Decoder() override;
+
+  // AcceleratedVideoDecoder implementation.
+  bool Flush() override WARN_UNUSED_RESULT;
+  void Reset() override;
+  void SetStream(const uint8_t* ptr, size_t size) override;
+  DecodeResult Decode() override WARN_UNUSED_RESULT;
+  Size GetPicSize() const override;
+  size_t GetRequiredNumOfPictures() const override;
+
+ private:
+  // We need to keep at most kDPBMaxSize pictures in DPB for
+  // reference/to display later and an additional one for the one currently
+  // being decoded. We also ask for some additional ones since VDA needs
+  // to accumulate a few ready-to-output pictures before it actually starts
+  // displaying and giving them back. +2 instead of +1 because of subjective
+  // smoothness improvement during testing.
+  enum {
+    // TODO(johnylin): see if we could get rid of kMaxVideoFrames.
+    kMaxVideoFrames = 4,
+    kPicsInPipeline = kMaxVideoFrames + 2,
+    kMaxNumReqPictures = H264DPB::kDPBMaxSize + kPicsInPipeline,
+  };
+
+  // Internal state of the decoder.
+  enum State {
+    kNeedStreamMetadata,  // After initialization, need an SPS.
+    kDecoding,            // Ready to decode from any point.
+    kAfterReset,          // After Reset(), need a resume point.
+    kError,               // Error in decode, can't continue.
+  };
+
+  // Process H264 stream structures.
+  bool ProcessSPS(int sps_id, bool* need_new_buffers);
+  // Process current slice header to discover if we need to start a new picture,
+  // finishing up the current one.
+  bool PreprocessCurrentSlice();
+  // Process current slice as a slice of the current picture.
+  bool ProcessCurrentSlice();
+
+  // Return true if we need to start a new picture.
+  bool IsNewPrimaryCodedPicture(const H264SliceHeader* slice_hdr) const;
+
+  // Initialize the current picture according to data in |slice_hdr|.
+  bool InitCurrPicture(const H264SliceHeader* slice_hdr);
+
+  // Initialize |pic| as a "non-existing" picture (see spec) with |frame_num|,
+  // to be used for frame gap concealment.
+  bool InitNonexistingPicture(scoped_refptr<H264Picture> pic, int frame_num);
+
+  // Calculate picture order counts for |pic| on initialization
+  // of a new frame (see spec).
+  bool CalculatePicOrderCounts(scoped_refptr<H264Picture> pic);
+
+  // Update PicNum values in pictures stored in DPB on creation of
+  // a picture with |frame_num|.
+  void UpdatePicNums(int frame_num);
+
+  bool UpdateMaxNumReorderFrames(const H264SPS* sps);
+
+  // Prepare reference picture lists for the current frame.
+  void PrepareRefPicLists(const H264SliceHeader* slice_hdr);
+  // Prepare reference picture lists for the given slice.
+  bool ModifyReferencePicLists(const H264SliceHeader* slice_hdr,
+                               H264Picture::Vector* ref_pic_list0,
+                               H264Picture::Vector* ref_pic_list1);
+
+  // Construct initial reference picture lists for use in decoding of
+  // P and B pictures (see 8.2.4 in spec).
+  void ConstructReferencePicListsP(const H264SliceHeader* slice_hdr);
+  void ConstructReferencePicListsB(const H264SliceHeader* slice_hdr);
+
+  // Helper functions for reference list construction, per spec.
+  int PicNumF(const scoped_refptr<H264Picture>& pic);
+  int LongTermPicNumF(const scoped_refptr<H264Picture>& pic);
+
+  // Perform the reference picture lists' modification (reordering), as
+  // specified in spec (8.2.4).
+  //
+  // |list| indicates list number and should be either 0 or 1.
+  bool ModifyReferencePicList(const H264SliceHeader* slice_hdr,
+                              int list,
+                              H264Picture::Vector* ref_pic_listx);
+
+  // Perform reference picture memory management operations (marking/unmarking
+  // of reference pictures, long term picture management, discarding, etc.).
+  // See 8.2.5 in spec.
+  bool HandleMemoryManagementOps(scoped_refptr<H264Picture> pic);
+  bool ReferencePictureMarking(scoped_refptr<H264Picture> pic);
+  bool SlidingWindowPictureMarking();
+
+  // Handle a gap in frame_num in the stream up to |frame_num|, by creating
+  // "non-existing" pictures (see spec).
+  bool HandleFrameNumGap(int frame_num);
+
+  // Start processing a new frame.
+  bool StartNewFrame(const H264SliceHeader* slice_hdr);
+
+  // All data for a frame received, process it and decode.
+  bool FinishPrevFrameIfPresent();
+
+  // Called after we are done processing |pic|. Performs all operations to be
+  // done after decoding, including DPB management, reference picture marking
+  // and memory management operations.
+  // This will also output pictures if any have become ready to be outputted
+  // after processing |pic|.
+  bool FinishPicture(scoped_refptr<H264Picture> pic);
+
+  // Clear DPB contents and remove all surfaces in DPB from *in_use_ list.
+  // Cleared pictures will be made available for decode, unless they are
+  // at client waiting to be displayed.
+  void ClearDPB();
+
+  // Commits all pending data for HW decoder and starts HW decoder.
+  bool DecodePicture();
+
+  // Notifies client that a picture is ready for output.
+  void OutputPic(scoped_refptr<H264Picture> pic);
+
+  // Output all pictures in DPB that have not been outputted yet.
+  bool OutputAllRemainingPics();
+
+  // Decoder state.
+  State state_;
+
+  // Parser in use.
+  H264Parser parser_;
+
+  // DPB in use.
+  H264DPB dpb_;
+
+  // Picture currently being processed/decoded.
+  scoped_refptr<H264Picture> curr_pic_;
+
+  // Reference picture lists, constructed for each frame.
+  H264Picture::Vector ref_pic_list_p0_;
+  H264Picture::Vector ref_pic_list_b0_;
+  H264Picture::Vector ref_pic_list_b1_;
+
+  // Global state values, needed in decoding. See spec.
+  int max_frame_num_;
+  int max_pic_num_;
+  int max_long_term_frame_idx_;
+  size_t max_num_reorder_frames_;
+
+  int prev_frame_num_;
+  int prev_ref_frame_num_;
+  int prev_frame_num_offset_;
+  bool prev_has_memmgmnt5_;
+
+  // Values related to previously decoded reference picture.
+  bool prev_ref_has_memmgmnt5_;
+  int prev_ref_top_field_order_cnt_;
+  int prev_ref_pic_order_cnt_msb_;
+  int prev_ref_pic_order_cnt_lsb_;
+  H264Picture::Field prev_ref_field_;
+
+  // Currently active SPS and PPS.
+  int curr_sps_id_;
+  int curr_pps_id_;
+
+  // Current NALU and slice header being processed.
+  std::unique_ptr<H264NALU> curr_nalu_;
+  std::unique_ptr<H264SliceHeader> curr_slice_hdr_;
+
+  // Output picture size.
+  Size pic_size_;
+
+  // PicOrderCount of the previously outputted frame.
+  int last_output_poc_;
+
+  H264Accelerator* accelerator_;
+
+  DISALLOW_COPY_AND_ASSIGN(H264Decoder);
+};
+
+}  // namespace media
+
+#endif  // H264_DECODER_H_
diff --git a/vda/h264_dpb.cc b/vda/h264_dpb.cc
new file mode 100644
index 0000000..fb4a98d
--- /dev/null
+++ b/vda/h264_dpb.cc
@@ -0,0 +1,166 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "h264_dpb.h"
+
+namespace media {
+
+H264Picture::H264Picture()
+    : pic_order_cnt_type(0),
+      top_field_order_cnt(0),
+      bottom_field_order_cnt(0),
+      pic_order_cnt(0),
+      pic_order_cnt_msb(0),
+      pic_order_cnt_lsb(0),
+      delta_pic_order_cnt_bottom(0),
+      delta_pic_order_cnt0(0),
+      delta_pic_order_cnt1(0),
+      pic_num(0),
+      long_term_pic_num(0),
+      frame_num(0),
+      frame_num_offset(0),
+      frame_num_wrap(0),
+      long_term_frame_idx(0),
+      type(H264SliceHeader::kPSlice),
+      nal_ref_idc(0),
+      idr(false),
+      idr_pic_id(0),
+      ref(false),
+      long_term(false),
+      outputted(false),
+      mem_mgmt_5(false),
+      nonexisting(false),
+      field(FIELD_NONE),
+      long_term_reference_flag(false),
+      adaptive_ref_pic_marking_mode_flag(false),
+      dpb_position(0) {
+  memset(&ref_pic_marking, 0, sizeof(ref_pic_marking));
+}
+
+H264Picture::~H264Picture() {}
+
+H264DPB::H264DPB() : max_num_pics_(0) {}
+H264DPB::~H264DPB() {}
+
+void H264DPB::Clear() {
+  pics_.clear();
+}
+
+void H264DPB::set_max_num_pics(size_t max_num_pics) {
+  DCHECK_LE(max_num_pics, static_cast<size_t>(kDPBMaxSize));
+  max_num_pics_ = max_num_pics;
+  if (pics_.size() > max_num_pics_)
+    pics_.resize(max_num_pics_);
+}
+
+void H264DPB::UpdatePicPositions() {
+  size_t i = 0;
+  for (auto& pic : pics_) {
+    pic->dpb_position = i;
+    ++i;
+  }
+}
+
+void H264DPB::DeleteByPOC(int poc) {
+  for (H264Picture::Vector::iterator it = pics_.begin(); it != pics_.end();
+       ++it) {
+    if ((*it)->pic_order_cnt == poc) {
+      pics_.erase(it);
+      UpdatePicPositions();
+      return;
+    }
+  }
+  NOTREACHED() << "Missing POC: " << poc;
+}
+
+void H264DPB::DeleteUnused() {
+  for (H264Picture::Vector::iterator it = pics_.begin(); it != pics_.end();) {
+    if ((*it)->outputted && !(*it)->ref)
+      it = pics_.erase(it);
+    else
+      ++it;
+  }
+  UpdatePicPositions();
+}
+
+void H264DPB::StorePic(const scoped_refptr<H264Picture>& pic) {
+  DCHECK_LT(pics_.size(), max_num_pics_);
+  DVLOG(3) << "Adding PicNum: " << pic->pic_num << " ref: " << (int)pic->ref
+           << " longterm: " << (int)pic->long_term << " to DPB";
+  pic->dpb_position = pics_.size();
+  pics_.push_back(pic);
+}
+
+int H264DPB::CountRefPics() {
+  int ret = 0;
+  for (size_t i = 0; i < pics_.size(); ++i) {
+    if (pics_[i]->ref)
+      ++ret;
+  }
+  return ret;
+}
+
+void H264DPB::MarkAllUnusedForRef() {
+  for (size_t i = 0; i < pics_.size(); ++i)
+    pics_[i]->ref = false;
+}
+
+scoped_refptr<H264Picture> H264DPB::GetShortRefPicByPicNum(int pic_num) {
+  for (const auto& pic : pics_) {
+    if (pic->ref && !pic->long_term && pic->pic_num == pic_num)
+      return pic;
+  }
+
+  DVLOG(1) << "Missing short ref pic num: " << pic_num;
+  return nullptr;
+}
+
+scoped_refptr<H264Picture> H264DPB::GetLongRefPicByLongTermPicNum(int pic_num) {
+  for (const auto& pic : pics_) {
+    if (pic->ref && pic->long_term && pic->long_term_pic_num == pic_num)
+      return pic;
+  }
+
+  DVLOG(1) << "Missing long term pic num: " << pic_num;
+  return nullptr;
+}
+
+scoped_refptr<H264Picture> H264DPB::GetLowestFrameNumWrapShortRefPic() {
+  scoped_refptr<H264Picture> ret;
+  for (const auto& pic : pics_) {
+    if (pic->ref && !pic->long_term &&
+        (!ret || pic->frame_num_wrap < ret->frame_num_wrap))
+      ret = pic;
+  }
+  return ret;
+}
+
+void H264DPB::GetNotOutputtedPicsAppending(H264Picture::Vector* out) {
+  for (const auto& pic : pics_) {
+    if (!pic->outputted)
+      out->push_back(pic);
+  }
+}
+
+void H264DPB::GetShortTermRefPicsAppending(H264Picture::Vector* out) {
+  for (const auto& pic : pics_) {
+    if (pic->ref && !pic->long_term)
+      out->push_back(pic);
+  }
+}
+
+void H264DPB::GetLongTermRefPicsAppending(H264Picture::Vector* out) {
+  for (const auto& pic : pics_) {
+    if (pic->ref && pic->long_term)
+      out->push_back(pic);
+  }
+}
+
+}  // namespace media
diff --git a/vda/h264_dpb.h b/vda/h264_dpb.h
new file mode 100644
index 0000000..703f5e3
--- /dev/null
+++ b/vda/h264_dpb.h
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains an implementation of an H.264 Decoded Picture Buffer
+// used in H264 decoders.
+
+#ifndef H264_DPB_H_
+#define H264_DPB_H_
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "h264_parser.h"
+
+namespace media {
+
+// A picture (a frame or a field) in the H.264 spec sense.
+// See spec at http://www.itu.int/rec/T-REC-H.264
+class H264Picture : public base::RefCounted<H264Picture> {
+ public:
+  using Vector = std::vector<scoped_refptr<H264Picture>>;
+
+  enum Field {
+    FIELD_NONE,
+    FIELD_TOP,
+    FIELD_BOTTOM,
+  };
+
+  H264Picture();
+
+  // Values calculated per H.264 specification or taken from slice header.
+  // See spec for more details on each (some names have been converted from
+  // CamelCase in spec to Chromium-style names).
+  int pic_order_cnt_type;
+  int top_field_order_cnt;
+  int bottom_field_order_cnt;
+  int pic_order_cnt;
+  int pic_order_cnt_msb;
+  int pic_order_cnt_lsb;
+  int delta_pic_order_cnt_bottom;
+  int delta_pic_order_cnt0;
+  int delta_pic_order_cnt1;
+
+  int pic_num;
+  int long_term_pic_num;
+  int frame_num;  // from slice header
+  int frame_num_offset;
+  int frame_num_wrap;
+  int long_term_frame_idx;
+
+  H264SliceHeader::Type type;
+  int nal_ref_idc;
+  bool idr;        // IDR picture?
+  int idr_pic_id;  // Valid only if idr == true.
+  bool ref;        // reference picture?
+  bool long_term;  // long term reference picture?
+  bool outputted;
+  // Does memory management op 5 needs to be executed after this
+  // picture has finished decoding?
+  bool mem_mgmt_5;
+
+  // Created by the decoding process for gaps in frame_num.
+  // Not for decode or output.
+  bool nonexisting;
+
+  Field field;
+
+  // Values from slice_hdr to be used during reference marking and
+  // memory management after finishing this picture.
+  bool long_term_reference_flag;
+  bool adaptive_ref_pic_marking_mode_flag;
+  H264DecRefPicMarking ref_pic_marking[H264SliceHeader::kRefListSize];
+
+  // Position in DPB (i.e. index in DPB).
+  int dpb_position;
+
+ protected:
+  friend class base::RefCounted<H264Picture>;
+  virtual ~H264Picture();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(H264Picture);
+};
+
+// DPB - Decoded Picture Buffer.
+// Stores decoded pictures that will be used for future display
+// and/or reference.
+class H264DPB {
+ public:
+  H264DPB();
+  ~H264DPB();
+
+  void set_max_num_pics(size_t max_num_pics);
+  size_t max_num_pics() const { return max_num_pics_; }
+
+  // Remove unused (not reference and already outputted) pictures from DPB
+  // and free it.
+  void DeleteUnused();
+
+  // Remove a picture by its pic_order_cnt and free it.
+  void DeleteByPOC(int poc);
+
+  // Clear DPB.
+  void Clear();
+
+  // Store picture in DPB. DPB takes ownership of its resources.
+  void StorePic(const scoped_refptr<H264Picture>& pic);
+
+  // Return the number of reference pictures in DPB.
+  int CountRefPics();
+
+  // Mark all pictures in DPB as unused for reference.
+  void MarkAllUnusedForRef();
+
+  // Return a short-term reference picture by its pic_num.
+  scoped_refptr<H264Picture> GetShortRefPicByPicNum(int pic_num);
+
+  // Return a long-term reference picture by its long_term_pic_num.
+  scoped_refptr<H264Picture> GetLongRefPicByLongTermPicNum(int pic_num);
+
+  // Return the short reference picture with lowest frame_num. Used for sliding
+  // window memory management.
+  scoped_refptr<H264Picture> GetLowestFrameNumWrapShortRefPic();
+
+  // Append all pictures that have not been outputted yet to the passed |out|
+  // vector, sorted by lowest pic_order_cnt (in output order).
+  void GetNotOutputtedPicsAppending(H264Picture::Vector* out);
+
+  // Append all short term reference pictures to the passed |out| vector.
+  void GetShortTermRefPicsAppending(H264Picture::Vector* out);
+
+  // Append all long term reference pictures to the passed |out| vector.
+  void GetLongTermRefPicsAppending(H264Picture::Vector* out);
+
+  // Iterators for direct access to DPB contents.
+  // Will be invalidated after any of Remove* calls.
+  H264Picture::Vector::iterator begin() { return pics_.begin(); }
+  H264Picture::Vector::iterator end() { return pics_.end(); }
+  H264Picture::Vector::const_iterator begin() const { return pics_.begin(); }
+  H264Picture::Vector::const_iterator end() const { return pics_.end(); }
+  H264Picture::Vector::const_reverse_iterator rbegin() const {
+    return pics_.rbegin();
+  }
+  H264Picture::Vector::const_reverse_iterator rend() const {
+    return pics_.rend();
+  }
+
+  size_t size() const { return pics_.size(); }
+  bool IsFull() const { return pics_.size() == max_num_pics_; }
+
+  // Per H264 spec, increase to 32 if interlaced video is supported.
+  enum {
+    kDPBMaxSize = 16,
+  };
+
+ private:
+  void UpdatePicPositions();
+
+  H264Picture::Vector pics_;
+  size_t max_num_pics_;
+
+  DISALLOW_COPY_AND_ASSIGN(H264DPB);
+};
+
+}  // namespace media
+
+#endif  // H264_DPB_H_
diff --git a/vda/h264_parser.cc b/vda/h264_parser.cc
new file mode 100644
index 0000000..0f37924
--- /dev/null
+++ b/vda/h264_parser.cc
@@ -0,0 +1,1382 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "h264_parser.h"
+
+#include <limits>
+#include <memory>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/safe_math.h"
+
+namespace media {
+
+bool H264SliceHeader::IsPSlice() const {
+  return (slice_type % 5 == kPSlice);
+}
+
+bool H264SliceHeader::IsBSlice() const {
+  return (slice_type % 5 == kBSlice);
+}
+
+bool H264SliceHeader::IsISlice() const {
+  return (slice_type % 5 == kISlice);
+}
+
+bool H264SliceHeader::IsSPSlice() const {
+  return (slice_type % 5 == kSPSlice);
+}
+
+bool H264SliceHeader::IsSISlice() const {
+  return (slice_type % 5 == kSISlice);
+}
+
+H264NALU::H264NALU() {
+  memset(this, 0, sizeof(*this));
+}
+
+H264SPS::H264SPS() {
+  memset(this, 0, sizeof(*this));
+}
+
+// Based on T-REC-H.264 7.4.2.1.1, "Sequence parameter set data semantics",
+// available from http://www.itu.int/rec/T-REC-H.264.
+base::Optional<Size> H264SPS::GetCodedSize() const {
+  // Interlaced frames are twice the height of each field.
+  const int mb_unit = 16;
+  int map_unit = frame_mbs_only_flag ? 16 : 32;
+
+  // Verify that the values are not too large before multiplying them.
+  // TODO(sandersd): These limits could be much smaller. The currently-largest
+  // specified limit (excluding SVC, multiview, etc., which I didn't bother to
+  // read) is 543 macroblocks (section A.3.1).
+  int max_mb_minus1 = std::numeric_limits<int>::max() / mb_unit - 1;
+  int max_map_units_minus1 = std::numeric_limits<int>::max() / map_unit - 1;
+  if (pic_width_in_mbs_minus1 > max_mb_minus1 ||
+      pic_height_in_map_units_minus1 > max_map_units_minus1) {
+    DVLOG(1) << "Coded size is too large.";
+    return base::nullopt;
+  }
+
+  return Size(mb_unit * (pic_width_in_mbs_minus1 + 1),
+              map_unit * (pic_height_in_map_units_minus1 + 1));
+}
+
+H264PPS::H264PPS() {
+  memset(this, 0, sizeof(*this));
+}
+
+H264SliceHeader::H264SliceHeader() {
+  memset(this, 0, sizeof(*this));
+}
+
+H264SEIMessage::H264SEIMessage() {
+  memset(this, 0, sizeof(*this));
+}
+
+#define READ_BITS_OR_RETURN(num_bits, out)                                 \
+  do {                                                                     \
+    int _out;                                                              \
+    if (!br_.ReadBits(num_bits, &_out)) {                                  \
+      DVLOG(1)                                                             \
+          << "Error in stream: unexpected EOS while trying to read " #out; \
+      return kInvalidStream;                                               \
+    }                                                                      \
+    *out = _out;                                                           \
+  } while (0)
+
+#define READ_BOOL_OR_RETURN(out)                                           \
+  do {                                                                     \
+    int _out;                                                              \
+    if (!br_.ReadBits(1, &_out)) {                                         \
+      DVLOG(1)                                                             \
+          << "Error in stream: unexpected EOS while trying to read " #out; \
+      return kInvalidStream;                                               \
+    }                                                                      \
+    *out = _out != 0;                                                      \
+  } while (0)
+
+#define READ_UE_OR_RETURN(out)                                                 \
+  do {                                                                         \
+    if (ReadUE(out) != kOk) {                                                  \
+      DVLOG(1) << "Error in stream: invalid value while trying to read " #out; \
+      return kInvalidStream;                                                   \
+    }                                                                          \
+  } while (0)
+
+#define READ_SE_OR_RETURN(out)                                                 \
+  do {                                                                         \
+    if (ReadSE(out) != kOk) {                                                  \
+      DVLOG(1) << "Error in stream: invalid value while trying to read " #out; \
+      return kInvalidStream;                                                   \
+    }                                                                          \
+  } while (0)
+
+#define IN_RANGE_OR_RETURN(val, min, max)                                   \
+  do {                                                                      \
+    if ((val) < (min) || (val) > (max)) {                                   \
+      DVLOG(1) << "Error in stream: invalid value, expected " #val " to be" \
+               << " in range [" << (min) << ":" << (max) << "]"             \
+               << " found " << (val) << " instead";                         \
+      return kInvalidStream;                                                \
+    }                                                                       \
+  } while (0)
+
+#define TRUE_OR_RETURN(a)                                            \
+  do {                                                               \
+    if (!(a)) {                                                      \
+      DVLOG(1) << "Error in stream: invalid value, expected " << #a; \
+      return kInvalidStream;                                         \
+    }                                                                \
+  } while (0)
+
+// ISO 14496 part 10
+// VUI parameters: Table E-1 "Meaning of sample aspect ratio indicator"
+static const int kTableSarWidth[] = {
+  0, 1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2
+};
+static const int kTableSarHeight[] = {
+  0, 1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1
+};
+static_assert(arraysize(kTableSarWidth) == arraysize(kTableSarHeight),
+              "sar tables must have the same size");
+
+H264Parser::H264Parser() {
+  Reset();
+}
+
+H264Parser::~H264Parser() {
+}
+
+void H264Parser::Reset() {
+  stream_ = NULL;
+  bytes_left_ = 0;
+  encrypted_ranges_.clear();
+}
+
+void H264Parser::SetStream(const uint8_t* stream, off_t stream_size) {
+  std::vector<SubsampleEntry> subsamples;
+  SetEncryptedStream(stream, stream_size, subsamples);
+}
+
+void H264Parser::SetEncryptedStream(
+    const uint8_t* stream,
+    off_t stream_size,
+    const std::vector<SubsampleEntry>& subsamples) {
+  DCHECK(stream);
+  DCHECK_GT(stream_size, 0);
+
+  stream_ = stream;
+  bytes_left_ = stream_size;
+
+  encrypted_ranges_.clear();
+  const uint8_t* start = stream;
+  const uint8_t* stream_end = stream_ + bytes_left_;
+  for (size_t i = 0; i < subsamples.size() && start < stream_end; ++i) {
+    start += subsamples[i].clear_bytes;
+
+    const uint8_t* end =
+        std::min(start + subsamples[i].cypher_bytes, stream_end);
+    encrypted_ranges_.Add(start, end);
+    start = end;
+  }
+}
+
+const H264PPS* H264Parser::GetPPS(int pps_id) const {
+  auto it = active_PPSes_.find(pps_id);
+  if (it == active_PPSes_.end()) {
+    DVLOG(1) << "Requested a nonexistent PPS id " << pps_id;
+    return nullptr;
+  }
+
+  return it->second.get();
+}
+
+const H264SPS* H264Parser::GetSPS(int sps_id) const {
+  auto it = active_SPSes_.find(sps_id);
+  if (it == active_SPSes_.end()) {
+    DVLOG(1) << "Requested a nonexistent SPS id " << sps_id;
+    return nullptr;
+  }
+
+  return it->second.get();
+}
+
+static inline bool IsStartCode(const uint8_t* data) {
+  return data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x01;
+}
+
+// static
+bool H264Parser::FindStartCode(const uint8_t* data,
+                               off_t data_size,
+                               off_t* offset,
+                               off_t* start_code_size) {
+  DCHECK_GE(data_size, 0);
+  off_t bytes_left = data_size;
+
+  while (bytes_left >= 3) {
+    if (IsStartCode(data)) {
+      // Found three-byte start code, set pointer at its beginning.
+      *offset = data_size - bytes_left;
+      *start_code_size = 3;
+
+      // If there is a zero byte before this start code,
+      // then it's actually a four-byte start code, so backtrack one byte.
+      if (*offset > 0 && *(data - 1) == 0x00) {
+        --(*offset);
+        ++(*start_code_size);
+      }
+
+      return true;
+    }
+
+    ++data;
+    --bytes_left;
+  }
+
+  // End of data: offset is pointing to the first byte that was not considered
+  // as a possible start of a start code.
+  // Note: there is no security issue when receiving a negative |data_size|
+  // since in this case, |bytes_left| is equal to |data_size| and thus
+  // |*offset| is equal to 0 (valid offset).
+  *offset = data_size - bytes_left;
+  *start_code_size = 0;
+  return false;
+}
+
+bool H264Parser::LocateNALU(off_t* nalu_size, off_t* start_code_size) {
+  // Find the start code of next NALU.
+  off_t nalu_start_off = 0;
+  off_t annexb_start_code_size = 0;
+
+  if (!FindStartCodeInClearRanges(stream_, bytes_left_,
+                                  encrypted_ranges_,
+                                  &nalu_start_off, &annexb_start_code_size)) {
+    DVLOG(4) << "Could not find start code, end of stream?";
+    return false;
+  }
+
+  // Move the stream to the beginning of the NALU (pointing at the start code).
+  stream_ += nalu_start_off;
+  bytes_left_ -= nalu_start_off;
+
+  const uint8_t* nalu_data = stream_ + annexb_start_code_size;
+  off_t max_nalu_data_size = bytes_left_ - annexb_start_code_size;
+  if (max_nalu_data_size <= 0) {
+    DVLOG(3) << "End of stream";
+    return false;
+  }
+
+  // Find the start code of next NALU;
+  // if successful, |nalu_size_without_start_code| is the number of bytes from
+  // after previous start code to before this one;
+  // if next start code is not found, it is still a valid NALU since there
+  // are some bytes left after the first start code: all the remaining bytes
+  // belong to the current NALU.
+  off_t next_start_code_size = 0;
+  off_t nalu_size_without_start_code = 0;
+  if (!FindStartCodeInClearRanges(nalu_data, max_nalu_data_size,
+                                  encrypted_ranges_,
+                                  &nalu_size_without_start_code,
+                                  &next_start_code_size)) {
+    nalu_size_without_start_code = max_nalu_data_size;
+  }
+  *nalu_size = nalu_size_without_start_code + annexb_start_code_size;
+  *start_code_size = annexb_start_code_size;
+  return true;
+}
+
+bool H264Parser::FindStartCodeInClearRanges(
+    const uint8_t* data,
+    off_t data_size,
+    const Ranges<const uint8_t*>& encrypted_ranges,
+    off_t* offset,
+    off_t* start_code_size) {
+  if (encrypted_ranges.size() == 0)
+    return FindStartCode(data, data_size, offset, start_code_size);
+
+  DCHECK_GE(data_size, 0);
+  const uint8_t* start = data;
+  do {
+    off_t bytes_left = data_size - (start - data);
+
+    if (!FindStartCode(start, bytes_left, offset, start_code_size))
+      return false;
+
+    // Construct a Ranges object that represents the region occupied
+    // by the start code and the 1 byte needed to read the NAL unit type.
+    const uint8_t* start_code = start + *offset;
+    const uint8_t* start_code_end = start_code + *start_code_size;
+    Ranges<const uint8_t*> start_code_range;
+    start_code_range.Add(start_code, start_code_end + 1);
+
+    if (encrypted_ranges.IntersectionWith(start_code_range).size() > 0) {
+      // The start code is inside an encrypted section so we need to scan
+      // for another start code.
+      *start_code_size = 0;
+      start += std::min(*offset + 1, bytes_left);
+    }
+  } while (*start_code_size == 0);
+
+  // Update |*offset| to include the data we skipped over.
+  *offset += start - data;
+  return true;
+}
+
+H264Parser::Result H264Parser::ReadUE(int* val) {
+  int num_bits = -1;
+  int bit;
+  int rest;
+
+  // Count the number of contiguous zero bits.
+  do {
+    READ_BITS_OR_RETURN(1, &bit);
+    num_bits++;
+  } while (bit == 0);
+
+  if (num_bits > 31)
+    return kInvalidStream;
+
+  // Calculate exp-Golomb code value of size num_bits.
+  // Special case for |num_bits| == 31 to avoid integer overflow. The only
+  // valid representation as an int is 2^31 - 1, so the remaining bits must
+  // be 0 or else the number is too large.
+  *val = (1u << num_bits) - 1u;
+
+  if (num_bits == 31) {
+    READ_BITS_OR_RETURN(num_bits, &rest);
+    return (rest == 0) ? kOk : kInvalidStream;
+  }
+
+  if (num_bits > 0) {
+    READ_BITS_OR_RETURN(num_bits, &rest);
+    *val += rest;
+  }
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ReadSE(int* val) {
+  int ue;
+  Result res;
+
+  // See Chapter 9 in the spec.
+  res = ReadUE(&ue);
+  if (res != kOk)
+    return res;
+
+  if (ue % 2 == 0)
+    *val = -(ue / 2);
+  else
+    *val = ue / 2 + 1;
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::AdvanceToNextNALU(H264NALU* nalu) {
+  off_t start_code_size;
+  off_t nalu_size_with_start_code;
+  if (!LocateNALU(&nalu_size_with_start_code, &start_code_size)) {
+    DVLOG(4) << "Could not find next NALU, bytes left in stream: "
+             << bytes_left_;
+    return kEOStream;
+  }
+
+  nalu->data = stream_ + start_code_size;
+  nalu->size = nalu_size_with_start_code - start_code_size;
+  DVLOG(4) << "NALU found: size=" << nalu_size_with_start_code;
+
+  // Initialize bit reader at the start of found NALU.
+  if (!br_.Initialize(nalu->data, nalu->size))
+    return kEOStream;
+
+  // Move parser state to after this NALU, so next time AdvanceToNextNALU
+  // is called, we will effectively be skipping it;
+  // other parsing functions will use the position saved
+  // in bit reader for parsing, so we don't have to remember it here.
+  stream_ += nalu_size_with_start_code;
+  bytes_left_ -= nalu_size_with_start_code;
+
+  // Read NALU header, skip the forbidden_zero_bit, but check for it.
+  int data;
+  READ_BITS_OR_RETURN(1, &data);
+  TRUE_OR_RETURN(data == 0);
+
+  READ_BITS_OR_RETURN(2, &nalu->nal_ref_idc);
+  READ_BITS_OR_RETURN(5, &nalu->nal_unit_type);
+
+  DVLOG(4) << "NALU type: " << static_cast<int>(nalu->nal_unit_type)
+           << " at: " << reinterpret_cast<const void*>(nalu->data)
+           << " size: " << nalu->size
+           << " ref: " << static_cast<int>(nalu->nal_ref_idc);
+
+  return kOk;
+}
+
+// Default scaling lists (per spec).
+static const int kDefault4x4Intra[kH264ScalingList4x4Length] = {
+    6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42, };
+
+static const int kDefault4x4Inter[kH264ScalingList4x4Length] = {
+    10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34, };
+
+static const int kDefault8x8Intra[kH264ScalingList8x8Length] = {
+    6,  10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, 18, 18, 18, 23,
+    23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27,
+    27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31,
+    31, 33, 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42, };
+
+static const int kDefault8x8Inter[kH264ScalingList8x8Length] = {
+    9,  13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, 19, 19, 19, 21,
+    21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, 24,
+    24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27,
+    27, 28, 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35, };
+
+static inline void DefaultScalingList4x4(
+    int i,
+    int scaling_list4x4[][kH264ScalingList4x4Length]) {
+  DCHECK_LT(i, 6);
+
+  if (i < 3)
+    memcpy(scaling_list4x4[i], kDefault4x4Intra, sizeof(kDefault4x4Intra));
+  else if (i < 6)
+    memcpy(scaling_list4x4[i], kDefault4x4Inter, sizeof(kDefault4x4Inter));
+}
+
+static inline void DefaultScalingList8x8(
+    int i,
+    int scaling_list8x8[][kH264ScalingList8x8Length]) {
+  DCHECK_LT(i, 6);
+
+  if (i % 2 == 0)
+    memcpy(scaling_list8x8[i], kDefault8x8Intra, sizeof(kDefault8x8Intra));
+  else
+    memcpy(scaling_list8x8[i], kDefault8x8Inter, sizeof(kDefault8x8Inter));
+}
+
+static void FallbackScalingList4x4(
+    int i,
+    const int default_scaling_list_intra[],
+    const int default_scaling_list_inter[],
+    int scaling_list4x4[][kH264ScalingList4x4Length]) {
+  static const int kScalingList4x4ByteSize =
+      sizeof(scaling_list4x4[0][0]) * kH264ScalingList4x4Length;
+
+  switch (i) {
+    case 0:
+      memcpy(scaling_list4x4[i], default_scaling_list_intra,
+             kScalingList4x4ByteSize);
+      break;
+
+    case 1:
+      memcpy(scaling_list4x4[i], scaling_list4x4[0], kScalingList4x4ByteSize);
+      break;
+
+    case 2:
+      memcpy(scaling_list4x4[i], scaling_list4x4[1], kScalingList4x4ByteSize);
+      break;
+
+    case 3:
+      memcpy(scaling_list4x4[i], default_scaling_list_inter,
+             kScalingList4x4ByteSize);
+      break;
+
+    case 4:
+      memcpy(scaling_list4x4[i], scaling_list4x4[3], kScalingList4x4ByteSize);
+      break;
+
+    case 5:
+      memcpy(scaling_list4x4[i], scaling_list4x4[4], kScalingList4x4ByteSize);
+      break;
+
+    default:
+      NOTREACHED();
+      break;
+  }
+}
+
+static void FallbackScalingList8x8(
+    int i,
+    const int default_scaling_list_intra[],
+    const int default_scaling_list_inter[],
+    int scaling_list8x8[][kH264ScalingList8x8Length]) {
+  static const int kScalingList8x8ByteSize =
+      sizeof(scaling_list8x8[0][0]) * kH264ScalingList8x8Length;
+
+  switch (i) {
+    case 0:
+      memcpy(scaling_list8x8[i], default_scaling_list_intra,
+             kScalingList8x8ByteSize);
+      break;
+
+    case 1:
+      memcpy(scaling_list8x8[i], default_scaling_list_inter,
+             kScalingList8x8ByteSize);
+      break;
+
+    case 2:
+      memcpy(scaling_list8x8[i], scaling_list8x8[0], kScalingList8x8ByteSize);
+      break;
+
+    case 3:
+      memcpy(scaling_list8x8[i], scaling_list8x8[1], kScalingList8x8ByteSize);
+      break;
+
+    case 4:
+      memcpy(scaling_list8x8[i], scaling_list8x8[2], kScalingList8x8ByteSize);
+      break;
+
+    case 5:
+      memcpy(scaling_list8x8[i], scaling_list8x8[3], kScalingList8x8ByteSize);
+      break;
+
+    default:
+      NOTREACHED();
+      break;
+  }
+}
+
+H264Parser::Result H264Parser::ParseScalingList(int size,
+                                                int* scaling_list,
+                                                bool* use_default) {
+  // See chapter 7.3.2.1.1.1.
+  int last_scale = 8;
+  int next_scale = 8;
+  int delta_scale;
+
+  *use_default = false;
+
+  for (int j = 0; j < size; ++j) {
+    if (next_scale != 0) {
+      READ_SE_OR_RETURN(&delta_scale);
+      IN_RANGE_OR_RETURN(delta_scale, -128, 127);
+      next_scale = (last_scale + delta_scale + 256) & 0xff;
+
+      if (j == 0 && next_scale == 0) {
+        *use_default = true;
+        return kOk;
+      }
+    }
+
+    scaling_list[j] = (next_scale == 0) ? last_scale : next_scale;
+    last_scale = scaling_list[j];
+  }
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParseSPSScalingLists(H264SPS* sps) {
+  // See 7.4.2.1.1.
+  bool seq_scaling_list_present_flag;
+  bool use_default;
+  Result res;
+
+  // Parse scaling_list4x4.
+  for (int i = 0; i < 6; ++i) {
+    READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag);
+
+    if (seq_scaling_list_present_flag) {
+      res = ParseScalingList(arraysize(sps->scaling_list4x4[i]),
+                             sps->scaling_list4x4[i],
+                             &use_default);
+      if (res != kOk)
+        return res;
+
+      if (use_default)
+        DefaultScalingList4x4(i, sps->scaling_list4x4);
+
+    } else {
+      FallbackScalingList4x4(
+          i, kDefault4x4Intra, kDefault4x4Inter, sps->scaling_list4x4);
+    }
+  }
+
+  // Parse scaling_list8x8.
+  for (int i = 0; i < ((sps->chroma_format_idc != 3) ? 2 : 6); ++i) {
+    READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag);
+
+    if (seq_scaling_list_present_flag) {
+      res = ParseScalingList(arraysize(sps->scaling_list8x8[i]),
+                             sps->scaling_list8x8[i],
+                             &use_default);
+      if (res != kOk)
+        return res;
+
+      if (use_default)
+        DefaultScalingList8x8(i, sps->scaling_list8x8);
+
+    } else {
+      FallbackScalingList8x8(
+          i, kDefault8x8Intra, kDefault8x8Inter, sps->scaling_list8x8);
+    }
+  }
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParsePPSScalingLists(const H264SPS& sps,
+                                                    H264PPS* pps) {
+  // See 7.4.2.2.
+  bool pic_scaling_list_present_flag;
+  bool use_default;
+  Result res;
+
+  for (int i = 0; i < 6; ++i) {
+    READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag);
+
+    if (pic_scaling_list_present_flag) {
+      res = ParseScalingList(arraysize(pps->scaling_list4x4[i]),
+                             pps->scaling_list4x4[i],
+                             &use_default);
+      if (res != kOk)
+        return res;
+
+      if (use_default)
+        DefaultScalingList4x4(i, pps->scaling_list4x4);
+
+    } else {
+      if (!sps.seq_scaling_matrix_present_flag) {
+        // Table 7-2 fallback rule A in spec.
+        FallbackScalingList4x4(
+            i, kDefault4x4Intra, kDefault4x4Inter, pps->scaling_list4x4);
+      } else {
+        // Table 7-2 fallback rule B in spec.
+        FallbackScalingList4x4(i,
+                               sps.scaling_list4x4[0],
+                               sps.scaling_list4x4[3],
+                               pps->scaling_list4x4);
+      }
+    }
+  }
+
+  if (pps->transform_8x8_mode_flag) {
+    for (int i = 0; i < ((sps.chroma_format_idc != 3) ? 2 : 6); ++i) {
+      READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag);
+
+      if (pic_scaling_list_present_flag) {
+        res = ParseScalingList(arraysize(pps->scaling_list8x8[i]),
+                               pps->scaling_list8x8[i],
+                               &use_default);
+        if (res != kOk)
+          return res;
+
+        if (use_default)
+          DefaultScalingList8x8(i, pps->scaling_list8x8);
+
+      } else {
+        if (!sps.seq_scaling_matrix_present_flag) {
+          // Table 7-2 fallback rule A in spec.
+          FallbackScalingList8x8(
+              i, kDefault8x8Intra, kDefault8x8Inter, pps->scaling_list8x8);
+        } else {
+          // Table 7-2 fallback rule B in spec.
+          FallbackScalingList8x8(i,
+                                 sps.scaling_list8x8[0],
+                                 sps.scaling_list8x8[1],
+                                 pps->scaling_list8x8);
+        }
+      }
+    }
+  }
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParseAndIgnoreHRDParameters(
+    bool* hrd_parameters_present) {
+  int data;
+  READ_BOOL_OR_RETURN(&data);  // {nal,vcl}_hrd_parameters_present_flag
+  if (!data)
+    return kOk;
+
+  *hrd_parameters_present = true;
+
+  int cpb_cnt_minus1;
+  READ_UE_OR_RETURN(&cpb_cnt_minus1);
+  IN_RANGE_OR_RETURN(cpb_cnt_minus1, 0, 31);
+  READ_BITS_OR_RETURN(8, &data);  // bit_rate_scale, cpb_size_scale
+  for (int i = 0; i <= cpb_cnt_minus1; ++i) {
+    READ_UE_OR_RETURN(&data);  // bit_rate_value_minus1[i]
+    READ_UE_OR_RETURN(&data);  // cpb_size_value_minus1[i]
+    READ_BOOL_OR_RETURN(&data);  // cbr_flag
+  }
+  READ_BITS_OR_RETURN(20, &data);  // cpb/dpb delays, etc.
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParseVUIParameters(H264SPS* sps) {
+  bool aspect_ratio_info_present_flag;
+  READ_BOOL_OR_RETURN(&aspect_ratio_info_present_flag);
+  if (aspect_ratio_info_present_flag) {
+    int aspect_ratio_idc;
+    READ_BITS_OR_RETURN(8, &aspect_ratio_idc);
+    if (aspect_ratio_idc == H264SPS::kExtendedSar) {
+      READ_BITS_OR_RETURN(16, &sps->sar_width);
+      READ_BITS_OR_RETURN(16, &sps->sar_height);
+    } else {
+      const int max_aspect_ratio_idc = arraysize(kTableSarWidth) - 1;
+      IN_RANGE_OR_RETURN(aspect_ratio_idc, 0, max_aspect_ratio_idc);
+      sps->sar_width = kTableSarWidth[aspect_ratio_idc];
+      sps->sar_height = kTableSarHeight[aspect_ratio_idc];
+    }
+  }
+
+  int data;
+  // Read and ignore overscan and video signal type info.
+  READ_BOOL_OR_RETURN(&data);  // overscan_info_present_flag
+  if (data)
+    READ_BOOL_OR_RETURN(&data);  // overscan_appropriate_flag
+
+  READ_BOOL_OR_RETURN(&sps->video_signal_type_present_flag);
+  if (sps->video_signal_type_present_flag) {
+    READ_BITS_OR_RETURN(3, &sps->video_format);
+    READ_BOOL_OR_RETURN(&sps->video_full_range_flag);
+    READ_BOOL_OR_RETURN(&sps->colour_description_present_flag);
+    if (sps->colour_description_present_flag) {
+      // color description syntax elements
+      READ_BITS_OR_RETURN(8, &sps->colour_primaries);
+      READ_BITS_OR_RETURN(8, &sps->transfer_characteristics);
+      READ_BITS_OR_RETURN(8, &sps->matrix_coefficients);
+    }
+  }
+
+  READ_BOOL_OR_RETURN(&data);  // chroma_loc_info_present_flag
+  if (data) {
+    READ_UE_OR_RETURN(&data);  // chroma_sample_loc_type_top_field
+    READ_UE_OR_RETURN(&data);  // chroma_sample_loc_type_bottom_field
+  }
+
+  // Read and ignore timing info.
+  READ_BOOL_OR_RETURN(&data);  // timing_info_present_flag
+  if (data) {
+    READ_BITS_OR_RETURN(16, &data);  // num_units_in_tick
+    READ_BITS_OR_RETURN(16, &data);  // num_units_in_tick
+    READ_BITS_OR_RETURN(16, &data);  // time_scale
+    READ_BITS_OR_RETURN(16, &data);  // time_scale
+    READ_BOOL_OR_RETURN(&data);  // fixed_frame_rate_flag
+  }
+
+  // Read and ignore NAL HRD parameters, if present.
+  bool hrd_parameters_present = false;
+  Result res = ParseAndIgnoreHRDParameters(&hrd_parameters_present);
+  if (res != kOk)
+    return res;
+
+  // Read and ignore VCL HRD parameters, if present.
+  res = ParseAndIgnoreHRDParameters(&hrd_parameters_present);
+  if (res != kOk)
+    return res;
+
+  if (hrd_parameters_present)  // One of NAL or VCL params present is enough.
+    READ_BOOL_OR_RETURN(&data);  // low_delay_hrd_flag
+
+  READ_BOOL_OR_RETURN(&data);  // pic_struct_present_flag
+  READ_BOOL_OR_RETURN(&sps->bitstream_restriction_flag);
+  if (sps->bitstream_restriction_flag) {
+    READ_BOOL_OR_RETURN(&data);  // motion_vectors_over_pic_boundaries_flag
+    READ_UE_OR_RETURN(&data);  // max_bytes_per_pic_denom
+    READ_UE_OR_RETURN(&data);  // max_bits_per_mb_denom
+    READ_UE_OR_RETURN(&data);  // log2_max_mv_length_horizontal
+    READ_UE_OR_RETURN(&data);  // log2_max_mv_length_vertical
+    READ_UE_OR_RETURN(&sps->max_num_reorder_frames);
+    READ_UE_OR_RETURN(&sps->max_dec_frame_buffering);
+    TRUE_OR_RETURN(sps->max_dec_frame_buffering >= sps->max_num_ref_frames);
+    IN_RANGE_OR_RETURN(
+        sps->max_num_reorder_frames, 0, sps->max_dec_frame_buffering);
+  }
+
+  return kOk;
+}
+
+static void FillDefaultSeqScalingLists(H264SPS* sps) {
+  for (int i = 0; i < 6; ++i)
+    for (int j = 0; j < kH264ScalingList4x4Length; ++j)
+      sps->scaling_list4x4[i][j] = 16;
+
+  for (int i = 0; i < 6; ++i)
+    for (int j = 0; j < kH264ScalingList8x8Length; ++j)
+      sps->scaling_list8x8[i][j] = 16;
+}
+
+H264Parser::Result H264Parser::ParseSPS(int* sps_id) {
+  // See 7.4.2.1.
+  int data;
+  Result res;
+
+  *sps_id = -1;
+
+  std::unique_ptr<H264SPS> sps(new H264SPS());
+
+  READ_BITS_OR_RETURN(8, &sps->profile_idc);
+  READ_BOOL_OR_RETURN(&sps->constraint_set0_flag);
+  READ_BOOL_OR_RETURN(&sps->constraint_set1_flag);
+  READ_BOOL_OR_RETURN(&sps->constraint_set2_flag);
+  READ_BOOL_OR_RETURN(&sps->constraint_set3_flag);
+  READ_BOOL_OR_RETURN(&sps->constraint_set4_flag);
+  READ_BOOL_OR_RETURN(&sps->constraint_set5_flag);
+  READ_BITS_OR_RETURN(2, &data);  // reserved_zero_2bits
+  READ_BITS_OR_RETURN(8, &sps->level_idc);
+  READ_UE_OR_RETURN(&sps->seq_parameter_set_id);
+  TRUE_OR_RETURN(sps->seq_parameter_set_id < 32);
+
+  if (sps->profile_idc == 100 || sps->profile_idc == 110 ||
+      sps->profile_idc == 122 || sps->profile_idc == 244 ||
+      sps->profile_idc == 44 || sps->profile_idc == 83 ||
+      sps->profile_idc == 86 || sps->profile_idc == 118 ||
+      sps->profile_idc == 128) {
+    READ_UE_OR_RETURN(&sps->chroma_format_idc);
+    TRUE_OR_RETURN(sps->chroma_format_idc < 4);
+
+    if (sps->chroma_format_idc == 3)
+      READ_BOOL_OR_RETURN(&sps->separate_colour_plane_flag);
+
+    READ_UE_OR_RETURN(&sps->bit_depth_luma_minus8);
+    TRUE_OR_RETURN(sps->bit_depth_luma_minus8 < 7);
+
+    READ_UE_OR_RETURN(&sps->bit_depth_chroma_minus8);
+    TRUE_OR_RETURN(sps->bit_depth_chroma_minus8 < 7);
+
+    READ_BOOL_OR_RETURN(&sps->qpprime_y_zero_transform_bypass_flag);
+    READ_BOOL_OR_RETURN(&sps->seq_scaling_matrix_present_flag);
+
+    if (sps->seq_scaling_matrix_present_flag) {
+      DVLOG(4) << "Scaling matrix present";
+      res = ParseSPSScalingLists(sps.get());
+      if (res != kOk)
+        return res;
+    } else {
+      FillDefaultSeqScalingLists(sps.get());
+    }
+  } else {
+    sps->chroma_format_idc = 1;
+    FillDefaultSeqScalingLists(sps.get());
+  }
+
+  if (sps->separate_colour_plane_flag)
+    sps->chroma_array_type = 0;
+  else
+    sps->chroma_array_type = sps->chroma_format_idc;
+
+  READ_UE_OR_RETURN(&sps->log2_max_frame_num_minus4);
+  TRUE_OR_RETURN(sps->log2_max_frame_num_minus4 < 13);
+
+  READ_UE_OR_RETURN(&sps->pic_order_cnt_type);
+  TRUE_OR_RETURN(sps->pic_order_cnt_type < 3);
+
+  if (sps->pic_order_cnt_type == 0) {
+    READ_UE_OR_RETURN(&sps->log2_max_pic_order_cnt_lsb_minus4);
+    TRUE_OR_RETURN(sps->log2_max_pic_order_cnt_lsb_minus4 < 13);
+    sps->expected_delta_per_pic_order_cnt_cycle = 0;
+  } else if (sps->pic_order_cnt_type == 1) {
+    READ_BOOL_OR_RETURN(&sps->delta_pic_order_always_zero_flag);
+    READ_SE_OR_RETURN(&sps->offset_for_non_ref_pic);
+    READ_SE_OR_RETURN(&sps->offset_for_top_to_bottom_field);
+    READ_UE_OR_RETURN(&sps->num_ref_frames_in_pic_order_cnt_cycle);
+    TRUE_OR_RETURN(sps->num_ref_frames_in_pic_order_cnt_cycle < 255);
+
+    base::CheckedNumeric<int> offset_acc = 0;
+    for (int i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; ++i) {
+      READ_SE_OR_RETURN(&sps->offset_for_ref_frame[i]);
+      offset_acc += sps->offset_for_ref_frame[i];
+    }
+    if (!offset_acc.IsValid())
+      return kInvalidStream;
+    sps->expected_delta_per_pic_order_cnt_cycle = offset_acc.ValueOrDefault(0);
+  }
+
+  READ_UE_OR_RETURN(&sps->max_num_ref_frames);
+  READ_BOOL_OR_RETURN(&sps->gaps_in_frame_num_value_allowed_flag);
+
+  READ_UE_OR_RETURN(&sps->pic_width_in_mbs_minus1);
+  READ_UE_OR_RETURN(&sps->pic_height_in_map_units_minus1);
+
+  READ_BOOL_OR_RETURN(&sps->frame_mbs_only_flag);
+  if (!sps->frame_mbs_only_flag)
+    READ_BOOL_OR_RETURN(&sps->mb_adaptive_frame_field_flag);
+
+  READ_BOOL_OR_RETURN(&sps->direct_8x8_inference_flag);
+
+  READ_BOOL_OR_RETURN(&sps->frame_cropping_flag);
+  if (sps->frame_cropping_flag) {
+    READ_UE_OR_RETURN(&sps->frame_crop_left_offset);
+    READ_UE_OR_RETURN(&sps->frame_crop_right_offset);
+    READ_UE_OR_RETURN(&sps->frame_crop_top_offset);
+    READ_UE_OR_RETURN(&sps->frame_crop_bottom_offset);
+  }
+
+  READ_BOOL_OR_RETURN(&sps->vui_parameters_present_flag);
+  if (sps->vui_parameters_present_flag) {
+    DVLOG(4) << "VUI parameters present";
+    res = ParseVUIParameters(sps.get());
+    if (res != kOk)
+      return res;
+  }
+
+  // If an SPS with the same id already exists, replace it.
+  *sps_id = sps->seq_parameter_set_id;
+  active_SPSes_[*sps_id] = std::move(sps);
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParsePPS(int* pps_id) {
+  // See 7.4.2.2.
+  const H264SPS* sps;
+  Result res;
+
+  *pps_id = -1;
+
+  std::unique_ptr<H264PPS> pps(new H264PPS());
+
+  READ_UE_OR_RETURN(&pps->pic_parameter_set_id);
+  READ_UE_OR_RETURN(&pps->seq_parameter_set_id);
+  TRUE_OR_RETURN(pps->seq_parameter_set_id < 32);
+
+  if (active_SPSes_.find(pps->seq_parameter_set_id) == active_SPSes_.end()) {
+    DVLOG(1) << "Invalid stream, no SPS id: " << pps->seq_parameter_set_id;
+    return kInvalidStream;
+  }
+
+  sps = GetSPS(pps->seq_parameter_set_id);
+  TRUE_OR_RETURN(sps);
+
+  READ_BOOL_OR_RETURN(&pps->entropy_coding_mode_flag);
+  READ_BOOL_OR_RETURN(&pps->bottom_field_pic_order_in_frame_present_flag);
+
+  READ_UE_OR_RETURN(&pps->num_slice_groups_minus1);
+  if (pps->num_slice_groups_minus1 > 1) {
+    DVLOG(1) << "Slice groups not supported";
+    return kUnsupportedStream;
+  }
+
+  READ_UE_OR_RETURN(&pps->num_ref_idx_l0_default_active_minus1);
+  TRUE_OR_RETURN(pps->num_ref_idx_l0_default_active_minus1 < 32);
+
+  READ_UE_OR_RETURN(&pps->num_ref_idx_l1_default_active_minus1);
+  TRUE_OR_RETURN(pps->num_ref_idx_l1_default_active_minus1 < 32);
+
+  READ_BOOL_OR_RETURN(&pps->weighted_pred_flag);
+  READ_BITS_OR_RETURN(2, &pps->weighted_bipred_idc);
+  TRUE_OR_RETURN(pps->weighted_bipred_idc < 3);
+
+  READ_SE_OR_RETURN(&pps->pic_init_qp_minus26);
+  IN_RANGE_OR_RETURN(pps->pic_init_qp_minus26, -26, 25);
+
+  READ_SE_OR_RETURN(&pps->pic_init_qs_minus26);
+  IN_RANGE_OR_RETURN(pps->pic_init_qs_minus26, -26, 25);
+
+  READ_SE_OR_RETURN(&pps->chroma_qp_index_offset);
+  IN_RANGE_OR_RETURN(pps->chroma_qp_index_offset, -12, 12);
+  pps->second_chroma_qp_index_offset = pps->chroma_qp_index_offset;
+
+  READ_BOOL_OR_RETURN(&pps->deblocking_filter_control_present_flag);
+  READ_BOOL_OR_RETURN(&pps->constrained_intra_pred_flag);
+  READ_BOOL_OR_RETURN(&pps->redundant_pic_cnt_present_flag);
+
+  if (br_.HasMoreRBSPData()) {
+    READ_BOOL_OR_RETURN(&pps->transform_8x8_mode_flag);
+    READ_BOOL_OR_RETURN(&pps->pic_scaling_matrix_present_flag);
+
+    if (pps->pic_scaling_matrix_present_flag) {
+      DVLOG(4) << "Picture scaling matrix present";
+      res = ParsePPSScalingLists(*sps, pps.get());
+      if (res != kOk)
+        return res;
+    }
+
+    READ_SE_OR_RETURN(&pps->second_chroma_qp_index_offset);
+  }
+
+  // If a PPS with the same id already exists, replace it.
+  *pps_id = pps->pic_parameter_set_id;
+  active_PPSes_[*pps_id] = std::move(pps);
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParseRefPicListModification(
+    int num_ref_idx_active_minus1,
+    H264ModificationOfPicNum* ref_list_mods) {
+  H264ModificationOfPicNum* pic_num_mod;
+
+  if (num_ref_idx_active_minus1 >= 32)
+    return kInvalidStream;
+
+  for (int i = 0; i < 32; ++i) {
+    pic_num_mod = &ref_list_mods[i];
+    READ_UE_OR_RETURN(&pic_num_mod->modification_of_pic_nums_idc);
+    TRUE_OR_RETURN(pic_num_mod->modification_of_pic_nums_idc < 4);
+
+    switch (pic_num_mod->modification_of_pic_nums_idc) {
+      case 0:
+      case 1:
+        READ_UE_OR_RETURN(&pic_num_mod->abs_diff_pic_num_minus1);
+        break;
+
+      case 2:
+        READ_UE_OR_RETURN(&pic_num_mod->long_term_pic_num);
+        break;
+
+      case 3:
+        // Per spec, list cannot be empty.
+        if (i == 0)
+          return kInvalidStream;
+        return kOk;
+
+      default:
+        return kInvalidStream;
+    }
+  }
+
+  // If we got here, we didn't get loop end marker prematurely,
+  // so make sure it is there for our client.
+  int modification_of_pic_nums_idc;
+  READ_UE_OR_RETURN(&modification_of_pic_nums_idc);
+  TRUE_OR_RETURN(modification_of_pic_nums_idc == 3);
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParseRefPicListModifications(
+    H264SliceHeader* shdr) {
+  Result res;
+
+  if (!shdr->IsISlice() && !shdr->IsSISlice()) {
+    READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l0);
+    if (shdr->ref_pic_list_modification_flag_l0) {
+      res = ParseRefPicListModification(shdr->num_ref_idx_l0_active_minus1,
+                                        shdr->ref_list_l0_modifications);
+      if (res != kOk)
+        return res;
+    }
+  }
+
+  if (shdr->IsBSlice()) {
+    READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l1);
+    if (shdr->ref_pic_list_modification_flag_l1) {
+      res = ParseRefPicListModification(shdr->num_ref_idx_l1_active_minus1,
+                                        shdr->ref_list_l1_modifications);
+      if (res != kOk)
+        return res;
+    }
+  }
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParseWeightingFactors(
+    int num_ref_idx_active_minus1,
+    int chroma_array_type,
+    int luma_log2_weight_denom,
+    int chroma_log2_weight_denom,
+    H264WeightingFactors* w_facts) {
+
+  int def_luma_weight = 1 << luma_log2_weight_denom;
+  int def_chroma_weight = 1 << chroma_log2_weight_denom;
+
+  for (int i = 0; i < num_ref_idx_active_minus1 + 1; ++i) {
+    READ_BOOL_OR_RETURN(&w_facts->luma_weight_flag);
+    if (w_facts->luma_weight_flag) {
+      READ_SE_OR_RETURN(&w_facts->luma_weight[i]);
+      IN_RANGE_OR_RETURN(w_facts->luma_weight[i], -128, 127);
+
+      READ_SE_OR_RETURN(&w_facts->luma_offset[i]);
+      IN_RANGE_OR_RETURN(w_facts->luma_offset[i], -128, 127);
+    } else {
+      w_facts->luma_weight[i] = def_luma_weight;
+      w_facts->luma_offset[i] = 0;
+    }
+
+    if (chroma_array_type != 0) {
+      READ_BOOL_OR_RETURN(&w_facts->chroma_weight_flag);
+      if (w_facts->chroma_weight_flag) {
+        for (int j = 0; j < 2; ++j) {
+          READ_SE_OR_RETURN(&w_facts->chroma_weight[i][j]);
+          IN_RANGE_OR_RETURN(w_facts->chroma_weight[i][j], -128, 127);
+
+          READ_SE_OR_RETURN(&w_facts->chroma_offset[i][j]);
+          IN_RANGE_OR_RETURN(w_facts->chroma_offset[i][j], -128, 127);
+        }
+      } else {
+        for (int j = 0; j < 2; ++j) {
+          w_facts->chroma_weight[i][j] = def_chroma_weight;
+          w_facts->chroma_offset[i][j] = 0;
+        }
+      }
+    }
+  }
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParsePredWeightTable(const H264SPS& sps,
+                                                    H264SliceHeader* shdr) {
+  READ_UE_OR_RETURN(&shdr->luma_log2_weight_denom);
+  TRUE_OR_RETURN(shdr->luma_log2_weight_denom < 8);
+
+  if (sps.chroma_array_type != 0)
+    READ_UE_OR_RETURN(&shdr->chroma_log2_weight_denom);
+  TRUE_OR_RETURN(shdr->chroma_log2_weight_denom < 8);
+
+  Result res = ParseWeightingFactors(shdr->num_ref_idx_l0_active_minus1,
+                                     sps.chroma_array_type,
+                                     shdr->luma_log2_weight_denom,
+                                     shdr->chroma_log2_weight_denom,
+                                     &shdr->pred_weight_table_l0);
+  if (res != kOk)
+    return res;
+
+  if (shdr->IsBSlice()) {
+    res = ParseWeightingFactors(shdr->num_ref_idx_l1_active_minus1,
+                                sps.chroma_array_type,
+                                shdr->luma_log2_weight_denom,
+                                shdr->chroma_log2_weight_denom,
+                                &shdr->pred_weight_table_l1);
+    if (res != kOk)
+      return res;
+  }
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParseDecRefPicMarking(H264SliceHeader* shdr) {
+  size_t bits_left_at_start = br_.NumBitsLeft();
+
+  if (shdr->idr_pic_flag) {
+    READ_BOOL_OR_RETURN(&shdr->no_output_of_prior_pics_flag);
+    READ_BOOL_OR_RETURN(&shdr->long_term_reference_flag);
+  } else {
+    READ_BOOL_OR_RETURN(&shdr->adaptive_ref_pic_marking_mode_flag);
+
+    H264DecRefPicMarking* marking;
+    if (shdr->adaptive_ref_pic_marking_mode_flag) {
+      size_t i;
+      for (i = 0; i < arraysize(shdr->ref_pic_marking); ++i) {
+        marking = &shdr->ref_pic_marking[i];
+
+        READ_UE_OR_RETURN(&marking->memory_mgmnt_control_operation);
+        if (marking->memory_mgmnt_control_operation == 0)
+          break;
+
+        if (marking->memory_mgmnt_control_operation == 1 ||
+            marking->memory_mgmnt_control_operation == 3)
+          READ_UE_OR_RETURN(&marking->difference_of_pic_nums_minus1);
+
+        if (marking->memory_mgmnt_control_operation == 2)
+          READ_UE_OR_RETURN(&marking->long_term_pic_num);
+
+        if (marking->memory_mgmnt_control_operation == 3 ||
+            marking->memory_mgmnt_control_operation == 6)
+          READ_UE_OR_RETURN(&marking->long_term_frame_idx);
+
+        if (marking->memory_mgmnt_control_operation == 4)
+          READ_UE_OR_RETURN(&marking->max_long_term_frame_idx_plus1);
+
+        if (marking->memory_mgmnt_control_operation > 6)
+          return kInvalidStream;
+      }
+
+      if (i == arraysize(shdr->ref_pic_marking)) {
+        DVLOG(1) << "Ran out of dec ref pic marking fields";
+        return kUnsupportedStream;
+      }
+    }
+  }
+
+  shdr->dec_ref_pic_marking_bit_size = bits_left_at_start - br_.NumBitsLeft();
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParseSliceHeader(const H264NALU& nalu,
+                                                H264SliceHeader* shdr) {
+  // See 7.4.3.
+  const H264SPS* sps;
+  const H264PPS* pps;
+  Result res;
+
+  memset(shdr, 0, sizeof(*shdr));
+
+  shdr->idr_pic_flag = (nalu.nal_unit_type == 5);
+  shdr->nal_ref_idc = nalu.nal_ref_idc;
+  shdr->nalu_data = nalu.data;
+  shdr->nalu_size = nalu.size;
+
+  READ_UE_OR_RETURN(&shdr->first_mb_in_slice);
+  READ_UE_OR_RETURN(&shdr->slice_type);
+  TRUE_OR_RETURN(shdr->slice_type < 10);
+
+  READ_UE_OR_RETURN(&shdr->pic_parameter_set_id);
+
+  pps = GetPPS(shdr->pic_parameter_set_id);
+  TRUE_OR_RETURN(pps);
+
+  sps = GetSPS(pps->seq_parameter_set_id);
+  TRUE_OR_RETURN(sps);
+
+  if (sps->separate_colour_plane_flag) {
+    DVLOG(1) << "Interlaced streams not supported";
+    return kUnsupportedStream;
+  }
+
+  READ_BITS_OR_RETURN(sps->log2_max_frame_num_minus4 + 4, &shdr->frame_num);
+  if (!sps->frame_mbs_only_flag) {
+    READ_BOOL_OR_RETURN(&shdr->field_pic_flag);
+    if (shdr->field_pic_flag) {
+      DVLOG(1) << "Interlaced streams not supported";
+      return kUnsupportedStream;
+    }
+  }
+
+  if (shdr->idr_pic_flag)
+    READ_UE_OR_RETURN(&shdr->idr_pic_id);
+
+  size_t bits_left_at_pic_order_cnt_start = br_.NumBitsLeft();
+  if (sps->pic_order_cnt_type == 0) {
+    READ_BITS_OR_RETURN(sps->log2_max_pic_order_cnt_lsb_minus4 + 4,
+                        &shdr->pic_order_cnt_lsb);
+    if (pps->bottom_field_pic_order_in_frame_present_flag &&
+        !shdr->field_pic_flag)
+      READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt_bottom);
+  }
+
+  if (sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag) {
+    READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt0);
+    if (pps->bottom_field_pic_order_in_frame_present_flag &&
+        !shdr->field_pic_flag)
+      READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt1);
+  }
+
+  shdr->pic_order_cnt_bit_size =
+      bits_left_at_pic_order_cnt_start - br_.NumBitsLeft();
+
+  if (pps->redundant_pic_cnt_present_flag) {
+    READ_UE_OR_RETURN(&shdr->redundant_pic_cnt);
+    TRUE_OR_RETURN(shdr->redundant_pic_cnt < 128);
+  }
+
+  if (shdr->IsBSlice())
+    READ_BOOL_OR_RETURN(&shdr->direct_spatial_mv_pred_flag);
+
+  if (shdr->IsPSlice() || shdr->IsSPSlice() || shdr->IsBSlice()) {
+    READ_BOOL_OR_RETURN(&shdr->num_ref_idx_active_override_flag);
+    if (shdr->num_ref_idx_active_override_flag) {
+      READ_UE_OR_RETURN(&shdr->num_ref_idx_l0_active_minus1);
+      if (shdr->IsBSlice())
+        READ_UE_OR_RETURN(&shdr->num_ref_idx_l1_active_minus1);
+    } else {
+      shdr->num_ref_idx_l0_active_minus1 =
+          pps->num_ref_idx_l0_default_active_minus1;
+      if (shdr->IsBSlice()) {
+        shdr->num_ref_idx_l1_active_minus1 =
+            pps->num_ref_idx_l1_default_active_minus1;
+      }
+    }
+  }
+  if (shdr->field_pic_flag) {
+    TRUE_OR_RETURN(shdr->num_ref_idx_l0_active_minus1 < 32);
+    TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 32);
+  } else {
+    TRUE_OR_RETURN(shdr->num_ref_idx_l0_active_minus1 < 16);
+    TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 16);
+  }
+
+  if (nalu.nal_unit_type == H264NALU::kCodedSliceExtension) {
+    return kUnsupportedStream;
+  } else {
+    res = ParseRefPicListModifications(shdr);
+    if (res != kOk)
+      return res;
+  }
+
+  if ((pps->weighted_pred_flag && (shdr->IsPSlice() || shdr->IsSPSlice())) ||
+      (pps->weighted_bipred_idc == 1 && shdr->IsBSlice())) {
+    res = ParsePredWeightTable(*sps, shdr);
+    if (res != kOk)
+      return res;
+  }
+
+  if (nalu.nal_ref_idc != 0) {
+    res = ParseDecRefPicMarking(shdr);
+    if (res != kOk)
+      return res;
+  }
+
+  if (pps->entropy_coding_mode_flag && !shdr->IsISlice() &&
+      !shdr->IsSISlice()) {
+    READ_UE_OR_RETURN(&shdr->cabac_init_idc);
+    TRUE_OR_RETURN(shdr->cabac_init_idc < 3);
+  }
+
+  READ_SE_OR_RETURN(&shdr->slice_qp_delta);
+
+  if (shdr->IsSPSlice() || shdr->IsSISlice()) {
+    if (shdr->IsSPSlice())
+      READ_BOOL_OR_RETURN(&shdr->sp_for_switch_flag);
+    READ_SE_OR_RETURN(&shdr->slice_qs_delta);
+  }
+
+  if (pps->deblocking_filter_control_present_flag) {
+    READ_UE_OR_RETURN(&shdr->disable_deblocking_filter_idc);
+    TRUE_OR_RETURN(shdr->disable_deblocking_filter_idc < 3);
+
+    if (shdr->disable_deblocking_filter_idc != 1) {
+      READ_SE_OR_RETURN(&shdr->slice_alpha_c0_offset_div2);
+      IN_RANGE_OR_RETURN(shdr->slice_alpha_c0_offset_div2, -6, 6);
+
+      READ_SE_OR_RETURN(&shdr->slice_beta_offset_div2);
+      IN_RANGE_OR_RETURN(shdr->slice_beta_offset_div2, -6, 6);
+    }
+  }
+
+  if (pps->num_slice_groups_minus1 > 0) {
+    DVLOG(1) << "Slice groups not supported";
+    return kUnsupportedStream;
+  }
+
+  size_t epb = br_.NumEmulationPreventionBytesRead();
+  shdr->header_bit_size = (shdr->nalu_size - epb) * 8 - br_.NumBitsLeft();
+
+  return kOk;
+}
+
+H264Parser::Result H264Parser::ParseSEI(H264SEIMessage* sei_msg) {
+  int byte;
+
+  memset(sei_msg, 0, sizeof(*sei_msg));
+
+  READ_BITS_OR_RETURN(8, &byte);
+  while (byte == 0xff) {
+    sei_msg->type += 255;
+    READ_BITS_OR_RETURN(8, &byte);
+  }
+  sei_msg->type += byte;
+
+  READ_BITS_OR_RETURN(8, &byte);
+  while (byte == 0xff) {
+    sei_msg->payload_size += 255;
+    READ_BITS_OR_RETURN(8, &byte);
+  }
+  sei_msg->payload_size += byte;
+
+  DVLOG(4) << "Found SEI message type: " << sei_msg->type
+           << " payload size: " << sei_msg->payload_size;
+
+  switch (sei_msg->type) {
+    case H264SEIMessage::kSEIRecoveryPoint:
+      READ_UE_OR_RETURN(&sei_msg->recovery_point.recovery_frame_cnt);
+      READ_BOOL_OR_RETURN(&sei_msg->recovery_point.exact_match_flag);
+      READ_BOOL_OR_RETURN(&sei_msg->recovery_point.broken_link_flag);
+      READ_BITS_OR_RETURN(2, &sei_msg->recovery_point.changing_slice_group_idc);
+      break;
+
+    default:
+      DVLOG(4) << "Unsupported SEI message";
+      break;
+  }
+
+  return kOk;
+}
+
+}  // namespace media
diff --git a/vda/h264_parser.h b/vda/h264_parser.h
new file mode 100644
index 0000000..fdd3f77
--- /dev/null
+++ b/vda/h264_parser.h
@@ -0,0 +1,502 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains an implementation of an H264 Annex-B video stream parser.
+
+#ifndef H264_PARSER_H_
+#define H264_PARSER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "h264_bit_reader.h"
+#include "ranges.h"
+#include "size.h"
+#include "subsample_entry.h"
+
+namespace media {
+
+struct SubsampleEntry;
+
+// For explanations of each struct and its members, see H.264 specification
+// at http://www.itu.int/rec/T-REC-H.264.
+struct H264NALU {
+  H264NALU();
+
+  enum Type {
+    kUnspecified = 0,
+    kNonIDRSlice = 1,
+    kSliceDataA = 2,
+    kSliceDataB = 3,
+    kSliceDataC = 4,
+    kIDRSlice = 5,
+    kSEIMessage = 6,
+    kSPS = 7,
+    kPPS = 8,
+    kAUD = 9,
+    kEOSeq = 10,
+    kEOStream = 11,
+    kFiller = 12,
+    kSPSExt = 13,
+    kReserved14 = 14,
+    kReserved15 = 15,
+    kReserved16 = 16,
+    kReserved17 = 17,
+    kReserved18 = 18,
+    kCodedSliceAux = 19,
+    kCodedSliceExtension = 20,
+  };
+
+  // After (without) start code; we don't own the underlying memory
+  // and a shallow copy should be made when copying this struct.
+  const uint8_t* data;
+  off_t size;  // From after start code to start code of next NALU (or EOS).
+
+  int nal_ref_idc;
+  int nal_unit_type;
+};
+
+enum {
+  kH264ScalingList4x4Length = 16,
+  kH264ScalingList8x8Length = 64,
+};
+
+struct H264SPS {
+  H264SPS();
+
+  enum H264ProfileIDC {
+    kProfileIDCBaseline = 66,
+    kProfileIDCConstrainedBaseline = kProfileIDCBaseline,
+    kProfileIDCMain = 77,
+    kProfileIDScalableBaseline = 83,
+    kProfileIDScalableHigh = 86,
+    kProfileIDCHigh = 100,
+    kProfileIDHigh10 = 110,
+    kProfileIDSMultiviewHigh = 118,
+    kProfileIDHigh422 = 122,
+    kProfileIDStereoHigh = 128,
+    kProfileIDHigh444Predictive = 244,
+  };
+
+  enum AspectRatioIdc {
+    kExtendedSar = 255,
+  };
+
+  enum {
+    // Constants for HRD parameters (spec ch. E.2.2).
+    kBitRateScaleConstantTerm = 6,  // Equation E-37.
+    kCPBSizeScaleConstantTerm = 4,  // Equation E-38.
+    kDefaultInitialCPBRemovalDelayLength = 24,
+    kDefaultDPBOutputDelayLength = 24,
+    kDefaultTimeOffsetLength = 24,
+  };
+
+  int profile_idc;
+  bool constraint_set0_flag;
+  bool constraint_set1_flag;
+  bool constraint_set2_flag;
+  bool constraint_set3_flag;
+  bool constraint_set4_flag;
+  bool constraint_set5_flag;
+  int level_idc;
+  int seq_parameter_set_id;
+
+  int chroma_format_idc;
+  bool separate_colour_plane_flag;
+  int bit_depth_luma_minus8;
+  int bit_depth_chroma_minus8;
+  bool qpprime_y_zero_transform_bypass_flag;
+
+  bool seq_scaling_matrix_present_flag;
+  int scaling_list4x4[6][kH264ScalingList4x4Length];
+  int scaling_list8x8[6][kH264ScalingList8x8Length];
+
+  int log2_max_frame_num_minus4;
+  int pic_order_cnt_type;
+  int log2_max_pic_order_cnt_lsb_minus4;
+  bool delta_pic_order_always_zero_flag;
+  int offset_for_non_ref_pic;
+  int offset_for_top_to_bottom_field;
+  int num_ref_frames_in_pic_order_cnt_cycle;
+  int expected_delta_per_pic_order_cnt_cycle;  // calculated
+  int offset_for_ref_frame[255];
+  int max_num_ref_frames;
+  bool gaps_in_frame_num_value_allowed_flag;
+  int pic_width_in_mbs_minus1;
+  int pic_height_in_map_units_minus1;
+  bool frame_mbs_only_flag;
+  bool mb_adaptive_frame_field_flag;
+  bool direct_8x8_inference_flag;
+  bool frame_cropping_flag;
+  int frame_crop_left_offset;
+  int frame_crop_right_offset;
+  int frame_crop_top_offset;
+  int frame_crop_bottom_offset;
+
+  bool vui_parameters_present_flag;
+  int sar_width;    // Set to 0 when not specified.
+  int sar_height;   // Set to 0 when not specified.
+  bool bitstream_restriction_flag;
+  int max_num_reorder_frames;
+  int max_dec_frame_buffering;
+  bool timing_info_present_flag;
+  int num_units_in_tick;
+  int time_scale;
+  bool fixed_frame_rate_flag;
+
+  bool video_signal_type_present_flag;
+  int video_format;
+  bool video_full_range_flag;
+  bool colour_description_present_flag;
+  int colour_primaries;
+  int transfer_characteristics;
+  int matrix_coefficients;
+
+  // TODO(posciak): actually parse these instead of ParseAndIgnoreHRDParameters.
+  bool nal_hrd_parameters_present_flag;
+  int cpb_cnt_minus1;
+  int bit_rate_scale;
+  int cpb_size_scale;
+  int bit_rate_value_minus1[32];
+  int cpb_size_value_minus1[32];
+  bool cbr_flag[32];
+  int initial_cpb_removal_delay_length_minus_1;
+  int cpb_removal_delay_length_minus1;
+  int dpb_output_delay_length_minus1;
+  int time_offset_length;
+
+  bool low_delay_hrd_flag;
+
+  int chroma_array_type;
+
+  // Helpers to compute frequently-used values. These methods return
+  // base::nullopt if they encounter integer overflow. They do not verify that
+  // the results are in-spec for the given profile or level.
+  base::Optional<Size> GetCodedSize() const;
+};
+
+struct H264PPS {
+  H264PPS();
+
+  int pic_parameter_set_id;
+  int seq_parameter_set_id;
+  bool entropy_coding_mode_flag;
+  bool bottom_field_pic_order_in_frame_present_flag;
+  int num_slice_groups_minus1;
+  // TODO(posciak): Slice groups not implemented, could be added at some point.
+  int num_ref_idx_l0_default_active_minus1;
+  int num_ref_idx_l1_default_active_minus1;
+  bool weighted_pred_flag;
+  int weighted_bipred_idc;
+  int pic_init_qp_minus26;
+  int pic_init_qs_minus26;
+  int chroma_qp_index_offset;
+  bool deblocking_filter_control_present_flag;
+  bool constrained_intra_pred_flag;
+  bool redundant_pic_cnt_present_flag;
+  bool transform_8x8_mode_flag;
+
+  bool pic_scaling_matrix_present_flag;
+  int scaling_list4x4[6][kH264ScalingList4x4Length];
+  int scaling_list8x8[6][kH264ScalingList8x8Length];
+
+  int second_chroma_qp_index_offset;
+};
+
+struct H264ModificationOfPicNum {
+  int modification_of_pic_nums_idc;
+  union {
+    int abs_diff_pic_num_minus1;
+    int long_term_pic_num;
+  };
+};
+
+struct H264WeightingFactors {
+  bool luma_weight_flag;
+  bool chroma_weight_flag;
+  int luma_weight[32];
+  int luma_offset[32];
+  int chroma_weight[32][2];
+  int chroma_offset[32][2];
+};
+
+struct H264DecRefPicMarking {
+  int memory_mgmnt_control_operation;
+  int difference_of_pic_nums_minus1;
+  int long_term_pic_num;
+  int long_term_frame_idx;
+  int max_long_term_frame_idx_plus1;
+};
+
+struct H264SliceHeader {
+  H264SliceHeader();
+
+  enum {
+    kRefListSize = 32,
+    kRefListModSize = kRefListSize
+  };
+
+  enum Type {
+    kPSlice = 0,
+    kBSlice = 1,
+    kISlice = 2,
+    kSPSlice = 3,
+    kSISlice = 4,
+  };
+
+  bool IsPSlice() const;
+  bool IsBSlice() const;
+  bool IsISlice() const;
+  bool IsSPSlice() const;
+  bool IsSISlice() const;
+
+  bool idr_pic_flag;       // from NAL header
+  int nal_ref_idc;         // from NAL header
+  const uint8_t* nalu_data;  // from NAL header
+  off_t nalu_size;         // from NAL header
+  off_t header_bit_size;   // calculated
+
+  int first_mb_in_slice;
+  int slice_type;
+  int pic_parameter_set_id;
+  int colour_plane_id;  // TODO(posciak): use this!  http://crbug.com/139878
+  int frame_num;
+  bool field_pic_flag;
+  bool bottom_field_flag;
+  int idr_pic_id;
+  int pic_order_cnt_lsb;
+  int delta_pic_order_cnt_bottom;
+  int delta_pic_order_cnt0;
+  int delta_pic_order_cnt1;
+  int redundant_pic_cnt;
+  bool direct_spatial_mv_pred_flag;
+
+  bool num_ref_idx_active_override_flag;
+  int num_ref_idx_l0_active_minus1;
+  int num_ref_idx_l1_active_minus1;
+  bool ref_pic_list_modification_flag_l0;
+  bool ref_pic_list_modification_flag_l1;
+  H264ModificationOfPicNum ref_list_l0_modifications[kRefListModSize];
+  H264ModificationOfPicNum ref_list_l1_modifications[kRefListModSize];
+
+  int luma_log2_weight_denom;
+  int chroma_log2_weight_denom;
+
+  bool luma_weight_l0_flag;
+  bool chroma_weight_l0_flag;
+  H264WeightingFactors pred_weight_table_l0;
+
+  bool luma_weight_l1_flag;
+  bool chroma_weight_l1_flag;
+  H264WeightingFactors pred_weight_table_l1;
+
+  bool no_output_of_prior_pics_flag;
+  bool long_term_reference_flag;
+
+  bool adaptive_ref_pic_marking_mode_flag;
+  H264DecRefPicMarking ref_pic_marking[kRefListSize];
+
+  int cabac_init_idc;
+  int slice_qp_delta;
+  bool sp_for_switch_flag;
+  int slice_qs_delta;
+  int disable_deblocking_filter_idc;
+  int slice_alpha_c0_offset_div2;
+  int slice_beta_offset_div2;
+
+  // Calculated.
+  // Size in bits of dec_ref_pic_marking() syntax element.
+  size_t dec_ref_pic_marking_bit_size;
+  size_t pic_order_cnt_bit_size;
+};
+
+struct H264SEIRecoveryPoint {
+  int recovery_frame_cnt;
+  bool exact_match_flag;
+  bool broken_link_flag;
+  int changing_slice_group_idc;
+};
+
+struct H264SEIMessage {
+  H264SEIMessage();
+
+  enum Type {
+    kSEIRecoveryPoint = 6,
+  };
+
+  int type;
+  int payload_size;
+  union {
+    // Placeholder; in future more supported types will contribute to more
+    // union members here.
+    H264SEIRecoveryPoint recovery_point;
+  };
+};
+
+// Class to parse an Annex-B H.264 stream,
+// as specified in chapters 7 and Annex B of the H.264 spec.
+class H264Parser {
+ public:
+  enum Result {
+    kOk,
+    kInvalidStream,      // error in stream
+    kUnsupportedStream,  // stream not supported by the parser
+    kEOStream,           // end of stream
+  };
+
+  // Find offset from start of data to next NALU start code
+  // and size of found start code (3 or 4 bytes).
+  // If no start code is found, offset is pointing to the first unprocessed byte
+  // (i.e. the first byte that was not considered as a possible start of a start
+  // code) and |*start_code_size| is set to 0.
+  // Preconditions:
+  // - |data_size| >= 0
+  // Postconditions:
+  // - |*offset| is between 0 and |data_size| included.
+  //   It is strictly less than |data_size| if |data_size| > 0.
+  // - |*start_code_size| is either 0, 3 or 4.
+  static bool FindStartCode(const uint8_t* data,
+                            off_t data_size,
+                            off_t* offset,
+                            off_t* start_code_size);
+
+  // Wrapper for FindStartCode() that skips over start codes that
+  // may appear inside of |encrypted_ranges_|.
+  // Returns true if a start code was found. Otherwise returns false.
+  static bool FindStartCodeInClearRanges(const uint8_t* data,
+                                         off_t data_size,
+                                         const Ranges<const uint8_t*>& ranges,
+                                         off_t* offset,
+                                         off_t* start_code_size);
+
+  H264Parser();
+  ~H264Parser();
+
+  void Reset();
+  // Set current stream pointer to |stream| of |stream_size| in bytes,
+  // |stream| owned by caller.
+  // |subsamples| contains information about what parts of |stream| are
+  // encrypted.
+  void SetStream(const uint8_t* stream, off_t stream_size);
+  void SetEncryptedStream(const uint8_t* stream,
+                          off_t stream_size,
+                          const std::vector<SubsampleEntry>& subsamples);
+
+  // Read the stream to find the next NALU, identify it and return
+  // that information in |*nalu|. This advances the stream to the beginning
+  // of this NALU, but not past it, so subsequent calls to NALU-specific
+  // parsing functions (ParseSPS, etc.)  will parse this NALU.
+  // If the caller wishes to skip the current NALU, it can call this function
+  // again, instead of any NALU-type specific parse functions below.
+  Result AdvanceToNextNALU(H264NALU* nalu);
+
+  // NALU-specific parsing functions.
+  // These should be called after AdvanceToNextNALU().
+
+  // SPSes and PPSes are owned by the parser class and the memory for their
+  // structures is managed here, not by the caller, as they are reused
+  // across NALUs.
+  //
+  // Parse an SPS/PPS NALU and save their data in the parser, returning id
+  // of the parsed structure in |*pps_id|/|*sps_id|.
+  // To get a pointer to a given SPS/PPS structure, use GetSPS()/GetPPS(),
+  // passing the returned |*sps_id|/|*pps_id| as parameter.
+  // TODO(posciak,fischman): consider replacing returning Result from Parse*()
+  // methods with a scoped_ptr and adding an AtEOS() function to check for EOS
+  // if Parse*() return NULL.
+  Result ParseSPS(int* sps_id);
+  Result ParsePPS(int* pps_id);
+
+  // Return a pointer to SPS/PPS with given |sps_id|/|pps_id| or NULL if not
+  // present.
+  const H264SPS* GetSPS(int sps_id) const;
+  const H264PPS* GetPPS(int pps_id) const;
+
+  // Slice headers and SEI messages are not used across NALUs by the parser
+  // and can be discarded after current NALU, so the parser does not store
+  // them, nor does it manage their memory.
+  // The caller has to provide and manage it instead.
+
+  // Parse a slice header, returning it in |*shdr|. |*nalu| must be set to
+  // the NALU returned from AdvanceToNextNALU() and corresponding to |*shdr|.
+  Result ParseSliceHeader(const H264NALU& nalu, H264SliceHeader* shdr);
+
+  // Parse a SEI message, returning it in |*sei_msg|, provided and managed
+  // by the caller.
+  Result ParseSEI(H264SEIMessage* sei_msg);
+
+ private:
+  // Move the stream pointer to the beginning of the next NALU,
+  // i.e. pointing at the next start code.
+  // Return true if a NALU has been found.
+  // If a NALU is found:
+  // - its size in bytes is returned in |*nalu_size| and includes
+  //   the start code as well as the trailing zero bits.
+  // - the size in bytes of the start code is returned in |*start_code_size|.
+  bool LocateNALU(off_t* nalu_size, off_t* start_code_size);
+
+  // Exp-Golomb code parsing as specified in chapter 9.1 of the spec.
+  // Read one unsigned exp-Golomb code from the stream and return in |*val|.
+  Result ReadUE(int* val);
+
+  // Read one signed exp-Golomb code from the stream and return in |*val|.
+  Result ReadSE(int* val);
+
+  // Parse scaling lists (see spec).
+  Result ParseScalingList(int size, int* scaling_list, bool* use_default);
+  Result ParseSPSScalingLists(H264SPS* sps);
+  Result ParsePPSScalingLists(const H264SPS& sps, H264PPS* pps);
+
+  // Parse optional VUI parameters in SPS (see spec).
+  Result ParseVUIParameters(H264SPS* sps);
+  // Set |hrd_parameters_present| to true only if they are present.
+  Result ParseAndIgnoreHRDParameters(bool* hrd_parameters_present);
+
+  // Parse reference picture lists' modifications (see spec).
+  Result ParseRefPicListModifications(H264SliceHeader* shdr);
+  Result ParseRefPicListModification(int num_ref_idx_active_minus1,
+                                     H264ModificationOfPicNum* ref_list_mods);
+
+  // Parse prediction weight table (see spec).
+  Result ParsePredWeightTable(const H264SPS& sps, H264SliceHeader* shdr);
+
+  // Parse weighting factors (see spec).
+  Result ParseWeightingFactors(int num_ref_idx_active_minus1,
+                               int chroma_array_type,
+                               int luma_log2_weight_denom,
+                               int chroma_log2_weight_denom,
+                               H264WeightingFactors* w_facts);
+
+  // Parse decoded reference picture marking information (see spec).
+  Result ParseDecRefPicMarking(H264SliceHeader* shdr);
+
+  // Pointer to the current NALU in the stream.
+  const uint8_t* stream_;
+
+  // Bytes left in the stream after the current NALU.
+  off_t bytes_left_;
+
+  H264BitReader br_;
+
+  // PPSes and SPSes stored for future reference.
+  std::map<int, std::unique_ptr<H264SPS>> active_SPSes_;
+  std::map<int, std::unique_ptr<H264PPS>> active_PPSes_;
+
+  // Ranges of encrypted bytes in the buffer passed to
+  // SetEncryptedStream().
+  Ranges<const uint8_t*> encrypted_ranges_;
+
+  DISALLOW_COPY_AND_ASSIGN(H264Parser);
+};
+
+}  // namespace media
+
+#endif  // H264_PARSER_H_
diff --git a/vda/ranges.cc b/vda/ranges.cc
new file mode 100644
index 0000000..00400b5
--- /dev/null
+++ b/vda/ranges.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ranges.h"
+
+namespace media {
+
+template<>
+void Ranges<base::TimeDelta>::DCheckLT(const base::TimeDelta& lhs,
+                                       const base::TimeDelta& rhs) const {
+  DCHECK(lhs < rhs) << lhs.ToInternalValue() << " < " << rhs.ToInternalValue();
+}
+
+}  // namespace media
diff --git a/vda/ranges.h b/vda/ranges.h
new file mode 100644
index 0000000..98b32ce
--- /dev/null
+++ b/vda/ranges.h
@@ -0,0 +1,162 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RANGES_H_
+#define RANGES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <ostream>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+
+namespace media {
+
+// Ranges allows holding an ordered list of ranges of [start,end) intervals.
+// The canonical example use-case is holding the list of ranges of buffered
+// bytes or times in a <video> tag.
+template <class T>  // Endpoint type; typically a base::TimeDelta or an int64_t.
+class Ranges {
+ public:
+  // Allow copy & assign.
+
+  // Add (start,end) to this object, coallescing overlaps as appropriate.
+  // Returns the number of stored ranges, post coallescing.
+  size_t Add(T start, T end);
+
+  // Return the number of disjoint ranges.
+  size_t size() const;
+
+  // Return the "i"'th range's start & end (0-based).
+  T start(size_t i) const;
+  T end(size_t i) const;
+
+  // Clear all ranges.
+  void clear();
+
+  // Computes the intersection between this range and |other|.
+  Ranges<T> IntersectionWith(const Ranges<T>& other) const;
+
+ private:
+  // Wrapper around DCHECK_LT allowing comparisons of operator<<'able T's.
+  void DCheckLT(const T& lhs, const T& rhs) const;
+
+  // Disjoint, in increasing order of start.
+  std::vector<std::pair<T, T> > ranges_;
+};
+
+//////////////////////////////////////////////////////////////////////
+// EVERYTHING BELOW HERE IS IMPLEMENTATION DETAIL!!
+//////////////////////////////////////////////////////////////////////
+
+template<class T>
+size_t Ranges<T>::Add(T start, T end) {
+  if (start == end)  // Nothing to be done with empty ranges.
+    return ranges_.size();
+
+  DCheckLT(start, end);
+  size_t i;
+  // Walk along the array of ranges until |start| is no longer larger than the
+  // current interval's end.
+  for (i = 0; i < ranges_.size() && ranges_[i].second < start; ++i) {
+    // Empty body
+  }
+
+  // Now we know |start| belongs in the i'th slot.
+  // If i is the end of the range, append new range and done.
+  if (i == ranges_.size()) {
+    ranges_.push_back(std::make_pair(start, end));
+    return ranges_.size();
+  }
+
+  // If |end| is less than i->first, then [start,end) is a new (non-overlapping)
+  // i'th entry pushing everyone else back, and done.
+  if (end < ranges_[i].first) {
+    ranges_.insert(ranges_.begin() + i, std::make_pair(start, end));
+    return ranges_.size();
+  }
+
+  // Easy cases done.  Getting here means there is overlap between [start,end)
+  // and the existing ranges.
+
+  // Now: start <= i->second && i->first <= end
+  if (start < ranges_[i].first)
+    ranges_[i].first = start;
+  if (ranges_[i].second < end)
+    ranges_[i].second = end;
+
+  // Now: [start,end) is contained in the i'th range, and we'd be done, except
+  // for the fact that the newly-extended i'th range might now overlap
+  // subsequent ranges.  Merge until discontinuities appear.  Note that there's
+  // no need to test/merge previous ranges, since needing that would mean the
+  // original loop went too far.
+  while ((i + 1) < ranges_.size() &&
+         ranges_[i + 1].first <= ranges_[i].second) {
+    ranges_[i].second = std::max(ranges_[i].second, ranges_[i + 1].second);
+    ranges_.erase(ranges_.begin() + i + 1);
+  }
+
+  return ranges_.size();
+}
+
+template<>
+void Ranges<base::TimeDelta>::DCheckLT(const base::TimeDelta& lhs,
+                                       const base::TimeDelta& rhs) const;
+
+template<class T>
+void Ranges<T>::DCheckLT(const T& lhs, const T& rhs) const {
+  DCHECK_LT(lhs, rhs);
+}
+
+template<class T>
+size_t Ranges<T>::size() const {
+  return ranges_.size();
+}
+
+template<class T>
+T Ranges<T>::start(size_t i) const {
+  return ranges_[i].first;
+}
+
+template<class T>
+T Ranges<T>::end(size_t i) const {
+  return ranges_[i].second;
+}
+
+template<class T>
+void Ranges<T>::clear() {
+  ranges_.clear();
+}
+
+template<class T>
+Ranges<T> Ranges<T>::IntersectionWith(const Ranges<T>& other) const {
+  Ranges<T> result;
+
+  size_t i = 0;
+  size_t j = 0;
+
+  while (i < size() && j < other.size()) {
+    T max_start = std::max(start(i), other.start(j));
+    T min_end = std::min(end(i), other.end(j));
+
+    // Add an intersection range to the result if the ranges overlap.
+    if (max_start < min_end)
+      result.Add(max_start, min_end);
+
+    if (end(i) < other.end(j))
+      ++i;
+    else
+      ++j;
+  }
+
+  return result;
+}
+
+}  // namespace media
+
+#endif  // RANGES_H_
diff --git a/vda/size.h b/vda/size.h
new file mode 100644
index 0000000..4806ddc
--- /dev/null
+++ b/vda/size.h
@@ -0,0 +1,60 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SIZE_H_
+#define SIZE_H_
+
+#include <string>
+
+#include "base/strings/stringprintf.h"
+
+namespace media {
+
+// Helper struct for size to replace gfx::size usage from original code.
+// Only partial functions of gfx::size is implemented here.
+struct Size {
+ public:
+  Size() : width_(0), height_(0) {}
+  Size(int width, int height)
+      : width_(width < 0 ? 0 : width), height_(height < 0 ? 0 : height) {}
+
+  constexpr int width() const { return width_; }
+  constexpr int height() const { return height_; }
+
+  void set_width(int width) { width_ = width < 0 ? 0 : width; }
+  void set_height(int height) { height_ = height < 0 ? 0 : height; }
+
+  void SetSize(int width, int height) {
+    set_width(width);
+    set_height(height);
+  }
+
+  bool IsEmpty() const { return !width() || !height(); }
+
+  std::string ToString() const {
+    return base::StringPrintf("%dx%d", width(), height());
+  }
+
+  Size& operator=(const Size& ps) {
+    set_width(ps.width());
+    set_height(ps.height());
+    return *this;
+  }
+
+ private:
+  int width_;
+  int height_;
+};
+
+inline bool operator==(const Size& lhs, const Size& rhs) {
+  return lhs.width() == rhs.width() && lhs.height() == rhs.height();
+}
+
+inline bool operator!=(const Size& lhs, const Size& rhs) {
+  return !(lhs == rhs);
+}
+
+}  // namespace media
+
+#endif  // SIZE_H_
diff --git a/vda/subsample_entry.h b/vda/subsample_entry.h
new file mode 100644
index 0000000..e7529fb
--- /dev/null
+++ b/vda/subsample_entry.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SUBSAMPLE_ENTRY_H_
+#define SUBSAMPLE_ENTRY_H_
+
+#include <stdint.h>
+
+namespace media {
+
+// The Common Encryption spec provides for subsample encryption, where portions
+// of a sample are set in cleartext. A SubsampleEntry specifies the number of
+// clear and encrypted bytes in each subsample. For decryption, all of the
+// encrypted bytes in a sample should be considered a single logical stream,
+// regardless of how they are divided into subsamples, and the clear bytes
+// should not be considered as part of decryption. This is logically equivalent
+// to concatenating all 'cypher_bytes' portions of subsamples, decrypting that
+// result, and then copying each byte from the decrypted block over the
+// position of the corresponding encrypted byte.
+struct SubsampleEntry {
+  SubsampleEntry() : clear_bytes(0), cypher_bytes(0) {}
+  SubsampleEntry(uint32_t clear_bytes, uint32_t cypher_bytes)
+      : clear_bytes(clear_bytes), cypher_bytes(cypher_bytes) {}
+  uint32_t clear_bytes;
+  uint32_t cypher_bytes;
+};
+
+}  // namespace media
+
+#endif  // SUBSAMPLE_ENTRY_H_
diff --git a/vda/vp8_bool_decoder.cc b/vda/vp8_bool_decoder.cc
new file mode 100644
index 0000000..e42aef0
--- /dev/null
+++ b/vda/vp8_bool_decoder.cc
@@ -0,0 +1,208 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+/*
+ * Copyright (c) 2010, The WebM Project authors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Google, nor the WebM Project, nor the names
+ *     of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written
+ *     permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This file is modified from the dboolhuff.{c,h} from the WebM's libvpx
+// project. (http://www.webmproject.org/code)
+// It is used to decode bits from a vp8 stream.
+
+#include <limits.h>
+
+#include <algorithm>
+
+#include "base/numerics/safe_conversions.h"
+#include "vp8_bool_decoder.h"
+
+namespace media {
+
+#define VP8_BD_VALUE_BIT \
+  static_cast<int>(sizeof(Vp8BoolDecoder::value_) * CHAR_BIT)
+
+static const int kDefaultProbability = 0x80;  // 0x80 / 256 = 0.5
+
+// This is meant to be a large, positive constant that can still be efficiently
+// loaded as an immediate (on platforms like ARM, for example). Even relatively
+// modest values like 100 would work fine.
+#define VP8_LOTS_OF_BITS (0x40000000)
+
+// The number of leading zeros.
+static const unsigned char kVp8Norm[256] = {
+    0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+Vp8BoolDecoder::Vp8BoolDecoder()
+    : user_buffer_(NULL),
+      user_buffer_end_(NULL),
+      value_(0),
+      count_(-8),
+      range_(255) {
+}
+
+bool Vp8BoolDecoder::Initialize(const uint8_t* data, size_t size) {
+  if (data == NULL || size == 0)
+    return false;
+  user_buffer_start_ = data;
+  user_buffer_ = data;
+  user_buffer_end_ = data + size;
+  value_ = 0;
+  count_ = -8;
+  range_ = 255;
+  return true;
+}
+
+void Vp8BoolDecoder::FillDecoder() {
+  DCHECK(user_buffer_ != NULL);
+  int shift = VP8_BD_VALUE_BIT - CHAR_BIT - (count_ + CHAR_BIT);
+  size_t bytes_left = user_buffer_end_ - user_buffer_;
+  size_t bits_left = bytes_left * CHAR_BIT;
+  int x = static_cast<int>(shift + CHAR_BIT - bits_left);
+  int loop_end = 0;
+
+  if (x >= 0) {
+    count_ += VP8_LOTS_OF_BITS;
+    loop_end = x;
+  }
+
+  if (x < 0 || bits_left) {
+    while (shift >= loop_end) {
+      count_ += CHAR_BIT;
+      value_ |= static_cast<size_t>(*user_buffer_) << shift;
+      ++user_buffer_;
+      shift -= CHAR_BIT;
+    }
+  }
+}
+
+int Vp8BoolDecoder::ReadBit(int probability) {
+  int bit = 0;
+  size_t split = 1 + (((range_ - 1) * probability) >> 8);
+  if (count_ < 0)
+    FillDecoder();
+  size_t bigsplit = static_cast<size_t>(split) << (VP8_BD_VALUE_BIT - 8);
+
+  if (value_ >= bigsplit) {
+    range_ -= split;
+    value_ -= bigsplit;
+    bit = 1;
+  } else {
+    range_ = split;
+  }
+
+  size_t shift = kVp8Norm[range_];
+  range_ <<= shift;
+  value_ <<= shift;
+  count_ -= shift;
+
+  DCHECK_EQ(1U, (range_ >> 7));  // In the range [128, 255].
+
+  return bit;
+}
+
+bool Vp8BoolDecoder::ReadLiteral(size_t num_bits, int* out) {
+  DCHECK_LE(num_bits, sizeof(int) * CHAR_BIT);
+  *out = 0;
+  for (; num_bits > 0; --num_bits)
+    *out = (*out << 1) | ReadBit(kDefaultProbability);
+  return !OutOfBuffer();
+}
+
+bool Vp8BoolDecoder::ReadBool(bool* out, uint8_t probability) {
+  *out = !!ReadBit(probability);
+  return !OutOfBuffer();
+}
+
+bool Vp8BoolDecoder::ReadBool(bool* out) {
+  return ReadBool(out, kDefaultProbability);
+}
+
+bool Vp8BoolDecoder::ReadLiteralWithSign(size_t num_bits, int* out) {
+  ReadLiteral(num_bits, out);
+  // Read sign.
+  if (ReadBit(kDefaultProbability))
+    *out = -*out;
+  return !OutOfBuffer();
+}
+
+size_t Vp8BoolDecoder::BitOffset() {
+  int bit_count = count_ + 8;
+  if (bit_count > VP8_BD_VALUE_BIT)
+    // Capped at 0 to ignore buffer underrun.
+    bit_count = std::max(0, bit_count - VP8_LOTS_OF_BITS);
+  return (user_buffer_ - user_buffer_start_) * 8 - bit_count;
+}
+
+uint8_t Vp8BoolDecoder::GetRange() {
+  return base::checked_cast<uint8_t>(range_);
+}
+
+uint8_t Vp8BoolDecoder::GetBottom() {
+  if (count_ < 0)
+    FillDecoder();
+  return static_cast<uint8_t>(value_ >> (VP8_BD_VALUE_BIT - 8));
+}
+
+inline bool Vp8BoolDecoder::OutOfBuffer() {
+  // Check if we have reached the end of the buffer.
+  //
+  // Variable |count_| stores the number of bits in the |value_| buffer, minus
+  // 8. The top byte is part of the algorithm and the remainder is buffered to
+  // be shifted into it. So, if |count_| == 8, the top 16 bits of |value_| are
+  // occupied, 8 for the algorithm and 8 in the buffer.
+  //
+  // When reading a byte from the user's buffer, |count_| is filled with 8 and
+  // one byte is filled into the |value_| buffer. When we reach the end of the
+  // data, |count_| is additionally filled with VP8_LOTS_OF_BITS. So when
+  // |count_| == VP8_LOTS_OF_BITS - 1, the user's data has been exhausted.
+  return (count_ > VP8_BD_VALUE_BIT) && (count_ < VP8_LOTS_OF_BITS);
+}
+
+}  // namespace media
diff --git a/vda/vp8_bool_decoder.h b/vda/vp8_bool_decoder.h
new file mode 100644
index 0000000..445fd68
--- /dev/null
+++ b/vda/vp8_bool_decoder.h
@@ -0,0 +1,134 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+/*
+ * Copyright (c) 2010, The WebM Project authors. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Google, nor the WebM Project, nor the names
+ *     of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written
+ *     permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This file is modified from the dboolhuff.{c,h} from the WebM's libvpx
+// project. (http://www.webmproject.org/code)
+// It is used to decode bits from a vp8 stream.
+
+#ifndef VP8_BOOL_DECODER_H_
+#define VP8_BOOL_DECODER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace media {
+
+// A class to decode the VP8's boolean entropy coded stream. It's a variant of
+// arithmetic coding. See RFC 6386 - Chapter 7. Boolean Entropy Decoder.
+class Vp8BoolDecoder {
+ public:
+  Vp8BoolDecoder();
+
+  // Initializes the decoder to start decoding |data|, |size| being size
+  // of |data| in bytes. Returns false if |data| is NULL or empty.
+  bool Initialize(const uint8_t* data, size_t size);
+
+  // Reads a boolean from the coded stream. Returns false if it has reached the
+  // end of |data| and failed to read the boolean. The probability of |out| to
+  // be true is |probability| / 256, e.g., when |probability| is 0x80, the
+  // chance is 1/2 (i.e., 0x80 / 256).
+  bool ReadBool(bool* out, uint8_t probability);
+
+  // Reads a boolean from the coded stream with the default probability 1/2.
+  // Returns false if it has reached the end of |data| and failed to read the
+  // boolean.
+  bool ReadBool(bool* out);
+
+  // Reads a "literal", that is, a "num_bits"-wide unsigned value whose bits
+  // come high- to low-order, with each bit encoded at probability 1/2.
+  // Returns false if it has reached the end of |data| and failed to read the
+  // literal.
+  bool ReadLiteral(size_t num_bits, int* out);
+
+  // Reads a literal with sign from the coded stream. This is similar to
+  // the ReadListeral(), it first read a "num_bits"-wide unsigned value, and
+  // then read an extra bit as the sign of the literal. Returns false if it has
+  // reached the end of |data| and failed to read the literal or the sign.
+  // This is different from the "read_signed_literal(d, n)" defined in RFC 6386.
+  bool ReadLiteralWithSign(size_t num_bits, int* out);
+
+  // The following methods are used to get the internal states of the decoder.
+
+  // Returns the bit offset to the current top bit of the coded stream. It is
+  // also the number of bits that have been written in the corresponding
+  // encoding state. More specifically, we have the following constraint:
+  //    w + (bottom * S) <= v < w + (bottom + range) * S,
+  // where "w" is for the bits already written,
+  //       "v" is for the possible values of the coded number.
+  //       "S" is the scale for the current bit position,
+  //           i.e., S = pow(2, -(n + 8)), where "n" is the bit number of "w".
+  // BitOffset() returns the bit count of "w", i.e., "n".
+  size_t BitOffset();
+
+  // Gets the "bottom" of the current coded value. See BitOffset() for
+  // more details.
+  uint8_t GetBottom();
+
+  // Gets the "range" of the current coded value. See BitOffset() for
+  // more details.
+  uint8_t GetRange();
+
+ private:
+  // Reads the next bit from the coded stream. The probability of the bit to
+  // be one is |probability| / 256.
+  int ReadBit(int probability);
+
+  // Fills more bits from |user_buffer_| to |value_|. We shall keep at least 8
+  // bits of the current |user_buffer_| in |value_|.
+  void FillDecoder();
+
+  // Returns true iff we have ran out of bits.
+  bool OutOfBuffer();
+
+  const uint8_t* user_buffer_;
+  const uint8_t* user_buffer_start_;
+  const uint8_t* user_buffer_end_;
+  size_t value_;
+  int count_;
+  size_t range_;
+
+  DISALLOW_COPY_AND_ASSIGN(Vp8BoolDecoder);
+};
+
+}  // namespace media
+
+#endif  // VP8_BOOL_DECODER_H_
diff --git a/vda/vp8_decoder.cc b/vda/vp8_decoder.cc
new file mode 100644
index 0000000..d9ee6e4
--- /dev/null
+++ b/vda/vp8_decoder.cc
@@ -0,0 +1,195 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vp8_decoder.h"
+
+namespace media {
+
+VP8Decoder::VP8Accelerator::VP8Accelerator() {}
+
+VP8Decoder::VP8Accelerator::~VP8Accelerator() {}
+
+VP8Decoder::VP8Decoder(VP8Accelerator* accelerator)
+    : state_(kNeedStreamMetadata),
+      curr_frame_start_(nullptr),
+      frame_size_(0),
+      accelerator_(accelerator) {
+  DCHECK(accelerator_);
+}
+
+VP8Decoder::~VP8Decoder() {}
+
+bool VP8Decoder::Flush() {
+  DVLOG(2) << "Decoder flush";
+  Reset();
+  return true;
+}
+
+void VP8Decoder::SetStream(const uint8_t* ptr, size_t size) {
+  DCHECK(ptr);
+  DCHECK(size);
+
+  curr_frame_start_ = ptr;
+  frame_size_ = size;
+  DVLOG(4) << "New input stream at: " << (void*)ptr << " size: " << size;
+}
+
+void VP8Decoder::Reset() {
+  curr_pic_ = nullptr;
+  curr_frame_hdr_ = nullptr;
+  curr_frame_start_ = nullptr;
+  frame_size_ = 0;
+
+  last_frame_ = nullptr;
+  golden_frame_ = nullptr;
+  alt_frame_ = nullptr;
+
+  if (state_ == kDecoding)
+    state_ = kAfterReset;
+}
+
+VP8Decoder::DecodeResult VP8Decoder::Decode() {
+  if (!curr_frame_start_ || frame_size_ == 0)
+    return kRanOutOfStreamData;
+
+  if (!curr_frame_hdr_) {
+    curr_frame_hdr_.reset(new Vp8FrameHeader());
+    if (!parser_.ParseFrame(curr_frame_start_, frame_size_,
+                            curr_frame_hdr_.get())) {
+      DVLOG(1) << "Error during decode";
+      state_ = kError;
+      return kDecodeError;
+    }
+  }
+
+  if (curr_frame_hdr_->IsKeyframe()) {
+    Size new_pic_size(curr_frame_hdr_->width, curr_frame_hdr_->height);
+    if (new_pic_size.IsEmpty())
+      return kDecodeError;
+
+    if (new_pic_size != pic_size_) {
+      DVLOG(2) << "New resolution: " << new_pic_size.ToString();
+      pic_size_ = new_pic_size;
+
+      DCHECK(!curr_pic_);
+      last_frame_ = nullptr;
+      golden_frame_ = nullptr;
+      alt_frame_ = nullptr;
+
+      return kAllocateNewSurfaces;
+    }
+
+    state_ = kDecoding;
+  } else {
+    if (state_ != kDecoding) {
+      // Need a resume point.
+      curr_frame_hdr_.reset();
+      return kRanOutOfStreamData;
+    }
+  }
+
+  curr_pic_ = accelerator_->CreateVP8Picture();
+  if (!curr_pic_)
+    return kRanOutOfSurfaces;
+
+  if (!DecodeAndOutputCurrentFrame())
+    return kDecodeError;
+
+  return kRanOutOfStreamData;
+}
+
+void VP8Decoder::RefreshReferenceFrames() {
+  if (curr_frame_hdr_->IsKeyframe()) {
+    last_frame_ = curr_pic_;
+    golden_frame_ = curr_pic_;
+    alt_frame_ = curr_pic_;
+    return;
+  }
+
+  // Save current golden since we overwrite it here,
+  // but may have to use it to update alt below.
+  scoped_refptr<VP8Picture> curr_golden = golden_frame_;
+
+  if (curr_frame_hdr_->refresh_golden_frame) {
+    golden_frame_ = curr_pic_;
+  } else {
+    switch (curr_frame_hdr_->copy_buffer_to_golden) {
+      case Vp8FrameHeader::COPY_LAST_TO_GOLDEN:
+        DCHECK(last_frame_);
+        golden_frame_ = last_frame_;
+        break;
+
+      case Vp8FrameHeader::COPY_ALT_TO_GOLDEN:
+        DCHECK(alt_frame_);
+        golden_frame_ = alt_frame_;
+        break;
+    }
+  }
+
+  if (curr_frame_hdr_->refresh_alternate_frame) {
+    alt_frame_ = curr_pic_;
+  } else {
+    switch (curr_frame_hdr_->copy_buffer_to_alternate) {
+      case Vp8FrameHeader::COPY_LAST_TO_ALT:
+        DCHECK(last_frame_);
+        alt_frame_ = last_frame_;
+        break;
+
+      case Vp8FrameHeader::COPY_GOLDEN_TO_ALT:
+        DCHECK(curr_golden);
+        alt_frame_ = curr_golden;
+        break;
+    }
+  }
+
+  if (curr_frame_hdr_->refresh_last)
+    last_frame_ = curr_pic_;
+}
+
+bool VP8Decoder::DecodeAndOutputCurrentFrame() {
+  DCHECK(!pic_size_.IsEmpty());
+  DCHECK(curr_pic_);
+  DCHECK(curr_frame_hdr_);
+
+  if (curr_frame_hdr_->IsKeyframe()) {
+    horizontal_scale_ = curr_frame_hdr_->horizontal_scale;
+    vertical_scale_ = curr_frame_hdr_->vertical_scale;
+  } else {
+    // Populate fields from decoder state instead.
+    curr_frame_hdr_->width = pic_size_.width();
+    curr_frame_hdr_->height = pic_size_.height();
+    curr_frame_hdr_->horizontal_scale = horizontal_scale_;
+    curr_frame_hdr_->vertical_scale = vertical_scale_;
+  }
+
+  if (!accelerator_->SubmitDecode(curr_pic_, curr_frame_hdr_.get(), last_frame_,
+                                  golden_frame_, alt_frame_))
+    return false;
+
+  if (curr_frame_hdr_->show_frame)
+    if (!accelerator_->OutputPicture(curr_pic_))
+      return false;
+
+  RefreshReferenceFrames();
+
+  curr_pic_ = nullptr;
+  curr_frame_hdr_ = nullptr;
+  curr_frame_start_ = nullptr;
+  frame_size_ = 0;
+  return true;
+}
+
+Size VP8Decoder::GetPicSize() const {
+  return pic_size_;
+}
+
+size_t VP8Decoder::GetRequiredNumOfPictures() const {
+  const size_t kVP8NumFramesActive = 4;
+  // TODO(johnylin): see if we could get rid of kMaxVideoFrames.
+  const size_t kMaxVideoFrames = 4;
+  const size_t kPicsInPipeline = kMaxVideoFrames + 2;
+  return kVP8NumFramesActive + kPicsInPipeline;
+}
+
+}  // namespace media
diff --git a/vda/vp8_decoder.h b/vda/vp8_decoder.h
new file mode 100644
index 0000000..653da40
--- /dev/null
+++ b/vda/vp8_decoder.h
@@ -0,0 +1,113 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VP8_DECODER_H_
+#define VP8_DECODER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "accelerated_video_decoder.h"
+#include "size.h"
+#include "vp8_parser.h"
+#include "vp8_picture.h"
+
+namespace media {
+
+// Clients of this class are expected to pass raw VP8 stream and are expected
+// to provide an implementation of VP8Accelerator for offloading final steps
+// of the decoding process.
+//
+// This class must be created, called and destroyed on a single thread, and
+// does nothing internally on any other thread.
+class VP8Decoder : public AcceleratedVideoDecoder {
+ public:
+  class VP8Accelerator {
+   public:
+    VP8Accelerator();
+    virtual ~VP8Accelerator();
+
+    // Create a new VP8Picture that the decoder client can use for decoding
+    // and pass back to this accelerator for decoding or reference.
+    // When the picture is no longer needed by decoder, it will just drop
+    // its reference to it, and it may do so at any time.
+    // Note that this may return nullptr if accelerator is not able to provide
+    // any new pictures at given time. The decoder is expected to handle
+    // this situation as normal and return from Decode() with kRanOutOfSurfaces.
+    virtual scoped_refptr<VP8Picture> CreateVP8Picture() = 0;
+
+    // Submit decode for |pic|, taking as arguments |frame_hdr| with parsed
+    // VP8 frame header information for current frame, and using |last_frame|,
+    // |golden_frame| and |alt_frame| as references, as per VP8 specification.
+    // Note that this runs the decode in hardware.
+    // Return true if successful.
+    virtual bool SubmitDecode(const scoped_refptr<VP8Picture>& pic,
+                              const Vp8FrameHeader* frame_hdr,
+                              const scoped_refptr<VP8Picture>& last_frame,
+                              const scoped_refptr<VP8Picture>& golden_frame,
+                              const scoped_refptr<VP8Picture>& alt_frame) = 0;
+
+    // Schedule output (display) of |pic|. Note that returning from this
+    // method does not mean that |pic| has already been outputted (displayed),
+    // but guarantees that all pictures will be outputted in the same order
+    // as this method was called for them. Decoder may drop its reference
+    // to |pic| after calling this method.
+    // Return true if successful.
+    virtual bool OutputPicture(const scoped_refptr<VP8Picture>& pic) = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(VP8Accelerator);
+  };
+
+  VP8Decoder(VP8Accelerator* accelerator);
+  ~VP8Decoder() override;
+
+  // AcceleratedVideoDecoder implementation.
+  bool Flush() override WARN_UNUSED_RESULT;
+  void Reset() override;
+  void SetStream(const uint8_t* ptr, size_t size) override;
+  DecodeResult Decode() override WARN_UNUSED_RESULT;
+  Size GetPicSize() const override;
+  size_t GetRequiredNumOfPictures() const override;
+
+ private:
+  bool DecodeAndOutputCurrentFrame();
+  void RefreshReferenceFrames();
+
+  enum State {
+    kNeedStreamMetadata,  // After initialization, need a keyframe.
+    kDecoding,            // Ready to decode from any point.
+    kAfterReset,          // After Reset(), need a resume point.
+    kError,               // Error in decode, can't continue.
+  };
+
+  State state_;
+
+  Vp8Parser parser_;
+
+  std::unique_ptr<Vp8FrameHeader> curr_frame_hdr_;
+  scoped_refptr<VP8Picture> curr_pic_;
+  scoped_refptr<VP8Picture> last_frame_;
+  scoped_refptr<VP8Picture> golden_frame_;
+  scoped_refptr<VP8Picture> alt_frame_;
+
+  const uint8_t* curr_frame_start_;
+  size_t frame_size_;
+
+  Size pic_size_;
+  int horizontal_scale_;
+  int vertical_scale_;
+
+  VP8Accelerator* accelerator_;
+
+  DISALLOW_COPY_AND_ASSIGN(VP8Decoder);
+};
+
+}  // namespace media
+
+#endif  // VP8_DECODER_H_
diff --git a/vda/vp8_parser.cc b/vda/vp8_parser.cc
new file mode 100644
index 0000000..46eb669
--- /dev/null
+++ b/vda/vp8_parser.cc
@@ -0,0 +1,876 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains an implementation of a VP8 raw stream parser,
+// as defined in RFC 6386.
+
+#include "base/logging.h"
+#include "vp8_parser.h"
+
+namespace media {
+
+#define ERROR_RETURN(what)                           \
+  do {                                               \
+    DVLOG(1) << "Error while trying to read " #what; \
+    return false;                                    \
+  } while (0)
+
+#define BD_READ_BOOL_OR_RETURN(out) \
+  do {                              \
+    if (!bd_.ReadBool(out))         \
+      ERROR_RETURN(out);            \
+  } while (0)
+
+#define BD_READ_BOOL_WITH_PROB_OR_RETURN(out, prob) \
+  do {                                              \
+    if (!bd_.ReadBool(out, prob))                   \
+      ERROR_RETURN(out);                            \
+  } while (0)
+
+#define BD_READ_UNSIGNED_OR_RETURN(num_bits, out) \
+  do {                                            \
+    int _out;                                     \
+    if (!bd_.ReadLiteral(num_bits, &_out))        \
+      ERROR_RETURN(out);                          \
+    *out = _out;                                  \
+  } while (0)
+
+#define BD_READ_SIGNED_OR_RETURN(num_bits, out)    \
+  do {                                             \
+    int _out;                                      \
+    if (!bd_.ReadLiteralWithSign(num_bits, &_out)) \
+      ERROR_RETURN(out);                           \
+    *out = _out;                                   \
+  } while (0)
+
+Vp8FrameHeader::Vp8FrameHeader() {
+  memset(this, 0, sizeof(*this));
+}
+
+Vp8Parser::Vp8Parser() : stream_(nullptr), bytes_left_(0) {
+}
+
+Vp8Parser::~Vp8Parser() {
+}
+
+bool Vp8Parser::ParseFrame(const uint8_t* ptr,
+                           size_t frame_size,
+                           Vp8FrameHeader* fhdr) {
+  stream_ = ptr;
+  bytes_left_ = frame_size;
+
+  memset(fhdr, 0, sizeof(*fhdr));
+  fhdr->data = stream_;
+  fhdr->frame_size = bytes_left_;
+
+  if (!ParseFrameTag(fhdr))
+    return false;
+
+  fhdr->first_part_offset = stream_ - fhdr->data;
+
+  if (!ParseFrameHeader(fhdr))
+    return false;
+
+  if (!ParsePartitions(fhdr))
+    return false;
+
+  DVLOG(4) << "Frame parsed, start: " << static_cast<const void*>(ptr)
+           << ", size: " << frame_size
+           << ", offsets: to first_part=" << fhdr->first_part_offset
+           << ", to macroblock data (in bits)=" << fhdr->macroblock_bit_offset;
+
+  return true;
+}
+
+static inline uint32_t GetBitsAt(uint32_t data, size_t shift, size_t num_bits) {
+  return ((data >> shift) & ((1 << num_bits) - 1));
+}
+
+bool Vp8Parser::ParseFrameTag(Vp8FrameHeader* fhdr) {
+  const size_t kFrameTagSize = 3;
+  if (bytes_left_ < kFrameTagSize)
+    return false;
+
+  uint32_t frame_tag = (stream_[2] << 16) | (stream_[1] << 8) | stream_[0];
+  fhdr->key_frame =
+      static_cast<Vp8FrameHeader::FrameType>(GetBitsAt(frame_tag, 0, 1));
+  fhdr->version = GetBitsAt(frame_tag, 1, 2);
+  fhdr->is_experimental = !!GetBitsAt(frame_tag, 3, 1);
+  fhdr->show_frame =!!GetBitsAt(frame_tag, 4, 1);
+  fhdr->first_part_size = GetBitsAt(frame_tag, 5, 19);
+
+  stream_ += kFrameTagSize;
+  bytes_left_ -= kFrameTagSize;
+
+  if (fhdr->IsKeyframe()) {
+    const size_t kKeyframeTagSize = 7;
+    if (bytes_left_ < kKeyframeTagSize)
+      return false;
+
+    static const uint8_t kVp8StartCode[] = {0x9d, 0x01, 0x2a};
+    if (memcmp(stream_, kVp8StartCode, sizeof(kVp8StartCode)) != 0)
+        return false;
+
+    stream_ += sizeof(kVp8StartCode);
+    bytes_left_ -= sizeof(kVp8StartCode);
+
+    uint16_t data = (stream_[1] << 8) | stream_[0];
+    fhdr->width = data & 0x3fff;
+    fhdr->horizontal_scale = data >> 14;
+
+    data = (stream_[3] << 8) | stream_[2];
+    fhdr->height = data & 0x3fff;
+    fhdr->vertical_scale = data >> 14;
+
+    stream_ += 4;
+    bytes_left_ -= 4;
+  }
+
+  return true;
+}
+
+bool Vp8Parser::ParseFrameHeader(Vp8FrameHeader* fhdr) {
+  if (!bd_.Initialize(stream_, bytes_left_))
+    return false;
+
+  bool keyframe = fhdr->IsKeyframe();
+  if (keyframe) {
+    unsigned int data;
+    BD_READ_UNSIGNED_OR_RETURN(1, &data);  // color_space
+    BD_READ_UNSIGNED_OR_RETURN(1, &data);  // clamping_type
+  }
+
+  if (!ParseSegmentationHeader(keyframe))
+    return false;
+
+  fhdr->segmentation_hdr = curr_segmentation_hdr_;
+
+  if (!ParseLoopFilterHeader(keyframe))
+    return false;
+
+  fhdr->loopfilter_hdr = curr_loopfilter_hdr_;
+
+  int log2_nbr_of_dct_partitions;
+  BD_READ_UNSIGNED_OR_RETURN(2, &log2_nbr_of_dct_partitions);
+  fhdr->num_of_dct_partitions = static_cast<size_t>(1)
+                                << log2_nbr_of_dct_partitions;
+
+  if (!ParseQuantizationHeader(&fhdr->quantization_hdr))
+    return false;
+
+  if (keyframe) {
+    BD_READ_BOOL_OR_RETURN(&fhdr->refresh_entropy_probs);
+  } else {
+    BD_READ_BOOL_OR_RETURN(&fhdr->refresh_golden_frame);
+    BD_READ_BOOL_OR_RETURN(&fhdr->refresh_alternate_frame);
+
+    int refresh_mode;
+    if (!fhdr->refresh_golden_frame) {
+      BD_READ_UNSIGNED_OR_RETURN(2, &refresh_mode);
+      fhdr->copy_buffer_to_golden =
+          static_cast<Vp8FrameHeader::GoldenRefreshMode>(refresh_mode);
+    }
+
+    if (!fhdr->refresh_alternate_frame) {
+      BD_READ_UNSIGNED_OR_RETURN(2, &refresh_mode);
+      fhdr->copy_buffer_to_alternate =
+          static_cast<Vp8FrameHeader::AltRefreshMode>(refresh_mode);
+    }
+
+    BD_READ_UNSIGNED_OR_RETURN(1, &fhdr->sign_bias_golden);
+    BD_READ_UNSIGNED_OR_RETURN(1, &fhdr->sign_bias_alternate);
+    BD_READ_BOOL_OR_RETURN(&fhdr->refresh_entropy_probs);
+    BD_READ_BOOL_OR_RETURN(&fhdr->refresh_last);
+  }
+
+  if (keyframe)
+    ResetProbs();
+
+  fhdr->entropy_hdr = curr_entropy_hdr_;
+
+  if (!ParseTokenProbs(&fhdr->entropy_hdr, fhdr->refresh_entropy_probs))
+    return false;
+
+  BD_READ_BOOL_OR_RETURN(&fhdr->mb_no_skip_coeff);
+  if (fhdr->mb_no_skip_coeff)
+    BD_READ_UNSIGNED_OR_RETURN(8, &fhdr->prob_skip_false);
+
+  if (!keyframe) {
+    BD_READ_UNSIGNED_OR_RETURN(8, &fhdr->prob_intra);
+    BD_READ_UNSIGNED_OR_RETURN(8, &fhdr->prob_last);
+    BD_READ_UNSIGNED_OR_RETURN(8, &fhdr->prob_gf);
+  }
+
+  if (!ParseIntraProbs(&fhdr->entropy_hdr, fhdr->refresh_entropy_probs,
+                       keyframe))
+    return false;
+
+  if (!keyframe) {
+    if (!ParseMVProbs(&fhdr->entropy_hdr, fhdr->refresh_entropy_probs))
+      return false;
+  }
+
+  fhdr->macroblock_bit_offset = bd_.BitOffset();
+  fhdr->bool_dec_range = bd_.GetRange();
+  fhdr->bool_dec_value = bd_.GetBottom();
+  fhdr->bool_dec_count = 7 - (bd_.BitOffset() + 7) % 8;
+
+  return true;
+}
+
+bool Vp8Parser::ParseSegmentationHeader(bool keyframe) {
+  Vp8SegmentationHeader* shdr = &curr_segmentation_hdr_;
+
+  if (keyframe)
+    memset(shdr, 0, sizeof(*shdr));
+
+  BD_READ_BOOL_OR_RETURN(&shdr->segmentation_enabled);
+  if (!shdr->segmentation_enabled)
+    return true;
+
+  BD_READ_BOOL_OR_RETURN(&shdr->update_mb_segmentation_map);
+  BD_READ_BOOL_OR_RETURN(&shdr->update_segment_feature_data);
+  if (shdr->update_segment_feature_data) {
+    int mode;
+    BD_READ_UNSIGNED_OR_RETURN(1, &mode);
+    shdr->segment_feature_mode =
+        static_cast<Vp8SegmentationHeader::SegmentFeatureMode>(mode);
+
+    for (size_t i = 0; i < kMaxMBSegments; ++i) {
+      bool quantizer_update;
+      BD_READ_BOOL_OR_RETURN(&quantizer_update);
+      if (quantizer_update)
+        BD_READ_SIGNED_OR_RETURN(7, &shdr->quantizer_update_value[i]);
+      else
+        shdr->quantizer_update_value[i] = 0;
+    }
+
+    for (size_t i = 0; i < kMaxMBSegments; ++i) {
+      bool loop_filter_update;
+      BD_READ_BOOL_OR_RETURN(&loop_filter_update);
+      if (loop_filter_update)
+        BD_READ_SIGNED_OR_RETURN(6, &shdr->lf_update_value[i]);
+      else
+        shdr->lf_update_value[i] = 0;
+    }
+  }
+
+  if (shdr->update_mb_segmentation_map) {
+    for (size_t i = 0; i < kNumMBFeatureTreeProbs; ++i) {
+      bool segment_prob_update;
+      BD_READ_BOOL_OR_RETURN(&segment_prob_update);
+      if (segment_prob_update)
+        BD_READ_UNSIGNED_OR_RETURN(8, &shdr->segment_prob[i]);
+      else
+        shdr->segment_prob[i] = Vp8SegmentationHeader::kDefaultSegmentProb;
+    }
+  }
+
+  return true;
+}
+
+bool Vp8Parser::ParseLoopFilterHeader(bool keyframe) {
+  Vp8LoopFilterHeader* lfhdr = &curr_loopfilter_hdr_;
+
+  if (keyframe)
+    memset(lfhdr, 0, sizeof(*lfhdr));
+
+  int type;
+  BD_READ_UNSIGNED_OR_RETURN(1, &type);
+  lfhdr->type = static_cast<Vp8LoopFilterHeader::Type>(type);
+  BD_READ_UNSIGNED_OR_RETURN(6, &lfhdr->level);
+  BD_READ_UNSIGNED_OR_RETURN(3, &lfhdr->sharpness_level);
+  BD_READ_BOOL_OR_RETURN(&lfhdr->loop_filter_adj_enable);
+
+  if (lfhdr->loop_filter_adj_enable) {
+    BD_READ_BOOL_OR_RETURN(&lfhdr->mode_ref_lf_delta_update);
+    if (lfhdr->mode_ref_lf_delta_update) {
+      for (size_t i = 0; i < kNumBlockContexts; ++i) {
+        bool ref_frame_delta_update_flag;
+        BD_READ_BOOL_OR_RETURN(&ref_frame_delta_update_flag);
+        if (ref_frame_delta_update_flag)
+          BD_READ_SIGNED_OR_RETURN(6, &lfhdr->ref_frame_delta[i]);
+      }
+
+      for (size_t i = 0; i < kNumBlockContexts; ++i) {
+        bool mb_mode_delta_update_flag;
+        BD_READ_BOOL_OR_RETURN(&mb_mode_delta_update_flag);
+        if (mb_mode_delta_update_flag)
+          BD_READ_SIGNED_OR_RETURN(6, &lfhdr->mb_mode_delta[i]);
+      }
+    }
+  }
+
+  return true;
+}
+
+bool Vp8Parser::ParseQuantizationHeader(Vp8QuantizationHeader* qhdr) {
+  // If any of the delta values is not present, the delta should be zero.
+  memset(qhdr, 0, sizeof(*qhdr));
+
+  BD_READ_UNSIGNED_OR_RETURN(7, &qhdr->y_ac_qi);
+
+  bool delta_present;
+
+  BD_READ_BOOL_OR_RETURN(&delta_present);
+  if (delta_present)
+    BD_READ_SIGNED_OR_RETURN(4, &qhdr->y_dc_delta);
+
+  BD_READ_BOOL_OR_RETURN(&delta_present);
+  if (delta_present)
+    BD_READ_SIGNED_OR_RETURN(4, &qhdr->y2_dc_delta);
+
+  BD_READ_BOOL_OR_RETURN(&delta_present);
+  if (delta_present)
+    BD_READ_SIGNED_OR_RETURN(4, &qhdr->y2_ac_delta);
+
+  BD_READ_BOOL_OR_RETURN(&delta_present);
+  if (delta_present)
+    BD_READ_SIGNED_OR_RETURN(4, &qhdr->uv_dc_delta);
+
+  BD_READ_BOOL_OR_RETURN(&delta_present);
+  if (delta_present)
+    BD_READ_SIGNED_OR_RETURN(4, &qhdr->uv_ac_delta);
+
+  return true;
+}
+
+// See spec for details on these values.
+const uint8_t kCoeffUpdateProbs[kNumBlockTypes][kNumCoeffBands]
+    [kNumPrevCoeffContexts][kNumEntropyNodes] = {
+  {
+    {
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255},
+      {249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255},
+      {234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255},
+      {250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255},
+      {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+  },
+  {
+    {
+      {217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255},
+      {234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255},
+    },
+    {
+      {255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+      {250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+  },
+  {
+    {
+      {186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255},
+      {234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255},
+      {251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255},
+    },
+    {
+      {255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+  },
+  {
+    {
+      {248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255},
+      {248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+      {246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+      {252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255},
+      {248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+      {253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+      {252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255},
+      {250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+    {
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+      {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
+    },
+  },
+};
+
+const uint8_t kKeyframeYModeProbs[kNumYModeProbs] = {145, 156, 163, 128};
+const uint8_t kKeyframeUVModeProbs[kNumUVModeProbs] = {142, 114, 183};
+
+const uint8_t kDefaultYModeProbs[kNumYModeProbs] = {112, 86, 140, 37};
+const uint8_t kDefaultUVModeProbs[kNumUVModeProbs] = {162, 101, 204};
+
+const uint8_t kDefaultCoeffProbs[kNumBlockTypes][kNumCoeffBands]
+    [kNumPrevCoeffContexts][kNumEntropyNodes] = {
+  {
+    {
+      {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+      {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+      {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+    },
+    {
+      {253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128},
+      {189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128},
+      {106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128},
+    },
+    {
+      {  1,  98, 248, 255, 236, 226, 255, 255, 128, 128, 128},
+      {181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128},
+      { 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128},
+    },
+    {
+      {  1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128},
+      {184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128},
+      { 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128},
+    },
+    {
+      {  1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128},
+      {170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128},
+      { 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128},
+    },
+    {
+      {  1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128},
+      {207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128},
+      {102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128},
+    },
+    {
+      {  1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128},
+      {177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128},
+      { 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128},
+    },
+    {
+      {  1,   1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+      {246,   1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+      {255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+    }
+  },
+  {
+    {
+      {198,  35, 237, 223, 193, 187, 162, 160, 145, 155,  62},
+      {131,  45, 198, 221, 172, 176, 220, 157, 252, 221,   1},
+      { 68,  47, 146, 208, 149, 167, 221, 162, 255, 223, 128},
+    },
+    {
+      {  1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128},
+      {184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128},
+      { 81,  99, 181, 242, 176, 190, 249, 202, 255, 255, 128},
+    },
+    {
+      {  1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128},
+      { 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128},
+      { 23,  91, 163, 242, 170, 187, 247, 210, 255, 255, 128},
+    },
+    {
+      {  1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128},
+      {109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128},
+      { 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128},
+    },
+    {
+      {  1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128},
+      { 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128},
+      { 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128},
+    },
+    {
+      {  1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128},
+      {124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128},
+      { 35,  77, 181, 251, 193, 211, 255, 205, 128, 128, 128},
+    },
+    {
+      {  1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128},
+      {121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128},
+      { 45,  99, 188, 251, 195, 217, 255, 224, 128, 128, 128},
+    },
+    {
+      {  1,   1, 251, 255, 213, 255, 128, 128, 128, 128, 128},
+      {203,   1, 248, 255, 255, 128, 128, 128, 128, 128, 128},
+      {137,   1, 177, 255, 224, 255, 128, 128, 128, 128, 128},
+    }
+  },
+  {
+    {
+      {253,   9, 248, 251, 207, 208, 255, 192, 128, 128, 128},
+      {175,  13, 224, 243, 193, 185, 249, 198, 255, 255, 128},
+      { 73,  17, 171, 221, 161, 179, 236, 167, 255, 234, 128},
+    },
+    {
+      {  1,  95, 247, 253, 212, 183, 255, 255, 128, 128, 128},
+      {239,  90, 244, 250, 211, 209, 255, 255, 128, 128, 128},
+      {155,  77, 195, 248, 188, 195, 255, 255, 128, 128, 128},
+    },
+    {
+      {  1,  24, 239, 251, 218, 219, 255, 205, 128, 128, 128},
+      {201,  51, 219, 255, 196, 186, 128, 128, 128, 128, 128},
+      { 69,  46, 190, 239, 201, 218, 255, 228, 128, 128, 128},
+    },
+    {
+      {  1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128},
+      {223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128},
+      {141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128},
+    },
+    {
+      {  1,  16, 248, 255, 255, 128, 128, 128, 128, 128, 128},
+      {190,  36, 230, 255, 236, 255, 128, 128, 128, 128, 128},
+      {149,   1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+    },
+    {
+      {  1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+      {247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+      {240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+    },
+    {
+      {  1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128},
+      {213,  62, 250, 255, 255, 128, 128, 128, 128, 128, 128},
+      { 55,  93, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+    },
+    {
+      {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+      {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+      {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128},
+    }
+  },
+  {
+    {
+      {202,  24, 213, 235, 186, 191, 220, 160, 240, 175, 255},
+      {126,  38, 182, 232, 169, 184, 228, 174, 255, 187, 128},
+      { 61,  46, 138, 219, 151, 178, 240, 170, 255, 216, 128},
+    },
+    {
+      {  1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128},
+      {166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128},
+      { 39,  77, 162, 232, 172, 180, 245, 178, 255, 255, 128},
+    },
+    {
+      {  1,  52, 220, 246, 198, 199, 249, 220, 255, 255, 128},
+      {124,  74, 191, 243, 183, 193, 250, 221, 255, 255, 128},
+      { 24,  71, 130, 219, 154, 170, 243, 182, 255, 255, 128},
+    },
+    {
+      {  1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128},
+      {149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128},
+      { 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128}
+    },
+    {
+      {  1,  81, 230, 252, 204, 203, 255, 192, 128, 128, 128},
+      {123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128},
+      { 20,  95, 153, 243, 164, 173, 255, 203, 128, 128, 128},
+    },
+    {
+      {  1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128},
+      {168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128},
+      { 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128},
+    },
+    {
+      {  1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128},
+      {141,  84, 213, 252, 201, 202, 255, 219, 128, 128, 128},
+      { 42,  80, 160, 240, 162, 185, 255, 205, 128, 128, 128},
+    },
+    {
+      {  1,   1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+      {244,   1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+      {238,   1, 255, 128, 128, 128, 128, 128, 128, 128, 128},
+    },
+  },
+};
+
+const uint8_t kMVUpdateProbs[kNumMVContexts][kNumMVProbs] =
+{
+  {
+    237, 246, 253, 253, 254, 254, 254, 254, 254,
+    254, 254, 254, 254, 254, 250, 250, 252, 254, 254,
+  },
+  {
+    231, 243, 245, 253, 254, 254, 254, 254, 254,
+    254, 254, 254, 254, 254, 251, 251, 254, 254, 254,
+  },
+};
+
+const uint8_t kDefaultMVProbs[kNumMVContexts][kNumMVProbs] = {
+  {
+    162, 128, 225, 146, 172, 147, 214,  39, 156,
+    128, 129, 132,  75, 145, 178, 206, 239, 254, 254,
+  },
+  {
+    164, 128, 204, 170, 119, 235, 140, 230, 228,
+    128, 130, 130,  74, 148, 180, 203, 236, 254, 254,
+  },
+};
+
+void Vp8Parser::ResetProbs() {
+  static_assert(
+      sizeof(curr_entropy_hdr_.coeff_probs) == sizeof(kDefaultCoeffProbs),
+      "coeff_probs_arrays_must_be_of_correct_size");
+  memcpy(curr_entropy_hdr_.coeff_probs, kDefaultCoeffProbs,
+         sizeof(curr_entropy_hdr_.coeff_probs));
+
+  static_assert(sizeof(curr_entropy_hdr_.mv_probs) == sizeof(kDefaultMVProbs),
+                "mv_probs_arrays_must_be_of_correct_size");
+  memcpy(curr_entropy_hdr_.mv_probs, kDefaultMVProbs,
+         sizeof(curr_entropy_hdr_.mv_probs));
+
+  static_assert(
+      sizeof(curr_entropy_hdr_.y_mode_probs) == sizeof(kDefaultYModeProbs),
+      "y_probs_arrays_must_be_of_correct_size");
+  memcpy(curr_entropy_hdr_.y_mode_probs, kDefaultYModeProbs,
+         sizeof(curr_entropy_hdr_.y_mode_probs));
+
+  static_assert(
+      sizeof(curr_entropy_hdr_.uv_mode_probs) == sizeof(kDefaultUVModeProbs),
+      "uv_probs_arrays_must_be_of_correct_size");
+  memcpy(curr_entropy_hdr_.uv_mode_probs, kDefaultUVModeProbs,
+         sizeof(curr_entropy_hdr_.uv_mode_probs));
+}
+
+bool Vp8Parser::ParseTokenProbs(Vp8EntropyHeader* ehdr,
+                                bool update_curr_probs) {
+  for (size_t i = 0; i < kNumBlockTypes; ++i) {
+    for (size_t j = 0; j < kNumCoeffBands; ++j) {
+      for (size_t k = 0; k < kNumPrevCoeffContexts; ++k) {
+        for (size_t l = 0; l < kNumEntropyNodes; ++l) {
+          bool coeff_prob_update_flag;
+          BD_READ_BOOL_WITH_PROB_OR_RETURN(&coeff_prob_update_flag,
+                                           kCoeffUpdateProbs[i][j][k][l]);
+          if (coeff_prob_update_flag)
+            BD_READ_UNSIGNED_OR_RETURN(8, &ehdr->coeff_probs[i][j][k][l]);
+        }
+      }
+    }
+  }
+
+  if (update_curr_probs) {
+    memcpy(curr_entropy_hdr_.coeff_probs, ehdr->coeff_probs,
+           sizeof(curr_entropy_hdr_.coeff_probs));
+  }
+
+  return true;
+}
+
+bool Vp8Parser::ParseIntraProbs(Vp8EntropyHeader* ehdr,
+                                bool update_curr_probs,
+                                bool keyframe) {
+  if (keyframe) {
+    static_assert(
+        sizeof(ehdr->y_mode_probs) == sizeof(kKeyframeYModeProbs),
+        "y_probs_arrays_must_be_of_correct_size");
+    memcpy(ehdr->y_mode_probs, kKeyframeYModeProbs,
+           sizeof(ehdr->y_mode_probs));
+
+    static_assert(
+        sizeof(ehdr->uv_mode_probs) == sizeof(kKeyframeUVModeProbs),
+        "uv_probs_arrays_must_be_of_correct_size");
+    memcpy(ehdr->uv_mode_probs, kKeyframeUVModeProbs,
+           sizeof(ehdr->uv_mode_probs));
+  } else {
+    bool intra_16x16_prob_update_flag;
+    BD_READ_BOOL_OR_RETURN(&intra_16x16_prob_update_flag);
+    if (intra_16x16_prob_update_flag) {
+      for (size_t i = 0; i < kNumYModeProbs; ++i)
+        BD_READ_UNSIGNED_OR_RETURN(8, &ehdr->y_mode_probs[i]);
+
+      if (update_curr_probs) {
+        memcpy(curr_entropy_hdr_.y_mode_probs, ehdr->y_mode_probs,
+               sizeof(curr_entropy_hdr_.y_mode_probs));
+      }
+    }
+
+    bool intra_chroma_prob_update_flag;
+    BD_READ_BOOL_OR_RETURN(&intra_chroma_prob_update_flag);
+    if (intra_chroma_prob_update_flag) {
+      for (size_t i = 0; i < kNumUVModeProbs; ++i)
+        BD_READ_UNSIGNED_OR_RETURN(8, &ehdr->uv_mode_probs[i]);
+
+      if (update_curr_probs) {
+        memcpy(curr_entropy_hdr_.uv_mode_probs, ehdr->uv_mode_probs,
+               sizeof(curr_entropy_hdr_.uv_mode_probs));
+      }
+    }
+  }
+
+  return true;
+}
+
+bool Vp8Parser::ParseMVProbs(Vp8EntropyHeader* ehdr, bool update_curr_probs) {
+  for (size_t mv_ctx = 0; mv_ctx < kNumMVContexts; ++mv_ctx) {
+    for (size_t p = 0; p < kNumMVProbs; ++p) {
+      bool mv_prob_update_flag;
+      BD_READ_BOOL_WITH_PROB_OR_RETURN(&mv_prob_update_flag,
+                                       kMVUpdateProbs[mv_ctx][p]);
+      if (mv_prob_update_flag) {
+        uint8_t prob;
+        BD_READ_UNSIGNED_OR_RETURN(7, &prob);
+        ehdr->mv_probs[mv_ctx][p] = prob ? (prob << 1) : 1;
+      }
+    }
+  }
+
+  if (update_curr_probs) {
+    memcpy(curr_entropy_hdr_.mv_probs, ehdr->mv_probs,
+           sizeof(curr_entropy_hdr_.mv_probs));
+  }
+
+  return true;
+}
+
+bool Vp8Parser::ParsePartitions(Vp8FrameHeader* fhdr) {
+  CHECK_GE(fhdr->num_of_dct_partitions, 1u);
+  CHECK_LE(fhdr->num_of_dct_partitions, kMaxDCTPartitions);
+
+  // DCT partitions start after the first partition and partition size values
+  // that follow it. There are num_of_dct_partitions - 1 sizes stored in the
+  // stream after the first partition, each 3 bytes long. The size of last
+  // DCT partition is not stored in the stream, but is instead calculated by
+  // taking the remainder of the frame size after the penultimate DCT partition.
+  size_t first_dct_pos = fhdr->first_part_offset + fhdr->first_part_size +
+                         (fhdr->num_of_dct_partitions - 1) * 3;
+
+  // Make sure we have enough data for the first partition and partition sizes.
+  if (fhdr->frame_size < first_dct_pos)
+    return false;
+
+  // Total size of all DCT partitions.
+  size_t bytes_left = fhdr->frame_size - first_dct_pos;
+
+  // Position ourselves at the beginning of partition size values.
+  const uint8_t* ptr =
+      fhdr->data + fhdr->first_part_offset + fhdr->first_part_size;
+
+  // Read sizes from the stream (if present).
+  for (size_t i = 0; i < fhdr->num_of_dct_partitions - 1; ++i) {
+    fhdr->dct_partition_sizes[i] = (ptr[2] << 16) | (ptr[1] << 8) | ptr[0];
+
+    // Make sure we have enough data in the stream for ith partition and
+    // subtract its size from total.
+    if (bytes_left < fhdr->dct_partition_sizes[i])
+      return false;
+
+    bytes_left -= fhdr->dct_partition_sizes[i];
+
+    // Move to the position of the next partition size value.
+    ptr += 3;
+  }
+
+  // The remainder of the data belongs to the last DCT partition.
+  fhdr->dct_partition_sizes[fhdr->num_of_dct_partitions - 1] = bytes_left;
+
+  DVLOG(4) << "Control part size: " << fhdr->first_part_size;
+  for (size_t i = 0; i < fhdr->num_of_dct_partitions; ++i)
+    DVLOG(4) << "DCT part " << i << " size: " << fhdr->dct_partition_sizes[i];
+
+  return true;
+}
+
+}  // namespace media
diff --git a/vda/vp8_parser.h b/vda/vp8_parser.h
new file mode 100644
index 0000000..ef9326c
--- /dev/null
+++ b/vda/vp8_parser.h
@@ -0,0 +1,198 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains an implementation of a VP8 raw stream parser,
+// as defined in RFC 6386.
+
+#ifndef VP8_PARSER_H_
+#define VP8_PARSER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "vp8_bool_decoder.h"
+
+namespace media {
+
+// See spec for definitions of values/fields.
+const size_t kMaxMBSegments = 4;
+const size_t kNumMBFeatureTreeProbs = 3;
+
+// Member of Vp8FrameHeader and will be 0-initialized
+// in Vp8FrameHeader's constructor.
+struct Vp8SegmentationHeader {
+  enum SegmentFeatureMode { FEATURE_MODE_DELTA = 0, FEATURE_MODE_ABSOLUTE = 1 };
+
+  bool segmentation_enabled;
+  bool update_mb_segmentation_map;
+  bool update_segment_feature_data;
+  SegmentFeatureMode segment_feature_mode;
+
+  int8_t quantizer_update_value[kMaxMBSegments];
+  int8_t lf_update_value[kMaxMBSegments];
+  static const int kDefaultSegmentProb = 255;
+  uint8_t segment_prob[kNumMBFeatureTreeProbs];
+};
+
+const size_t kNumBlockContexts = 4;
+
+// Member of Vp8FrameHeader and will be 0-initialized
+// in Vp8FrameHeader's constructor.
+struct Vp8LoopFilterHeader {
+  enum Type { LOOP_FILTER_TYPE_NORMAL = 0, LOOP_FILTER_TYPE_SIMPLE = 1 };
+  Type type;
+  uint8_t level;
+  uint8_t sharpness_level;
+  bool loop_filter_adj_enable;
+  bool mode_ref_lf_delta_update;
+
+  int8_t ref_frame_delta[kNumBlockContexts];
+  int8_t mb_mode_delta[kNumBlockContexts];
+};
+
+// Member of Vp8FrameHeader and will be 0-initialized
+// in Vp8FrameHeader's constructor.
+struct Vp8QuantizationHeader {
+  uint8_t y_ac_qi;
+  int8_t y_dc_delta;
+  int8_t y2_dc_delta;
+  int8_t y2_ac_delta;
+  int8_t uv_dc_delta;
+  int8_t uv_ac_delta;
+};
+
+const size_t kNumBlockTypes = 4;
+const size_t kNumCoeffBands = 8;
+const size_t kNumPrevCoeffContexts = 3;
+const size_t kNumEntropyNodes = 11;
+
+const size_t kNumMVContexts = 2;
+const size_t kNumMVProbs = 19;
+
+const size_t kNumYModeProbs = 4;
+const size_t kNumUVModeProbs = 3;
+
+// Member of Vp8FrameHeader and will be 0-initialized
+// in Vp8FrameHeader's constructor.
+struct Vp8EntropyHeader {
+  uint8_t coeff_probs[kNumBlockTypes][kNumCoeffBands][kNumPrevCoeffContexts]
+                     [kNumEntropyNodes];
+
+  uint8_t y_mode_probs[kNumYModeProbs];
+  uint8_t uv_mode_probs[kNumUVModeProbs];
+
+  uint8_t mv_probs[kNumMVContexts][kNumMVProbs];
+};
+
+const size_t kMaxDCTPartitions = 8;
+
+struct Vp8FrameHeader {
+  Vp8FrameHeader();
+
+  enum FrameType { KEYFRAME = 0, INTERFRAME = 1 };
+  bool IsKeyframe() const { return key_frame == KEYFRAME; }
+
+  enum GoldenRefreshMode {
+    COPY_LAST_TO_GOLDEN = 1,
+    COPY_ALT_TO_GOLDEN = 2,
+  };
+
+  enum AltRefreshMode {
+    COPY_LAST_TO_ALT = 1,
+    COPY_GOLDEN_TO_ALT = 2,
+  };
+
+  FrameType key_frame;
+  uint8_t version;
+  bool is_experimental;
+  bool show_frame;
+  size_t first_part_size;
+
+  uint16_t width;
+  uint8_t horizontal_scale;
+  uint16_t height;
+  uint8_t vertical_scale;
+
+  Vp8SegmentationHeader segmentation_hdr;
+  Vp8LoopFilterHeader loopfilter_hdr;
+  Vp8QuantizationHeader quantization_hdr;
+
+  size_t num_of_dct_partitions;
+
+  Vp8EntropyHeader entropy_hdr;
+
+  bool refresh_entropy_probs;
+  bool refresh_golden_frame;
+  bool refresh_alternate_frame;
+  GoldenRefreshMode copy_buffer_to_golden;
+  AltRefreshMode copy_buffer_to_alternate;
+  uint8_t sign_bias_golden;
+  uint8_t sign_bias_alternate;
+  bool refresh_last;
+
+  bool mb_no_skip_coeff;
+  uint8_t prob_skip_false;
+  uint8_t prob_intra;
+  uint8_t prob_last;
+  uint8_t prob_gf;
+
+  const uint8_t* data;
+  size_t frame_size;
+
+  size_t dct_partition_sizes[kMaxDCTPartitions];
+  // Offset in bytes from data.
+  off_t first_part_offset;
+  // Offset in bits from first_part_offset.
+  off_t macroblock_bit_offset;
+
+  // Bool decoder state
+  uint8_t bool_dec_range;
+  uint8_t bool_dec_value;
+  uint8_t bool_dec_count;
+};
+
+// A parser for raw VP8 streams as specified in RFC 6386.
+class Vp8Parser {
+ public:
+  Vp8Parser();
+  ~Vp8Parser();
+
+  // Try to parse exactly one VP8 frame starting at |ptr| and of size |size|,
+  // filling the parsed data in |fhdr|. Return true on success.
+  // Size has to be exactly the size of the frame and coming from the caller,
+  // who needs to acquire it from elsewhere (normally from a container).
+  bool ParseFrame(const uint8_t* ptr, size_t size, Vp8FrameHeader* fhdr);
+
+ private:
+  bool ParseFrameTag(Vp8FrameHeader* fhdr);
+  bool ParseFrameHeader(Vp8FrameHeader* fhdr);
+
+  bool ParseSegmentationHeader(bool keyframe);
+  bool ParseLoopFilterHeader(bool keyframe);
+  bool ParseQuantizationHeader(Vp8QuantizationHeader* qhdr);
+  bool ParseTokenProbs(Vp8EntropyHeader* ehdr, bool update_curr_probs);
+  bool ParseIntraProbs(Vp8EntropyHeader* ehdr,
+                       bool update_curr_probs,
+                       bool keyframe);
+  bool ParseMVProbs(Vp8EntropyHeader* ehdr, bool update_curr_probs);
+  bool ParsePartitions(Vp8FrameHeader* fhdr);
+  void ResetProbs();
+
+  // These persist across calls to ParseFrame() and may be used and/or updated
+  // for subsequent frames if the stream instructs us to do so.
+  Vp8SegmentationHeader curr_segmentation_hdr_;
+  Vp8LoopFilterHeader curr_loopfilter_hdr_;
+  Vp8EntropyHeader curr_entropy_hdr_;
+
+  const uint8_t* stream_;
+  size_t bytes_left_;
+  Vp8BoolDecoder bd_;
+
+  DISALLOW_COPY_AND_ASSIGN(Vp8Parser);
+};
+
+}  // namespace media
+
+#endif  // VP8_PARSER_H_
diff --git a/vda/vp8_picture.cc b/vda/vp8_picture.cc
new file mode 100644
index 0000000..7c01a11
--- /dev/null
+++ b/vda/vp8_picture.cc
@@ -0,0 +1,13 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vp8_picture.h"
+
+namespace media {
+
+VP8Picture::VP8Picture() {}
+
+VP8Picture::~VP8Picture() {}
+
+}  // namespace media
diff --git a/vda/vp8_picture.h b/vda/vp8_picture.h
new file mode 100644
index 0000000..b8e7417
--- /dev/null
+++ b/vda/vp8_picture.h
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VP8_PICTURE_H_
+#define VP8_PICTURE_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+
+namespace media {
+
+class VP8Picture : public base::RefCounted<VP8Picture> {
+ public:
+  VP8Picture();
+
+ protected:
+  friend class base::RefCounted<VP8Picture>;
+  virtual ~VP8Picture();
+
+  DISALLOW_COPY_AND_ASSIGN(VP8Picture);
+};
+
+}  // namespace media
+
+#endif  // VP8_PICTURE_H_
diff --git a/vda/vp9_bool_decoder.cc b/vda/vp9_bool_decoder.cc
new file mode 100644
index 0000000..bf227b2
--- /dev/null
+++ b/vda/vp9_bool_decoder.cc
@@ -0,0 +1,164 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vp9_bool_decoder.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "bit_reader.h"
+
+namespace media {
+
+namespace {
+
+// This is an optimization lookup table for the loop in spec 9.2.2.
+//     while BoolRange <= 128:
+//       read 1 bit
+//       BoolRange *= 2
+// This table indicates how many iterations to run for a given BoolRange. So
+// the loop could be reduced to
+//     read (kCountToShiftTo128[BoolRange]) bits
+const int kCountToShiftTo128[256] = {
+    0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3,
+    3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+}  // namespace
+
+Vp9BoolDecoder::Vp9BoolDecoder() {}
+
+Vp9BoolDecoder::~Vp9BoolDecoder() {}
+
+// 9.2.1 Initialization process for Boolean decoder
+bool Vp9BoolDecoder::Initialize(const uint8_t* data, size_t size) {
+  DCHECK(data);
+  if (size < 1) {
+    DVLOG(1) << "input size of bool decoder shall be at least 1";
+    valid_ = false;
+    return false;
+  }
+
+  reader_.reset(new BitReader(data, size));
+  valid_ = true;
+
+  bool_value_ = 0;
+  count_to_fill_ = 8;
+  bool_range_ = 255;
+  if (ReadLiteral(1) != 0) {
+    DVLOG(1) << "marker bit should be 0";
+    valid_ = false;
+    return false;
+  }
+  return true;
+}
+
+// Fill at least |count_to_fill_| bits and prefill remain bits of |bool_value_|
+// if data is enough.
+bool Vp9BoolDecoder::Fill() {
+  DCHECK_GE(count_to_fill_, 0);
+
+  int bits_left = reader_->bits_available();
+  if (bits_left < count_to_fill_) {
+    valid_ = false;
+    DVLOG(1) << "Vp9BoolDecoder reads beyond the end of stream";
+    return false;
+  }
+
+  DCHECK_LE(count_to_fill_, kBoolSize);
+  int max_bits_to_read = kBigBoolBitSize - kBoolSize + count_to_fill_;
+  int bits_to_read = std::min(max_bits_to_read, bits_left);
+
+  BigBool data;
+  reader_->ReadBits(bits_to_read, &data);
+  bool_value_ |= data << (max_bits_to_read - bits_to_read);
+  count_to_fill_ -= bits_to_read;
+
+  return true;
+}
+
+// 9.2.2 Boolean decoding process
+bool Vp9BoolDecoder::ReadBool(int prob) {
+  DCHECK(reader_);
+
+  if (count_to_fill_ > 0) {
+    if (!Fill())
+      return false;
+  }
+
+  unsigned int split = (bool_range_ * prob + (256 - prob)) >> kBoolSize;
+  BigBool big_split = static_cast<BigBool>(split)
+                      << (kBigBoolBitSize - kBoolSize);
+
+  bool bit;
+  if (bool_value_ < big_split) {
+    bool_range_ = split;
+    bit = false;
+  } else {
+    bool_range_ -= split;
+    bool_value_ -= big_split;
+    bit = true;
+  }
+
+  // Need to fill |count| bits next time in order to make |bool_range_| >=
+  // 128.
+  DCHECK_LT(bool_range_, arraysize(kCountToShiftTo128));
+  DCHECK_GT(bool_range_, 0u);
+  int count = kCountToShiftTo128[bool_range_];
+  bool_range_ <<= count;
+  bool_value_ <<= count;
+  count_to_fill_ += count;
+
+  return bit;
+}
+
+// 9.2.4 Parsing process for read_literal
+uint8_t Vp9BoolDecoder::ReadLiteral(int bits) {
+  DCHECK_LT(static_cast<size_t>(bits), sizeof(uint8_t) * 8);
+  DCHECK(reader_);
+
+  uint8_t x = 0;
+  for (int i = 0; i < bits; i++)
+    x = 2 * x + ReadBool(128);
+
+  return x;
+}
+
+bool Vp9BoolDecoder::ConsumePaddingBits() {
+  DCHECK(reader_);
+
+  if (count_to_fill_ > reader_->bits_available()) {
+    // 9.2.2 Boolean decoding process
+    // Although we actually don't used the value, spec says the bitstream
+    // should have enough bits to fill bool range, this should never happen.
+    DVLOG(2) << "not enough bits in bitstream to fill bool range";
+    return false;
+  }
+
+  if (bool_value_ != 0) {
+    DVLOG(1) << "prefilled padding bits are not zero";
+    return false;
+  }
+  while (reader_->bits_available() > 0) {
+    int data;
+    int size_to_read =
+        std::min(reader_->bits_available(), static_cast<int>(sizeof(data) * 8));
+    reader_->ReadBits(size_to_read, &data);
+    if (data != 0) {
+      DVLOG(1) << "padding bits are not zero";
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace media
diff --git a/vda/vp9_bool_decoder.h b/vda/vp9_bool_decoder.h
new file mode 100644
index 0000000..3862e51
--- /dev/null
+++ b/vda/vp9_bool_decoder.h
@@ -0,0 +1,72 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VP9_BOOL_DECODER_H_
+#define VP9_BOOL_DECODER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+
+namespace media {
+
+class BitReader;
+
+class Vp9BoolDecoder {
+ public:
+  Vp9BoolDecoder();
+  ~Vp9BoolDecoder();
+
+  // |data| is the input buffer with |size| bytes.
+  // Returns true if read first marker bit successfully.
+  bool Initialize(const uint8_t* data, size_t size);
+
+  // Returns true if none of the reads since the last Initialize() call has
+  // gone beyond the end of available data.
+  bool IsValid() const { return valid_; }
+
+  // Reads one bit. B(p).
+  // If the read goes beyond the end of buffer, the return value is undefined.
+  bool ReadBool(int prob);
+
+  // Reads a literal. L(n).
+  // If the read goes beyond the end of buffer, the return value is undefined.
+  uint8_t ReadLiteral(int bits);
+
+  // Consumes padding bits up to end of data. Returns true if no
+  // padding bits or they are all zero.
+  bool ConsumePaddingBits();
+
+ private:
+  // The highest 8 bits of BigBool is actual "bool value". The remain bits
+  // are optimization of prefill buffer.
+  using BigBool = size_t;
+  // The size of "bool value" used for boolean decoding defined in spec.
+  const int kBoolSize = 8;
+  const int kBigBoolBitSize = sizeof(BigBool) * 8;
+
+  bool Fill();
+
+  std::unique_ptr<BitReader> reader_;
+
+  // Indicates if none of the reads since the last Initialize() call has gone
+  // beyond the end of available data.
+  bool valid_ = true;
+
+  BigBool bool_value_ = 0;
+
+  // Need to fill at least |count_to_fill_| bits. Negative value means extra
+  // bits pre-filled.
+  int count_to_fill_ = 0;
+  unsigned int bool_range_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(Vp9BoolDecoder);
+};
+
+}  // namespace media
+
+#endif  // VP9_BOOL_DECODER_H_
diff --git a/vda/vp9_compressed_header_parser.cc b/vda/vp9_compressed_header_parser.cc
new file mode 100644
index 0000000..d5ee772
--- /dev/null
+++ b/vda/vp9_compressed_header_parser.cc
@@ -0,0 +1,293 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vp9_compressed_header_parser.h"
+
+#include "base/logging.h"
+
+namespace media {
+
+namespace {
+
+// 6.3.6 Inv recenter noneg syntax, inv_recenter_nonneg().
+int InvRecenterNonneg(int v, int m) {
+  DCHECK_LE(m, kVp9MaxProb / 2);
+  if (v > 2 * m)
+    return v;
+
+  if (v & 1)
+    return m - ((v + 1) >> 1);
+  return m + (v >> 1);
+}
+
+// 6.3.5 Inv remap prob syntax, inv_remap_prob().
+Vp9Prob InvRemapProb(uint8_t delta_prob, uint8_t prob) {
+  static uint8_t inv_map_table[kVp9MaxProb] = {
+      7,   20,  33,  46,  59,  72,  85,  98,  111, 124, 137, 150, 163, 176,
+      189, 202, 215, 228, 241, 254, 1,   2,   3,   4,   5,   6,   8,   9,
+      10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  21,  22,  23,  24,
+      25,  26,  27,  28,  29,  30,  31,  32,  34,  35,  36,  37,  38,  39,
+      40,  41,  42,  43,  44,  45,  47,  48,  49,  50,  51,  52,  53,  54,
+      55,  56,  57,  58,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
+      70,  71,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,
+      86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  99,  100,
+      101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 112, 113, 114, 115,
+      116, 117, 118, 119, 120, 121, 122, 123, 125, 126, 127, 128, 129, 130,
+      131, 132, 133, 134, 135, 136, 138, 139, 140, 141, 142, 143, 144, 145,
+      146, 147, 148, 149, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
+      161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+      177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 190, 191,
+      192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 203, 204, 205, 206,
+      207, 208, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, 220, 221,
+      222, 223, 224, 225, 226, 227, 229, 230, 231, 232, 233, 234, 235, 236,
+      237, 238, 239, 240, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
+      252, 253, 253};
+  uint8_t m = prob;
+  uint8_t v = delta_prob;
+  DCHECK_GE(m, 1);
+  DCHECK_LE(m, kVp9MaxProb);
+  DCHECK_LT(v, arraysize(inv_map_table));
+  v = inv_map_table[v];
+  m--;
+  if ((m << 1) <= kVp9MaxProb) {
+    return 1 + InvRecenterNonneg(v, m);
+  } else {
+    return kVp9MaxProb - InvRecenterNonneg(v, kVp9MaxProb - 1 - m);
+  }
+}
+
+}  // namespace
+
+Vp9CompressedHeaderParser::Vp9CompressedHeaderParser() {}
+
+// 6.3.1 Tx mode syntax
+void Vp9CompressedHeaderParser::ReadTxMode(Vp9FrameHeader* fhdr) {
+  int tx_mode;
+  if (fhdr->quant_params.IsLossless()) {
+    tx_mode = Vp9CompressedHeader::ONLY_4X4;
+  } else {
+    tx_mode = reader_.ReadLiteral(2);
+    if (tx_mode == Vp9CompressedHeader::ALLOW_32X32)
+      tx_mode += reader_.ReadLiteral(1);
+  }
+  fhdr->compressed_header.tx_mode =
+      static_cast<Vp9CompressedHeader::Vp9TxMode>(tx_mode);
+}
+
+// 6.3.4 Decode term subexp syntax
+uint8_t Vp9CompressedHeaderParser::DecodeTermSubexp() {
+  if (reader_.ReadLiteral(1) == 0)
+    return reader_.ReadLiteral(4);
+  if (reader_.ReadLiteral(1) == 0)
+    return reader_.ReadLiteral(4) + 16;
+  if (reader_.ReadLiteral(1) == 0)
+    return reader_.ReadLiteral(5) + 32;
+  uint8_t v = reader_.ReadLiteral(7);
+  if (v < 65)
+    return v + 64;
+  return (v << 1) - 1 + reader_.ReadLiteral(1);
+}
+
+// 6.3.3 Diff update prob syntax
+void Vp9CompressedHeaderParser::DiffUpdateProb(Vp9Prob* prob) {
+  const Vp9Prob kUpdateProb = 252;
+  if (reader_.ReadBool(kUpdateProb)) {
+    uint8_t delta_prob = DecodeTermSubexp();
+    *prob = InvRemapProb(delta_prob, *prob);
+  }
+}
+
+// Helper function to DiffUpdateProb an array of probs.
+template <int N>
+void Vp9CompressedHeaderParser::DiffUpdateProbArray(Vp9Prob (&prob_array)[N]) {
+  for (auto& x : prob_array) {
+    DiffUpdateProb(&x);
+  }
+}
+
+// 6.3.2 Tx mode probs syntax
+void Vp9CompressedHeaderParser::ReadTxModeProbs(
+    Vp9FrameContext* frame_context) {
+  for (auto& a : frame_context->tx_probs_8x8) {
+    DiffUpdateProbArray(a);
+  }
+  for (auto& a : frame_context->tx_probs_16x16) {
+    DiffUpdateProbArray(a);
+  }
+  for (auto& a : frame_context->tx_probs_32x32) {
+    DiffUpdateProbArray(a);
+  }
+}
+
+// 6.3.7 Coef probs syntax
+void Vp9CompressedHeaderParser::ReadCoefProbs(Vp9FrameHeader* fhdr) {
+  const int tx_mode_to_biggest_tx_size[Vp9CompressedHeader::TX_MODES] = {
+      0, 1, 2, 3, 3,
+  };
+  const int max_tx_size =
+      tx_mode_to_biggest_tx_size[fhdr->compressed_header.tx_mode];
+  for (int tx_size = 0; tx_size <= max_tx_size; tx_size++) {
+    if (reader_.ReadLiteral(1) == 0)
+      continue;
+
+    for (auto& ai : fhdr->frame_context.coef_probs[tx_size]) {
+      for (auto& aj : ai) {
+        for (auto& ak : aj) {
+          int max_l = (ak == aj[0]) ? 3 : 6;
+          for (int l = 0; l < max_l; l++) {
+            DiffUpdateProbArray(ak[l]);
+          }
+        }
+      }
+    }
+  }
+}
+
+// 6.3.8 Skip probs syntax
+void Vp9CompressedHeaderParser::ReadSkipProb(Vp9FrameContext* frame_context) {
+  DiffUpdateProbArray(frame_context->skip_prob);
+}
+
+// 6.3.9 Inter mode probs syntax
+void Vp9CompressedHeaderParser::ReadInterModeProbs(
+    Vp9FrameContext* frame_context) {
+  for (auto& a : frame_context->inter_mode_probs)
+    DiffUpdateProbArray(a);
+}
+
+// 6.3.10 Interp filter probs syntax
+void Vp9CompressedHeaderParser::ReadInterpFilterProbs(
+    Vp9FrameContext* frame_context) {
+  for (auto& a : frame_context->interp_filter_probs)
+    DiffUpdateProbArray(a);
+}
+
+// 6.3.11 Intra inter probs syntax
+void Vp9CompressedHeaderParser::ReadIsInterProbs(
+    Vp9FrameContext* frame_context) {
+  DiffUpdateProbArray(frame_context->is_inter_prob);
+}
+
+// 6.3.12 Frame reference mode syntax
+void Vp9CompressedHeaderParser::ReadFrameReferenceMode(Vp9FrameHeader* fhdr) {
+  bool compound_reference_allowed = false;
+  for (int i = VP9_FRAME_LAST + 1; i < VP9_FRAME_MAX; i++)
+    if (fhdr->ref_frame_sign_bias[i] != fhdr->ref_frame_sign_bias[1])
+      compound_reference_allowed = true;
+
+  if (compound_reference_allowed && reader_.ReadLiteral(1)) {
+    fhdr->compressed_header.reference_mode =
+        reader_.ReadLiteral(1) ? REFERENCE_MODE_SELECT : COMPOUND_REFERENCE;
+  } else {
+    fhdr->compressed_header.reference_mode = SINGLE_REFERENCE;
+  }
+}
+
+// 6.3.13 Frame reference mode probs syntax
+void Vp9CompressedHeaderParser::ReadFrameReferenceModeProbs(
+    Vp9FrameHeader* fhdr) {
+  Vp9FrameContext* frame_context = &fhdr->frame_context;
+  if (fhdr->compressed_header.reference_mode == REFERENCE_MODE_SELECT)
+    DiffUpdateProbArray(frame_context->comp_mode_prob);
+
+  if (fhdr->compressed_header.reference_mode != COMPOUND_REFERENCE)
+    for (auto& a : frame_context->single_ref_prob)
+      DiffUpdateProbArray(a);
+
+  if (fhdr->compressed_header.reference_mode != SINGLE_REFERENCE)
+    DiffUpdateProbArray(frame_context->comp_ref_prob);
+}
+
+// 6.3.14 Y mode probs syntax
+void Vp9CompressedHeaderParser::ReadYModeProbs(Vp9FrameContext* frame_context) {
+  for (auto& a : frame_context->y_mode_probs)
+    DiffUpdateProbArray(a);
+}
+
+// 6.3.15 Partition probs syntax
+void Vp9CompressedHeaderParser::ReadPartitionProbs(
+    Vp9FrameContext* frame_context) {
+  for (auto& a : frame_context->partition_probs)
+    DiffUpdateProbArray(a);
+}
+
+// 6.3.16 MV probs syntax
+void Vp9CompressedHeaderParser::ReadMvProbs(bool allow_high_precision_mv,
+                                            Vp9FrameContext* frame_context) {
+  UpdateMvProbArray(frame_context->mv_joint_probs);
+
+  for (int i = 0; i < 2; i++) {
+    UpdateMvProb(&frame_context->mv_sign_prob[i]);
+    UpdateMvProbArray(frame_context->mv_class_probs[i]);
+    UpdateMvProb(&frame_context->mv_class0_bit_prob[i]);
+    UpdateMvProbArray(frame_context->mv_bits_prob[i]);
+  }
+
+  for (int i = 0; i < 2; i++) {
+    for (auto& a : frame_context->mv_class0_fr_probs[i])
+      UpdateMvProbArray(a);
+    UpdateMvProbArray(frame_context->mv_fr_probs[i]);
+  }
+
+  if (allow_high_precision_mv) {
+    for (int i = 0; i < 2; i++) {
+      UpdateMvProb(&frame_context->mv_class0_hp_prob[i]);
+      UpdateMvProb(&frame_context->mv_hp_prob[i]);
+    }
+  }
+}
+
+// 6.3.17 Update mv prob syntax
+void Vp9CompressedHeaderParser::UpdateMvProb(Vp9Prob* prob) {
+  if (reader_.ReadBool(252))
+    *prob = reader_.ReadLiteral(7) << 1 | 1;
+}
+
+// Helper function to UpdateMvProb an array of probs.
+template <int N>
+void Vp9CompressedHeaderParser::UpdateMvProbArray(Vp9Prob (&prob_array)[N]) {
+  for (auto& x : prob_array) {
+    UpdateMvProb(&x);
+  }
+}
+
+// 6.3 Compressed header syntax
+bool Vp9CompressedHeaderParser::Parse(const uint8_t* stream,
+                                      off_t frame_size,
+                                      Vp9FrameHeader* fhdr) {
+  DVLOG(2) << "Vp9CompressedHeaderParser::Parse";
+  if (!reader_.Initialize(stream, frame_size))
+    return false;
+
+  ReadTxMode(fhdr);
+  if (fhdr->compressed_header.tx_mode == Vp9CompressedHeader::TX_MODE_SELECT)
+    ReadTxModeProbs(&fhdr->frame_context);
+
+  ReadCoefProbs(fhdr);
+  ReadSkipProb(&fhdr->frame_context);
+
+  if (!fhdr->IsIntra()) {
+    ReadInterModeProbs(&fhdr->frame_context);
+    if (fhdr->interpolation_filter == SWITCHABLE)
+      ReadInterpFilterProbs(&fhdr->frame_context);
+    ReadIsInterProbs(&fhdr->frame_context);
+    ReadFrameReferenceMode(fhdr);
+    ReadFrameReferenceModeProbs(fhdr);
+    ReadYModeProbs(&fhdr->frame_context);
+    ReadPartitionProbs(&fhdr->frame_context);
+    ReadMvProbs(fhdr->allow_high_precision_mv, &fhdr->frame_context);
+  }
+
+  if (!reader_.IsValid()) {
+    DVLOG(1) << "parser reads beyond the end of buffer";
+    return false;
+  }
+  if (!reader_.ConsumePaddingBits()) {
+    DVLOG(1) << "padding bits are not zero";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace media
diff --git a/vda/vp9_compressed_header_parser.h b/vda/vp9_compressed_header_parser.h
new file mode 100644
index 0000000..032a880
--- /dev/null
+++ b/vda/vp9_compressed_header_parser.h
@@ -0,0 +1,51 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VP9_COMPRESSED_HEADER_PARSER_H_
+#define VP9_COMPRESSED_HEADER_PARSER_H_
+
+#include "vp9_bool_decoder.h"
+#include "vp9_parser.h"
+
+namespace media {
+
+class Vp9CompressedHeaderParser {
+ public:
+  Vp9CompressedHeaderParser();
+
+  // Parses VP9 compressed header in |stream| with |frame_size| into |fhdr|.
+  // Returns true if no error.
+  bool Parse(const uint8_t* stream, off_t frame_size, Vp9FrameHeader* fhdr);
+
+ private:
+  void ReadTxMode(Vp9FrameHeader* fhdr);
+  uint8_t DecodeTermSubexp();
+  void DiffUpdateProb(Vp9Prob* prob);
+  template <int N>
+  void DiffUpdateProbArray(Vp9Prob (&prob_array)[N]);
+  void ReadTxModeProbs(Vp9FrameContext* frame_context);
+  void ReadCoefProbs(Vp9FrameHeader* fhdr);
+  void ReadSkipProb(Vp9FrameContext* frame_context);
+  void ReadInterModeProbs(Vp9FrameContext* frame_context);
+  void ReadInterpFilterProbs(Vp9FrameContext* frame_context);
+  void ReadIsInterProbs(Vp9FrameContext* frame_context);
+  void ReadFrameReferenceMode(Vp9FrameHeader* fhdr);
+  void ReadFrameReferenceModeProbs(Vp9FrameHeader* fhdr);
+  void ReadYModeProbs(Vp9FrameContext* frame_context);
+  void ReadPartitionProbs(Vp9FrameContext* frame_context);
+  void ReadMvProbs(bool allow_high_precision_mv,
+                   Vp9FrameContext* frame_context);
+  void UpdateMvProb(Vp9Prob* prob);
+  template <int N>
+  void UpdateMvProbArray(Vp9Prob (&prob_array)[N]);
+
+  // Bool decoder for compressed frame header.
+  Vp9BoolDecoder reader_;
+
+  DISALLOW_COPY_AND_ASSIGN(Vp9CompressedHeaderParser);
+};
+
+}  // namespace media
+
+#endif  // VP9_COMPRESSED_HEADER_PARSER_H_
diff --git a/vda/vp9_decoder.cc b/vda/vp9_decoder.cc
new file mode 100644
index 0000000..2ea6d16
--- /dev/null
+++ b/vda/vp9_decoder.cc
@@ -0,0 +1,213 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vp9_decoder.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/logging.h"
+
+namespace media {
+
+VP9Decoder::VP9Accelerator::VP9Accelerator() {}
+
+VP9Decoder::VP9Accelerator::~VP9Accelerator() {}
+
+VP9Decoder::VP9Decoder(VP9Accelerator* accelerator)
+    : state_(kNeedStreamMetadata),
+      accelerator_(accelerator),
+      parser_(accelerator->IsFrameContextRequired()) {
+  ref_frames_.resize(kVp9NumRefFrames);
+}
+
+VP9Decoder::~VP9Decoder() {}
+
+void VP9Decoder::SetStream(const uint8_t* ptr, size_t size) {
+  DCHECK(ptr);
+  DCHECK(size);
+
+  DVLOG(4) << "New input stream at: " << (void*)ptr << " size: " << size;
+  parser_.SetStream(ptr, size);
+}
+
+bool VP9Decoder::Flush() {
+  DVLOG(2) << "Decoder flush";
+  Reset();
+  return true;
+}
+
+void VP9Decoder::Reset() {
+  curr_frame_hdr_ = nullptr;
+  for (auto& ref_frame : ref_frames_)
+    ref_frame = nullptr;
+
+  parser_.Reset();
+
+  if (state_ == kDecoding)
+    state_ = kAfterReset;
+}
+
+VP9Decoder::DecodeResult VP9Decoder::Decode() {
+  while (1) {
+    // Read a new frame header if one is not awaiting decoding already.
+    if (!curr_frame_hdr_) {
+      std::unique_ptr<Vp9FrameHeader> hdr(new Vp9FrameHeader());
+      Vp9Parser::Result res = parser_.ParseNextFrame(hdr.get());
+      switch (res) {
+        case Vp9Parser::kOk:
+          curr_frame_hdr_ = std::move(hdr);
+          break;
+
+        case Vp9Parser::kEOStream:
+          return kRanOutOfStreamData;
+
+        case Vp9Parser::kInvalidStream:
+          DVLOG(1) << "Error parsing stream";
+          SetError();
+          return kDecodeError;
+
+        case Vp9Parser::kAwaitingRefresh:
+          DVLOG(4) << "Awaiting context update";
+          return kNeedContextUpdate;
+      }
+    }
+
+    if (state_ != kDecoding) {
+      // Not kDecoding, so we need a resume point (a keyframe), as we are after
+      // reset or at the beginning of the stream. Drop anything that is not
+      // a keyframe in such case, and continue looking for a keyframe.
+      if (curr_frame_hdr_->IsKeyframe()) {
+        state_ = kDecoding;
+      } else {
+        curr_frame_hdr_.reset();
+        continue;
+      }
+    }
+
+    if (curr_frame_hdr_->show_existing_frame) {
+      // This frame header only instructs us to display one of the
+      // previously-decoded frames, but has no frame data otherwise. Display
+      // and continue decoding subsequent frames.
+      size_t frame_to_show = curr_frame_hdr_->frame_to_show_map_idx;
+      if (frame_to_show >= ref_frames_.size() || !ref_frames_[frame_to_show]) {
+        DVLOG(1) << "Request to show an invalid frame";
+        SetError();
+        return kDecodeError;
+      }
+
+      if (!accelerator_->OutputPicture(ref_frames_[frame_to_show])) {
+        SetError();
+        return kDecodeError;
+      }
+
+      curr_frame_hdr_.reset();
+      continue;
+    }
+
+    Size new_pic_size(curr_frame_hdr_->frame_width,
+                           curr_frame_hdr_->frame_height);
+    DCHECK(!new_pic_size.IsEmpty());
+
+    if (new_pic_size != pic_size_) {
+      DVLOG(1) << "New resolution: " << new_pic_size.ToString();
+
+      if (!curr_frame_hdr_->IsKeyframe()) {
+        // TODO(posciak): This is doable, but requires a few modifications to
+        // VDA implementations to allow multiple picture buffer sets in flight.
+        DVLOG(1) << "Resolution change currently supported for keyframes only";
+        SetError();
+        return kDecodeError;
+      }
+
+      // TODO(posciak): This requires us to be on a keyframe (see above) and is
+      // required, because VDA clients expect all surfaces to be returned before
+      // they can cycle surface sets after receiving kAllocateNewSurfaces.
+      // This is only an implementation detail of VDAs and can be improved.
+      for (auto& ref_frame : ref_frames_)
+        ref_frame = nullptr;
+
+      pic_size_ = new_pic_size;
+      return kAllocateNewSurfaces;
+    }
+
+    scoped_refptr<VP9Picture> pic = accelerator_->CreateVP9Picture();
+    if (!pic)
+      return kRanOutOfSurfaces;
+
+    pic->frame_hdr.reset(curr_frame_hdr_.release());
+
+    if (!DecodeAndOutputPicture(pic)) {
+      SetError();
+      return kDecodeError;
+    }
+  }
+}
+
+void VP9Decoder::RefreshReferenceFrames(const scoped_refptr<VP9Picture>& pic) {
+  for (size_t i = 0; i < kVp9NumRefFrames; ++i) {
+    DCHECK(!pic->frame_hdr->IsKeyframe() || pic->frame_hdr->RefreshFlag(i));
+    if (pic->frame_hdr->RefreshFlag(i))
+      ref_frames_[i] = pic;
+  }
+}
+
+void VP9Decoder::UpdateFrameContext(
+    const scoped_refptr<VP9Picture>& pic,
+    const base::Callback<void(const Vp9FrameContext&)>& context_refresh_cb) {
+  DCHECK(!context_refresh_cb.is_null());
+  Vp9FrameContext frame_ctx;
+  memset(&frame_ctx, 0, sizeof(frame_ctx));
+
+  if (!accelerator_->GetFrameContext(pic, &frame_ctx)) {
+    SetError();
+    return;
+  }
+
+  context_refresh_cb.Run(frame_ctx);
+}
+
+bool VP9Decoder::DecodeAndOutputPicture(scoped_refptr<VP9Picture> pic) {
+  DCHECK(!pic_size_.IsEmpty());
+  DCHECK(pic->frame_hdr);
+
+  base::Closure done_cb;
+  const auto& context_refresh_cb =
+      parser_.GetContextRefreshCb(pic->frame_hdr->frame_context_idx);
+  if (!context_refresh_cb.is_null())
+    done_cb = base::Bind(&VP9Decoder::UpdateFrameContext,
+                         base::Unretained(this), pic, context_refresh_cb);
+
+  const Vp9Parser::Context& context = parser_.context();
+  if (!accelerator_->SubmitDecode(pic, context.segmentation(),
+                                  context.loop_filter(), ref_frames_, done_cb))
+    return false;
+
+  if (pic->frame_hdr->show_frame) {
+    if (!accelerator_->OutputPicture(pic))
+      return false;
+  }
+
+  RefreshReferenceFrames(pic);
+  return true;
+}
+
+void VP9Decoder::SetError() {
+  Reset();
+  state_ = kError;
+}
+
+Size VP9Decoder::GetPicSize() const {
+  return pic_size_;
+}
+
+size_t VP9Decoder::GetRequiredNumOfPictures() const {
+  // kMaxVideoFrames to keep higher level media pipeline populated, +2 for the
+  // pictures being parsed and decoded currently.
+  // TODO(johnylin): see if we could get rid of kMaxVideoFrames.
+  const size_t kMaxVideoFrames = 4;
+  return kMaxVideoFrames + kVp9NumRefFrames + 2;
+}
+
+}  // namespace media
diff --git a/vda/vp9_decoder.h b/vda/vp9_decoder.h
new file mode 100644
index 0000000..77a8d88
--- /dev/null
+++ b/vda/vp9_decoder.h
@@ -0,0 +1,153 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VP9_DECODER_H_
+#define VP9_DECODER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "accelerated_video_decoder.h"
+#include "vp9_parser.h"
+#include "vp9_picture.h"
+
+namespace media {
+
+// This class implements an AcceleratedVideoDecoder for VP9 decoding.
+// Clients of this class are expected to pass raw VP9 stream and are expected
+// to provide an implementation of VP9Accelerator for offloading final steps
+// of the decoding process.
+//
+// This class must be created, called and destroyed on a single thread, and
+// does nothing internally on any other thread.
+class VP9Decoder : public AcceleratedVideoDecoder {
+ public:
+  class VP9Accelerator {
+   public:
+    VP9Accelerator();
+    virtual ~VP9Accelerator();
+
+    // Create a new VP9Picture that the decoder client can use for initial
+    // stages of the decoding process and pass back to this accelerator for
+    // final, accelerated stages of it, or for reference when decoding other
+    // pictures.
+    //
+    // When a picture is no longer needed by the decoder, it will just drop
+    // its reference to it, and it may do so at any time.
+    //
+    // Note that this may return nullptr if the accelerator is not able to
+    // provide any new pictures at the given time. The decoder must handle this
+    // case and treat it as normal, returning kRanOutOfSurfaces from Decode().
+    virtual scoped_refptr<VP9Picture> CreateVP9Picture() = 0;
+
+    // Submit decode for |pic| to be run in accelerator, taking as arguments
+    // information contained in it, as well as current segmentation and loop
+    // filter state in |segm_params| and |lf_params|, respectively, and using
+    // pictures in |ref_pictures| for reference.
+    // If done_cb_ is not null, it will be run once decode is done in hardware.
+    //
+    // Note that returning from this method does not mean that the decode
+    // process is finished, but the caller may drop its references to |pic|
+    // and |ref_pictures| immediately, and the data in |segm_params| and
+    // |lf_params| does not need to remain valid after this method returns.
+    //
+    // Return true when successful, false otherwise.
+    virtual bool SubmitDecode(
+        const scoped_refptr<VP9Picture>& pic,
+        const Vp9SegmentationParams& segm_params,
+        const Vp9LoopFilterParams& lf_params,
+        const std::vector<scoped_refptr<VP9Picture>>& ref_pictures,
+        const base::Closure& done_cb) = 0;
+
+    // Schedule output (display) of |pic|.
+    //
+    // Note that returning from this method does not mean that |pic| has already
+    // been outputted (displayed), but guarantees that all pictures will be
+    // outputted in the same order as this method was called for them, and that
+    // they are decoded before outputting (assuming SubmitDecode() has been
+    // called for them beforehand). Decoder may drop its references to |pic|
+    // immediately after calling this method.
+    //
+    // Return true when successful, false otherwise.
+    virtual bool OutputPicture(const scoped_refptr<VP9Picture>& pic) = 0;
+
+    // Return true if the accelerator requires the client to provide frame
+    // context in order to decode. If so, the Vp9FrameHeader provided by the
+    // client must contain a valid compressed header and frame context data.
+    virtual bool IsFrameContextRequired() const = 0;
+
+    // Set |frame_ctx| to the state after decoding |pic|, returning true on
+    // success, false otherwise.
+    virtual bool GetFrameContext(const scoped_refptr<VP9Picture>& pic,
+                                 Vp9FrameContext* frame_ctx) = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(VP9Accelerator);
+  };
+
+  explicit VP9Decoder(VP9Accelerator* accelerator);
+  ~VP9Decoder() override;
+
+  // AcceleratedVideoDecoder implementation.
+  void SetStream(const uint8_t* ptr, size_t size) override;
+  bool Flush() override WARN_UNUSED_RESULT;
+  void Reset() override;
+  DecodeResult Decode() override WARN_UNUSED_RESULT;
+  Size GetPicSize() const override;
+  size_t GetRequiredNumOfPictures() const override;
+
+ private:
+  // Update ref_frames_ based on the information in current frame header.
+  void RefreshReferenceFrames(const scoped_refptr<VP9Picture>& pic);
+
+  // Decode and possibly output |pic| (if the picture is to be shown).
+  // Return true on success, false otherwise.
+  bool DecodeAndOutputPicture(scoped_refptr<VP9Picture> pic);
+
+  // Get frame context state after decoding |pic| from the accelerator, and call
+  // |context_refresh_cb| with the acquired state.
+  void UpdateFrameContext(
+      const scoped_refptr<VP9Picture>& pic,
+      const base::Callback<void(const Vp9FrameContext&)>& context_refresh_cb);
+
+  // Called on error, when decoding cannot continue. Sets state_ to kError and
+  // releases current state.
+  void SetError();
+
+  enum State {
+    kNeedStreamMetadata,  // After initialization, need a keyframe.
+    kDecoding,            // Ready to decode from any point.
+    kAfterReset,          // After Reset(), need a resume point.
+    kError,               // Error in decode, can't continue.
+  };
+
+  // Current decoder state.
+  State state_;
+
+  // Current frame header to be used in decoding the next picture.
+  std::unique_ptr<Vp9FrameHeader> curr_frame_hdr_;
+
+  // Reference frames currently in use.
+  std::vector<scoped_refptr<VP9Picture>> ref_frames_;
+
+  // Current coded resolution.
+  Size pic_size_;
+
+  // VP9Accelerator instance owned by the client.
+  VP9Accelerator* accelerator_;
+
+  Vp9Parser parser_;
+
+  DISALLOW_COPY_AND_ASSIGN(VP9Decoder);
+};
+
+}  // namespace media
+
+#endif  // VP9_DECODER_H_
diff --git a/vda/vp9_parser.cc b/vda/vp9_parser.cc
new file mode 100644
index 0000000..de51c7b
--- /dev/null
+++ b/vda/vp9_parser.cc
@@ -0,0 +1,595 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains an implementation of a VP9 bitstream parser.
+//
+// VERBOSE level:
+//  1 something wrong in bitstream
+//  2 parsing steps
+//  3 parsed values (selected)
+
+#include "vp9_parser.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "vp9_compressed_header_parser.h"
+#include "vp9_uncompressed_header_parser.h"
+
+namespace media {
+
+bool Vp9FrameHeader::IsKeyframe() const {
+  // When show_existing_frame is true, the frame header does not precede an
+  // actual frame to be decoded, so frame_type does not apply (and is not read
+  // from the stream).
+  return !show_existing_frame && frame_type == KEYFRAME;
+}
+
+bool Vp9FrameHeader::IsIntra() const {
+  return !show_existing_frame && (frame_type == KEYFRAME || intra_only);
+}
+
+Vp9Parser::FrameInfo::FrameInfo(const uint8_t* ptr, off_t size)
+    : ptr(ptr), size(size) {}
+
+bool Vp9FrameContext::IsValid() const {
+  // probs should be in [1, 255] range.
+  static_assert(sizeof(Vp9Prob) == 1,
+                "following checks assuming Vp9Prob is single byte");
+  if (memchr(tx_probs_8x8, 0, sizeof(tx_probs_8x8)))
+    return false;
+  if (memchr(tx_probs_16x16, 0, sizeof(tx_probs_16x16)))
+    return false;
+  if (memchr(tx_probs_32x32, 0, sizeof(tx_probs_32x32)))
+    return false;
+
+  for (auto& a : coef_probs) {
+    for (auto& ai : a) {
+      for (auto& aj : ai) {
+        for (auto& ak : aj) {
+          int max_l = (ak == aj[0]) ? 3 : 6;
+          for (int l = 0; l < max_l; l++) {
+            for (auto& x : ak[l]) {
+              if (x == 0)
+                return false;
+            }
+          }
+        }
+      }
+    }
+  }
+  if (memchr(skip_prob, 0, sizeof(skip_prob)))
+    return false;
+  if (memchr(inter_mode_probs, 0, sizeof(inter_mode_probs)))
+    return false;
+  if (memchr(interp_filter_probs, 0, sizeof(interp_filter_probs)))
+    return false;
+  if (memchr(is_inter_prob, 0, sizeof(is_inter_prob)))
+    return false;
+  if (memchr(comp_mode_prob, 0, sizeof(comp_mode_prob)))
+    return false;
+  if (memchr(single_ref_prob, 0, sizeof(single_ref_prob)))
+    return false;
+  if (memchr(comp_ref_prob, 0, sizeof(comp_ref_prob)))
+    return false;
+  if (memchr(y_mode_probs, 0, sizeof(y_mode_probs)))
+    return false;
+  if (memchr(uv_mode_probs, 0, sizeof(uv_mode_probs)))
+    return false;
+  if (memchr(partition_probs, 0, sizeof(partition_probs)))
+    return false;
+  if (memchr(mv_joint_probs, 0, sizeof(mv_joint_probs)))
+    return false;
+  if (memchr(mv_sign_prob, 0, sizeof(mv_sign_prob)))
+    return false;
+  if (memchr(mv_class_probs, 0, sizeof(mv_class_probs)))
+    return false;
+  if (memchr(mv_class0_bit_prob, 0, sizeof(mv_class0_bit_prob)))
+    return false;
+  if (memchr(mv_bits_prob, 0, sizeof(mv_bits_prob)))
+    return false;
+  if (memchr(mv_class0_fr_probs, 0, sizeof(mv_class0_fr_probs)))
+    return false;
+  if (memchr(mv_fr_probs, 0, sizeof(mv_fr_probs)))
+    return false;
+  if (memchr(mv_class0_hp_prob, 0, sizeof(mv_class0_hp_prob)))
+    return false;
+  if (memchr(mv_hp_prob, 0, sizeof(mv_hp_prob)))
+    return false;
+
+  return true;
+}
+
+Vp9Parser::Context::Vp9FrameContextManager::Vp9FrameContextManager()
+    : weak_ptr_factory_(this) {}
+
+Vp9Parser::Context::Vp9FrameContextManager::~Vp9FrameContextManager() {}
+
+const Vp9FrameContext&
+Vp9Parser::Context::Vp9FrameContextManager::frame_context() const {
+  DCHECK(initialized_);
+  DCHECK(!needs_client_update_);
+  return frame_context_;
+}
+
+void Vp9Parser::Context::Vp9FrameContextManager::Reset() {
+  initialized_ = false;
+  needs_client_update_ = false;
+  weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void Vp9Parser::Context::Vp9FrameContextManager::SetNeedsClientUpdate() {
+  DCHECK(!needs_client_update_);
+  initialized_ = true;
+  needs_client_update_ = true;
+}
+
+Vp9Parser::ContextRefreshCallback
+Vp9Parser::Context::Vp9FrameContextManager::GetUpdateCb() {
+  if (needs_client_update_)
+    return base::Bind(&Vp9FrameContextManager::UpdateFromClient,
+                      weak_ptr_factory_.GetWeakPtr());
+  else
+    return Vp9Parser::ContextRefreshCallback();
+}
+
+void Vp9Parser::Context::Vp9FrameContextManager::Update(
+    const Vp9FrameContext& frame_context) {
+  // DCHECK because we can trust values from our parser.
+  DCHECK(frame_context.IsValid());
+  initialized_ = true;
+  frame_context_ = frame_context;
+
+  // For frame context we are updating, it may be still awaiting previous
+  // ContextRefreshCallback. Because we overwrite the value of context here and
+  // previous ContextRefreshCallback no longer matters, invalidate the weak ptr
+  // to prevent previous ContextRefreshCallback run.
+  // With this optimization, we may be able to parse more frames while previous
+  // are still decoding.
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  needs_client_update_ = false;
+}
+
+void Vp9Parser::Context::Vp9FrameContextManager::UpdateFromClient(
+    const Vp9FrameContext& frame_context) {
+  DVLOG(2) << "Got external frame_context update";
+  DCHECK(needs_client_update_);
+  if (!frame_context.IsValid()) {
+    DLOG(ERROR) << "Invalid prob value in frame_context";
+    return;
+  }
+  needs_client_update_ = false;
+  initialized_ = true;
+  frame_context_ = frame_context;
+}
+
+void Vp9Parser::Context::Reset() {
+  memset(&segmentation_, 0, sizeof(segmentation_));
+  memset(&loop_filter_, 0, sizeof(loop_filter_));
+  memset(&ref_slots_, 0, sizeof(ref_slots_));
+  for (auto& manager : frame_context_managers_)
+    manager.Reset();
+}
+
+void Vp9Parser::Context::MarkFrameContextForUpdate(size_t frame_context_idx) {
+  DCHECK_LT(frame_context_idx, arraysize(frame_context_managers_));
+  frame_context_managers_[frame_context_idx].SetNeedsClientUpdate();
+}
+
+void Vp9Parser::Context::UpdateFrameContext(
+    size_t frame_context_idx,
+    const Vp9FrameContext& frame_context) {
+  DCHECK_LT(frame_context_idx, arraysize(frame_context_managers_));
+  frame_context_managers_[frame_context_idx].Update(frame_context);
+}
+
+const Vp9Parser::ReferenceSlot& Vp9Parser::Context::GetRefSlot(
+    size_t ref_type) const {
+  DCHECK_LT(ref_type, arraysize(ref_slots_));
+  return ref_slots_[ref_type];
+}
+
+void Vp9Parser::Context::UpdateRefSlot(
+    size_t ref_type,
+    const Vp9Parser::ReferenceSlot& ref_slot) {
+  DCHECK_LT(ref_type, arraysize(ref_slots_));
+  ref_slots_[ref_type] = ref_slot;
+}
+
+Vp9Parser::Vp9Parser(bool parsing_compressed_header)
+    : parsing_compressed_header_(parsing_compressed_header) {
+  Reset();
+}
+
+Vp9Parser::~Vp9Parser() {}
+
+void Vp9Parser::SetStream(const uint8_t* stream, off_t stream_size) {
+  DCHECK(stream);
+  stream_ = stream;
+  bytes_left_ = stream_size;
+  frames_.clear();
+}
+
+void Vp9Parser::Reset() {
+  stream_ = nullptr;
+  bytes_left_ = 0;
+  frames_.clear();
+  curr_frame_info_.Reset();
+
+  context_.Reset();
+}
+
+Vp9Parser::Result Vp9Parser::ParseNextFrame(Vp9FrameHeader* fhdr) {
+  DCHECK(fhdr);
+  DVLOG(2) << "ParseNextFrame";
+
+  // If |curr_frame_info_| is valid, uncompressed header was parsed into
+  // |curr_frame_header_| and we are awaiting context update to proceed with
+  // compressed header parsing.
+  if (!curr_frame_info_.IsValid()) {
+    if (frames_.empty()) {
+      // No frames to be decoded, if there is no more stream, request more.
+      if (!stream_)
+        return kEOStream;
+
+      // New stream to be parsed, parse it and fill frames_.
+      frames_ = ParseSuperframe();
+      if (frames_.empty()) {
+        DVLOG(1) << "Failed parsing superframes";
+        return kInvalidStream;
+      }
+    }
+
+    curr_frame_info_ = frames_.front();
+    frames_.pop_front();
+
+    memset(&curr_frame_header_, 0, sizeof(curr_frame_header_));
+
+    Vp9UncompressedHeaderParser uncompressed_parser(&context_);
+    if (!uncompressed_parser.Parse(curr_frame_info_.ptr, curr_frame_info_.size,
+                                   &curr_frame_header_))
+      return kInvalidStream;
+
+    if (curr_frame_header_.header_size_in_bytes == 0) {
+      // Verify padding bits are zero.
+      for (off_t i = curr_frame_header_.uncompressed_header_size;
+           i < curr_frame_info_.size; i++) {
+        if (curr_frame_info_.ptr[i] != 0) {
+          DVLOG(1) << "Padding bits are not zeros.";
+          return kInvalidStream;
+        }
+      }
+      *fhdr = curr_frame_header_;
+      curr_frame_info_.Reset();
+      return kOk;
+    }
+    if (curr_frame_header_.uncompressed_header_size +
+            curr_frame_header_.header_size_in_bytes >
+        base::checked_cast<size_t>(curr_frame_info_.size)) {
+      DVLOG(1) << "header_size_in_bytes="
+               << curr_frame_header_.header_size_in_bytes
+               << " is larger than bytes left in buffer: "
+               << curr_frame_info_.size -
+                      curr_frame_header_.uncompressed_header_size;
+      return kInvalidStream;
+    }
+  }
+
+  if (parsing_compressed_header_) {
+    size_t frame_context_idx = curr_frame_header_.frame_context_idx;
+    const Context::Vp9FrameContextManager& context_to_load =
+        context_.frame_context_managers_[frame_context_idx];
+    if (!context_to_load.initialized()) {
+      // 8.2 Frame order constraints
+      // must load an initialized set of probabilities.
+      DVLOG(1) << "loading uninitialized frame context, index="
+               << frame_context_idx;
+      return kInvalidStream;
+    }
+    if (context_to_load.needs_client_update()) {
+      DVLOG(3) << "waiting frame_context_idx=" << frame_context_idx
+               << " to update";
+      return kAwaitingRefresh;
+    }
+    curr_frame_header_.initial_frame_context =
+        curr_frame_header_.frame_context = context_to_load.frame_context();
+
+    Vp9CompressedHeaderParser compressed_parser;
+    if (!compressed_parser.Parse(
+            curr_frame_info_.ptr + curr_frame_header_.uncompressed_header_size,
+            curr_frame_header_.header_size_in_bytes, &curr_frame_header_)) {
+      return kInvalidStream;
+    }
+
+    if (curr_frame_header_.refresh_frame_context) {
+      // In frame parallel mode, we can refresh the context without decoding
+      // tile data.
+      if (curr_frame_header_.frame_parallel_decoding_mode) {
+        context_.UpdateFrameContext(frame_context_idx,
+                                    curr_frame_header_.frame_context);
+      } else {
+        context_.MarkFrameContextForUpdate(frame_context_idx);
+      }
+    }
+  }
+
+  SetupSegmentationDequant();
+  SetupLoopFilter();
+  UpdateSlots();
+
+  *fhdr = curr_frame_header_;
+  curr_frame_info_.Reset();
+  return kOk;
+}
+
+Vp9Parser::ContextRefreshCallback Vp9Parser::GetContextRefreshCb(
+    size_t frame_context_idx) {
+  DCHECK_LT(frame_context_idx, arraysize(context_.frame_context_managers_));
+  auto& frame_context_manager =
+      context_.frame_context_managers_[frame_context_idx];
+
+  return frame_context_manager.GetUpdateCb();
+}
+
+// Annex B Superframes
+std::deque<Vp9Parser::FrameInfo> Vp9Parser::ParseSuperframe() {
+  const uint8_t* stream = stream_;
+  off_t bytes_left = bytes_left_;
+
+  // Make sure we don't parse stream_ more than once.
+  stream_ = nullptr;
+  bytes_left_ = 0;
+
+  if (bytes_left < 1)
+    return std::deque<FrameInfo>();
+
+  // If this is a superframe, the last byte in the stream will contain the
+  // superframe marker. If not, the whole buffer contains a single frame.
+  uint8_t marker = *(stream + bytes_left - 1);
+  if ((marker & 0xe0) != 0xc0) {
+    return {FrameInfo(stream, bytes_left)};
+  }
+
+  DVLOG(1) << "Parsing a superframe";
+
+  // The bytes immediately before the superframe marker constitute superframe
+  // index, which stores information about sizes of each frame in it.
+  // Calculate its size and set index_ptr to the beginning of it.
+  size_t num_frames = (marker & 0x7) + 1;
+  size_t mag = ((marker >> 3) & 0x3) + 1;
+  off_t index_size = 2 + mag * num_frames;
+
+  if (bytes_left < index_size)
+    return std::deque<FrameInfo>();
+
+  const uint8_t* index_ptr = stream + bytes_left - index_size;
+  if (marker != *index_ptr)
+    return std::deque<FrameInfo>();
+
+  ++index_ptr;
+  bytes_left -= index_size;
+
+  // Parse frame information contained in the index and add a pointer to and
+  // size of each frame to frames.
+  std::deque<FrameInfo> frames;
+  for (size_t i = 0; i < num_frames; ++i) {
+    uint32_t size = 0;
+    for (size_t j = 0; j < mag; ++j) {
+      size |= *index_ptr << (j * 8);
+      ++index_ptr;
+    }
+
+    if (base::checked_cast<off_t>(size) > bytes_left) {
+      DVLOG(1) << "Not enough data in the buffer for frame " << i;
+      return std::deque<FrameInfo>();
+    }
+
+    frames.push_back(FrameInfo(stream, size));
+    stream += size;
+    bytes_left -= size;
+
+    DVLOG(1) << "Frame " << i << ", size: " << size;
+  }
+
+  return frames;
+}
+
+// 8.6.1
+const size_t QINDEX_RANGE = 256;
+const int16_t kDcQLookup[QINDEX_RANGE] = {
+  4,       8,    8,    9,   10,   11,   12,   12,
+  13,     14,   15,   16,   17,   18,   19,   19,
+  20,     21,   22,   23,   24,   25,   26,   26,
+  27,     28,   29,   30,   31,   32,   32,   33,
+  34,     35,   36,   37,   38,   38,   39,   40,
+  41,     42,   43,   43,   44,   45,   46,   47,
+  48,     48,   49,   50,   51,   52,   53,   53,
+  54,     55,   56,   57,   57,   58,   59,   60,
+  61,     62,   62,   63,   64,   65,   66,   66,
+  67,     68,   69,   70,   70,   71,   72,   73,
+  74,     74,   75,   76,   77,   78,   78,   79,
+  80,     81,   81,   82,   83,   84,   85,   85,
+  87,     88,   90,   92,   93,   95,   96,   98,
+  99,    101,  102,  104,  105,  107,  108,  110,
+  111,   113,  114,  116,  117,  118,  120,  121,
+  123,   125,  127,  129,  131,  134,  136,  138,
+  140,   142,  144,  146,  148,  150,  152,  154,
+  156,   158,  161,  164,  166,  169,  172,  174,
+  177,   180,  182,  185,  187,  190,  192,  195,
+  199,   202,  205,  208,  211,  214,  217,  220,
+  223,   226,  230,  233,  237,  240,  243,  247,
+  250,   253,  257,  261,  265,  269,  272,  276,
+  280,   284,  288,  292,  296,  300,  304,  309,
+  313,   317,  322,  326,  330,  335,  340,  344,
+  349,   354,  359,  364,  369,  374,  379,  384,
+  389,   395,  400,  406,  411,  417,  423,  429,
+  435,   441,  447,  454,  461,  467,  475,  482,
+  489,   497,  505,  513,  522,  530,  539,  549,
+  559,   569,  579,  590,  602,  614,  626,  640,
+  654,   668,  684,  700,  717,  736,  755,  775,
+  796,   819,  843,  869,  896,  925,  955,  988,
+  1022, 1058, 1098, 1139, 1184, 1232, 1282, 1336,
+};
+
+const int16_t kAcQLookup[QINDEX_RANGE] = {
+  4,       8,    9,   10,   11,   12,   13,   14,
+  15,     16,   17,   18,   19,   20,   21,   22,
+  23,     24,   25,   26,   27,   28,   29,   30,
+  31,     32,   33,   34,   35,   36,   37,   38,
+  39,     40,   41,   42,   43,   44,   45,   46,
+  47,     48,   49,   50,   51,   52,   53,   54,
+  55,     56,   57,   58,   59,   60,   61,   62,
+  63,     64,   65,   66,   67,   68,   69,   70,
+  71,     72,   73,   74,   75,   76,   77,   78,
+  79,     80,   81,   82,   83,   84,   85,   86,
+  87,     88,   89,   90,   91,   92,   93,   94,
+  95,     96,   97,   98,   99,  100,  101,  102,
+  104,   106,  108,  110,  112,  114,  116,  118,
+  120,   122,  124,  126,  128,  130,  132,  134,
+  136,   138,  140,  142,  144,  146,  148,  150,
+  152,   155,  158,  161,  164,  167,  170,  173,
+  176,   179,  182,  185,  188,  191,  194,  197,
+  200,   203,  207,  211,  215,  219,  223,  227,
+  231,   235,  239,  243,  247,  251,  255,  260,
+  265,   270,  275,  280,  285,  290,  295,  300,
+  305,   311,  317,  323,  329,  335,  341,  347,
+  353,   359,  366,  373,  380,  387,  394,  401,
+  408,   416,  424,  432,  440,  448,  456,  465,
+  474,   483,  492,  501,  510,  520,  530,  540,
+  550,   560,  571,  582,  593,  604,  615,  627,
+  639,   651,  663,  676,  689,  702,  715,  729,
+  743,   757,  771,  786,  801,  816,  832,  848,
+  864,   881,  898,  915,  933,  951,  969,  988,
+  1007, 1026, 1046, 1066, 1087, 1108, 1129, 1151,
+  1173, 1196, 1219, 1243, 1267, 1292, 1317, 1343,
+  1369, 1396, 1423, 1451, 1479, 1508, 1537, 1567,
+  1597, 1628, 1660, 1692, 1725, 1759, 1793, 1828,
+};
+
+static_assert(arraysize(kDcQLookup) == arraysize(kAcQLookup),
+              "quantizer lookup arrays of incorrect size");
+
+static size_t ClampQ(size_t q) {
+  return std::min(std::max(static_cast<size_t>(0), q),
+                  arraysize(kDcQLookup) - 1);
+}
+
+// 8.6.1 Dequantization functions
+size_t Vp9Parser::GetQIndex(const Vp9QuantizationParams& quant,
+                            size_t segid) const {
+  const Vp9SegmentationParams& segmentation = context_.segmentation();
+
+  if (segmentation.FeatureEnabled(segid,
+                                  Vp9SegmentationParams::SEG_LVL_ALT_Q)) {
+    int16_t feature_data =
+        segmentation.FeatureData(segid, Vp9SegmentationParams::SEG_LVL_ALT_Q);
+    size_t q_index = segmentation.abs_or_delta_update
+                         ? feature_data
+                         : quant.base_q_idx + feature_data;
+    return ClampQ(q_index);
+  }
+
+  return quant.base_q_idx;
+}
+
+// 8.6.1 Dequantization functions
+void Vp9Parser::SetupSegmentationDequant() {
+  const Vp9QuantizationParams& quant = curr_frame_header_.quant_params;
+  Vp9SegmentationParams& segmentation = context_.segmentation_;
+
+  DLOG_IF(ERROR, curr_frame_header_.bit_depth > 8)
+      << "bit_depth > 8 is not supported "
+         "yet, kDcQLookup and kAcQLookup "
+         "need extended";
+  if (segmentation.enabled) {
+    for (size_t i = 0; i < Vp9SegmentationParams::kNumSegments; ++i) {
+      const size_t q_index = GetQIndex(quant, i);
+      segmentation.y_dequant[i][0] =
+          kDcQLookup[ClampQ(q_index + quant.delta_q_y_dc)];
+      segmentation.y_dequant[i][1] = kAcQLookup[ClampQ(q_index)];
+      segmentation.uv_dequant[i][0] =
+          kDcQLookup[ClampQ(q_index + quant.delta_q_uv_dc)];
+      segmentation.uv_dequant[i][1] =
+          kAcQLookup[ClampQ(q_index + quant.delta_q_uv_ac)];
+    }
+  } else {
+    const size_t q_index = quant.base_q_idx;
+    segmentation.y_dequant[0][0] =
+        kDcQLookup[ClampQ(q_index + quant.delta_q_y_dc)];
+    segmentation.y_dequant[0][1] = kAcQLookup[ClampQ(q_index)];
+    segmentation.uv_dequant[0][0] =
+        kDcQLookup[ClampQ(q_index + quant.delta_q_uv_dc)];
+    segmentation.uv_dequant[0][1] =
+        kAcQLookup[ClampQ(q_index + quant.delta_q_uv_ac)];
+  }
+}
+
+static int ClampLf(int lf) {
+  const int kMaxLoopFilterLevel = 63;
+  return std::min(std::max(0, lf), kMaxLoopFilterLevel);
+}
+
+// 8.8.1 Loop filter frame init process
+void Vp9Parser::SetupLoopFilter() {
+  Vp9LoopFilterParams& loop_filter = context_.loop_filter_;
+  if (!loop_filter.level)
+    return;
+
+  int scale = loop_filter.level < 32 ? 1 : 2;
+
+  for (size_t i = 0; i < Vp9SegmentationParams::kNumSegments; ++i) {
+    int level = loop_filter.level;
+    const Vp9SegmentationParams& segmentation = context_.segmentation();
+
+    if (segmentation.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_ALT_LF)) {
+      int feature_data =
+          segmentation.FeatureData(i, Vp9SegmentationParams::SEG_LVL_ALT_LF);
+      level = ClampLf(segmentation.abs_or_delta_update ? feature_data
+                                                       : level + feature_data);
+    }
+
+    if (!loop_filter.delta_enabled) {
+      memset(loop_filter.lvl[i], level, sizeof(loop_filter.lvl[i]));
+    } else {
+      loop_filter.lvl[i][Vp9RefType::VP9_FRAME_INTRA][0] = ClampLf(
+          level + loop_filter.ref_deltas[Vp9RefType::VP9_FRAME_INTRA] * scale);
+      loop_filter.lvl[i][Vp9RefType::VP9_FRAME_INTRA][1] = 0;
+
+      for (size_t type = Vp9RefType::VP9_FRAME_LAST;
+           type < Vp9RefType::VP9_FRAME_MAX; ++type) {
+        for (size_t mode = 0; mode < Vp9LoopFilterParams::kNumModeDeltas;
+             ++mode) {
+          loop_filter.lvl[i][type][mode] =
+              ClampLf(level + loop_filter.ref_deltas[type] * scale +
+                      loop_filter.mode_deltas[mode] * scale);
+        }
+      }
+    }
+  }
+}
+
+void Vp9Parser::UpdateSlots() {
+  // 8.10 Reference frame update process
+  for (size_t i = 0; i < kVp9NumRefFrames; i++) {
+    if (curr_frame_header_.RefreshFlag(i)) {
+      ReferenceSlot ref_slot;
+      ref_slot.initialized = true;
+
+      ref_slot.frame_width = curr_frame_header_.frame_width;
+      ref_slot.frame_height = curr_frame_header_.frame_height;
+      ref_slot.subsampling_x = curr_frame_header_.subsampling_x;
+      ref_slot.subsampling_y = curr_frame_header_.subsampling_y;
+      ref_slot.bit_depth = curr_frame_header_.bit_depth;
+
+      ref_slot.profile = curr_frame_header_.profile;
+      ref_slot.color_space = curr_frame_header_.color_space;
+      context_.UpdateRefSlot(i, ref_slot);
+    }
+  }
+}
+
+}  // namespace media
diff --git a/vda/vp9_parser.h b/vda/vp9_parser.h
new file mode 100644
index 0000000..c6e1d9f
--- /dev/null
+++ b/vda/vp9_parser.h
@@ -0,0 +1,433 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains an implementation of a VP9 bitstream parser. The main
+// purpose of this parser is to support hardware decode acceleration. Some
+// accelerators, e.g. libva which implements VA-API, require the caller
+// (chrome) to feed them parsed VP9 frame header.
+//
+// See media::VP9Decoder for example usage.
+//
+#ifndef VP9_PARSER_H_
+#define VP9_PARSER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <deque>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+
+namespace media {
+
+const int kVp9MaxProfile = 4;
+const int kVp9NumRefFramesLog2 = 3;
+const size_t kVp9NumRefFrames = 1 << kVp9NumRefFramesLog2;
+const uint8_t kVp9MaxProb = 255;
+const size_t kVp9NumRefsPerFrame = 3;
+const size_t kVp9NumFrameContextsLog2 = 2;
+const size_t kVp9NumFrameContexts = 1 << kVp9NumFrameContextsLog2;
+
+using Vp9Prob = uint8_t;
+
+enum class Vp9ColorSpace {
+  UNKNOWN = 0,
+  BT_601 = 1,
+  BT_709 = 2,
+  SMPTE_170 = 3,
+  SMPTE_240 = 4,
+  BT_2020 = 5,
+  RESERVED = 6,
+  SRGB = 7,
+};
+
+enum Vp9InterpolationFilter {
+  EIGHTTAP = 0,
+  EIGHTTAP_SMOOTH = 1,
+  EIGHTTAP_SHARP = 2,
+  BILINEAR = 3,
+  SWITCHABLE = 4,
+};
+
+enum Vp9RefType {
+  VP9_FRAME_INTRA = 0,
+  VP9_FRAME_LAST = 1,
+  VP9_FRAME_GOLDEN = 2,
+  VP9_FRAME_ALTREF = 3,
+  VP9_FRAME_MAX = 4,
+};
+
+enum Vp9ReferenceMode {
+  SINGLE_REFERENCE = 0,
+  COMPOUND_REFERENCE = 1,
+  REFERENCE_MODE_SELECT = 2,
+};
+
+struct Vp9SegmentationParams {
+  static const size_t kNumSegments = 8;
+  static const size_t kNumTreeProbs = kNumSegments - 1;
+  static const size_t kNumPredictionProbs = 3;
+  enum SegmentLevelFeature {
+    SEG_LVL_ALT_Q = 0,
+    SEG_LVL_ALT_LF = 1,
+    SEG_LVL_REF_FRAME = 2,
+    SEG_LVL_SKIP = 3,
+    SEG_LVL_MAX
+  };
+
+  bool enabled;
+
+  bool update_map;
+  uint8_t tree_probs[kNumTreeProbs];
+  bool temporal_update;
+  uint8_t pred_probs[kNumPredictionProbs];
+
+  bool update_data;
+  bool abs_or_delta_update;
+  bool feature_enabled[kNumSegments][SEG_LVL_MAX];
+  int16_t feature_data[kNumSegments][SEG_LVL_MAX];
+
+  int16_t y_dequant[kNumSegments][2];
+  int16_t uv_dequant[kNumSegments][2];
+
+  bool FeatureEnabled(size_t seg_id, SegmentLevelFeature feature) const {
+    return feature_enabled[seg_id][feature];
+  }
+
+  int16_t FeatureData(size_t seg_id, SegmentLevelFeature feature) const {
+    return feature_data[seg_id][feature];
+  }
+};
+
+struct Vp9LoopFilterParams {
+  static const size_t kNumModeDeltas = 2;
+
+  uint8_t level;
+  uint8_t sharpness;
+
+  bool delta_enabled;
+  bool delta_update;
+  bool update_ref_deltas[VP9_FRAME_MAX];
+  int8_t ref_deltas[VP9_FRAME_MAX];
+  bool update_mode_deltas[kNumModeDeltas];
+  int8_t mode_deltas[kNumModeDeltas];
+
+  // Calculated from above fields.
+  uint8_t lvl[Vp9SegmentationParams::kNumSegments][VP9_FRAME_MAX]
+             [kNumModeDeltas];
+};
+
+// Members of Vp9FrameHeader will be 0-initialized by Vp9Parser::ParseNextFrame.
+struct Vp9QuantizationParams {
+  bool IsLossless() const {
+    return base_q_idx == 0 && delta_q_y_dc == 0 && delta_q_uv_dc == 0 &&
+           delta_q_uv_ac == 0;
+  }
+
+  uint8_t base_q_idx;
+  int8_t delta_q_y_dc;
+  int8_t delta_q_uv_dc;
+  int8_t delta_q_uv_ac;
+};
+
+// Entropy context for frame parsing
+struct Vp9FrameContext {
+  bool IsValid() const;
+
+  Vp9Prob tx_probs_8x8[2][1];
+  Vp9Prob tx_probs_16x16[2][2];
+  Vp9Prob tx_probs_32x32[2][3];
+
+  Vp9Prob coef_probs[4][2][2][6][6][3];
+  Vp9Prob skip_prob[3];
+  Vp9Prob inter_mode_probs[7][3];
+  Vp9Prob interp_filter_probs[4][2];
+  Vp9Prob is_inter_prob[4];
+
+  Vp9Prob comp_mode_prob[5];
+  Vp9Prob single_ref_prob[5][2];
+  Vp9Prob comp_ref_prob[5];
+
+  Vp9Prob y_mode_probs[4][9];
+  Vp9Prob uv_mode_probs[10][9];
+  Vp9Prob partition_probs[16][3];
+
+  Vp9Prob mv_joint_probs[3];
+  Vp9Prob mv_sign_prob[2];
+  Vp9Prob mv_class_probs[2][10];
+  Vp9Prob mv_class0_bit_prob[2];
+  Vp9Prob mv_bits_prob[2][10];
+  Vp9Prob mv_class0_fr_probs[2][2][3];
+  Vp9Prob mv_fr_probs[2][3];
+  Vp9Prob mv_class0_hp_prob[2];
+  Vp9Prob mv_hp_prob[2];
+};
+
+struct Vp9CompressedHeader {
+  enum Vp9TxMode {
+    ONLY_4X4 = 0,
+    ALLOW_8X8 = 1,
+    ALLOW_16X16 = 2,
+    ALLOW_32X32 = 3,
+    TX_MODE_SELECT = 4,
+    TX_MODES = 5,
+  };
+
+  Vp9TxMode tx_mode;
+  Vp9ReferenceMode reference_mode;
+};
+
+// VP9 frame header.
+struct Vp9FrameHeader {
+  enum FrameType {
+    KEYFRAME = 0,
+    INTERFRAME = 1,
+  };
+
+  bool IsKeyframe() const;
+  bool IsIntra() const;
+  bool RefreshFlag(size_t i) const {
+    return !!(refresh_frame_flags & (1u << i));
+  }
+
+  uint8_t profile;
+
+  bool show_existing_frame;
+  uint8_t frame_to_show_map_idx;
+
+  FrameType frame_type;
+
+  bool show_frame;
+  bool error_resilient_mode;
+
+  uint8_t bit_depth;
+  Vp9ColorSpace color_space;
+  bool color_range;
+  uint8_t subsampling_x;
+  uint8_t subsampling_y;
+
+  // The range of frame_width and frame_height is 1..2^16.
+  uint32_t frame_width;
+  uint32_t frame_height;
+  uint32_t render_width;
+  uint32_t render_height;
+
+  bool intra_only;
+  uint8_t reset_frame_context;
+  uint8_t refresh_frame_flags;
+  uint8_t ref_frame_idx[kVp9NumRefsPerFrame];
+  bool ref_frame_sign_bias[Vp9RefType::VP9_FRAME_MAX];
+  bool allow_high_precision_mv;
+  Vp9InterpolationFilter interpolation_filter;
+
+  bool refresh_frame_context;
+  bool frame_parallel_decoding_mode;
+  uint8_t frame_context_idx;
+  // |frame_context_idx_to_save_probs| is to be used by save_probs() only, and
+  // |frame_context_idx| otherwise.
+  uint8_t frame_context_idx_to_save_probs;
+
+  Vp9QuantizationParams quant_params;
+
+  uint8_t tile_cols_log2;
+  uint8_t tile_rows_log2;
+
+  // Pointer to the beginning of frame data. It is a responsibility of the
+  // client of the Vp9Parser to maintain validity of this data while it is
+  // being used outside of that class.
+  const uint8_t* data;
+
+  // Size of |data| in bytes.
+  size_t frame_size;
+
+  // Size of compressed header in bytes.
+  size_t header_size_in_bytes;
+
+  // Size of uncompressed header in bytes.
+  size_t uncompressed_header_size;
+
+  Vp9CompressedHeader compressed_header;
+  // Initial frame entropy context after load_probs2(frame_context_idx).
+  Vp9FrameContext initial_frame_context;
+  // Current frame entropy context after header parsing.
+  Vp9FrameContext frame_context;
+};
+
+// A parser for VP9 bitstream.
+class Vp9Parser {
+ public:
+  // If context update is needed after decoding a frame, the client must
+  // execute this callback, passing the updated context state.
+  using ContextRefreshCallback = base::Callback<void(const Vp9FrameContext&)>;
+
+  // ParseNextFrame() return values. See documentation for ParseNextFrame().
+  enum Result {
+    kOk,
+    kInvalidStream,
+    kEOStream,
+    kAwaitingRefresh,
+  };
+
+  // The parsing context to keep track of references.
+  struct ReferenceSlot {
+    bool initialized;
+    uint32_t frame_width;
+    uint32_t frame_height;
+    uint8_t subsampling_x;
+    uint8_t subsampling_y;
+    uint8_t bit_depth;
+
+    // More fields for consistency checking.
+    uint8_t profile;
+    Vp9ColorSpace color_space;
+  };
+
+  // The parsing context that persists across frames.
+  class Context {
+   public:
+    class Vp9FrameContextManager {
+     public:
+      Vp9FrameContextManager();
+      ~Vp9FrameContextManager();
+      bool initialized() const { return initialized_; }
+      bool needs_client_update() const { return needs_client_update_; }
+      const Vp9FrameContext& frame_context() const;
+
+      // Resets to uninitialized state.
+      void Reset();
+
+      // Marks this context as requiring an update from parser's client.
+      void SetNeedsClientUpdate();
+
+      // Updates frame context.
+      void Update(const Vp9FrameContext& frame_context);
+
+      // Returns a callback to update frame context at a later time with.
+      ContextRefreshCallback GetUpdateCb();
+
+     private:
+      // Updates frame context from parser's client.
+      void UpdateFromClient(const Vp9FrameContext& frame_context);
+
+      bool initialized_ = false;
+      bool needs_client_update_ = false;
+      Vp9FrameContext frame_context_;
+
+      base::WeakPtrFactory<Vp9FrameContextManager> weak_ptr_factory_;
+    };
+
+    void Reset();
+
+    // Mark |frame_context_idx| as requiring update from the client.
+    void MarkFrameContextForUpdate(size_t frame_context_idx);
+
+    // Update frame context at |frame_context_idx| with the contents of
+    // |frame_context|.
+    void UpdateFrameContext(size_t frame_context_idx,
+                            const Vp9FrameContext& frame_context);
+
+    // Return ReferenceSlot for frame at |ref_idx|.
+    const ReferenceSlot& GetRefSlot(size_t ref_idx) const;
+
+    // Update contents of ReferenceSlot at |ref_idx| with the contents of
+    // |ref_slot|.
+    void UpdateRefSlot(size_t ref_idx, const ReferenceSlot& ref_slot);
+
+    const Vp9SegmentationParams& segmentation() const { return segmentation_; }
+
+    const Vp9LoopFilterParams& loop_filter() const { return loop_filter_; }
+
+   private:
+    friend class Vp9UncompressedHeaderParser;
+    friend class Vp9Parser;
+
+    // Segmentation and loop filter state.
+    Vp9SegmentationParams segmentation_;
+    Vp9LoopFilterParams loop_filter_;
+
+    // Frame references.
+    ReferenceSlot ref_slots_[kVp9NumRefFrames];
+
+    Vp9FrameContextManager frame_context_managers_[kVp9NumFrameContexts];
+  };
+
+  // The constructor. See ParseNextFrame() for comments for
+  // |parsing_compressed_header|.
+  explicit Vp9Parser(bool parsing_compressed_header);
+  ~Vp9Parser();
+
+  // Set a new stream buffer to read from, starting at |stream| and of size
+  // |stream_size| in bytes. |stream| must point to the beginning of a single
+  // frame or a single superframe, is owned by caller and must remain valid
+  // until the next call to SetStream().
+  void SetStream(const uint8_t* stream, off_t stream_size);
+
+  // Parse the next frame in the current stream buffer, filling |fhdr| with
+  // the parsed frame header and updating current segmentation and loop filter
+  // state.
+  // Return kOk if a frame has successfully been parsed,
+  //        kEOStream if there is no more data in the current stream buffer,
+  //        kAwaitingRefresh if this frame awaiting frame context update, or
+  //        kInvalidStream on error.
+  Result ParseNextFrame(Vp9FrameHeader* fhdr);
+
+  // Return current parsing context.
+  const Context& context() const { return context_; }
+
+  // Return a ContextRefreshCallback, which, if not null, has to be called with
+  // the new context state after the frame associated with |frame_context_idx|
+  // is decoded.
+  ContextRefreshCallback GetContextRefreshCb(size_t frame_context_idx);
+
+  // Clear parser state and return to an initialized state.
+  void Reset();
+
+ private:
+  // Stores start pointer and size of each frame within the current superframe.
+  struct FrameInfo {
+    FrameInfo() = default;
+    FrameInfo(const uint8_t* ptr, off_t size);
+    bool IsValid() const { return ptr != nullptr; }
+    void Reset() { ptr = nullptr; }
+
+    // Starting address of the frame.
+    const uint8_t* ptr = nullptr;
+
+    // Size of the frame in bytes.
+    off_t size = 0;
+  };
+
+  std::deque<FrameInfo> ParseSuperframe();
+
+  size_t GetQIndex(const Vp9QuantizationParams& quant, size_t segid) const;
+  void SetupSegmentationDequant();
+  void SetupLoopFilter();
+  void UpdateSlots();
+
+  // Current address in the bitstream buffer.
+  const uint8_t* stream_;
+
+  // Remaining bytes in stream_.
+  off_t bytes_left_;
+
+  bool parsing_compressed_header_;
+
+  // FrameInfo for the remaining frames in the current superframe to be parsed.
+  std::deque<FrameInfo> frames_;
+
+  Context context_;
+
+  FrameInfo curr_frame_info_;
+  Vp9FrameHeader curr_frame_header_;
+
+  DISALLOW_COPY_AND_ASSIGN(Vp9Parser);
+};
+
+}  // namespace media
+
+#endif  // VP9_PARSER_H_
diff --git a/vda/vp9_picture.cc b/vda/vp9_picture.cc
new file mode 100644
index 0000000..ed3c65a
--- /dev/null
+++ b/vda/vp9_picture.cc
@@ -0,0 +1,13 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vp9_picture.h"
+
+namespace media {
+
+VP9Picture::VP9Picture() {}
+
+VP9Picture::~VP9Picture() {}
+
+}  // namespace media
diff --git a/vda/vp9_picture.h b/vda/vp9_picture.h
new file mode 100644
index 0000000..2bd5bbc
--- /dev/null
+++ b/vda/vp9_picture.h
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VP9_PICTURE_H_
+#define VP9_PICTURE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "vp9_parser.h"
+
+namespace media {
+
+class VP9Picture : public base::RefCounted<VP9Picture> {
+ public:
+  VP9Picture();
+
+  std::unique_ptr<Vp9FrameHeader> frame_hdr;
+
+ protected:
+  friend class base::RefCounted<VP9Picture>;
+  virtual ~VP9Picture();
+
+  DISALLOW_COPY_AND_ASSIGN(VP9Picture);
+};
+
+}  // namespace media
+
+#endif  // VP9_PICTURE_H_
diff --git a/vda/vp9_raw_bits_reader.cc b/vda/vp9_raw_bits_reader.cc
new file mode 100644
index 0000000..7cad4d9
--- /dev/null
+++ b/vda/vp9_raw_bits_reader.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vp9_raw_bits_reader.h"
+
+#include <limits.h>
+
+#include "base/logging.h"
+#include "bit_reader.h"
+
+namespace media {
+
+Vp9RawBitsReader::Vp9RawBitsReader() : valid_(true) {}
+
+Vp9RawBitsReader::~Vp9RawBitsReader() {}
+
+void Vp9RawBitsReader::Initialize(const uint8_t* data, size_t size) {
+  DCHECK(data);
+  reader_.reset(new BitReader(data, size));
+  valid_ = true;
+}
+
+bool Vp9RawBitsReader::ReadBool() {
+  DCHECK(reader_);
+  if (!valid_)
+    return false;
+
+  int value = 0;
+  valid_ = reader_->ReadBits(1, &value);
+  return valid_ ? value == 1 : false;
+}
+
+int Vp9RawBitsReader::ReadLiteral(int bits) {
+  DCHECK(reader_);
+  if (!valid_)
+    return 0;
+
+  int value = 0;
+  DCHECK_LT(static_cast<size_t>(bits), sizeof(value) * 8);
+  valid_ = reader_->ReadBits(bits, &value);
+  return valid_ ? value : 0;
+}
+
+int Vp9RawBitsReader::ReadSignedLiteral(int bits) {
+  int value = ReadLiteral(bits);
+  return ReadBool() ? -value : value;
+}
+
+size_t Vp9RawBitsReader::GetBytesRead() const {
+  DCHECK(reader_);
+  return (reader_->bits_read() + 7) / 8;
+}
+
+bool Vp9RawBitsReader::ConsumeTrailingBits() {
+  DCHECK(reader_);
+  int bits_left = GetBytesRead() * 8 - reader_->bits_read();
+  return ReadLiteral(bits_left) == 0;
+}
+
+}  // namespace media
diff --git a/vda/vp9_raw_bits_reader.h b/vda/vp9_raw_bits_reader.h
new file mode 100644
index 0000000..9f112b8
--- /dev/null
+++ b/vda/vp9_raw_bits_reader.h
@@ -0,0 +1,66 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VP9_RAW_BITS_READER_H_
+#define VP9_RAW_BITS_READER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+
+namespace media {
+
+class BitReader;
+
+// A class to read raw bits stream. See VP9 spec, "RAW-BITS DECODING" section
+// for detail.
+class Vp9RawBitsReader {
+ public:
+  Vp9RawBitsReader();
+  ~Vp9RawBitsReader();
+
+  // |data| is the input buffer with |size| bytes.
+  void Initialize(const uint8_t* data, size_t size);
+
+  // Returns true if none of the reads since the last Initialize() call has
+  // gone beyond the end of available data.
+  bool IsValid() const { return valid_; }
+
+  // Returns how many bytes were read since the last Initialize() call.
+  // Partial bytes will be counted as one byte. For example, it will return 1
+  // if 3 bits were read.
+  size_t GetBytesRead() const;
+
+  // Reads one bit.
+  // If the read goes beyond the end of buffer, the return value is undefined.
+  bool ReadBool();
+
+  // Reads a literal with |bits| bits.
+  // If the read goes beyond the end of buffer, the return value is undefined.
+  int ReadLiteral(int bits);
+
+  // Reads a signed literal with |bits| bits (not including the sign bit).
+  // If the read goes beyond the end of buffer, the return value is undefined.
+  int ReadSignedLiteral(int bits);
+
+  // Consumes trailing bits up to next byte boundary. Returns true if no
+  // trailing bits or they are all zero.
+  bool ConsumeTrailingBits();
+
+ private:
+  std::unique_ptr<BitReader> reader_;
+
+  // Indicates if none of the reads since the last Initialize() call has gone
+  // beyond the end of available data.
+  bool valid_;
+
+  DISALLOW_COPY_AND_ASSIGN(Vp9RawBitsReader);
+};
+
+}  // namespace media
+
+#endif  // VP9_RAW_BITS_READER_H_
diff --git a/vda/vp9_uncompressed_header_parser.cc b/vda/vp9_uncompressed_header_parser.cc
new file mode 100644
index 0000000..067b40c
--- /dev/null
+++ b/vda/vp9_uncompressed_header_parser.cc
@@ -0,0 +1,1102 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vp9_uncompressed_header_parser.h"
+
+#include "base/logging.h"
+
+namespace media {
+
+namespace {
+
+// 10.5 Default probability tables
+Vp9FrameContext kVp9DefaultFrameContext = {
+    // tx_probs_8x8
+    {{100}, {66}},
+    // tx_probs_16x16
+    {{20, 152}, {15, 101}},
+    // tx_probs_32x32
+    {{3, 136, 37}, {5, 52, 13}},
+    // coef_probs
+    {// 4x4
+     {{{{{195, 29, 183}, {84, 49, 136}, {8, 42, 71}},
+        {{31, 107, 169},
+         {35, 99, 159},
+         {17, 82, 140},
+         {8, 66, 114},
+         {2, 44, 76},
+         {1, 19, 32}},
+        {{40, 132, 201},
+         {29, 114, 187},
+         {13, 91, 157},
+         {7, 75, 127},
+         {3, 58, 95},
+         {1, 28, 47}},
+        {{69, 142, 221},
+         {42, 122, 201},
+         {15, 91, 159},
+         {6, 67, 121},
+         {1, 42, 77},
+         {1, 17, 31}},
+        {{102, 148, 228},
+         {67, 117, 204},
+         {17, 82, 154},
+         {6, 59, 114},
+         {2, 39, 75},
+         {1, 15, 29}},
+        {{156, 57, 233},
+         {119, 57, 212},
+         {58, 48, 163},
+         {29, 40, 124},
+         {12, 30, 81},
+         {3, 12, 31}}},
+       {{{191, 107, 226}, {124, 117, 204}, {25, 99, 155}},
+        {{29, 148, 210},
+         {37, 126, 194},
+         {8, 93, 157},
+         {2, 68, 118},
+         {1, 39, 69},
+         {1, 17, 33}},
+        {{41, 151, 213},
+         {27, 123, 193},
+         {3, 82, 144},
+         {1, 58, 105},
+         {1, 32, 60},
+         {1, 13, 26}},
+        {{59, 159, 220},
+         {23, 126, 198},
+         {4, 88, 151},
+         {1, 66, 114},
+         {1, 38, 71},
+         {1, 18, 34}},
+        {{114, 136, 232},
+         {51, 114, 207},
+         {11, 83, 155},
+         {3, 56, 105},
+         {1, 33, 65},
+         {1, 17, 34}},
+        {{149, 65, 234},
+         {121, 57, 215},
+         {61, 49, 166},
+         {28, 36, 114},
+         {12, 25, 76},
+         {3, 16, 42}}}},
+      {{{{214, 49, 220}, {132, 63, 188}, {42, 65, 137}},
+        {{85, 137, 221},
+         {104, 131, 216},
+         {49, 111, 192},
+         {21, 87, 155},
+         {2, 49, 87},
+         {1, 16, 28}},
+        {{89, 163, 230},
+         {90, 137, 220},
+         {29, 100, 183},
+         {10, 70, 135},
+         {2, 42, 81},
+         {1, 17, 33}},
+        {{108, 167, 237},
+         {55, 133, 222},
+         {15, 97, 179},
+         {4, 72, 135},
+         {1, 45, 85},
+         {1, 19, 38}},
+        {{124, 146, 240},
+         {66, 124, 224},
+         {17, 88, 175},
+         {4, 58, 122},
+         {1, 36, 75},
+         {1, 18, 37}},
+        {{141, 79, 241},
+         {126, 70, 227},
+         {66, 58, 182},
+         {30, 44, 136},
+         {12, 34, 96},
+         {2, 20, 47}}},
+       {{{229, 99, 249}, {143, 111, 235}, {46, 109, 192}},
+        {{82, 158, 236},
+         {94, 146, 224},
+         {25, 117, 191},
+         {9, 87, 149},
+         {3, 56, 99},
+         {1, 33, 57}},
+        {{83, 167, 237},
+         {68, 145, 222},
+         {10, 103, 177},
+         {2, 72, 131},
+         {1, 41, 79},
+         {1, 20, 39}},
+        {{99, 167, 239},
+         {47, 141, 224},
+         {10, 104, 178},
+         {2, 73, 133},
+         {1, 44, 85},
+         {1, 22, 47}},
+        {{127, 145, 243},
+         {71, 129, 228},
+         {17, 93, 177},
+         {3, 61, 124},
+         {1, 41, 84},
+         {1, 21, 52}},
+        {{157, 78, 244},
+         {140, 72, 231},
+         {69, 58, 184},
+         {31, 44, 137},
+         {14, 38, 105},
+         {8, 23, 61}}}}},
+     // 8x8
+     {{{{{125, 34, 187}, {52, 41, 133}, {6, 31, 56}},
+        {{37, 109, 153},
+         {51, 102, 147},
+         {23, 87, 128},
+         {8, 67, 101},
+         {1, 41, 63},
+         {1, 19, 29}},
+        {{31, 154, 185},
+         {17, 127, 175},
+         {6, 96, 145},
+         {2, 73, 114},
+         {1, 51, 82},
+         {1, 28, 45}},
+        {{23, 163, 200},
+         {10, 131, 185},
+         {2, 93, 148},
+         {1, 67, 111},
+         {1, 41, 69},
+         {1, 14, 24}},
+        {{29, 176, 217},
+         {12, 145, 201},
+         {3, 101, 156},
+         {1, 69, 111},
+         {1, 39, 63},
+         {1, 14, 23}},
+        {{57, 192, 233},
+         {25, 154, 215},
+         {6, 109, 167},
+         {3, 78, 118},
+         {1, 48, 69},
+         {1, 21, 29}}},
+       {{{202, 105, 245}, {108, 106, 216}, {18, 90, 144}},
+        {{33, 172, 219},
+         {64, 149, 206},
+         {14, 117, 177},
+         {5, 90, 141},
+         {2, 61, 95},
+         {1, 37, 57}},
+        {{33, 179, 220},
+         {11, 140, 198},
+         {1, 89, 148},
+         {1, 60, 104},
+         {1, 33, 57},
+         {1, 12, 21}},
+        {{30, 181, 221},
+         {8, 141, 198},
+         {1, 87, 145},
+         {1, 58, 100},
+         {1, 31, 55},
+         {1, 12, 20}},
+        {{32, 186, 224},
+         {7, 142, 198},
+         {1, 86, 143},
+         {1, 58, 100},
+         {1, 31, 55},
+         {1, 12, 22}},
+        {{57, 192, 227},
+         {20, 143, 204},
+         {3, 96, 154},
+         {1, 68, 112},
+         {1, 42, 69},
+         {1, 19, 32}}}},
+      {{{{212, 35, 215}, {113, 47, 169}, {29, 48, 105}},
+        {{74, 129, 203},
+         {106, 120, 203},
+         {49, 107, 178},
+         {19, 84, 144},
+         {4, 50, 84},
+         {1, 15, 25}},
+        {{71, 172, 217},
+         {44, 141, 209},
+         {15, 102, 173},
+         {6, 76, 133},
+         {2, 51, 89},
+         {1, 24, 42}},
+        {{64, 185, 231},
+         {31, 148, 216},
+         {8, 103, 175},
+         {3, 74, 131},
+         {1, 46, 81},
+         {1, 18, 30}},
+        {{65, 196, 235},
+         {25, 157, 221},
+         {5, 105, 174},
+         {1, 67, 120},
+         {1, 38, 69},
+         {1, 15, 30}},
+        {{65, 204, 238},
+         {30, 156, 224},
+         {7, 107, 177},
+         {2, 70, 124},
+         {1, 42, 73},
+         {1, 18, 34}}},
+       {{{225, 86, 251}, {144, 104, 235}, {42, 99, 181}},
+        {{85, 175, 239},
+         {112, 165, 229},
+         {29, 136, 200},
+         {12, 103, 162},
+         {6, 77, 123},
+         {2, 53, 84}},
+        {{75, 183, 239},
+         {30, 155, 221},
+         {3, 106, 171},
+         {1, 74, 128},
+         {1, 44, 76},
+         {1, 17, 28}},
+        {{73, 185, 240},
+         {27, 159, 222},
+         {2, 107, 172},
+         {1, 75, 127},
+         {1, 42, 73},
+         {1, 17, 29}},
+        {{62, 190, 238},
+         {21, 159, 222},
+         {2, 107, 172},
+         {1, 72, 122},
+         {1, 40, 71},
+         {1, 18, 32}},
+        {{61, 199, 240},
+         {27, 161, 226},
+         {4, 113, 180},
+         {1, 76, 129},
+         {1, 46, 80},
+         {1, 23, 41}}}}},
+     // 16x16
+     {{{{{7, 27, 153}, {5, 30, 95}, {1, 16, 30}},
+        {{50, 75, 127},
+         {57, 75, 124},
+         {27, 67, 108},
+         {10, 54, 86},
+         {1, 33, 52},
+         {1, 12, 18}},
+        {{43, 125, 151},
+         {26, 108, 148},
+         {7, 83, 122},
+         {2, 59, 89},
+         {1, 38, 60},
+         {1, 17, 27}},
+        {{23, 144, 163},
+         {13, 112, 154},
+         {2, 75, 117},
+         {1, 50, 81},
+         {1, 31, 51},
+         {1, 14, 23}},
+        {{18, 162, 185},
+         {6, 123, 171},
+         {1, 78, 125},
+         {1, 51, 86},
+         {1, 31, 54},
+         {1, 14, 23}},
+        {{15, 199, 227},
+         {3, 150, 204},
+         {1, 91, 146},
+         {1, 55, 95},
+         {1, 30, 53},
+         {1, 11, 20}}},
+       {{{19, 55, 240}, {19, 59, 196}, {3, 52, 105}},
+        {{41, 166, 207},
+         {104, 153, 199},
+         {31, 123, 181},
+         {14, 101, 152},
+         {5, 72, 106},
+         {1, 36, 52}},
+        {{35, 176, 211},
+         {12, 131, 190},
+         {2, 88, 144},
+         {1, 60, 101},
+         {1, 36, 60},
+         {1, 16, 28}},
+        {{28, 183, 213},
+         {8, 134, 191},
+         {1, 86, 142},
+         {1, 56, 96},
+         {1, 30, 53},
+         {1, 12, 20}},
+        {{20, 190, 215},
+         {4, 135, 192},
+         {1, 84, 139},
+         {1, 53, 91},
+         {1, 28, 49},
+         {1, 11, 20}},
+        {{13, 196, 216},
+         {2, 137, 192},
+         {1, 86, 143},
+         {1, 57, 99},
+         {1, 32, 56},
+         {1, 13, 24}}}},
+      {{{{211, 29, 217}, {96, 47, 156}, {22, 43, 87}},
+        {{78, 120, 193},
+         {111, 116, 186},
+         {46, 102, 164},
+         {15, 80, 128},
+         {2, 49, 76},
+         {1, 18, 28}},
+        {{71, 161, 203},
+         {42, 132, 192},
+         {10, 98, 150},
+         {3, 69, 109},
+         {1, 44, 70},
+         {1, 18, 29}},
+        {{57, 186, 211},
+         {30, 140, 196},
+         {4, 93, 146},
+         {1, 62, 102},
+         {1, 38, 65},
+         {1, 16, 27}},
+        {{47, 199, 217},
+         {14, 145, 196},
+         {1, 88, 142},
+         {1, 57, 98},
+         {1, 36, 62},
+         {1, 15, 26}},
+        {{26, 219, 229},
+         {5, 155, 207},
+         {1, 94, 151},
+         {1, 60, 104},
+         {1, 36, 62},
+         {1, 16, 28}}},
+       {{{233, 29, 248}, {146, 47, 220}, {43, 52, 140}},
+        {{100, 163, 232},
+         {179, 161, 222},
+         {63, 142, 204},
+         {37, 113, 174},
+         {26, 89, 137},
+         {18, 68, 97}},
+        {{85, 181, 230},
+         {32, 146, 209},
+         {7, 100, 164},
+         {3, 71, 121},
+         {1, 45, 77},
+         {1, 18, 30}},
+        {{65, 187, 230},
+         {20, 148, 207},
+         {2, 97, 159},
+         {1, 68, 116},
+         {1, 40, 70},
+         {1, 14, 29}},
+        {{40, 194, 227},
+         {8, 147, 204},
+         {1, 94, 155},
+         {1, 65, 112},
+         {1, 39, 66},
+         {1, 14, 26}},
+        {{16, 208, 228},
+         {3, 151, 207},
+         {1, 98, 160},
+         {1, 67, 117},
+         {1, 41, 74},
+         {1, 17, 31}}}}},
+     // 32x32
+     {{{{{17, 38, 140}, {7, 34, 80}, {1, 17, 29}},
+        {{37, 75, 128},
+         {41, 76, 128},
+         {26, 66, 116},
+         {12, 52, 94},
+         {2, 32, 55},
+         {1, 10, 16}},
+        {{50, 127, 154},
+         {37, 109, 152},
+         {16, 82, 121},
+         {5, 59, 85},
+         {1, 35, 54},
+         {1, 13, 20}},
+        {{40, 142, 167},
+         {17, 110, 157},
+         {2, 71, 112},
+         {1, 44, 72},
+         {1, 27, 45},
+         {1, 11, 17}},
+        {{30, 175, 188},
+         {9, 124, 169},
+         {1, 74, 116},
+         {1, 48, 78},
+         {1, 30, 49},
+         {1, 11, 18}},
+        {{10, 222, 223},
+         {2, 150, 194},
+         {1, 83, 128},
+         {1, 48, 79},
+         {1, 27, 45},
+         {1, 11, 17}}},
+       {{{36, 41, 235}, {29, 36, 193}, {10, 27, 111}},
+        {{85, 165, 222},
+         {177, 162, 215},
+         {110, 135, 195},
+         {57, 113, 168},
+         {23, 83, 120},
+         {10, 49, 61}},
+        {{85, 190, 223},
+         {36, 139, 200},
+         {5, 90, 146},
+         {1, 60, 103},
+         {1, 38, 65},
+         {1, 18, 30}},
+        {{72, 202, 223},
+         {23, 141, 199},
+         {2, 86, 140},
+         {1, 56, 97},
+         {1, 36, 61},
+         {1, 16, 27}},
+        {{55, 218, 225},
+         {13, 145, 200},
+         {1, 86, 141},
+         {1, 57, 99},
+         {1, 35, 61},
+         {1, 13, 22}},
+        {{15, 235, 212},
+         {1, 132, 184},
+         {1, 84, 139},
+         {1, 57, 97},
+         {1, 34, 56},
+         {1, 14, 23}}}},
+      {{{{181, 21, 201}, {61, 37, 123}, {10, 38, 71}},
+        {{47, 106, 172},
+         {95, 104, 173},
+         {42, 93, 159},
+         {18, 77, 131},
+         {4, 50, 81},
+         {1, 17, 23}},
+        {{62, 147, 199},
+         {44, 130, 189},
+         {28, 102, 154},
+         {18, 75, 115},
+         {2, 44, 65},
+         {1, 12, 19}},
+        {{55, 153, 210},
+         {24, 130, 194},
+         {3, 93, 146},
+         {1, 61, 97},
+         {1, 31, 50},
+         {1, 10, 16}},
+        {{49, 186, 223},
+         {17, 148, 204},
+         {1, 96, 142},
+         {1, 53, 83},
+         {1, 26, 44},
+         {1, 11, 17}},
+        {{13, 217, 212},
+         {2, 136, 180},
+         {1, 78, 124},
+         {1, 50, 83},
+         {1, 29, 49},
+         {1, 14, 23}}},
+       {{{197, 13, 247}, {82, 17, 222}, {25, 17, 162}},
+        {{126, 186, 247},
+         {234, 191, 243},
+         {176, 177, 234},
+         {104, 158, 220},
+         {66, 128, 186},
+         {55, 90, 137}},
+        {{111, 197, 242},
+         {46, 158, 219},
+         {9, 104, 171},
+         {2, 65, 125},
+         {1, 44, 80},
+         {1, 17, 91}},
+        {{104, 208, 245},
+         {39, 168, 224},
+         {3, 109, 162},
+         {1, 79, 124},
+         {1, 50, 102},
+         {1, 43, 102}},
+        {{84, 220, 246},
+         {31, 177, 231},
+         {2, 115, 180},
+         {1, 79, 134},
+         {1, 55, 77},
+         {1, 60, 79}},
+        {{43, 243, 240},
+         {8, 180, 217},
+         {1, 115, 166},
+         {1, 84, 121},
+         {1, 51, 67},
+         {1, 16, 6}}}}}},
+    // skip_prob
+    {192, 128, 64},
+    // inter_mode_probs
+    {{2, 173, 34},
+     {7, 145, 85},
+     {7, 166, 63},
+     {7, 94, 66},
+     {8, 64, 46},
+     {17, 81, 31},
+     {25, 29, 30}},
+    // interp_filter_probs
+    {{235, 162}, {36, 255}, {34, 3}, {149, 144}},
+    // is_inter_prob
+    {9, 102, 187, 225},
+    // comp_mode_prob
+    {239, 183, 119, 96, 41},
+    // single_ref_prob
+    {{33, 16}, {77, 74}, {142, 142}, {172, 170}, {238, 247}},
+    // comp_ref_prob
+    {50, 126, 123, 221, 226},
+    // y_mode_probs
+    {{65, 32, 18, 144, 162, 194, 41, 51, 98},
+     {132, 68, 18, 165, 217, 196, 45, 40, 78},
+     {173, 80, 19, 176, 240, 193, 64, 35, 46},
+     {221, 135, 38, 194, 248, 121, 96, 85, 29}},
+    // uv_mode_probs
+    {{120, 7, 76, 176, 208, 126, 28, 54, 103},
+     {48, 12, 154, 155, 139, 90, 34, 117, 119},
+     {67, 6, 25, 204, 243, 158, 13, 21, 96},
+     {97, 5, 44, 131, 176, 139, 48, 68, 97},
+     {83, 5, 42, 156, 111, 152, 26, 49, 152},
+     {80, 5, 58, 178, 74, 83, 33, 62, 145},
+     {86, 5, 32, 154, 192, 168, 14, 22, 163},
+     {85, 5, 32, 156, 216, 148, 19, 29, 73},
+     {77, 7, 64, 116, 132, 122, 37, 126, 120},
+     {101, 21, 107, 181, 192, 103, 19, 67, 125}},
+    // partition_probs
+    {{199, 122, 141},
+     {147, 63, 159},
+     {148, 133, 118},
+     {121, 104, 114},
+     {174, 73, 87},
+     {92, 41, 83},
+     {82, 99, 50},
+     {53, 39, 39},
+     {177, 58, 59},
+     {68, 26, 63},
+     {52, 79, 25},
+     {17, 14, 12},
+     {222, 34, 30},
+     {72, 16, 44},
+     {58, 32, 12},
+     {10, 7, 6}},
+    // mv_joint_probs
+    {32, 64, 96},
+    // mv_sign_prob
+    {128, 128},
+    // mv_class_probs
+    {{224, 144, 192, 168, 192, 176, 192, 198, 198, 245},
+     {216, 128, 176, 160, 176, 176, 192, 198, 198, 208}},
+    // mv_class0_bit_prob
+    {216, 208},
+    // mv_bits_prob
+    {{136, 140, 148, 160, 176, 192, 224, 234, 234, 240},
+     {136, 140, 148, 160, 176, 192, 224, 234, 234, 240}},
+    // mv_class0_fr_probs
+    {{{128, 128, 64}, {96, 112, 64}}, {{128, 128, 64}, {96, 112, 64}}},
+    // mv_fr_probs
+    {{64, 96, 64}, {64, 96, 64}},
+    // mv_class0_hp_prob
+    {160, 160},
+    // mv_hp_prob
+    {128, 128},
+};
+
+// Helper function for Vp9Parser::ReadTileInfo. Defined as
+// calc_min_log2_tile_cols in spec 6.2.14 Tile size calculation.
+int GetMinLog2TileCols(int sb64_cols) {
+  const int kMaxTileWidthB64 = 64;
+  int min_log2 = 0;
+  while ((kMaxTileWidthB64 << min_log2) < sb64_cols)
+    min_log2++;
+  return min_log2;
+}
+
+// Helper function for Vp9Parser::ReadTileInfo. Defined as
+// calc_max_log2_tile_cols in spec 6.2.14 Tile size calculation.
+int GetMaxLog2TileCols(int sb64_cols) {
+  const int kMinTileWidthB64 = 4;
+  int max_log2 = 1;
+  while ((sb64_cols >> max_log2) >= kMinTileWidthB64)
+    max_log2++;
+  return max_log2 - 1;
+}
+
+}  // namespace
+
+Vp9UncompressedHeaderParser::Vp9UncompressedHeaderParser(
+    Vp9Parser::Context* context)
+    : context_(context) {}
+
+uint8_t Vp9UncompressedHeaderParser::ReadProfile() {
+  uint8_t profile = 0;
+
+  // LSB first.
+  if (reader_.ReadBool())
+    profile |= 1;
+  if (reader_.ReadBool())
+    profile |= 2;
+  if (profile > 2 && reader_.ReadBool())
+    profile += 1;
+  return profile;
+}
+
+// 6.2.1 Frame sync syntax
+bool Vp9UncompressedHeaderParser::VerifySyncCode() {
+  const int kSyncCode = 0x498342;
+  if (reader_.ReadLiteral(8 * 3) != kSyncCode) {
+    DVLOG(1) << "Invalid frame sync code";
+    return false;
+  }
+  return true;
+}
+
+// 6.2.2 Color config syntax
+bool Vp9UncompressedHeaderParser::ReadColorConfig(Vp9FrameHeader* fhdr) {
+  if (fhdr->profile == 2 || fhdr->profile == 3) {
+    fhdr->bit_depth = reader_.ReadBool() ? 12 : 10;
+  } else {
+    fhdr->bit_depth = 8;
+  }
+
+  fhdr->color_space = static_cast<Vp9ColorSpace>(reader_.ReadLiteral(3));
+  if (fhdr->color_space != Vp9ColorSpace::SRGB) {
+    fhdr->color_range = reader_.ReadBool();
+    if (fhdr->profile == 1 || fhdr->profile == 3) {
+      fhdr->subsampling_x = reader_.ReadBool() ? 1 : 0;
+      fhdr->subsampling_y = reader_.ReadBool() ? 1 : 0;
+      if (fhdr->subsampling_x == 1 && fhdr->subsampling_y == 1) {
+        DVLOG(1) << "4:2:0 color not supported in profile 1 or 3";
+        return false;
+      }
+      bool reserved = reader_.ReadBool();
+      if (reserved) {
+        DVLOG(1) << "reserved bit set";
+        return false;
+      }
+    } else {
+      fhdr->subsampling_x = fhdr->subsampling_y = 1;
+    }
+  } else {
+    fhdr->color_range = true;
+    if (fhdr->profile == 1 || fhdr->profile == 3) {
+      fhdr->subsampling_x = fhdr->subsampling_y = 0;
+
+      bool reserved = reader_.ReadBool();
+      if (reserved) {
+        DVLOG(1) << "reserved bit set";
+        return false;
+      }
+    } else {
+      DVLOG(1) << "4:4:4 color not supported in profile 0 or 2";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// 6.2.3 Frame size syntax
+void Vp9UncompressedHeaderParser::ReadFrameSize(Vp9FrameHeader* fhdr) {
+  fhdr->frame_width = reader_.ReadLiteral(16) + 1;
+  fhdr->frame_height = reader_.ReadLiteral(16) + 1;
+}
+
+// 6.2.4 Render size syntax
+void Vp9UncompressedHeaderParser::ReadRenderSize(Vp9FrameHeader* fhdr) {
+  if (reader_.ReadBool()) {
+    fhdr->render_width = reader_.ReadLiteral(16) + 1;
+    fhdr->render_height = reader_.ReadLiteral(16) + 1;
+  } else {
+    fhdr->render_width = fhdr->frame_width;
+    fhdr->render_height = fhdr->frame_height;
+  }
+}
+
+// 6.2.5 Frame size with refs syntax
+bool Vp9UncompressedHeaderParser::ReadFrameSizeFromRefs(Vp9FrameHeader* fhdr) {
+  bool found_ref = false;
+  for (const auto& idx : fhdr->ref_frame_idx) {
+    found_ref = reader_.ReadBool();
+    if (found_ref) {
+      const Vp9Parser::ReferenceSlot& ref = context_->GetRefSlot(idx);
+      DCHECK(ref.initialized);
+      fhdr->frame_width = ref.frame_width;
+      fhdr->frame_height = ref.frame_height;
+
+      const unsigned kMaxDimension = 1u << 16;
+      DCHECK_LE(fhdr->frame_width, kMaxDimension);
+      DCHECK_LE(fhdr->frame_height, kMaxDimension);
+      break;
+    }
+  }
+
+  if (!found_ref)
+    ReadFrameSize(fhdr);
+
+  // 7.2.5 Frame size with refs semantics
+  bool has_valid_ref_frame = false;
+  for (const auto& idx : fhdr->ref_frame_idx) {
+    const Vp9Parser::ReferenceSlot& ref = context_->GetRefSlot(idx);
+    if (2 * fhdr->frame_width >= ref.frame_width &&
+        2 * fhdr->frame_height >= ref.frame_height &&
+        fhdr->frame_width <= 16 * ref.frame_width &&
+        fhdr->frame_height <= 16 * ref.frame_height) {
+      has_valid_ref_frame = true;
+      break;
+    }
+  }
+  if (!has_valid_ref_frame) {
+    DVLOG(1) << "There should be at least one reference frame meeting "
+             << "size conditions.";
+    return false;
+  }
+
+  ReadRenderSize(fhdr);
+  return true;
+}
+
+// 6.2.7 Interpolation filter syntax
+Vp9InterpolationFilter Vp9UncompressedHeaderParser::ReadInterpolationFilter() {
+  if (reader_.ReadBool())
+    return Vp9InterpolationFilter::SWITCHABLE;
+
+  // The mapping table for next two bits.
+  const Vp9InterpolationFilter table[] = {
+      Vp9InterpolationFilter::EIGHTTAP_SMOOTH, Vp9InterpolationFilter::EIGHTTAP,
+      Vp9InterpolationFilter::EIGHTTAP_SHARP, Vp9InterpolationFilter::BILINEAR,
+  };
+  return table[reader_.ReadLiteral(2)];
+}
+
+void Vp9UncompressedHeaderParser::SetupPastIndependence(Vp9FrameHeader* fhdr) {
+  memset(&context_->segmentation_, 0, sizeof(context_->segmentation_));
+  ResetLoopfilter();
+  fhdr->frame_context = kVp9DefaultFrameContext;
+  DCHECK(fhdr->frame_context.IsValid());
+}
+
+// 6.2.8 Loop filter params syntax
+void Vp9UncompressedHeaderParser::ReadLoopFilterParams() {
+  Vp9LoopFilterParams& loop_filter = context_->loop_filter_;
+
+  loop_filter.level = reader_.ReadLiteral(6);
+  loop_filter.sharpness = reader_.ReadLiteral(3);
+  loop_filter.delta_update = false;
+
+  loop_filter.delta_enabled = reader_.ReadBool();
+  if (loop_filter.delta_enabled) {
+    loop_filter.delta_update = reader_.ReadBool();
+    if (loop_filter.delta_update) {
+      for (size_t i = 0; i < Vp9RefType::VP9_FRAME_MAX; i++) {
+        loop_filter.update_ref_deltas[i] = reader_.ReadBool();
+        if (loop_filter.update_ref_deltas[i])
+          loop_filter.ref_deltas[i] = reader_.ReadSignedLiteral(6);
+      }
+
+      for (size_t i = 0; i < Vp9LoopFilterParams::kNumModeDeltas; i++) {
+        loop_filter.update_mode_deltas[i] = reader_.ReadBool();
+        if (loop_filter.update_mode_deltas[i])
+          loop_filter.mode_deltas[i] = reader_.ReadLiteral(6);
+      }
+    }
+  }
+}
+
+// 6.2.9 Quantization params syntax
+void Vp9UncompressedHeaderParser::ReadQuantizationParams(
+    Vp9QuantizationParams* quants) {
+  quants->base_q_idx = reader_.ReadLiteral(8);
+
+  quants->delta_q_y_dc = ReadDeltaQ();
+  quants->delta_q_uv_dc = ReadDeltaQ();
+  quants->delta_q_uv_ac = ReadDeltaQ();
+}
+
+// 6.2.10 Delta quantizer syntax
+int8_t Vp9UncompressedHeaderParser::ReadDeltaQ() {
+  if (reader_.ReadBool())
+    return reader_.ReadSignedLiteral(4);
+  return 0;
+}
+
+// 6.2.11 Segmentation params syntax
+bool Vp9UncompressedHeaderParser::ReadSegmentationParams() {
+  Vp9SegmentationParams& segmentation = context_->segmentation_;
+  segmentation.update_map = false;
+  segmentation.update_data = false;
+
+  segmentation.enabled = reader_.ReadBool();
+  if (!segmentation.enabled)
+    return true;
+
+  segmentation.update_map = reader_.ReadBool();
+  if (segmentation.update_map) {
+    for (auto& tree_prob : segmentation.tree_probs) {
+      tree_prob = ReadProb();
+    }
+
+    segmentation.temporal_update = reader_.ReadBool();
+    for (auto& pred_prob : segmentation.pred_probs) {
+      pred_prob = segmentation.temporal_update ? ReadProb() : kVp9MaxProb;
+    }
+  }
+
+  segmentation.update_data = reader_.ReadBool();
+  if (segmentation.update_data) {
+    segmentation.abs_or_delta_update = reader_.ReadBool();
+
+    const int kFeatureDataBits[] = {8, 6, 2, 0};
+    const bool kFeatureDataSigned[] = {true, true, false, false};
+
+    for (size_t i = 0; i < Vp9SegmentationParams::kNumSegments; i++) {
+      for (size_t j = 0; j < Vp9SegmentationParams::SEG_LVL_MAX; j++) {
+        int16_t data = 0;
+        segmentation.feature_enabled[i][j] = reader_.ReadBool();
+        if (segmentation.feature_enabled[i][j]) {
+          data = reader_.ReadLiteral(kFeatureDataBits[j]);
+          if (kFeatureDataSigned[j])
+            if (reader_.ReadBool()) {
+              // 7.2.9
+              if (segmentation.abs_or_delta_update) {
+                DVLOG(1) << "feature_sign should be 0"
+                         << " if abs_or_delta_update is 1";
+                return false;
+              }
+              data = -data;
+            }
+        }
+        segmentation.feature_data[i][j] = data;
+      }
+    }
+  }
+  return true;
+}
+
+// 6.2.12 Probability syntax
+uint8_t Vp9UncompressedHeaderParser::ReadProb() {
+  return reader_.ReadBool() ? reader_.ReadLiteral(8) : kVp9MaxProb;
+}
+
+// 6.2.13 Tile info syntax
+bool Vp9UncompressedHeaderParser::ReadTileInfo(Vp9FrameHeader* fhdr) {
+  int sb64_cols = (fhdr->frame_width + 63) / 64;
+
+  int min_log2_tile_cols = GetMinLog2TileCols(sb64_cols);
+  int max_log2_tile_cols = GetMaxLog2TileCols(sb64_cols);
+
+  int max_ones = max_log2_tile_cols - min_log2_tile_cols;
+  fhdr->tile_cols_log2 = min_log2_tile_cols;
+  while (max_ones-- && reader_.ReadBool())
+    fhdr->tile_cols_log2++;
+
+  fhdr->tile_rows_log2 = reader_.ReadBool() ? 1 : 0;
+  if (fhdr->tile_rows_log2 > 0 && reader_.ReadBool())
+    fhdr->tile_rows_log2++;
+
+  // 7.2.11 Tile info semantics
+  if (fhdr->tile_cols_log2 > 6) {
+    DVLOG(1) << "tile_cols_log2 should be <= 6";
+    return false;
+  }
+
+  return true;
+}
+
+void Vp9UncompressedHeaderParser::ResetLoopfilter() {
+  Vp9LoopFilterParams& loop_filter = context_->loop_filter_;
+
+  loop_filter.delta_enabled = true;
+  loop_filter.delta_update = true;
+
+  loop_filter.ref_deltas[VP9_FRAME_INTRA] = 1;
+  loop_filter.ref_deltas[VP9_FRAME_LAST] = 0;
+  loop_filter.ref_deltas[VP9_FRAME_GOLDEN] = -1;
+  loop_filter.ref_deltas[VP9_FRAME_ALTREF] = -1;
+
+  memset(loop_filter.mode_deltas, 0, sizeof(loop_filter.mode_deltas));
+}
+
+// 6.2 Uncompressed header syntax
+bool Vp9UncompressedHeaderParser::Parse(const uint8_t* stream,
+                                        off_t frame_size,
+                                        Vp9FrameHeader* fhdr) {
+  DVLOG(2) << "Vp9UncompressedHeaderParser::Parse";
+  reader_.Initialize(stream, frame_size);
+
+  fhdr->data = stream;
+  fhdr->frame_size = frame_size;
+
+  // frame marker
+  if (reader_.ReadLiteral(2) != 0x2) {
+    DVLOG(1) << "frame marker shall be equal to 2";
+    return false;
+  }
+
+  fhdr->profile = ReadProfile();
+  if (fhdr->profile >= kVp9MaxProfile) {
+    DVLOG(1) << "Unsupported bitstream profile";
+    return false;
+  }
+
+  fhdr->show_existing_frame = reader_.ReadBool();
+  if (fhdr->show_existing_frame) {
+    fhdr->frame_to_show_map_idx = reader_.ReadLiteral(3);
+    fhdr->show_frame = true;
+
+    if (!reader_.ConsumeTrailingBits()) {
+      DVLOG(1) << "trailing bits are not zero";
+      return false;
+    }
+    if (!reader_.IsValid()) {
+      DVLOG(1) << "parser reads beyond the end of buffer";
+      return false;
+    }
+    fhdr->uncompressed_header_size = reader_.GetBytesRead();
+    fhdr->header_size_in_bytes = 0;
+    return true;
+  }
+
+  fhdr->frame_type = static_cast<Vp9FrameHeader::FrameType>(reader_.ReadBool());
+  fhdr->show_frame = reader_.ReadBool();
+  fhdr->error_resilient_mode = reader_.ReadBool();
+
+  if (fhdr->IsKeyframe()) {
+    if (!VerifySyncCode())
+      return false;
+
+    if (!ReadColorConfig(fhdr))
+      return false;
+
+    ReadFrameSize(fhdr);
+    ReadRenderSize(fhdr);
+    fhdr->refresh_frame_flags = 0xff;
+  } else {
+    if (!fhdr->show_frame)
+      fhdr->intra_only = reader_.ReadBool();
+
+    if (!fhdr->error_resilient_mode)
+      fhdr->reset_frame_context = reader_.ReadLiteral(2);
+
+    if (fhdr->intra_only) {
+      if (!VerifySyncCode())
+        return false;
+
+      if (fhdr->profile > 0) {
+        if (!ReadColorConfig(fhdr))
+          return false;
+      } else {
+        fhdr->bit_depth = 8;
+        fhdr->color_space = Vp9ColorSpace::BT_601;
+        fhdr->subsampling_x = fhdr->subsampling_y = 1;
+      }
+
+      fhdr->refresh_frame_flags = reader_.ReadLiteral(8);
+
+      ReadFrameSize(fhdr);
+      ReadRenderSize(fhdr);
+    } else {
+      fhdr->refresh_frame_flags = reader_.ReadLiteral(8);
+
+      static_assert(arraysize(fhdr->ref_frame_sign_bias) >=
+                        Vp9RefType::VP9_FRAME_LAST + kVp9NumRefsPerFrame,
+                    "ref_frame_sign_bias is not big enough");
+      for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) {
+        fhdr->ref_frame_idx[i] = reader_.ReadLiteral(kVp9NumRefFramesLog2);
+        fhdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_LAST + i] =
+            reader_.ReadBool();
+
+        // 8.2 Frame order constraints
+        // ref_frame_idx[i] refers to an earlier decoded frame.
+        const Vp9Parser::ReferenceSlot& ref =
+            context_->GetRefSlot(fhdr->ref_frame_idx[i]);
+        if (!ref.initialized) {
+          DVLOG(1) << "ref_frame_idx[" << i
+                   << "]=" << static_cast<int>(fhdr->ref_frame_idx[i])
+                   << " refers to unused frame";
+          return false;
+        }
+
+        // 7.2 Uncompressed header semantics
+        // the selected reference frames match the current frame in bit depth,
+        // profile, chroma subsampling, and color space.
+        if (ref.profile != fhdr->profile) {
+          DVLOG(1) << "profile of referenced frame mismatch";
+          return false;
+        }
+        if (i == 0) {
+          // Below fields are not specified for inter-frame in header, so copy
+          // them from referenced frame.
+          fhdr->bit_depth = ref.bit_depth;
+          fhdr->color_space = ref.color_space;
+          fhdr->subsampling_x = ref.subsampling_x;
+          fhdr->subsampling_y = ref.subsampling_y;
+        } else {
+          if (fhdr->bit_depth != ref.bit_depth) {
+            DVLOG(1) << "bit_depth of referenced frame mismatch";
+            return false;
+          }
+          if (fhdr->color_space != ref.color_space) {
+            DVLOG(1) << "color_space of referenced frame mismatch";
+            return false;
+          }
+          if (fhdr->subsampling_x != ref.subsampling_x ||
+              fhdr->subsampling_y != ref.subsampling_y) {
+            DVLOG(1) << "chroma subsampling of referenced frame mismatch";
+            return false;
+          }
+        }
+      }
+
+      if (!ReadFrameSizeFromRefs(fhdr))
+        return false;
+
+      fhdr->allow_high_precision_mv = reader_.ReadBool();
+      fhdr->interpolation_filter = ReadInterpolationFilter();
+    }
+  }
+
+  if (fhdr->error_resilient_mode) {
+    fhdr->refresh_frame_context = false;
+    fhdr->frame_parallel_decoding_mode = true;
+  } else {
+    fhdr->refresh_frame_context = reader_.ReadBool();
+    fhdr->frame_parallel_decoding_mode = reader_.ReadBool();
+  }
+
+  fhdr->frame_context_idx_to_save_probs = fhdr->frame_context_idx =
+      reader_.ReadLiteral(kVp9NumFrameContextsLog2);
+
+  if (fhdr->IsIntra()) {
+    SetupPastIndependence(fhdr);
+    if (fhdr->IsKeyframe() || fhdr->error_resilient_mode ||
+        fhdr->reset_frame_context == 3) {
+      for (size_t i = 0; i < kVp9NumFrameContexts; ++i)
+        context_->UpdateFrameContext(i, fhdr->frame_context);
+    } else if (fhdr->reset_frame_context == 2) {
+      context_->UpdateFrameContext(fhdr->frame_context_idx,
+                                   fhdr->frame_context);
+    }
+    fhdr->frame_context_idx = 0;
+  }
+
+  ReadLoopFilterParams();
+  ReadQuantizationParams(&fhdr->quant_params);
+  if (!ReadSegmentationParams())
+    return false;
+
+  if (!ReadTileInfo(fhdr))
+    return false;
+
+  fhdr->header_size_in_bytes = reader_.ReadLiteral(16);
+  if (fhdr->header_size_in_bytes == 0) {
+    DVLOG(1) << "invalid header size";
+    return false;
+  }
+
+  if (!reader_.ConsumeTrailingBits()) {
+    DVLOG(1) << "trailing bits are not zero";
+    return false;
+  }
+  if (!reader_.IsValid()) {
+    DVLOG(1) << "parser reads beyond the end of buffer";
+    return false;
+  }
+  fhdr->uncompressed_header_size = reader_.GetBytesRead();
+
+  return true;
+}
+
+}  // namespace media
diff --git a/vda/vp9_uncompressed_header_parser.h b/vda/vp9_uncompressed_header_parser.h
new file mode 100644
index 0000000..655ba38
--- /dev/null
+++ b/vda/vp9_uncompressed_header_parser.h
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VP9_UNCOMPRESSED_HEADER_PARSER_H_
+#define VP9_UNCOMPRESSED_HEADER_PARSER_H_
+
+#include "vp9_parser.h"
+#include "vp9_raw_bits_reader.h"
+
+namespace media {
+
+class Vp9UncompressedHeaderParser {
+ public:
+  Vp9UncompressedHeaderParser(Vp9Parser::Context* context);
+
+  // Parses VP9 uncompressed header in |stream| with |frame_size| into |fhdr|.
+  // Returns true if no error.
+  bool Parse(const uint8_t* stream, off_t frame_size, Vp9FrameHeader* fhdr);
+
+ private:
+  uint8_t ReadProfile();
+  bool VerifySyncCode();
+  bool ReadColorConfig(Vp9FrameHeader* fhdr);
+  void ReadFrameSize(Vp9FrameHeader* fhdr);
+  bool ReadFrameSizeFromRefs(Vp9FrameHeader* fhdr);
+  void ReadRenderSize(Vp9FrameHeader* fhdr);
+  Vp9InterpolationFilter ReadInterpolationFilter();
+  void ResetLoopfilter();
+  void SetupPastIndependence(Vp9FrameHeader* fhdr);
+  void ReadLoopFilterParams();
+  void ReadQuantizationParams(Vp9QuantizationParams* quants);
+  int8_t ReadDeltaQ();
+  uint8_t ReadProb();
+  bool ReadSegmentationParams();
+  bool ReadTileInfo(Vp9FrameHeader* fhdr);
+
+  // Raw bits reader for uncompressed frame header.
+  Vp9RawBitsReader reader_;
+
+  Vp9Parser::Context* context_;
+
+  DISALLOW_COPY_AND_ASSIGN(Vp9UncompressedHeaderParser);
+};
+
+}  // namespace media
+
+#endif  // VP9_UNCOMPRESSED_HEADER_PARSER_H_