blob: ff95f9f2d5edda5f502f110ea4d68ec9ff6b8aef [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "SoftAAC2"
#include <utils/Log.h>
#include "SoftAAC2.h"
#include <cutils/properties.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaErrors.h>
#define FILEREAD_MAX_LAYERS 2
namespace android {
template<class T>
static void InitOMXParams(T *params) {
params->nSize = sizeof(T);
params->nVersion.s.nVersionMajor = 1;
params->nVersion.s.nVersionMinor = 0;
params->nVersion.s.nRevision = 0;
params->nVersion.s.nStep = 0;
}
SoftAAC2::SoftAAC2(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
OMX_COMPONENTTYPE **component)
: SimpleSoftOMXComponent(name, callbacks, appData, component),
mAACDecoder(NULL),
mStreamInfo(NULL),
mIsADTS(false),
mInputBufferCount(0),
mSignalledError(false),
mAnchorTimeUs(0),
mNumSamplesOutput(0),
mOutputPortSettingsChange(NONE) {
initPorts();
CHECK_EQ(initDecoder(), (status_t)OK);
}
SoftAAC2::~SoftAAC2() {
aacDecoder_Close(mAACDecoder);
}
void SoftAAC2::initPorts() {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = 0;
def.eDir = OMX_DirInput;
def.nBufferCountMin = kNumInputBuffers;
def.nBufferCountActual = def.nBufferCountMin;
def.nBufferSize = 8192;
def.bEnabled = OMX_TRUE;
def.bPopulated = OMX_FALSE;
def.eDomain = OMX_PortDomainAudio;
def.bBuffersContiguous = OMX_FALSE;
def.nBufferAlignment = 1;
def.format.audio.cMIMEType = const_cast<char *>("audio/aac");
def.format.audio.pNativeRender = NULL;
def.format.audio.bFlagErrorConcealment = OMX_FALSE;
def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
addPort(def);
def.nPortIndex = 1;
def.eDir = OMX_DirOutput;
def.nBufferCountMin = kNumOutputBuffers;
def.nBufferCountActual = def.nBufferCountMin;
def.nBufferSize = 8192 * 2;
def.bEnabled = OMX_TRUE;
def.bPopulated = OMX_FALSE;
def.eDomain = OMX_PortDomainAudio;
def.bBuffersContiguous = OMX_FALSE;
def.nBufferAlignment = 2;
def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
def.format.audio.pNativeRender = NULL;
def.format.audio.bFlagErrorConcealment = OMX_FALSE;
def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
addPort(def);
}
status_t SoftAAC2::initDecoder() {
status_t status = UNKNOWN_ERROR;
mAACDecoder = aacDecoder_Open(TT_MP4_ADIF, /* num layers */ 1);
if (mAACDecoder != NULL) {
mStreamInfo = aacDecoder_GetStreamInfo(mAACDecoder);
if (mStreamInfo != NULL) {
status = OK;
}
}
mIsFirst = true;
return status;
}
OMX_ERRORTYPE SoftAAC2::internalGetParameter(
OMX_INDEXTYPE index, OMX_PTR params) {
switch (index) {
case OMX_IndexParamAudioAac:
{
OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
(OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
if (aacParams->nPortIndex != 0) {
return OMX_ErrorUndefined;
}
aacParams->nBitRate = 0;
aacParams->nAudioBandWidth = 0;
aacParams->nAACtools = 0;
aacParams->nAACERtools = 0;
aacParams->eAACProfile = OMX_AUDIO_AACObjectMain;
aacParams->eAACStreamFormat =
mIsADTS
? OMX_AUDIO_AACStreamFormatMP4ADTS
: OMX_AUDIO_AACStreamFormatMP4FF;
aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo;
if (!isConfigured()) {
aacParams->nChannels = 1;
aacParams->nSampleRate = 44100;
aacParams->nFrameLength = 0;
} else {
aacParams->nChannels = mStreamInfo->numChannels;
aacParams->nSampleRate = mStreamInfo->sampleRate;
aacParams->nFrameLength = mStreamInfo->frameSize;
}
return OMX_ErrorNone;
}
case OMX_IndexParamAudioPcm:
{
OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
(OMX_AUDIO_PARAM_PCMMODETYPE *)params;
if (pcmParams->nPortIndex != 1) {
return OMX_ErrorUndefined;
}
pcmParams->eNumData = OMX_NumericalDataSigned;
pcmParams->eEndian = OMX_EndianBig;
pcmParams->bInterleaved = OMX_TRUE;
pcmParams->nBitPerSample = 16;
pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
pcmParams->eChannelMapping[2] = OMX_AUDIO_ChannelCF;
pcmParams->eChannelMapping[3] = OMX_AUDIO_ChannelLFE;
pcmParams->eChannelMapping[4] = OMX_AUDIO_ChannelLS;
pcmParams->eChannelMapping[5] = OMX_AUDIO_ChannelRS;
if (!isConfigured()) {
pcmParams->nChannels = 1;
pcmParams->nSamplingRate = 44100;
} else {
pcmParams->nChannels = mStreamInfo->numChannels;
pcmParams->nSamplingRate = mStreamInfo->sampleRate;
}
return OMX_ErrorNone;
}
default:
return SimpleSoftOMXComponent::internalGetParameter(index, params);
}
}
OMX_ERRORTYPE SoftAAC2::internalSetParameter(
OMX_INDEXTYPE index, const OMX_PTR params) {
switch (index) {
case OMX_IndexParamStandardComponentRole:
{
const OMX_PARAM_COMPONENTROLETYPE *roleParams =
(const OMX_PARAM_COMPONENTROLETYPE *)params;
if (strncmp((const char *)roleParams->cRole,
"audio_decoder.aac",
OMX_MAX_STRINGNAME_SIZE - 1)) {
return OMX_ErrorUndefined;
}
return OMX_ErrorNone;
}
case OMX_IndexParamAudioAac:
{
const OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
(const OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
if (aacParams->nPortIndex != 0) {
return OMX_ErrorUndefined;
}
if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) {
mIsADTS = false;
} else if (aacParams->eAACStreamFormat
== OMX_AUDIO_AACStreamFormatMP4ADTS) {
mIsADTS = true;
} else {
return OMX_ErrorUndefined;
}
return OMX_ErrorNone;
}
case OMX_IndexParamAudioPcm:
{
const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
(OMX_AUDIO_PARAM_PCMMODETYPE *)params;
if (pcmParams->nPortIndex != 1) {
return OMX_ErrorUndefined;
}
return OMX_ErrorNone;
}
default:
return SimpleSoftOMXComponent::internalSetParameter(index, params);
}
}
bool SoftAAC2::isConfigured() const {
return mInputBufferCount > 0;
}
void SoftAAC2::maybeConfigureDownmix() const {
if (mStreamInfo->numChannels > 2) {
char value[PROPERTY_VALUE_MAX];
if (!(property_get("media.aac_51_output_enabled", value, NULL) &&
(!strcmp(value, "1") || !strcasecmp(value, "true")))) {
ALOGI("Downmixing multichannel AAC to stereo");
aacDecoder_SetParam(mAACDecoder, AAC_PCM_OUTPUT_CHANNELS, 2);
mStreamInfo->numChannels = 2;
}
}
}
void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
if (mSignalledError || mOutputPortSettingsChange != NONE) {
return;
}
UCHAR* inBuffer[FILEREAD_MAX_LAYERS];
UINT inBufferLength[FILEREAD_MAX_LAYERS] = {0};
UINT bytesValid[FILEREAD_MAX_LAYERS] = {0};
List<BufferInfo *> &inQueue = getPortQueue(0);
List<BufferInfo *> &outQueue = getPortQueue(1);
if (portIndex == 0 && mInputBufferCount == 0) {
++mInputBufferCount;
BufferInfo *info = *inQueue.begin();
OMX_BUFFERHEADERTYPE *header = info->mHeader;
inBuffer[0] = header->pBuffer + header->nOffset;
inBufferLength[0] = header->nFilledLen;
AAC_DECODER_ERROR decoderErr =
aacDecoder_ConfigRaw(mAACDecoder,
inBuffer,
inBufferLength);
if (decoderErr != AAC_DEC_OK) {
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
inQueue.erase(inQueue.begin());
info->mOwnedByUs = false;
notifyEmptyBufferDone(header);
// Only send out port settings changed event if both sample rate
// and numChannels are valid.
if (mStreamInfo->sampleRate && mStreamInfo->numChannels) {
maybeConfigureDownmix();
ALOGI("Initially configuring decoder: %d Hz, %d channels",
mStreamInfo->sampleRate,
mStreamInfo->numChannels);
notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
mOutputPortSettingsChange = AWAITING_DISABLED;
}
return;
}
while (!inQueue.empty() && !outQueue.empty()) {
BufferInfo *inInfo = *inQueue.begin();
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
BufferInfo *outInfo = *outQueue.begin();
OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
inQueue.erase(inQueue.begin());
inInfo->mOwnedByUs = false;
notifyEmptyBufferDone(inHeader);
// flush out the decoder's delayed data by calling DecodeFrame one more time, with
// the AACDEC_FLUSH flag set
INT_PCM *outBuffer =
reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
AAC_DECODER_ERROR decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
outBuffer,
outHeader->nAllocLen,
AACDEC_FLUSH);
if (decoderErr != AAC_DEC_OK) {
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
outHeader->nFilledLen =
mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
outQueue.erase(outQueue.begin());
outInfo->mOwnedByUs = false;
notifyFillBufferDone(outHeader);
return;
}
if (inHeader->nOffset == 0) {
mAnchorTimeUs = inHeader->nTimeStamp;
mNumSamplesOutput = 0;
}
size_t adtsHeaderSize = 0;
if (mIsADTS) {
// skip 30 bits, aac_frame_length follows.
// ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
bool signalError = false;
if (inHeader->nFilledLen < 7) {
ALOGE("Audio data too short to contain even the ADTS header. "
"Got %ld bytes.", inHeader->nFilledLen);
hexdump(adtsHeader, inHeader->nFilledLen);
signalError = true;
} else {
bool protectionAbsent = (adtsHeader[1] & 1);
unsigned aac_frame_length =
((adtsHeader[3] & 3) << 11)
| (adtsHeader[4] << 3)
| (adtsHeader[5] >> 5);
if (inHeader->nFilledLen < aac_frame_length) {
ALOGE("Not enough audio data for the complete frame. "
"Got %ld bytes, frame size according to the ADTS "
"header is %u bytes.",
inHeader->nFilledLen, aac_frame_length);
hexdump(adtsHeader, inHeader->nFilledLen);
signalError = true;
} else {
adtsHeaderSize = (protectionAbsent ? 7 : 9);
inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
inBufferLength[0] = aac_frame_length - adtsHeaderSize;
inHeader->nOffset += adtsHeaderSize;
inHeader->nFilledLen -= adtsHeaderSize;
}
}
if (signalError) {
mSignalledError = true;
notify(OMX_EventError,
OMX_ErrorStreamCorrupt,
ERROR_MALFORMED,
NULL);
return;
}
} else {
inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
inBufferLength[0] = inHeader->nFilledLen;
}
// Fill and decode
INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
bytesValid[0] = inBufferLength[0];
int prevSampleRate = mStreamInfo->sampleRate;
int prevNumChannels = mStreamInfo->numChannels;
AAC_DECODER_ERROR decoderErr = AAC_DEC_NOT_ENOUGH_BITS;
while (bytesValid[0] > 0 && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
aacDecoder_Fill(mAACDecoder,
inBuffer,
inBufferLength,
bytesValid);
decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
outBuffer,
outHeader->nAllocLen,
0 /* flags */);
if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
ALOGW("Not enough bits, bytesValid %d", bytesValid[0]);
}
}
/*
* AAC+/eAAC+ streams can be signalled in two ways: either explicitly
* or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
* rate system and the sampling rate in the final output is actually
* doubled compared with the core AAC decoder sampling rate.
*
* Explicit signalling is done by explicitly defining SBR audio object
* type in the bitstream. Implicit signalling is done by embedding
* SBR content in AAC extension payload specific to SBR, and hence
* requires an AAC decoder to perform pre-checks on actual audio frames.
*
* Thus, we could not say for sure whether a stream is
* AAC+/eAAC+ until the first data frame is decoded.
*/
if (mInputBufferCount <= 2) {
if (mStreamInfo->sampleRate != prevSampleRate ||
mStreamInfo->numChannels != prevNumChannels) {
maybeConfigureDownmix();
ALOGI("Reconfiguring decoder: %d Hz, %d channels",
mStreamInfo->sampleRate,
mStreamInfo->numChannels);
// We're going to want to revisit this input buffer, but
// may have already advanced the offset. Undo that if
// necessary.
inHeader->nOffset -= adtsHeaderSize;
inHeader->nFilledLen += adtsHeaderSize;
notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
mOutputPortSettingsChange = AWAITING_DISABLED;
return;
}
} else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
ALOGW("Invalid AAC stream");
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
size_t numOutBytes =
mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
if (decoderErr == AAC_DEC_OK) {
UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
inHeader->nFilledLen -= inBufferUsedLength;
inHeader->nOffset += inBufferUsedLength;
} else {
ALOGW("AAC decoder returned error %d, substituting silence",
decoderErr);
memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes);
// Discard input buffer.
inHeader->nFilledLen = 0;
aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
// fall through
}
if (decoderErr == AAC_DEC_OK || mNumSamplesOutput > 0) {
// We'll only output data if we successfully decoded it or
// we've previously decoded valid data, in the latter case
// (decode failed) we'll output a silent frame.
if (mIsFirst) {
mIsFirst = false;
// the first decoded frame should be discarded to account for decoder delay
numOutBytes = 0;
}
outHeader->nFilledLen = numOutBytes;
outHeader->nFlags = 0;
outHeader->nTimeStamp =
mAnchorTimeUs
+ (mNumSamplesOutput * 1000000ll) / mStreamInfo->sampleRate;
mNumSamplesOutput += mStreamInfo->frameSize;
outInfo->mOwnedByUs = false;
outQueue.erase(outQueue.begin());
outInfo = NULL;
notifyFillBufferDone(outHeader);
outHeader = NULL;
}
if (inHeader->nFilledLen == 0) {
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
inInfo = NULL;
notifyEmptyBufferDone(inHeader);
inHeader = NULL;
}
if (decoderErr == AAC_DEC_OK) {
++mInputBufferCount;
}
}
}
void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) {
if (portIndex == 0) {
// Make sure that the next buffer output does not still
// depend on fragments from the last one decoded.
aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
mIsFirst = true;
}
}
void SoftAAC2::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
if (portIndex != 1) {
return;
}
switch (mOutputPortSettingsChange) {
case NONE:
break;
case AWAITING_DISABLED:
{
CHECK(!enabled);
mOutputPortSettingsChange = AWAITING_ENABLED;
break;
}
default:
{
CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
CHECK(enabled);
mOutputPortSettingsChange = NONE;
break;
}
}
}
} // namespace android
android::SoftOMXComponent *createSoftOMXComponent(
const char *name, const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData, OMX_COMPONENTTYPE **component) {
return new android::SoftAAC2(name, callbacks, appData, component);
}