aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2017-05-23 08:43:26 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2017-05-23 08:43:33 +0000
commitad441c8d0c0b54fa2dfa7f55721d1978872d8b96 (patch)
tree2f18fd2676e2ae543963defb4bc02c34da68ae91
parentc5aa15cfac75087d0b86fd9cd511fa8c67d2dfa3 (diff)
parent602fe7bfa20792a998c5d3dc896bb7962010a90f (diff)
downloadv4l2_codec2-oreo-dr1-dev.tar.gz
Merge changes I53a231cc,I9cc63969,I40b25182,I888b930d,I4399c843, ...oreo-dr1-dev
* changes: Port vp9_picture and vp9_decoder from Chromium Port vp9_bool_decoder and vp9_parser from Chromium Port vp8_picture and vp8_decoder from Chromium Port vp8_bool_decoder and vp8_parser from Chromium Port h264_decoder from Chromium Port h264_dpb from Chromium Port h264_parser from Chromium
-rw-r--r--vda/Android.mk20
-rw-r--r--vda/accelerated_video_decoder.h67
-rw-r--r--vda/bit_reader.cc48
-rw-r--r--vda/bit_reader.h70
-rw-r--r--vda/bit_reader_core.cc193
-rw-r--r--vda/bit_reader_core.h124
-rw-r--r--vda/h264_decoder.cc1436
-rw-r--r--vda/h264_decoder.h280
-rw-r--r--vda/h264_dpb.cc166
-rw-r--r--vda/h264_dpb.h171
-rw-r--r--vda/h264_parser.cc1382
-rw-r--r--vda/h264_parser.h502
-rw-r--r--vda/ranges.cc15
-rw-r--r--vda/ranges.h162
-rw-r--r--vda/size.h60
-rw-r--r--vda/subsample_entry.h31
-rw-r--r--vda/vp8_bool_decoder.cc208
-rw-r--r--vda/vp8_bool_decoder.h134
-rw-r--r--vda/vp8_decoder.cc195
-rw-r--r--vda/vp8_decoder.h113
-rw-r--r--vda/vp8_parser.cc876
-rw-r--r--vda/vp8_parser.h198
-rw-r--r--vda/vp8_picture.cc13
-rw-r--r--vda/vp8_picture.h26
-rw-r--r--vda/vp9_bool_decoder.cc164
-rw-r--r--vda/vp9_bool_decoder.h72
-rw-r--r--vda/vp9_compressed_header_parser.cc293
-rw-r--r--vda/vp9_compressed_header_parser.h51
-rw-r--r--vda/vp9_decoder.cc213
-rw-r--r--vda/vp9_decoder.h153
-rw-r--r--vda/vp9_parser.cc595
-rw-r--r--vda/vp9_parser.h433
-rw-r--r--vda/vp9_picture.cc13
-rw-r--r--vda/vp9_picture.h31
-rw-r--r--vda/vp9_raw_bits_reader.cc61
-rw-r--r--vda/vp9_raw_bits_reader.h66
-rw-r--r--vda/vp9_uncompressed_header_parser.cc1102
-rw-r--r--vda/vp9_uncompressed_header_parser.h48
38 files changed, 9784 insertions, 1 deletions
diff --git a/vda/Android.mk b/vda/Android.mk
index 35ff999..c005941 100644
--- a/vda/Android.mk
+++ b/vda/Android.mk
@@ -3,7 +3,24 @@ 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 \
@@ -12,7 +29,8 @@ LOCAL_MODULE:= libv4l2_codec2_vda
LOCAL_SHARED_LIBRARIES := libchrome \
-LOCAL_CFLAGS += -Werror -Wall
+# -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
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_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_