blob: 965c55e01945431d2beec2ecfec99d320b9d7810 [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_NDEBUG 0
#define LOG_TAG "SoftAAC2"
#include <utils/Log.h>
#include "SoftAAC2.h"
#include <OMX_AudioExt.h>
#include <OMX_IndexExt.h>
#include <cutils/properties.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaErrors.h>
#include <math.h>
#define FILEREAD_MAX_LAYERS 2
#define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1 /* switch for heavy compression for mobile conf */
#define DRC_DEFAULT_MOBILE_ENC_LEVEL -1 /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
#define MAX_CHANNEL_COUNT 8 /* maximum number of audio channels that can be decoded */
// names of properties that can be used to override the default DRC settings
#define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level"
#define PROP_DRC_OVERRIDE_CUT "aac_drc_cut"
#define PROP_DRC_OVERRIDE_BOOST "aac_drc_boost"
#define PROP_DRC_OVERRIDE_HEAVY "aac_drc_heavy"
#define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level"
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),
mOutputBufferCount(0),
mSignalledError(false),
mLastInHeader(NULL),
mOutputPortSettingsChange(NONE) {
initPorts();
CHECK_EQ(initDecoder(), (status_t)OK);
}
SoftAAC2::~SoftAAC2() {
aacDecoder_Close(mAACDecoder);
delete[] mOutputDelayRingBuffer;
}
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 = 4096 * MAX_CHANNEL_COUNT;
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() {
ALOGV("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;
}
}
mEndOfInput = false;
mEndOfOutput = false;
mOutputDelayCompensated = 0;
mOutputDelayRingBufferSize = 2048 * MAX_CHANNEL_COUNT * kNumDelayBlocksMax;
mOutputDelayRingBuffer = new short[mOutputDelayRingBufferSize];
mOutputDelayRingBufferWritePos = 0;
mOutputDelayRingBufferReadPos = 0;
mOutputDelayRingBufferFilled = 0;
if (mAACDecoder == NULL) {
ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code");
}
//aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE, 0);
//init DRC wrapper
mDrcWrap.setDecoderHandle(mAACDecoder);
mDrcWrap.submitStreamData(mStreamInfo);
// for streams that contain metadata, use the mobile profile DRC settings unless overridden by platform properties
// TODO: change the DRC settings depending on audio output device type (HDMI, loadspeaker, headphone)
char value[PROPERTY_VALUE_MAX];
// DRC_PRES_MODE_WRAP_DESIRED_TARGET
if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) {
unsigned refLevel = atoi(value);
ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d", refLevel,
DRC_DEFAULT_MOBILE_REF_LEVEL);
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, refLevel);
} else {
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, DRC_DEFAULT_MOBILE_REF_LEVEL);
}
// DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR
if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) {
unsigned cut = atoi(value);
ALOGV("AAC decoder using desired DRC attenuation factor of %d instead of %d", cut,
DRC_DEFAULT_MOBILE_DRC_CUT);
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, cut);
} else {
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT);
}
// DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR
if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) {
unsigned boost = atoi(value);
ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", boost,
DRC_DEFAULT_MOBILE_DRC_BOOST);
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, boost);
} else {
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST);
}
// DRC_PRES_MODE_WRAP_DESIRED_HEAVY
if (property_get(PROP_DRC_OVERRIDE_HEAVY, value, NULL)) {
unsigned heavy = atoi(value);
ALOGV("AAC decoder using desried DRC heavy compression switch of %d instead of %d", heavy,
DRC_DEFAULT_MOBILE_DRC_HEAVY);
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, heavy);
} else {
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, DRC_DEFAULT_MOBILE_DRC_HEAVY);
}
// DRC_PRES_MODE_WRAP_ENCODER_TARGET
if (property_get(PROP_DRC_OVERRIDE_ENC_LEVEL, value, NULL)) {
unsigned encoderRefLevel = atoi(value);
ALOGV("AAC decoder using encoder-side DRC reference level of %d instead of %d",
encoderRefLevel, DRC_DEFAULT_MOBILE_ENC_LEVEL);
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, encoderRefLevel);
} else {
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, DRC_DEFAULT_MOBILE_ENC_LEVEL);
}
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 ((int)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_IndexParamAudioAndroidAacPresentation:
{
const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *aacPresParams =
(const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *)params;
// for the following parameters of the OMX_AUDIO_PARAM_AACPROFILETYPE structure,
// a value of -1 implies the parameter is not set by the application:
// nMaxOutputChannels uses default platform properties, see configureDownmix()
// nDrcCut uses default platform properties, see initDecoder()
// nDrcBoost idem
// nHeavyCompression idem
// nTargetReferenceLevel idem
// nEncodedTargetLevel idem
if (aacPresParams->nMaxOutputChannels >= 0) {
int max;
if (aacPresParams->nMaxOutputChannels >= 8) { max = 8; }
else if (aacPresParams->nMaxOutputChannels >= 6) { max = 6; }
else if (aacPresParams->nMaxOutputChannels >= 2) { max = 2; }
else {
// -1 or 0: disable downmix, 1: mono
max = aacPresParams->nMaxOutputChannels;
}
ALOGV("set nMaxOutputChannels=%d", max);
aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, max);
}
bool updateDrcWrapper = false;
if (aacPresParams->nDrcBoost >= 0) {
ALOGV("set nDrcBoost=%d", aacPresParams->nDrcBoost);
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR,
aacPresParams->nDrcBoost);
updateDrcWrapper = true;
}
if (aacPresParams->nDrcCut >= 0) {
ALOGV("set nDrcCut=%d", aacPresParams->nDrcCut);
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, aacPresParams->nDrcCut);
updateDrcWrapper = true;
}
if (aacPresParams->nHeavyCompression >= 0) {
ALOGV("set nHeavyCompression=%d", aacPresParams->nHeavyCompression);
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY,
aacPresParams->nHeavyCompression);
updateDrcWrapper = true;
}
if (aacPresParams->nTargetReferenceLevel >= 0) {
ALOGV("set nTargetReferenceLevel=%d", aacPresParams->nTargetReferenceLevel);
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET,
aacPresParams->nTargetReferenceLevel);
updateDrcWrapper = true;
}
if (aacPresParams->nEncodedTargetLevel >= 0) {
ALOGV("set nEncodedTargetLevel=%d", aacPresParams->nEncodedTargetLevel);
mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET,
aacPresParams->nEncodedTargetLevel);
updateDrcWrapper = true;
}
if (aacPresParams->nPCMLimiterEnable >= 0) {
aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE,
(aacPresParams->nPCMLimiterEnable != 0));
}
if (updateDrcWrapper) {
mDrcWrap.update();
}
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::configureDownmix() const {
char value[PROPERTY_VALUE_MAX];
if (!(property_get("media.aac_51_output_enabled", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true")))) {
ALOGI("limiting to stereo output");
aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
// By default, the decoder creates a 5.1 channel downmix signal
// for seven and eight channel input streams. To enable 6.1 and 7.1 channel output
// use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1)
}
}
bool SoftAAC2::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) {
if (numSamples == 0) {
return true;
}
if (outputDelayRingBufferSpaceLeft() < numSamples) {
ALOGE("RING BUFFER WOULD OVERFLOW");
return false;
}
if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize
&& (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos
|| mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) {
// faster memcopy loop without checks, if the preconditions allow this
for (int32_t i = 0; i < numSamples; i++) {
mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos++] = samples[i];
}
if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
}
} else {
ALOGV("slow SoftAAC2::outputDelayRingBufferPutSamples()");
for (int32_t i = 0; i < numSamples; i++) {
mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos] = samples[i];
mOutputDelayRingBufferWritePos++;
if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
}
}
}
mOutputDelayRingBufferFilled += numSamples;
return true;
}
int32_t SoftAAC2::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) {
if (numSamples > mOutputDelayRingBufferFilled) {
ALOGE("RING BUFFER WOULD UNDERRUN");
return -1;
}
if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize
&& (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos
|| mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) {
// faster memcopy loop without checks, if the preconditions allow this
if (samples != 0) {
for (int32_t i = 0; i < numSamples; i++) {
samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos++];
}
} else {
mOutputDelayRingBufferReadPos += numSamples;
}
if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
}
} else {
ALOGV("slow SoftAAC2::outputDelayRingBufferGetSamples()");
for (int32_t i = 0; i < numSamples; i++) {
if (samples != 0) {
samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos];
}
mOutputDelayRingBufferReadPos++;
if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
}
}
}
mOutputDelayRingBufferFilled -= numSamples;
return numSamples;
}
int32_t SoftAAC2::outputDelayRingBufferSamplesAvailable() {
return mOutputDelayRingBufferFilled;
}
int32_t SoftAAC2::outputDelayRingBufferSpaceLeft() {
return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
}
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);
while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) {
if (!inQueue.empty()) {
INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
BufferInfo *inInfo = *inQueue.begin();
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
mEndOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0;
if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
ALOGE("first buffer should have OMX_BUFFERFLAG_CODECCONFIG set");
inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
}
if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) {
BufferInfo *inInfo = *inQueue.begin();
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
inBufferLength[0] = inHeader->nFilledLen;
AAC_DECODER_ERROR decoderErr =
aacDecoder_ConfigRaw(mAACDecoder,
inBuffer,
inBufferLength);
if (decoderErr != AAC_DEC_OK) {
ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
mInputBufferCount++;
mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
mLastInHeader = NULL;
inInfo = NULL;
notifyEmptyBufferDone(inHeader);
inHeader = NULL;
configureDownmix();
// Only send out port settings changed event if both sample rate
// and numChannels are valid.
if (mStreamInfo->sampleRate && mStreamInfo->numChannels) {
ALOGI("Initially configuring decoder: %d Hz, %d channels",
mStreamInfo->sampleRate,
mStreamInfo->numChannels);
notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
mOutputPortSettingsChange = AWAITING_DISABLED;
}
return;
}
if (inHeader->nFilledLen == 0) {
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
mLastInHeader = NULL;
inInfo = NULL;
notifyEmptyBufferDone(inHeader);
inHeader = NULL;
continue;
}
if (mIsADTS) {
size_t adtsHeaderSize = 0;
// 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 %d 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 %d 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;
}
// insert buffer size and time stamp
mBufferSizes.add(inBufferLength[0]);
if (mLastInHeader != inHeader) {
mBufferTimestamps.add(inHeader->nTimeStamp);
mLastInHeader = inHeader;
} else {
int64_t currentTime = mBufferTimestamps.top();
currentTime += mStreamInfo->aacSamplesPerFrame *
1000000ll / mStreamInfo->aacSampleRate;
mBufferTimestamps.add(currentTime);
}
} else {
inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
inBufferLength[0] = inHeader->nFilledLen;
mLastInHeader = inHeader;
mBufferTimestamps.add(inHeader->nTimeStamp);
mBufferSizes.add(inHeader->nFilledLen);
}
// Fill and decode
bytesValid[0] = inBufferLength[0];
INT prevSampleRate = mStreamInfo->sampleRate;
INT prevNumChannels = mStreamInfo->numChannels;
aacDecoder_Fill(mAACDecoder,
inBuffer,
inBufferLength,
bytesValid);
// run DRC check
mDrcWrap.submitStreamData(mStreamInfo);
mDrcWrap.update();
UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
inHeader->nFilledLen -= inBufferUsedLength;
inHeader->nOffset += inBufferUsedLength;
AAC_DECODER_ERROR decoderErr;
int numLoops = 0;
do {
if (outputDelayRingBufferSpaceLeft() <
(mStreamInfo->frameSize * mStreamInfo->numChannels)) {
ALOGV("skipping decode: not enough space left in ringbuffer");
break;
}
int numConsumed = mStreamInfo->numTotalBytes;
decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
tmpOutBuffer,
2048 * MAX_CHANNEL_COUNT,
0 /* flags */);
numConsumed = mStreamInfo->numTotalBytes - numConsumed;
numLoops++;
if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
break;
}
mDecodedSizes.add(numConsumed);
if (decoderErr != AAC_DEC_OK) {
ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
}
if (bytesValid[0] != 0) {
ALOGE("bytesValid[0] != 0 should never happen");
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
return;
}
size_t numOutBytes =
mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
if (decoderErr == AAC_DEC_OK) {
if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
mStreamInfo->frameSize * mStreamInfo->numChannels)) {
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
} else {
ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
mStreamInfo->frameSize * mStreamInfo->numChannels)) {
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
// Discard input buffer.
if (inHeader) {
inHeader->nFilledLen = 0;
}
aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
// After an error, replace the last entry in mBufferSizes with the sum of the
// last <numLoops> entries from mDecodedSizes to resynchronize the in/out lists.
mBufferSizes.pop();
int n = 0;
for (int i = 0; i < numLoops; i++) {
n += mDecodedSizes.itemAt(mDecodedSizes.size() - numLoops + i);
}
mBufferSizes.add(n);
// fall through
}
/*
* 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 || mOutputBufferCount > 1) { // TODO: <= 1
if (mStreamInfo->sampleRate != prevSampleRate ||
mStreamInfo->numChannels != prevNumChannels) {
ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
prevSampleRate, mStreamInfo->sampleRate,
prevNumChannels, mStreamInfo->numChannels);
notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
mOutputPortSettingsChange = AWAITING_DISABLED;
if (inHeader && inHeader->nFilledLen == 0) {
inInfo->mOwnedByUs = false;
mInputBufferCount++;
inQueue.erase(inQueue.begin());
mLastInHeader = NULL;
inInfo = NULL;
notifyEmptyBufferDone(inHeader);
inHeader = NULL;
}
return;
}
} else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
ALOGW("Invalid AAC stream");
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
if (inHeader && inHeader->nFilledLen == 0) {
inInfo->mOwnedByUs = false;
mInputBufferCount++;
inQueue.erase(inQueue.begin());
mLastInHeader = NULL;
inInfo = NULL;
notifyEmptyBufferDone(inHeader);
inHeader = NULL;
} else {
ALOGV("inHeader->nFilledLen = %d", inHeader ? inHeader->nFilledLen : 0);
}
} while (decoderErr == AAC_DEC_OK);
}
int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
if (!mEndOfInput && mOutputDelayCompensated < outputDelay) {
// discard outputDelay at the beginning
int32_t toCompensate = outputDelay - mOutputDelayCompensated;
int32_t discard = outputDelayRingBufferSamplesAvailable();
if (discard > toCompensate) {
discard = toCompensate;
}
int32_t discarded = outputDelayRingBufferGetSamples(0, discard);
mOutputDelayCompensated += discarded;
continue;
}
if (mEndOfInput) {
while (mOutputDelayCompensated > 0) {
// a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
// run DRC check
mDrcWrap.submitStreamData(mStreamInfo);
mDrcWrap.update();
AAC_DECODER_ERROR decoderErr =
aacDecoder_DecodeFrame(mAACDecoder,
tmpOutBuffer,
2048 * MAX_CHANNEL_COUNT,
AACDEC_FLUSH);
if (decoderErr != AAC_DEC_OK) {
ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
}
int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
if (tmpOutBufferSamples > mOutputDelayCompensated) {
tmpOutBufferSamples = mOutputDelayCompensated;
}
outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
mOutputDelayCompensated -= tmpOutBufferSamples;
}
}
while (!outQueue.empty()
&& outputDelayRingBufferSamplesAvailable()
>= mStreamInfo->frameSize * mStreamInfo->numChannels) {
BufferInfo *outInfo = *outQueue.begin();
OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
if (outHeader->nOffset != 0) {
ALOGE("outHeader->nOffset != 0 is not handled");
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
return;
}
INT_PCM *outBuffer =
reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
int samplesize = mStreamInfo->numChannels * sizeof(int16_t);
if (outHeader->nOffset
+ mStreamInfo->frameSize * samplesize
> outHeader->nAllocLen) {
ALOGE("buffer overflow");
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
return;
}
int available = outputDelayRingBufferSamplesAvailable();
int numSamples = outHeader->nAllocLen / sizeof(int16_t);
if (numSamples > available) {
numSamples = available;
}
int64_t currentTime = 0;
if (available) {
int numFrames = numSamples / (mStreamInfo->frameSize * mStreamInfo->numChannels);
numSamples = numFrames * (mStreamInfo->frameSize * mStreamInfo->numChannels);
ALOGV("%d samples available (%d), or %d frames",
numSamples, available, numFrames);
int64_t *nextTimeStamp = &mBufferTimestamps.editItemAt(0);
currentTime = *nextTimeStamp;
int32_t *currentBufLeft = &mBufferSizes.editItemAt(0);
for (int i = 0; i < numFrames; i++) {
int32_t decodedSize = mDecodedSizes.itemAt(0);
mDecodedSizes.removeAt(0);
ALOGV("decoded %d of %d", decodedSize, *currentBufLeft);
if (*currentBufLeft > decodedSize) {
// adjust/interpolate next time stamp
*currentBufLeft -= decodedSize;
*nextTimeStamp += mStreamInfo->aacSamplesPerFrame *
1000000ll / mStreamInfo->aacSampleRate;
ALOGV("adjusted nextTimeStamp/size to %lld/%d",
(long long) *nextTimeStamp, *currentBufLeft);
} else {
// move to next timestamp in list
if (mBufferTimestamps.size() > 0) {
mBufferTimestamps.removeAt(0);
nextTimeStamp = &mBufferTimestamps.editItemAt(0);
mBufferSizes.removeAt(0);
currentBufLeft = &mBufferSizes.editItemAt(0);
ALOGV("moved to next time/size: %lld/%d",
(long long) *nextTimeStamp, *currentBufLeft);
}
// try to limit output buffer size to match input buffers
// (e.g when an input buffer contained 4 "sub" frames, output
// at most 4 decoded units in the corresponding output buffer)
// This is optional. Remove the next three lines to fill the output
// buffer with as many units as available.
numFrames = i + 1;
numSamples = numFrames * mStreamInfo->frameSize * mStreamInfo->numChannels;
break;
}
}
ALOGV("getting %d from ringbuffer", numSamples);
int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples);
if (ns != numSamples) {
ALOGE("not a complete frame of samples available");
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
return;
}
}
outHeader->nFilledLen = numSamples * sizeof(int16_t);
if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
mEndOfOutput = true;
} else {
outHeader->nFlags = 0;
}
outHeader->nTimeStamp = currentTime;
mOutputBufferCount++;
outInfo->mOwnedByUs = false;
outQueue.erase(outQueue.begin());
outInfo = NULL;
ALOGV("out timestamp %lld / %d", outHeader->nTimeStamp, outHeader->nFilledLen);
notifyFillBufferDone(outHeader);
outHeader = NULL;
}
if (mEndOfInput) {
int ringBufAvail = outputDelayRingBufferSamplesAvailable();
if (!outQueue.empty()
&& ringBufAvail < mStreamInfo->frameSize * mStreamInfo->numChannels) {
if (!mEndOfOutput) {
// send partial or empty block signaling EOS
mEndOfOutput = true;
BufferInfo *outInfo = *outQueue.begin();
OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(outHeader->pBuffer
+ outHeader->nOffset);
int32_t ns = outputDelayRingBufferGetSamples(outBuffer, ringBufAvail);
if (ns < 0) {
ns = 0;
}
outHeader->nFilledLen = ns;
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
outHeader->nTimeStamp = mBufferTimestamps.itemAt(0);
mBufferTimestamps.clear();
mBufferSizes.clear();
mDecodedSizes.clear();
mOutputBufferCount++;
outInfo->mOwnedByUs = false;
outQueue.erase(outQueue.begin());
outInfo = NULL;
notifyFillBufferDone(outHeader);
outHeader = NULL;
}
break; // if outQueue not empty but no more output
}
}
}
}
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.
// drain all existing data
drainDecoder();
mBufferTimestamps.clear();
mBufferSizes.clear();
mDecodedSizes.clear();
mLastInHeader = NULL;
mEndOfInput = false;
} else {
int avail;
while ((avail = outputDelayRingBufferSamplesAvailable()) > 0) {
if (avail > mStreamInfo->frameSize * mStreamInfo->numChannels) {
avail = mStreamInfo->frameSize * mStreamInfo->numChannels;
}
int32_t ns = outputDelayRingBufferGetSamples(0, avail);
if (ns != avail) {
ALOGW("not a complete frame of samples available");
break;
}
mOutputBufferCount++;
}
mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos;
mEndOfOutput = false;
}
}
void SoftAAC2::drainDecoder() {
// flush decoder until outputDelay is compensated
while (mOutputDelayCompensated > 0) {
// a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
// run DRC check
mDrcWrap.submitStreamData(mStreamInfo);
mDrcWrap.update();
AAC_DECODER_ERROR decoderErr =
aacDecoder_DecodeFrame(mAACDecoder,
tmpOutBuffer,
2048 * MAX_CHANNEL_COUNT,
AACDEC_FLUSH);
if (decoderErr != AAC_DEC_OK) {
ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
}
int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
if (tmpOutBufferSamples > mOutputDelayCompensated) {
tmpOutBufferSamples = mOutputDelayCompensated;
}
outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
mOutputDelayCompensated -= tmpOutBufferSamples;
}
}
void SoftAAC2::onReset() {
drainDecoder();
// reset the "configured" state
mInputBufferCount = 0;
mOutputBufferCount = 0;
mOutputDelayCompensated = 0;
mOutputDelayRingBufferWritePos = 0;
mOutputDelayRingBufferReadPos = 0;
mOutputDelayRingBufferFilled = 0;
mEndOfInput = false;
mEndOfOutput = false;
mBufferTimestamps.clear();
mBufferSizes.clear();
mDecodedSizes.clear();
mLastInHeader = NULL;
// To make the codec behave the same before and after a reset, we need to invalidate the
// streaminfo struct. This does that:
mStreamInfo->sampleRate = 0; // TODO: mStreamInfo is read only
mSignalledError = false;
mOutputPortSettingsChange = NONE;
}
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);
}