1b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke/* 2b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * Copyright (C) 2012 The Android Open Source Project 3b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * 4b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * Licensed under the Apache License, Version 2.0 (the "License"); 5b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * you may not use this file except in compliance with the License. 6b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * You may obtain a copy of the License at 7b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * 8b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * http://www.apache.org/licenses/LICENSE-2.0 9b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * 10b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * Unless required by applicable law or agreed to in writing, software 11b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * distributed under the License is distributed on an "AS IS" BASIS, 12b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * See the License for the specific language governing permissions and 14b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke * limitations under the License. 15b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke */ 16b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 17b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke#define LOG_TAG "SoftAAC2" 184213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi//#define LOG_NDEBUG 0 19b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke#include <utils/Log.h> 20b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 21b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke#include "SoftAAC2.h" 22b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 231adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke#include <cutils/properties.h> 24b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke#include <media/stagefright/foundation/ADebug.h> 25b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke#include <media/stagefright/foundation/hexdump.h> 268370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber#include <media/stagefright/MediaErrors.h> 27b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 28b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke#define FILEREAD_MAX_LAYERS 2 29b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 30347f35436be724a64272c41e6325f03fe6b9d43aJean-Michel Trivi#define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */ 314213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi#define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */ 324213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi#define MAX_CHANNEL_COUNT 6 /* maximum number of audio channels that can be decoded */ 334213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi// names of properties that can be used to override the default DRC settings 344213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi#define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level" 354213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi#define PROP_DRC_OVERRIDE_CUT "aac_drc_cut" 364213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi#define PROP_DRC_OVERRIDE_BOOST "aac_drc_boost" 375696a4efef79ee474ac6bcf2639c741e159bed1bJean-Michel Trivi 38b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burkenamespace android { 39b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 40b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burketemplate<class T> 41b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burkestatic void InitOMXParams(T *params) { 42b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke params->nSize = sizeof(T); 43b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke params->nVersion.s.nVersionMajor = 1; 44b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke params->nVersion.s.nVersionMinor = 0; 45b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke params->nVersion.s.nRevision = 0; 46b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke params->nVersion.s.nStep = 0; 47b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 48b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 49b7ddcc9460f488f0b032aeb27b52a423318a97eaDave BurkeSoftAAC2::SoftAAC2( 50b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke const char *name, 51b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke const OMX_CALLBACKTYPE *callbacks, 52b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_PTR appData, 53b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_COMPONENTTYPE **component) 54b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke : SimpleSoftOMXComponent(name, callbacks, appData, component), 55b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mAACDecoder(NULL), 56b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mStreamInfo(NULL), 57b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mIsADTS(false), 58b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mInputBufferCount(0), 59b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mSignalledError(false), 60b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mAnchorTimeUs(0), 61b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mNumSamplesOutput(0), 62b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mOutputPortSettingsChange(NONE) { 63b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke initPorts(); 64b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke CHECK_EQ(initDecoder(), (status_t)OK); 65b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 66b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 67b7ddcc9460f488f0b032aeb27b52a423318a97eaDave BurkeSoftAAC2::~SoftAAC2() { 68b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacDecoder_Close(mAACDecoder); 69b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 70b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 71b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burkevoid SoftAAC2::initPorts() { 72b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_PARAM_PORTDEFINITIONTYPE def; 73b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke InitOMXParams(&def); 74b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 75b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.nPortIndex = 0; 76b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.eDir = OMX_DirInput; 77eb61431af13741aa8b7e57a39f69bba5a6c190dcAndreas Huber def.nBufferCountMin = kNumInputBuffers; 78b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.nBufferCountActual = def.nBufferCountMin; 79b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.nBufferSize = 8192; 80b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.bEnabled = OMX_TRUE; 81b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.bPopulated = OMX_FALSE; 82b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.eDomain = OMX_PortDomainAudio; 83b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.bBuffersContiguous = OMX_FALSE; 84b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.nBufferAlignment = 1; 85b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 86b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.format.audio.cMIMEType = const_cast<char *>("audio/aac"); 87b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.format.audio.pNativeRender = NULL; 88b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.format.audio.bFlagErrorConcealment = OMX_FALSE; 89b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; 90b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 91b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke addPort(def); 92b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 93b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.nPortIndex = 1; 94b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.eDir = OMX_DirOutput; 95eb61431af13741aa8b7e57a39f69bba5a6c190dcAndreas Huber def.nBufferCountMin = kNumOutputBuffers; 96b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.nBufferCountActual = def.nBufferCountMin; 97888f63bcde37347061de1aabb1b7febb2eca0875Jean-Michel Trivi def.nBufferSize = 4096 * MAX_CHANNEL_COUNT; 98b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.bEnabled = OMX_TRUE; 99b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.bPopulated = OMX_FALSE; 100b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.eDomain = OMX_PortDomainAudio; 101b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.bBuffersContiguous = OMX_FALSE; 102b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.nBufferAlignment = 2; 103b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 104b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.format.audio.cMIMEType = const_cast<char *>("audio/raw"); 105b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.format.audio.pNativeRender = NULL; 106b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.format.audio.bFlagErrorConcealment = OMX_FALSE; 107b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke def.format.audio.eEncoding = OMX_AUDIO_CodingPCM; 108b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 109b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke addPort(def); 110b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 111b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 112b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burkestatus_t SoftAAC2::initDecoder() { 113b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke status_t status = UNKNOWN_ERROR; 114e672a0eea2cdc2ed43e002f0583902f20705de57Andreas Huber mAACDecoder = aacDecoder_Open(TT_MP4_ADIF, /* num layers */ 1); 115b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (mAACDecoder != NULL) { 116b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mStreamInfo = aacDecoder_GetStreamInfo(mAACDecoder); 117b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (mStreamInfo != NULL) { 118b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke status = OK; 119b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 120b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 12141914becfd019c619783d875c61ef71db0e67400Marco Nelissen mIsFirst = true; 1224213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi 1234213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi // for streams that contain metadata, use the mobile profile DRC settings unless overridden 1244213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi // by platform properties: 1254213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi char value[PROPERTY_VALUE_MAX]; 1264213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi // * AAC_DRC_REFERENCE_LEVEL 1274213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) { 1284213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi unsigned refLevel = atoi(value); 1294213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi ALOGV("AAC decoder using AAC_DRC_REFERENCE_LEVEL of %d instead of %d", 1304213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi refLevel, DRC_DEFAULT_MOBILE_REF_LEVEL); 1314213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, refLevel); 1324213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi } else { 1334213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, DRC_DEFAULT_MOBILE_REF_LEVEL); 1344213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi } 1354213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi // * AAC_DRC_ATTENUATION_FACTOR 1364213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) { 1374213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi unsigned cut = atoi(value); 1384213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi ALOGV("AAC decoder using AAC_DRC_ATTENUATION_FACTOR of %d instead of %d", 1394213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi cut, DRC_DEFAULT_MOBILE_DRC_CUT); 1404213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, cut); 1414213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi } else { 1424213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT); 1434213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi } 1444213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi // * AAC_DRC_BOOST_FACTOR (note: no default, using cut) 1454213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) { 1464213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi unsigned boost = atoi(value); 1474213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi ALOGV("AAC decoder using AAC_DRC_BOOST_FACTOR of %d", boost); 1484213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, boost); 1494213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi } 1504213e9db1cc57ab593bb63432b4e0cf477c3f835Jean-Michel Trivi 151b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return status; 152b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 153b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 154b7ddcc9460f488f0b032aeb27b52a423318a97eaDave BurkeOMX_ERRORTYPE SoftAAC2::internalGetParameter( 155b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_INDEXTYPE index, OMX_PTR params) { 156b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke switch (index) { 157b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke case OMX_IndexParamAudioAac: 158b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke { 159b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = 160b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke (OMX_AUDIO_PARAM_AACPROFILETYPE *)params; 161b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 162b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (aacParams->nPortIndex != 0) { 163b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorUndefined; 164b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 165b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 166b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacParams->nBitRate = 0; 167b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacParams->nAudioBandWidth = 0; 168b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacParams->nAACtools = 0; 169b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacParams->nAACERtools = 0; 170b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacParams->eAACProfile = OMX_AUDIO_AACObjectMain; 171b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 172b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacParams->eAACStreamFormat = 173b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mIsADTS 174b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke ? OMX_AUDIO_AACStreamFormatMP4ADTS 175b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke : OMX_AUDIO_AACStreamFormatMP4FF; 176b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 177b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo; 178b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 179bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke if (!isConfigured()) { 180b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacParams->nChannels = 1; 181b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacParams->nSampleRate = 44100; 182b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacParams->nFrameLength = 0; 183b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } else { 184f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke aacParams->nChannels = mStreamInfo->numChannels; 185f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke aacParams->nSampleRate = mStreamInfo->sampleRate; 186f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke aacParams->nFrameLength = mStreamInfo->frameSize; 187b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 188b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 189b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorNone; 190b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 191b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 192b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke case OMX_IndexParamAudioPcm: 193b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke { 194b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = 195b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke (OMX_AUDIO_PARAM_PCMMODETYPE *)params; 196b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 197b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (pcmParams->nPortIndex != 1) { 198b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorUndefined; 199b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 200b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 201b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke pcmParams->eNumData = OMX_NumericalDataSigned; 202b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke pcmParams->eEndian = OMX_EndianBig; 203b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke pcmParams->bInterleaved = OMX_TRUE; 204b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke pcmParams->nBitPerSample = 16; 205b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; 206b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; 207b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; 208095c2da8d6c2058b02142c940f6e6346d6581780Dave Burke pcmParams->eChannelMapping[2] = OMX_AUDIO_ChannelCF; 209095c2da8d6c2058b02142c940f6e6346d6581780Dave Burke pcmParams->eChannelMapping[3] = OMX_AUDIO_ChannelLFE; 210095c2da8d6c2058b02142c940f6e6346d6581780Dave Burke pcmParams->eChannelMapping[4] = OMX_AUDIO_ChannelLS; 211095c2da8d6c2058b02142c940f6e6346d6581780Dave Burke pcmParams->eChannelMapping[5] = OMX_AUDIO_ChannelRS; 212b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 213bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke if (!isConfigured()) { 214b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke pcmParams->nChannels = 1; 215b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke pcmParams->nSamplingRate = 44100; 216b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } else { 217f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke pcmParams->nChannels = mStreamInfo->numChannels; 218b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke pcmParams->nSamplingRate = mStreamInfo->sampleRate; 219b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 220b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 221b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorNone; 222b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 223b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 224b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke default: 225b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return SimpleSoftOMXComponent::internalGetParameter(index, params); 226b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 227b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 228b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 229b7ddcc9460f488f0b032aeb27b52a423318a97eaDave BurkeOMX_ERRORTYPE SoftAAC2::internalSetParameter( 230b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_INDEXTYPE index, const OMX_PTR params) { 231b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke switch (index) { 232b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke case OMX_IndexParamStandardComponentRole: 233b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke { 234b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke const OMX_PARAM_COMPONENTROLETYPE *roleParams = 235b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke (const OMX_PARAM_COMPONENTROLETYPE *)params; 236b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 237b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (strncmp((const char *)roleParams->cRole, 238b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke "audio_decoder.aac", 239b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_MAX_STRINGNAME_SIZE - 1)) { 240b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorUndefined; 241b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 242b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 243b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorNone; 244b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 245b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 246b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke case OMX_IndexParamAudioAac: 247b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke { 248b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke const OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams = 249b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke (const OMX_AUDIO_PARAM_AACPROFILETYPE *)params; 250b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 251b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (aacParams->nPortIndex != 0) { 252b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorUndefined; 253b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 254b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 255b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) { 256b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mIsADTS = false; 257b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } else if (aacParams->eAACStreamFormat 258b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke == OMX_AUDIO_AACStreamFormatMP4ADTS) { 259b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mIsADTS = true; 260b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } else { 261b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorUndefined; 262b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 263b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 264b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorNone; 265b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 266b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 267b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke case OMX_IndexParamAudioPcm: 268b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke { 269b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = 270b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke (OMX_AUDIO_PARAM_PCMMODETYPE *)params; 271b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 272b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (pcmParams->nPortIndex != 1) { 273b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorUndefined; 274b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 275b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 276b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return OMX_ErrorNone; 277b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 278b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 279b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke default: 280b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return SimpleSoftOMXComponent::internalSetParameter(index, params); 281b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 282b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 283b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 284b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burkebool SoftAAC2::isConfigured() const { 285b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return mInputBufferCount > 0; 286b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 287b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 2881adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burkevoid SoftAAC2::maybeConfigureDownmix() const { 2891adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke if (mStreamInfo->numChannels > 2) { 2901adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke char value[PROPERTY_VALUE_MAX]; 2911adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke if (!(property_get("media.aac_51_output_enabled", value, NULL) && 2921adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke (!strcmp(value, "1") || !strcasecmp(value, "true")))) { 2931adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke ALOGI("Downmixing multichannel AAC to stereo"); 2941adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke aacDecoder_SetParam(mAACDecoder, AAC_PCM_OUTPUT_CHANNELS, 2); 2951adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke mStreamInfo->numChannels = 2; 2961adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke } 2971adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke } 2981adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke} 2991adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke 300b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burkevoid SoftAAC2::onQueueFilled(OMX_U32 portIndex) { 301b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (mSignalledError || mOutputPortSettingsChange != NONE) { 302b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return; 303b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 304b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 305b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke UCHAR* inBuffer[FILEREAD_MAX_LAYERS]; 306b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke UINT inBufferLength[FILEREAD_MAX_LAYERS] = {0}; 307b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke UINT bytesValid[FILEREAD_MAX_LAYERS] = {0}; 308b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 309b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke List<BufferInfo *> &inQueue = getPortQueue(0); 310b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke List<BufferInfo *> &outQueue = getPortQueue(1); 311b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 312b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (portIndex == 0 && mInputBufferCount == 0) { 313b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke ++mInputBufferCount; 314b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke BufferInfo *info = *inQueue.begin(); 315b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_BUFFERHEADERTYPE *header = info->mHeader; 316b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 317b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inBuffer[0] = header->pBuffer + header->nOffset; 318b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inBufferLength[0] = header->nFilledLen; 319b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 320b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke AAC_DECODER_ERROR decoderErr = 321b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke aacDecoder_ConfigRaw(mAACDecoder, 322b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inBuffer, 323b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inBufferLength); 324b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 325b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (decoderErr != AAC_DEC_OK) { 326b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mSignalledError = true; 327b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); 328b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return; 329b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 330b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inQueue.erase(inQueue.begin()); 331b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke info->mOwnedByUs = false; 332b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke notifyEmptyBufferDone(header); 333b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 334cc9833b5db0e96f12daddb90a747fd146627377dJames Dong // Only send out port settings changed event if both sample rate 335cc9833b5db0e96f12daddb90a747fd146627377dJames Dong // and numChannels are valid. 336cc9833b5db0e96f12daddb90a747fd146627377dJames Dong if (mStreamInfo->sampleRate && mStreamInfo->numChannels) { 337cc9833b5db0e96f12daddb90a747fd146627377dJames Dong maybeConfigureDownmix(); 338cc9833b5db0e96f12daddb90a747fd146627377dJames Dong ALOGI("Initially configuring decoder: %d Hz, %d channels", 339cc9833b5db0e96f12daddb90a747fd146627377dJames Dong mStreamInfo->sampleRate, 340cc9833b5db0e96f12daddb90a747fd146627377dJames Dong mStreamInfo->numChannels); 341cc9833b5db0e96f12daddb90a747fd146627377dJames Dong 342cc9833b5db0e96f12daddb90a747fd146627377dJames Dong notify(OMX_EventPortSettingsChanged, 1, 0, NULL); 343cc9833b5db0e96f12daddb90a747fd146627377dJames Dong mOutputPortSettingsChange = AWAITING_DISABLED; 344cc9833b5db0e96f12daddb90a747fd146627377dJames Dong } 345cc9833b5db0e96f12daddb90a747fd146627377dJames Dong 346b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return; 347b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 348b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 349b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke while (!inQueue.empty() && !outQueue.empty()) { 350b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke BufferInfo *inInfo = *inQueue.begin(); 351b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; 352b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 353b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke BufferInfo *outInfo = *outQueue.begin(); 354b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; 355b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 356b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { 357b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inQueue.erase(inQueue.begin()); 358b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inInfo->mOwnedByUs = false; 359b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke notifyEmptyBufferDone(inHeader); 360b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 36151d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber if (!mIsFirst) { 36251d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber // flush out the decoder's delayed data by calling DecodeFrame 36351d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber // one more time, with the AACDEC_FLUSH flag set 36451d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber INT_PCM *outBuffer = 36551d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber reinterpret_cast<INT_PCM *>( 36651d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber outHeader->pBuffer + outHeader->nOffset); 36751d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber 36851d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber AAC_DECODER_ERROR decoderErr = 36951d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber aacDecoder_DecodeFrame(mAACDecoder, 37051d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber outBuffer, 37151d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber outHeader->nAllocLen, 37251d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber AACDEC_FLUSH); 37351d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber 37451d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber if (decoderErr != AAC_DEC_OK) { 37551d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber mSignalledError = true; 37651d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber 37751d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, 37851d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber NULL); 37951d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber 38051d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber return; 38151d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber } 38251d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber 38351d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber outHeader->nFilledLen = 38451d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber mStreamInfo->frameSize 38551d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber * sizeof(int16_t) 38651d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber * mStreamInfo->numChannels; 38751d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber } else { 38851d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber // Since we never discarded frames from the start, we won't have 38951d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber // to add any padding at the end either. 39051d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber 39151d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber outHeader->nFilledLen = 0; 392f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke } 393f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke 394b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke outHeader->nFlags = OMX_BUFFERFLAG_EOS; 395b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 396b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke outQueue.erase(outQueue.begin()); 397b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke outInfo->mOwnedByUs = false; 398b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke notifyFillBufferDone(outHeader); 399b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return; 400b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 401b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 402b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (inHeader->nOffset == 0) { 403b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mAnchorTimeUs = inHeader->nTimeStamp; 404b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mNumSamplesOutput = 0; 405b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 406b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 4076b7b8227cc8753fde4094ca8af2d381c2740826fAndreas Huber size_t adtsHeaderSize = 0; 408b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (mIsADTS) { 409b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke // skip 30 bits, aac_frame_length follows. 410b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll????? 411b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 412b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset; 413b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 4148370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber bool signalError = false; 4158370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber if (inHeader->nFilledLen < 7) { 4168370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber ALOGE("Audio data too short to contain even the ADTS header. " 4178370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber "Got %ld bytes.", inHeader->nFilledLen); 4188370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber hexdump(adtsHeader, inHeader->nFilledLen); 4198370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber signalError = true; 4208370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber } else { 4218370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber bool protectionAbsent = (adtsHeader[1] & 1); 4228370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber 4238370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber unsigned aac_frame_length = 4248370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber ((adtsHeader[3] & 3) << 11) 4258370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber | (adtsHeader[4] << 3) 4268370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber | (adtsHeader[5] >> 5); 4278370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber 4288370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber if (inHeader->nFilledLen < aac_frame_length) { 4298370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber ALOGE("Not enough audio data for the complete frame. " 4308370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber "Got %ld bytes, frame size according to the ADTS " 4318370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber "header is %u bytes.", 4328370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber inHeader->nFilledLen, aac_frame_length); 4338370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber hexdump(adtsHeader, inHeader->nFilledLen); 4348370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber signalError = true; 4358370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber } else { 4368370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber adtsHeaderSize = (protectionAbsent ? 7 : 9); 4378370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber 4388370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize; 4398370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber inBufferLength[0] = aac_frame_length - adtsHeaderSize; 4408370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber 4418370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber inHeader->nOffset += adtsHeaderSize; 4428370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber inHeader->nFilledLen -= adtsHeaderSize; 4438370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber } 444e35ac2860e99d809a2ccca59bd4eb2f8c02d15adAndreas Huber } 4458370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber 446e35ac2860e99d809a2ccca59bd4eb2f8c02d15adAndreas Huber if (signalError) { 447e35ac2860e99d809a2ccca59bd4eb2f8c02d15adAndreas Huber mSignalledError = true; 4488370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber 449e35ac2860e99d809a2ccca59bd4eb2f8c02d15adAndreas Huber notify(OMX_EventError, 450e35ac2860e99d809a2ccca59bd4eb2f8c02d15adAndreas Huber OMX_ErrorStreamCorrupt, 451e35ac2860e99d809a2ccca59bd4eb2f8c02d15adAndreas Huber ERROR_MALFORMED, 452e35ac2860e99d809a2ccca59bd4eb2f8c02d15adAndreas Huber NULL); 4538370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber 454e35ac2860e99d809a2ccca59bd4eb2f8c02d15adAndreas Huber return; 4558370c7ad4136ad7e0787d5a91ccfa3d63cfbe5ccAndreas Huber } 456b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } else { 457b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inBuffer[0] = inHeader->pBuffer + inHeader->nOffset; 458b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inBufferLength[0] = inHeader->nFilledLen; 459b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 460b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 461b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke // Fill and decode 46251d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>( 46351d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber outHeader->pBuffer + outHeader->nOffset); 46451d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber 465b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke bytesValid[0] = inBufferLength[0]; 466b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 467b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke int prevSampleRate = mStreamInfo->sampleRate; 468f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke int prevNumChannels = mStreamInfo->numChannels; 4696b7b8227cc8753fde4094ca8af2d381c2740826fAndreas Huber 470f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke AAC_DECODER_ERROR decoderErr = AAC_DEC_NOT_ENOUGH_BITS; 471f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke while (bytesValid[0] > 0 && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { 472f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke aacDecoder_Fill(mAACDecoder, 473f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke inBuffer, 474f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke inBufferLength, 475f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke bytesValid); 4766b7b8227cc8753fde4094ca8af2d381c2740826fAndreas Huber 477f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke decoderErr = aacDecoder_DecodeFrame(mAACDecoder, 478f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke outBuffer, 479f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke outHeader->nAllocLen, 4803748b71a7fe73b0365a93f1fd28ced14219f85e5Dave Burke 0 /* flags */); 481f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke 482503775e4e2c3ead1cbd3c10561e3bdd90f2e70d5Dave Burke if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) { 483503775e4e2c3ead1cbd3c10561e3bdd90f2e70d5Dave Burke ALOGW("Not enough bits, bytesValid %d", bytesValid[0]); 484503775e4e2c3ead1cbd3c10561e3bdd90f2e70d5Dave Burke } 485f60c660f048d5f5e2458cff243c20400d73757a7Dave Burke } 4863748b71a7fe73b0365a93f1fd28ced14219f85e5Dave Burke 487bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke /* 488bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * AAC+/eAAC+ streams can be signalled in two ways: either explicitly 489bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual 490bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * rate system and the sampling rate in the final output is actually 491bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * doubled compared with the core AAC decoder sampling rate. 492bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * 493bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * Explicit signalling is done by explicitly defining SBR audio object 494bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * type in the bitstream. Implicit signalling is done by embedding 495bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * SBR content in AAC extension payload specific to SBR, and hence 496bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * requires an AAC decoder to perform pre-checks on actual audio frames. 497bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * 498bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * Thus, we could not say for sure whether a stream is 499bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke * AAC+/eAAC+ until the first data frame is decoded. 500bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke */ 501bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke if (mInputBufferCount <= 2) { 502bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke if (mStreamInfo->sampleRate != prevSampleRate || 503bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke mStreamInfo->numChannels != prevNumChannels) { 5041adacd921319ec52fac1a4596ab6eaf8689e6a7dDave Burke maybeConfigureDownmix(); 505bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke ALOGI("Reconfiguring decoder: %d Hz, %d channels", 506bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke mStreamInfo->sampleRate, 507bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke mStreamInfo->numChannels); 508bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke 509bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke // We're going to want to revisit this input buffer, but 510bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke // may have already advanced the offset. Undo that if 511bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke // necessary. 512bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke inHeader->nOffset -= adtsHeaderSize; 513bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke inHeader->nFilledLen += adtsHeaderSize; 514bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke 515bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke notify(OMX_EventPortSettingsChanged, 1, 0, NULL); 516bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke mOutputPortSettingsChange = AWAITING_DISABLED; 517bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke return; 518bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke } 519bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) { 520bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke ALOGW("Invalid AAC stream"); 521bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke mSignalledError = true; 522bf2461ecc71c0aacf8c03fcdaf0dc46bc8285c7fDave Burke notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL); 5233748b71a7fe73b0365a93f1fd28ced14219f85e5Dave Burke return; 524b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 525b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 526b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke size_t numOutBytes = 527b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels; 528b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 529b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (decoderErr == AAC_DEC_OK) { 530b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0]; 531b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inHeader->nFilledLen -= inBufferUsedLength; 532b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inHeader->nOffset += inBufferUsedLength; 533b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } else { 534b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke ALOGW("AAC decoder returned error %d, substituting silence", 535b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke decoderErr); 536b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 537b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes); 538b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 539b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke // Discard input buffer. 540b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inHeader->nFilledLen = 0; 541b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 542e672a0eea2cdc2ed43e002f0583902f20705de57Andreas Huber aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); 543e672a0eea2cdc2ed43e002f0583902f20705de57Andreas Huber 544b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke // fall through 545b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 546b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 547b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (decoderErr == AAC_DEC_OK || mNumSamplesOutput > 0) { 548b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke // We'll only output data if we successfully decoded it or 549b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke // we've previously decoded valid data, in the latter case 550b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke // (decode failed) we'll output a silent frame. 55141914becfd019c619783d875c61ef71db0e67400Marco Nelissen if (mIsFirst) { 55241914becfd019c619783d875c61ef71db0e67400Marco Nelissen mIsFirst = false; 55351d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber // the first decoded frame should be discarded to account 55451d7547944967d1157b7fe18e1fb8f7ee6810412Andreas Huber // for decoder delay 55541914becfd019c619783d875c61ef71db0e67400Marco Nelissen numOutBytes = 0; 55641914becfd019c619783d875c61ef71db0e67400Marco Nelissen } 55741914becfd019c619783d875c61ef71db0e67400Marco Nelissen 558b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke outHeader->nFilledLen = numOutBytes; 559b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke outHeader->nFlags = 0; 560b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 561b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke outHeader->nTimeStamp = 562b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mAnchorTimeUs 563b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke + (mNumSamplesOutput * 1000000ll) / mStreamInfo->sampleRate; 564b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 565b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mNumSamplesOutput += mStreamInfo->frameSize; 566b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 567b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke outInfo->mOwnedByUs = false; 568b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke outQueue.erase(outQueue.begin()); 569b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke outInfo = NULL; 570b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke notifyFillBufferDone(outHeader); 571b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke outHeader = NULL; 572b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 573b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 574b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (inHeader->nFilledLen == 0) { 575b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inInfo->mOwnedByUs = false; 576b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inQueue.erase(inQueue.begin()); 577b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inInfo = NULL; 578b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke notifyEmptyBufferDone(inHeader); 579b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke inHeader = NULL; 580b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 581b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 582b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (decoderErr == AAC_DEC_OK) { 583b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke ++mInputBufferCount; 584b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 585b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 586b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 587b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 588b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burkevoid SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) { 589b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (portIndex == 0) { 590b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke // Make sure that the next buffer output does not still 591b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke // depend on fragments from the last one decoded. 5923748b71a7fe73b0365a93f1fd28ced14219f85e5Dave Burke aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1); 59341914becfd019c619783d875c61ef71db0e67400Marco Nelissen mIsFirst = true; 594b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 595b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 596b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 597b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burkevoid SoftAAC2::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) { 598b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke if (portIndex != 1) { 599b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return; 600b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 601b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 602b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke switch (mOutputPortSettingsChange) { 603b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke case NONE: 604b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke break; 605b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 606b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke case AWAITING_DISABLED: 607b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke { 608b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke CHECK(!enabled); 609b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mOutputPortSettingsChange = AWAITING_ENABLED; 610b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke break; 611b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 612b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 613b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke default: 614b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke { 615b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED); 616b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke CHECK(enabled); 617b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke mOutputPortSettingsChange = NONE; 618b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke break; 619b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 620b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke } 621b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 622b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 623b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} // namespace android 624b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke 625b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burkeandroid::SoftOMXComponent *createSoftOMXComponent( 626b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke const char *name, const OMX_CALLBACKTYPE *callbacks, 627b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke OMX_PTR appData, OMX_COMPONENTTYPE **component) { 628b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke return new android::SoftAAC2(name, callbacks, appData, component); 629b7ddcc9460f488f0b032aeb27b52a423318a97eaDave Burke} 630