diff options
author | posciak@chromium.org <posciak@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-25 07:08:01 +0000 |
---|---|---|
committer | posciak@chromium.org <posciak@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-25 07:08:01 +0000 |
commit | 9a6ea10bec70ccaa31a105fa9d628988923cce53 (patch) | |
tree | 5775239e9207e39c3e12eacde6717c498d814e11 /media | |
parent | 4e5e52894edb28ea88fa76f2ac5d38b6fd2258aa (diff) | |
download | chromium_src-9a6ea10bec70ccaa31a105fa9d628988923cce53.zip chromium_src-9a6ea10bec70ccaa31a105fa9d628988923cce53.tar.gz chromium_src-9a6ea10bec70ccaa31a105fa9d628988923cce53.tar.bz2 |
Add VaapiVideoEncodeAccelerator for HW-accelerated video encode.
Add an implementation of VideoEncodeAccelerator utilizing VA-API for
hardware encode on Intel-based ChromeOS platforms.
BUG=378962
TEST=video_encode_accelerator_unittest
Review URL: https://codereview.chromium.org/333253002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@279650 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/h264_bitstream_buffer.cc | 152 | ||||
-rw-r--r-- | media/filters/h264_bitstream_buffer.h | 120 | ||||
-rw-r--r-- | media/filters/h264_bitstream_buffer_unittest.cc | 55 | ||||
-rw-r--r-- | media/filters/h264_parser.cc | 6 | ||||
-rw-r--r-- | media/filters/h264_parser.h | 39 | ||||
-rw-r--r-- | media/media.gyp | 12 |
6 files changed, 379 insertions, 5 deletions
diff --git a/media/filters/h264_bitstream_buffer.cc b/media/filters/h264_bitstream_buffer.cc new file mode 100644 index 0000000..48463a5 --- /dev/null +++ b/media/filters/h264_bitstream_buffer.cc @@ -0,0 +1,152 @@ +// 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 "media/filters/h264_bitstream_buffer.h" + +#include "base/sys_byteorder.h" + +namespace media { + +H264BitstreamBuffer::H264BitstreamBuffer() : data_(NULL) { + Reset(); +} + +H264BitstreamBuffer::~H264BitstreamBuffer() { + free(data_); + data_ = NULL; +} + +void H264BitstreamBuffer::Reset() { + free(data_); + data_ = NULL; + + capacity_ = 0; + pos_ = 0; + reg_ = 0; + + Grow(); + + bits_left_in_reg_ = kRegBitSize; +} + +void H264BitstreamBuffer::Grow() { + data_ = static_cast<uint8*>(realloc(data_, capacity_ + kGrowBytes)); + CHECK(data_) << "Failed growing the buffer"; + capacity_ += kGrowBytes; +} + +void H264BitstreamBuffer::FlushReg() { + // Flush all bytes that have at least one bit cached, but not more + // (on Flush(), reg_ may not be full). + size_t bits_in_reg = kRegBitSize - bits_left_in_reg_; + if (bits_in_reg == 0) + return; + + size_t bytes_in_reg = (bits_in_reg + 7) / 8; + reg_ <<= (kRegBitSize - bits_in_reg); + + // Convert to MSB and append as such to the stream. + reg_ = base::HostToNet64(reg_); + + // Make sure we have enough space. Grow() will CHECK() on allocation failure. + if (pos_ + bytes_in_reg < capacity_) + Grow(); + + memcpy(data_ + pos_, ®_, bytes_in_reg); + pos_ += bytes_in_reg; + + reg_ = 0; + bits_left_in_reg_ = kRegBitSize; +} + +void H264BitstreamBuffer::AppendU64(size_t num_bits, uint64 val) { + CHECK_LE(num_bits, kRegBitSize); + + while (num_bits > 0) { + if (bits_left_in_reg_ == 0) + FlushReg(); + + uint64 bits_to_write = + num_bits > bits_left_in_reg_ ? bits_left_in_reg_ : num_bits; + uint64 val_to_write = (val >> (num_bits - bits_to_write)); + if (bits_to_write < 64) + val_to_write &= ((1ull << bits_to_write) - 1); + reg_ <<= bits_to_write; + reg_ |= val_to_write; + num_bits -= bits_to_write; + bits_left_in_reg_ -= bits_to_write; + } +} + +void H264BitstreamBuffer::AppendBool(bool val) { + if (bits_left_in_reg_ == 0) + FlushReg(); + + reg_ <<= 1; + reg_ |= (static_cast<uint64>(val) & 1); + --bits_left_in_reg_; +} + +void H264BitstreamBuffer::AppendSE(int val) { + if (val > 0) + AppendUE(val * 2 - 1); + else + AppendUE(-val * 2); +} + +void H264BitstreamBuffer::AppendUE(unsigned int val) { + size_t num_zeros = 0; + unsigned int v = val + 1; + + while (v > 1) { + v >>= 1; + ++num_zeros; + } + + AppendBits(num_zeros, 0); + AppendBits(num_zeros + 1, val + 1); +} + +#define DCHECK_FINISHED() \ + DCHECK_EQ(bits_left_in_reg_, kRegBitSize) << "Pending bits not yet written " \ + "to the buffer, call " \ + "FinishNALU() first." + +void H264BitstreamBuffer::BeginNALU(H264NALU::Type nalu_type, int nal_ref_idc) { + DCHECK_FINISHED(); + + DCHECK_LE(nalu_type, H264NALU::kEOStream); + DCHECK_GE(nal_ref_idc, 0); + DCHECK_LE(nal_ref_idc, 3); + + AppendBits(32, 0x00000001); + AppendBits(1, 0); // forbidden_zero_bit + AppendBits(2, nal_ref_idc); + AppendBits(5, nalu_type); +} + +void H264BitstreamBuffer::FinishNALU() { + // RBSP stop one bit. + AppendBits(1, 1); + + // Byte-alignment zero bits. + AppendBits(bits_left_in_reg_ % 8, 0); + + if (bits_left_in_reg_ != kRegBitSize) + FlushReg(); +} + +size_t H264BitstreamBuffer::BytesInBuffer() { + DCHECK_FINISHED(); + return pos_; +} + +uint8* H264BitstreamBuffer::data() { + DCHECK(data_); + DCHECK_FINISHED(); + + return data_; +} + +} // namespace media diff --git a/media/filters/h264_bitstream_buffer.h b/media/filters/h264_bitstream_buffer.h new file mode 100644 index 0000000..4b0511d --- /dev/null +++ b/media/filters/h264_bitstream_buffer.h @@ -0,0 +1,120 @@ +// 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 a H264BitstreamBuffer class for +// constructing raw bitstream buffers containing NAL units in +// H.264 Annex-B stream format. +// See H.264 spec Annex B and chapter 7for more details. + +#ifndef MEDIA_FILTERS_H264_BITSTREAM_BUFFER_H_ +#define MEDIA_FILTERS_H264_BITSTREAM_BUFFER_H_ + +#include "base/gtest_prod_util.h" +#include "base/numerics/safe_conversions.h" +#include "media/base/media_export.h" +#include "media/base/video_frame.h" +#include "media/filters/h264_parser.h" + +namespace media { + +// Holds one or more NALUs as a raw bitstream buffer in H.264 Annex-B format. +// Note that this class currently does NOT insert emulation prevention +// three-byte sequences (spec 7.3.1). +class MEDIA_EXPORT H264BitstreamBuffer { + public: + H264BitstreamBuffer(); + ~H264BitstreamBuffer(); + + // Discard all data and reset the buffer for reuse. + void Reset(); + + // Append |num_bits| bits to the stream from |val|. + // |val| is interpreted in the host endianness. + template <typename T> + void AppendBits(size_t num_bits, T val) { + AppendU64(num_bits, static_cast<uint64>(val)); + } + + void AppendBits(size_t num_bits, bool val) { + DCHECK_EQ(num_bits, 1ul); + AppendBool(val); + } + + // Append a one-bit bool/flag value |val| to the stream. + void AppendBool(bool val); + + // Append a signed value in |val| in Exp-Golomb code. + void AppendSE(int val); + + // Append an unsigned value in |val| in Exp-Golomb code. + void AppendUE(unsigned int val); + + // Start a new NALU of type |nalu_type| and with given |nal_ref_idc| + // (see spec). Note, that until FinishNALU() is called, some of the bits + // may not be flushed into the buffer and the data will not be correctly + // aligned with trailing bits. + void BeginNALU(H264NALU::Type nalu_type, int nal_ref_idc); + + // Finish current NALU. This will flush any cached bits and correctly align + // the buffer with RBSP trailing bits. This MUST be called for the stream + // returned by data() to be correct. + void FinishNALU(); + + // Return number of full bytes in the stream. Note that FinishNALU() has to + // be called to flush cached bits, or the return value will not include them. + size_t BytesInBuffer(); + + // Return a pointer to the stream. FinishNALU() must be called before + // accessing the stream, otherwise some bits may still be cached and not + // in the buffer. + uint8* data(); + + private: + FRIEND_TEST_ALL_PREFIXES(H264BitstreamBufferAppendBitsTest, + AppendAndVerifyBits); + + // Allocate additional memory (kGrowBytes bytes) for the buffer. + void Grow(); + + // Append |num_bits| bits from U64 value |val| (in host endianness). + void AppendU64(size_t num_bits, uint64 val); + + // Flush any cached bits in the reg with byte granularity, i.e. enough + // bytes to flush all pending bits, but not more. + void FlushReg(); + + typedef uint64 RegType; + enum { + // Sizes of reg_. + kRegByteSize = sizeof(RegType), + kRegBitSize = kRegByteSize * 8, + // Amount of bytes to grow the buffer by when we run out of + // previously-allocated memory for it. + kGrowBytes = 4096, + }; + + COMPILE_ASSERT(kGrowBytes >= kRegByteSize, + kGrowBytes_must_be_larger_than_kRegByteSize); + + // Unused bits left in reg_. + size_t bits_left_in_reg_; + + // Cache for appended bits. Bits are flushed to data_ with kRegByteSize + // granularity, i.e. when reg_ becomes full, or when an explicit FlushReg() + // is called. + RegType reg_; + + // Current capacity of data_, in bytes. + size_t capacity_; + + // Current byte offset in data_ (points to the start of unwritten bits). + size_t pos_; + + // Buffer for stream data. + uint8* data_; +}; + +} // namespace media + +#endif // MEDIA_FILTERS_H264_BITSTREAM_BUFFER_H_ diff --git a/media/filters/h264_bitstream_buffer_unittest.cc b/media/filters/h264_bitstream_buffer_unittest.cc new file mode 100644 index 0000000..3775829 --- /dev/null +++ b/media/filters/h264_bitstream_buffer_unittest.cc @@ -0,0 +1,55 @@ +// 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 "media/filters/h264_bitstream_buffer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +namespace { +const uint64 kTestPattern = 0xfedcba0987654321; +} + +class H264BitstreamBufferAppendBitsTest + : public ::testing::TestWithParam<size_t> {}; + +// TODO(posciak): More tests! + +TEST_P(H264BitstreamBufferAppendBitsTest, AppendAndVerifyBits) { + H264BitstreamBuffer b; + uint64 num_bits = GetParam(); + // TODO(posciak): Tests for >64 bits. + ASSERT_LE(num_bits, 64u); + uint64 num_bytes = (num_bits + 7) / 8; + + b.AppendBits(num_bits, kTestPattern); + b.FlushReg(); + + EXPECT_EQ(b.BytesInBuffer(), num_bytes); + + uint8* ptr = b.data(); + uint64 got = 0; + uint64 expected = kTestPattern; + + if (num_bits < 64) + expected &= ((1ull << num_bits) - 1); + + while (num_bits > 8) { + got |= (*ptr & 0xff); + num_bits -= 8; + got <<= (num_bits > 8 ? 8 : num_bits); + ptr++; + } + if (num_bits > 0) { + uint64 temp = (*ptr & 0xff); + temp >>= (8 - num_bits); + got |= temp; + } + EXPECT_EQ(got, expected) << std::hex << "0x" << got << " vs 0x" << expected; +} + +INSTANTIATE_TEST_CASE_P(AppendNumBits, + H264BitstreamBufferAppendBitsTest, + ::testing::Range(1ul, 65ul)); +} // namespace media diff --git a/media/filters/h264_parser.cc b/media/filters/h264_parser.cc index 4cdc695..ee21ab8 100644 --- a/media/filters/h264_parser.cc +++ b/media/filters/h264_parser.cc @@ -106,10 +106,6 @@ H264SEIMessage::H264SEIMessage() { } \ } while (0) -enum AspectRatioIdc { - kExtendedSar = 255, -}; - // ISO 14496 part 10 // VUI parameters: Table E-1 "Meaning of sample aspect ratio indicator" static const int kTableSarWidth[] = { @@ -608,7 +604,7 @@ H264Parser::Result H264Parser::ParseVUIParameters(H264SPS* sps) { if (aspect_ratio_info_present_flag) { int aspect_ratio_idc; READ_BITS_OR_RETURN(8, &aspect_ratio_idc); - if (aspect_ratio_idc == kExtendedSar) { + if (aspect_ratio_idc == H264SPS::kExtendedSar) { READ_BITS_OR_RETURN(16, &sps->sar_width); READ_BITS_OR_RETURN(16, &sps->sar_height); } else { diff --git a/media/filters/h264_parser.h b/media/filters/h264_parser.h index 3a60dce..45020af 100644 --- a/media/filters/h264_parser.h +++ b/media/filters/h264_parser.h @@ -63,6 +63,26 @@ enum { struct MEDIA_EXPORT H264SPS { H264SPS(); + enum H264ProfileIDC { + kProfileIDCBaseline = 66, + kProfileIDCConstrainedBaseline = kProfileIDCBaseline, + kProfileIDCMain = 77, + kProfileIDCHigh = 100, + }; + + 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; @@ -111,6 +131,25 @@ struct MEDIA_EXPORT H264SPS { 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; + + // 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; }; diff --git a/media/media.gyp b/media/media.gyp index 7a38a4f..1b3987a 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -659,6 +659,13 @@ 'formats/webm/chromeos/webm_encoder.h', ], }], + # For VaapiVideoEncodeAccelerator. + ['target_arch != "arm" and chromeos == 1 and use_x11 == 1', { + 'sources': [ + 'filters/h264_bitstream_buffer.cc', + 'filters/h264_bitstream_buffer.h', + ], + }], ['OS!="ios"', { 'dependencies': [ '../third_party/libyuv/libyuv.gyp:libyuv', @@ -1223,6 +1230,11 @@ }], ], }], + ['target_arch != "arm" and chromeos == 1 and use_x11 == 1', { + 'sources': [ + 'filters/h264_bitstream_buffer_unittest.cc', + ], + }], ['use_alsa==0', { 'sources!': [ 'audio/alsa/alsa_output_unittest.cc', |