diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2017-05-23 08:43:26 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2017-05-23 08:43:33 +0000 |
commit | ad441c8d0c0b54fa2dfa7f55721d1978872d8b96 (patch) | |
tree | 2f18fd2676e2ae543963defb4bc02c34da68ae91 | |
parent | c5aa15cfac75087d0b86fd9cd511fa8c67d2dfa3 (diff) | |
parent | 602fe7bfa20792a998c5d3dc896bb7962010a90f (diff) | |
download | v4l2_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
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(®_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(¬_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_ |