| // 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 { |
| |
| namespace { |
| |
| // Creates an ADTS header and stores in |hdr| |
| // Assumes |hdr| points to an array of length |kAdtsHeaderSize| |
| // Returns false if parameter values are for an unsupported configuration. |
| bool 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, uint8* hdr) { |
| DCHECK_EQ(codec, CODEC_ID_AAC); |
| |
| memset(reinterpret_cast<void *>(hdr), 0, |
| FFmpegAACBitstreamConverter::kAdtsHeaderSize); |
| // 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; |
| } |
| |
| } |
| |
| FFmpegAACBitstreamConverter::FFmpegAACBitstreamConverter( |
| AVCodecContext* stream_codec_context) |
| : stream_codec_context_(stream_codec_context), |
| header_generated_(false) { |
| CHECK(stream_codec_context_); |
| } |
| |
| FFmpegAACBitstreamConverter::~FFmpegAACBitstreamConverter() { |
| } |
| |
| bool FFmpegAACBitstreamConverter::ConvertPacket(AVPacket* packet) { |
| if (packet == NULL || !packet->data) { |
| return false; |
| } |
| |
| int header_plus_packet_size = |
| packet->size + kAdtsHeaderSize; |
| if (!header_generated_) { |
| if (!stream_codec_context_->extradata) { |
| 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 |
| hdr_); |
| } |
| |
| // 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); |
| 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; |
| } |
| |
| } // namespace media |