summaryrefslogtreecommitdiffstats
path: root/media/base/h264_bitstream_converter.cc
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-04 20:34:14 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-04 20:34:14 +0000
commit58023bea8fa444c4ac08c12c825dcc79b5954abe (patch)
treed4d4ce8dc812b8118aaa3e49d6465265cf19aad8 /media/base/h264_bitstream_converter.cc
parentb781e774ae4f5d579c8e9122ea9baafb1f54261a (diff)
downloadchromium_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.cc314
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
+