diff options
author | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-04 20:34:14 +0000 |
---|---|---|
committer | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-04 20:34:14 +0000 |
commit | 58023bea8fa444c4ac08c12c825dcc79b5954abe (patch) | |
tree | d4d4ce8dc812b8118aaa3e49d6465265cf19aad8 /media/base/h264_bitstream_converter.cc | |
parent | b781e774ae4f5d579c8e9122ea9baafb1f54261a (diff) | |
download | chromium_src-58023bea8fa444c4ac08c12c825dcc79b5954abe.zip chromium_src-58023bea8fa444c4ac08c12c825dcc79b5954abe.tar.gz chromium_src-58023bea8fa444c4ac08c12c825dcc79b5954abe.tar.bz2 |
Reworked player_x11:
- Turned support for EGL image ON for OpenMAX decoding.
- Added simple fix for compilation issue due to missing definition for MessageLoop.
- Added changes to GLES Video Renderer to use EGL image.
- Added H264BitstreamConverter and H264BitstreamConverterFFmpegAdaptor classes to Chromium.
- Introduced new h264 bitstream converter to FFmpegDemuxer
- Added h264 bitstream converter related classes to media targets and introduced new target for unit testing bitstream converter.
Patch by vmr@chromium.org:
http://codereview.chromium.org/6260010/
BUG=None
TEST=Test H.264 decode clip with player_x11 OpenMAX enabled.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@73839 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/base/h264_bitstream_converter.cc')
-rw-r--r-- | media/base/h264_bitstream_converter.cc | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/media/base/h264_bitstream_converter.cc b/media/base/h264_bitstream_converter.cc new file mode 100644 index 0000000..bdf6363 --- /dev/null +++ b/media/base/h264_bitstream_converter.cc @@ -0,0 +1,314 @@ +// Copyright (c) 2011 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/base/h264_bitstream_converter.h" + +#include "base/logging.h" + +namespace media { + +static const uint8 kStartCodePrefix[3] = {0, 0, 1}; + +// Helper function which determines whether NAL unit of given type marks +// access unit boundary. +static bool IsAccessUnitBoundaryNal(int nal_unit_type) { + // Check if this packet marks access unit boundary by checking the + // packet type. + if (nal_unit_type == 6 || // Supplemental enhancement information + nal_unit_type == 7 || // Picture parameter set + nal_unit_type == 8 || // Sequence parameter set + nal_unit_type == 9 || // Access unit delimiter + (nal_unit_type >= 14 && nal_unit_type <= 18)) { // Reserved types + return true; + } + return false; +} + +H264BitstreamConverter::H264BitstreamConverter() + : configuration_processed_(false), + first_nal_unit_in_access_unit_(true), + nal_unit_length_field_width_(0) { +} + +H264BitstreamConverter::~H264BitstreamConverter() {} + +uint32 H264BitstreamConverter::ParseConfigurationAndCalculateSize( + const uint8* configuration_record, + uint32 configuration_record_size) { + // FFmpeg's AVCodecContext's extradata field contains the Decoder Specific + // Information from MP4 headers that contain the H.264 SPS and PPS members. + // ISO 14496-15 Chapter 5.2.4 AVCDecoderConfigurationRecord. + // AVCConfigurationRecord must be at least 7 bytes long. + if (configuration_record == NULL || configuration_record_size < 7) { + return 0; // Error: invalid input + } + const uint8* decoder_configuration = configuration_record; + uint32 parameter_set_size_bytes = 0; + + // We can skip the four first bytes as they're only profile information + decoder_configuration += 4; + // Fifth byte's two LSBs contain the interleaving field's size minus one + uint8 size_of_len_field = (*decoder_configuration & 0x3) + 1; + if (size_of_len_field != 1 && size_of_len_field != 2 && + size_of_len_field != 4) { + return 0; // Error: invalid input, NAL unit field len is not correct + } + decoder_configuration++; + // Sixth byte's five LSBs contain the number of SPSs + uint8 sps_count = *decoder_configuration & 0x1F; + decoder_configuration++; + // Then we have N * SPS's with two byte length field and actual SPS + while (sps_count-- > 0) { + if ((decoder_configuration - configuration_record) + 2 > + static_cast<int32>(configuration_record_size)) { + return 0; // Error: ran out of data + } + uint16 sps_len = decoder_configuration[0] << 8 | decoder_configuration[1]; + decoder_configuration += 2; + // write the SPS to output, always with zero byte + start code prefix + parameter_set_size_bytes += 1 + sizeof(kStartCodePrefix); + decoder_configuration += sps_len; + parameter_set_size_bytes += sps_len; + } + // Then we have the numner of pps in one byte + uint8 pps_count = *decoder_configuration; + decoder_configuration++; + // And finally, we have N * PPS with two byte length field and actual PPS + while (pps_count-- > 0) { + if ((decoder_configuration - configuration_record) + 2 > + static_cast<int32>(configuration_record_size)) { + return 0; // Error: ran out of data + } + uint16 pps_len = decoder_configuration[0] << 8 | decoder_configuration[1]; + decoder_configuration += 2; + // write the SPS to output, always with zero byte + start code prefix + parameter_set_size_bytes += 1 + sizeof(kStartCodePrefix); + decoder_configuration += pps_len; + parameter_set_size_bytes += pps_len; + } + // We're done processing the AVCDecoderConfigurationRecord, + // store the needed information for parsing actual payload + nal_unit_length_field_width_ = size_of_len_field; + configuration_processed_ = true; + return parameter_set_size_bytes; +} + +uint32 H264BitstreamConverter::CalculateNeededOutputBufferSize( + const uint8* input, + uint32 input_size) const { + uint32 output_size = 0; + uint32 data_left = input_size; + bool first_nal_in_this_access_unit = first_nal_unit_in_access_unit_; + + if (input == NULL || input_size == 0) { + return 0; // Error: invalid input data + } + if (!configuration_processed_) { + return 0; // Error: configuration not handled, we don't know nal unit width + } + CHECK(nal_unit_length_field_width_ == 1 || + nal_unit_length_field_width_ == 2 || + nal_unit_length_field_width_ == 4); + + // Then add the needed size for the actual packet + while (data_left > 0) { + // Read the next NAL unit length from the input buffer + uint8 size_of_len_field; + uint32 nal_unit_length; + for (nal_unit_length = 0, size_of_len_field = nal_unit_length_field_width_; + size_of_len_field > 0; + input++, size_of_len_field--, data_left--) { + nal_unit_length <<= 8; + nal_unit_length |= *input; + } + + if (nal_unit_length == 0) { + break; // Signifies that no more data left in the buffer + } else if (nal_unit_length > data_left) { + return 0; // Error: Not enough data for correct conversion + } + data_left -= nal_unit_length; + + // five least significant bits of first NAL unit byte signify nal_unit_type + int nal_unit_type = *input & 0x1F; + if (first_nal_in_this_access_unit || + IsAccessUnitBoundaryNal(nal_unit_type)) { + output_size += 1; // Extra zero_byte for these nal units + first_nal_in_this_access_unit = false; + } + // Start code prefix + output_size += sizeof(kStartCodePrefix); + // Actual NAL unit size + output_size += nal_unit_length; + input += nal_unit_length; + first_nal_in_this_access_unit = false; + // No need for trailing zero bits + } + return output_size; +} + +bool H264BitstreamConverter::ConvertAVCDecoderConfigurationRecordToByteStream( + const uint8* input, + uint32 input_size, + uint8* output, + uint32* output_size) { + uint8* outscan = output; + // FFmpeg's AVCodecContext's extradata field contains the Decoder Specific + // Information from MP4 headers that contain the H.264 SPS and PPS members. + // ISO 14496-15 Chapter 5.2.4 AVCDecoderConfigurationRecord. + const uint8* decoder_configuration = input; + uint32 decoderconfiguration_size = input_size; + uint32 out_size = 0; + + if (decoder_configuration == NULL || decoderconfiguration_size == 0) { + return 0; // Error: input invalid + } + + // We can skip the four first bytes as they're only profile information. + decoder_configuration += 4; + // Fifth byte's two LSBs contain the interleaving field's size minus one + uint8 size_of_len_field = (*decoder_configuration & 0x3) + 1; + if (size_of_len_field != 1 && size_of_len_field != 2 && + size_of_len_field != 4) { + return 0; // Error: invalid input, NAL unit field len is not correct + } + decoder_configuration++; + // Sixth byte's five LSBs contain the number of SPSs + uint8 sps_count = *decoder_configuration & 0x1F; + decoder_configuration++; + // Then we have N * SPS's with two byte length field and actual SPS + while (sps_count-- > 0) { + uint16 sps_len = decoder_configuration[0] << 8 | + decoder_configuration[1]; + decoder_configuration += 2; + if (out_size + 1 + sizeof(kStartCodePrefix) + sps_len > + *output_size) { + *output_size = 0; + return 0; // too small output buffer; + } + // write the SPS to output, always with zero byte + start code prefix + *outscan = 0; // zero byte + outscan += 1; + memcpy(outscan, kStartCodePrefix, sizeof(kStartCodePrefix)); + outscan += sizeof(kStartCodePrefix); + memcpy(outscan, decoder_configuration, sps_len); + decoder_configuration += sps_len; + outscan += sps_len; + out_size += 1 + sizeof(kStartCodePrefix) + sps_len; + } + // Then we have the numner of pps in one byte + uint8 pps_count = *decoder_configuration; + decoder_configuration++; + // And finally, we have N * PPS with two byte length field and actual PPS + while (pps_count-- > 0) { + uint16 pps_len = decoder_configuration[0] << 8 | decoder_configuration[1]; + decoder_configuration += 2; + if (out_size + 1 + sizeof(kStartCodePrefix) + pps_len > + *output_size) { + *output_size = 0; + return 0; // too small output buffer; + } + // write the SPS to output, always with zero byte + start code prefix + *outscan = 0; // zero byte + outscan += 1; + memcpy(outscan, kStartCodePrefix, sizeof(kStartCodePrefix)); + outscan += sizeof(kStartCodePrefix); + memcpy(outscan, decoder_configuration, pps_len); + decoder_configuration += pps_len; + outscan += pps_len; + out_size += 1 + sizeof(kStartCodePrefix) + pps_len; + } + // We're done processing the AVCDecoderConfigurationRecord, store the needed + // information + nal_unit_length_field_width_ = size_of_len_field; + configuration_processed_ = true; + *output_size = out_size; + return true; +} + +bool H264BitstreamConverter::ConvertNalUnitStreamToByteStream( + const uint8* input, uint32 input_size, + uint8* output, uint32* output_size) { + const uint8* inscan = input; // We read the input from here progressively + uint8* outscan = output; // We write the output to here progressively + uint32 data_left = input_size; + + if (inscan == NULL || input_size == 0 || + outscan == NULL || *output_size == 0) { + *output_size = 0; + return false; // Error: invalid input + } + + // NAL unit width should be known at this point + CHECK(nal_unit_length_field_width_ == 1 || + nal_unit_length_field_width_ == 2 || + nal_unit_length_field_width_ == 4); + + // Do the actual conversion for the actual input packet + while (data_left > 0) { + uint8 i; + uint32 nal_unit_length; + + // Read the next NAL unit length from the input buffer by scanning + // the input stream with the specific length field width + for (nal_unit_length = 0, i = nal_unit_length_field_width_; + i > 0 && data_left > 0; + inscan++, i--, data_left--) { + nal_unit_length <<= 8; + nal_unit_length |= *inscan; + } + + if (nal_unit_length == 0) { + break; // Successful conversion, end of buffer + } else if (nal_unit_length > data_left) { + *output_size = 0; + return false; // Error: not enough data for correct conversion + } + + uint32 start_code_len; + first_nal_unit_in_access_unit_ ? + start_code_len = sizeof(kStartCodePrefix) + 1 : + start_code_len = sizeof(kStartCodePrefix); + if (static_cast<uint32>(outscan - output) + + start_code_len + nal_unit_length > *output_size) { + *output_size = 0; + return false; // Error: too small output buffer + } + + // Five least significant bits of first NAL unit byte signify + // nal_unit_type. + int nal_unit_type = *inscan & 0x1F; + + // Check if this packet marks access unit boundary by checking the + // packet type. + if (IsAccessUnitBoundaryNal(nal_unit_type)) { + first_nal_unit_in_access_unit_ = true; + } + + // Write extra zero-byte before start code prefix if this packet + // signals next access unit. + if (first_nal_unit_in_access_unit_) { + *outscan = 0; + outscan++; + first_nal_unit_in_access_unit_ = false; + } + + // No need to write leading zero bits. + // Write start-code prefix. + memcpy(outscan, kStartCodePrefix, sizeof(kStartCodePrefix)); + outscan += sizeof(kStartCodePrefix); + // Then write the actual NAL unit from the input buffer. + memcpy(outscan, inscan, nal_unit_length); + inscan += nal_unit_length; + data_left -= nal_unit_length; + outscan += nal_unit_length; + // No need for trailing zero bits. + } + // Successful conversion, output the freshly allocated bitstream buffer. + *output_size = static_cast<uint32>(outscan - output); + return true; +} + +} // namespace media + |