blob: c1d407d634f9a12afba8e7e56d77e4c9557381f6 [file] [log] [blame]
// 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_t* hdr) {
DCHECK_EQ(codec, AV_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_HE:
case FF_PROFILE_AAC_HE_V2:
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;
default:
DLOG(ERROR) << "[" << __FUNCTION__ << "] "
<< "unsupported audio profile:"
<< audio_profile;
return false;
}
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 8:
// 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(
AVCodecParameters* stream_codec_parameters)
: stream_codec_parameters_(stream_codec_parameters),
header_generated_(false),
codec_(),
audio_profile_(),
sample_rate_index_(),
channel_configuration_(),
frame_length_() {
CHECK(stream_codec_parameters_);
}
FFmpegAACBitstreamConverter::~FFmpegAACBitstreamConverter() {
}
bool FFmpegAACBitstreamConverter::ConvertPacket(AVPacket* packet) {
if (packet == NULL || !packet->data) {
return false;
}
int header_plus_packet_size =
packet->size + kAdtsHeaderSize;
if (!stream_codec_parameters_->extradata) {
DLOG(ERROR) << "extradata is null";
return false;
}
if (stream_codec_parameters_->extradata_size < 2) {
DLOG(ERROR) << "extradata too small to contain MP4A header";
return false;
}
int sample_rate_index =
((stream_codec_parameters_->extradata[0] & 0x07) << 1) |
((stream_codec_parameters_->extradata[1] & 0x80) >> 7);
if (sample_rate_index > 12) {
sample_rate_index = 4;
}
if (!header_generated_ || codec_ != stream_codec_parameters_->codec_id ||
audio_profile_ != stream_codec_parameters_->profile ||
sample_rate_index_ != sample_rate_index ||
channel_configuration_ != stream_codec_parameters_->channels ||
frame_length_ != header_plus_packet_size) {
header_generated_ =
GenerateAdtsHeader(stream_codec_parameters_->codec_id,
0, // layer
stream_codec_parameters_->profile, sample_rate_index,
0, // private stream
stream_codec_parameters_->channels,
0, // originality
0, // home
0, // copyrighted_stream
0, // copyright_ start
header_plus_packet_size,
0x7FF, // buffer fullness
0, // one frame per packet
hdr_);
codec_ = stream_codec_parameters_->codec_id;
audio_profile_ = stream_codec_parameters_->profile;
sample_rate_index_ = sample_rate_index;
channel_configuration_ = stream_codec_parameters_->channels;
frame_length_ = header_plus_packet_size;
}
// 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_packet_unref(packet);
*packet = dest_packet; // Finally, replace the values in the input packet.
return true;
}
} // namespace media