Chromium Code Reviews| Index: media/filters/ffmpeg_aac_bitstream_converter.cc |
| diff --git a/media/filters/ffmpeg_aac_bitstream_converter.cc b/media/filters/ffmpeg_aac_bitstream_converter.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d45e9c72c0ebf25f2f34e6bb55a924f4ed5846a8 |
| --- /dev/null |
| +++ b/media/filters/ffmpeg_aac_bitstream_converter.cc |
| @@ -0,0 +1,205 @@ |
| +// 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/ffmpeg_aac_bitstream_converter.h" |
| + |
| +#include "base/logging.h" |
| +#include "media/ffmpeg/ffmpeg_common.h" |
| + |
| +namespace media { |
| + |
| +FFmpegAACBitstreamConverter::FFmpegAACBitstreamConverter( |
| + AVCodecContext* stream_codec_context) |
| + : stream_codec_context_(stream_codec_context) { |
| + CHECK(stream_codec_context_); |
| + header_generated_ = false; |
| +} |
| + |
| +FFmpegAACBitstreamConverter::~FFmpegAACBitstreamConverter() { |
| +} |
| + |
| +bool FFmpegAACBitstreamConverter::ConvertPacket(AVPacket* packet) { |
| + int header_plus_packet_size = |
| + packet->size + kAdtsHeaderSize * sizeof(unsigned char); |
|
DaleCurtis
2014/10/31 21:50:15
Is the * sizeof(unsigned char) necessary? kAdtsHe
kjoswiak
2014/11/01 00:44:26
Done.
|
| + if (!header_generated_) { |
| + if (stream_codec_context_->extradata == 0) { |
|
DaleCurtis
2014/10/31 21:50:15
If !extradata
kjoswiak
2014/11/01 00:44:26
Done.
|
| + DLOG(ERROR) << "extradata is null"; |
| + return false; |
| + } |
| + if (stream_codec_context_->extradata_size < 2) { |
| + DLOG(ERROR) << "extradata too small to contain MP4A header"; |
| + return false; |
| + } |
| + int sample_rate_index = |
| + ((stream_codec_context_->extradata[0] & 0x07) << 1) | |
| + ((stream_codec_context_->extradata[1] & 0x80) >> 7); |
| + if (sample_rate_index > 12) { |
| + sample_rate_index = 4; |
| + } |
| + header_generated_ = GenerateAdtsHeader(stream_codec_context_->codec_id, |
| + 0, // layer |
| + stream_codec_context_->profile, |
| + sample_rate_index, |
| + 0, // private stream |
| + stream_codec_context_->channels, |
| + 0, // originality |
| + 0, // home |
| + 0, // copyrighted_stream |
| + 0, // copyright start |
| + header_plus_packet_size, |
| + 0x7FF, // buffer fullness |
| + 0); // one frame per packet |
| + } |
| + |
| + // Inform caller if the header generation failed. |
| + if (!header_generated_) |
| + return false; |
| + |
| + // Allocate new packet for the output. |
| + AVPacket dest_packet; |
| + if (av_new_packet(&dest_packet, header_plus_packet_size) != 0) |
| + return false; // Memory allocation failure. |
| + |
| + memcpy(dest_packet.data, hdr_, kAdtsHeaderSize * sizeof(unsigned char)); |
| + memcpy(reinterpret_cast<void*>(dest_packet.data + kAdtsHeaderSize), |
| + reinterpret_cast<void*>(packet->data), packet->size); |
| + |
| + // This is a bit tricky: since the interface does not allow us to replace |
| + // the pointer of the old packet with a new one, we will initially copy the |
| + // metadata from old packet to new bigger packet. |
| + av_packet_copy_props(&dest_packet, packet); |
| + |
| + // Release the old packet. |
| + av_free_packet(packet); |
| + *packet = dest_packet; // Finally, replace the values in the input packet. |
| + |
| + return true; |
| +} |
| + |
| +bool FFmpegAACBitstreamConverter::GenerateAdtsHeader( |
| + int codec, int layer, int audio_profile, int sample_rate_index, |
| + int private_stream, int channel_configuration, int originality, int home, |
| + int copyrighted_stream, int copyright_start, int frame_length, |
| + int buffer_fullness, int number_of_frames_minus_one) { |
| + DCHECK(codec == CODEC_ID_AAC); |
|
DaleCurtis
2014/10/31 21:50:15
DCHECK_EQ
kjoswiak
2014/11/01 00:44:27
Done.
|
| + |
| + memset(reinterpret_cast<void *>(hdr_), 0, |
|
DaleCurtis
2014/10/31 21:50:15
Up to you, but you can zero initialize hdr_ by spe
kjoswiak
2014/11/01 00:44:26
I kind of like it this way, ensures is zeroed prec
|
| + kAdtsHeaderSize * sizeof(unsigned char)); |
| + // Ref: http://wiki.multimedia.cx/index.php?title=ADTS |
| + // ADTS header structure is the following |
| + // AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP |
| + // |
| + // A Syncword 0xFFF, all bits must be 1 |
| + // B MPEG Version: 0 for MPEG-4, 1 for MPEG-2 |
| + // C Layer: always 0 |
| + // D Protection absent: Set to 1 if no CRC and 0 if there is CRC. |
| + // E Profile: the MPEG-4 Audio Object Type minus 1. |
| + // F MPEG-4 Sampling Frequency Index (15 is forbidden) |
| + // G Private stream: |
| + // H MPEG-4 Channel Configuration |
| + // I Originality |
| + // J Home |
| + // K Copyrighted Stream |
| + // L Copyright start |
| + // M Frame length. This must include the ADTS header length. |
| + // O Buffer fullness |
| + // P Number of AAC frames in ADTS frame minus 1. |
| + // For maximum compatibility always use 1 AAC frame per ADTS frame. |
| + |
| + // Syncword |
| + hdr_[0] = 0xFF; |
| + hdr_[1] = 0xF0; |
| + |
| + // Layer is always 0. No further action required. |
| + |
| + // Protection absent (no CRC) is always 1. |
| + hdr_[1] |= 1; |
| + |
| + switch (audio_profile) { |
| + case FF_PROFILE_AAC_MAIN: |
| + break; |
| + case FF_PROFILE_AAC_LOW: |
| + hdr_[2] |= (1 << 6); |
| + break; |
| + case FF_PROFILE_AAC_SSR: |
| + hdr_[2] |= (2 << 6); |
| + break; |
| + case FF_PROFILE_AAC_LTP: |
| + hdr_[2] |= (3 << 6); |
| + break; |
| + } |
| + |
| + hdr_[2] |= ((sample_rate_index & 0xf) << 2); |
| + |
| + if (private_stream) |
| + hdr_[2] |= (1 << 1); |
| + |
| + switch (channel_configuration) { |
| + case 1: |
| + // front-center |
| + hdr_[3] |= (1 << 6); |
| + break; |
| + case 2: |
| + // front-left, front-right |
| + hdr_[3] |= (2 << 6); |
| + break; |
| + case 3: |
| + // front-center, front-left, front-right |
| + hdr_[3] |= (3 << 6); |
| + break; |
| + case 4: |
| + // front-center, front-left, front-right, back-center |
| + hdr_[2] |= 1; |
| + break; |
| + case 5: |
| + // front-center, front-left, front-right, back-left, back-right |
| + hdr_[2] |= 1; |
| + hdr_[3] |= (1 << 6); |
| + break; |
| + case 6: |
| + // front-center, front-left, front-right, back-left, back-right, |
| + // LFE-channel |
| + hdr_[2] |= 1; |
| + hdr_[3] |= (2 << 6); |
| + break; |
| + case 7: |
| + // front-center, front-left, front-right, side-left, side-right, |
| + // back-left, back-right, LFE-channel |
| + hdr_[2] |= 1; |
| + hdr_[3] |= (3 << 6); |
| + break; |
| + default: |
| + DLOG(ERROR) << "[" << __FUNCTION__ << "] " |
| + << "unsupported number of audio channels:" |
| + << channel_configuration; |
| + return false; |
| + } |
| + |
| + if (originality) |
| + hdr_[3] |= (1 << 5); |
| + |
| + if (home) |
| + hdr_[3] |= (1 << 4); |
| + |
| + if (copyrighted_stream) |
| + hdr_[3] |= (1 << 3); |
| + |
| + if (copyright_start) |
| + hdr_[3] |= (1 << 2); |
| + |
| + // frame length |
| + hdr_[3] |= (frame_length >> 11) & 0x03; |
| + hdr_[4] = (frame_length >> 3) & 0xFF; |
| + hdr_[5] |= (frame_length & 7) << 5; |
| + |
| + // buffer fullness |
| + hdr_[5] |= (buffer_fullness >> 6) & 0x1F; |
| + hdr_[6] |= (buffer_fullness & 0x3F) << 2; |
| + |
| + hdr_[6] |= number_of_frames_minus_one & 0x3; |
| + |
| + return true; |
| +} |
| + |
| +} // namespace media |