| /* |
| * Copyright (C) 2011 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 "SoftAMR" |
| #include <utils/Log.h> |
| |
| #include "SoftAMR.h" |
| |
| #include "gsmamr_dec.h" |
| #include "pvamrwbdecoder.h" |
| |
| #include <media/stagefright/foundation/ADebug.h> |
| |
| 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; |
| } |
| |
| SoftAMR::SoftAMR( |
| const char *name, |
| const OMX_CALLBACKTYPE *callbacks, |
| OMX_PTR appData, |
| OMX_COMPONENTTYPE **component) |
| : SimpleSoftOMXComponent(name, callbacks, appData, component), |
| mMode(MODE_NARROW), |
| mState(NULL), |
| mDecoderBuf(NULL), |
| mDecoderCookie(NULL), |
| mInputBufferCount(0), |
| mAnchorTimeUs(0), |
| mNumSamplesOutput(0), |
| mSignalledError(false), |
| mOutputPortSettingsChange(NONE) { |
| if (!strcmp(name, "OMX.google.amrwb.decoder")) { |
| mMode = MODE_WIDE; |
| } else { |
| CHECK(!strcmp(name, "OMX.google.amrnb.decoder")); |
| } |
| |
| initPorts(); |
| CHECK_EQ(initDecoder(), (status_t)OK); |
| } |
| |
| SoftAMR::~SoftAMR() { |
| if (mMode == MODE_NARROW) { |
| GSMDecodeFrameExit(&mState); |
| mState = NULL; |
| } else { |
| free(mDecoderBuf); |
| mDecoderBuf = NULL; |
| |
| mState = NULL; |
| mDecoderCookie = NULL; |
| } |
| } |
| |
| void SoftAMR::initPorts() { |
| OMX_PARAM_PORTDEFINITIONTYPE def; |
| InitOMXParams(&def); |
| |
| def.nPortIndex = 0; |
| def.eDir = OMX_DirInput; |
| def.nBufferCountMin = kNumBuffers; |
| 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 = |
| mMode == MODE_NARROW |
| ? const_cast<char *>("audio/amr") |
| : const_cast<char *>("audio/amrwb"); |
| |
| def.format.audio.pNativeRender = NULL; |
| def.format.audio.bFlagErrorConcealment = OMX_FALSE; |
| def.format.audio.eEncoding = OMX_AUDIO_CodingAMR; |
| |
| addPort(def); |
| |
| def.nPortIndex = 1; |
| def.eDir = OMX_DirOutput; |
| def.nBufferCountMin = kNumBuffers; |
| def.nBufferCountActual = def.nBufferCountMin; |
| |
| def.nBufferSize = |
| (mMode == MODE_NARROW ? kNumSamplesPerFrameNB : kNumSamplesPerFrameWB) |
| * sizeof(int16_t); |
| |
| 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 SoftAMR::initDecoder() { |
| if (mMode == MODE_NARROW) { |
| Word16 err = GSMInitDecode(&mState, (Word8 *)"AMRNBDecoder"); |
| |
| if (err != 0) { |
| return UNKNOWN_ERROR; |
| } |
| } else { |
| int32_t memReq = pvDecoder_AmrWbMemRequirements(); |
| mDecoderBuf = malloc(memReq); |
| |
| pvDecoder_AmrWb_Init(&mState, mDecoderBuf, &mDecoderCookie); |
| } |
| |
| return OK; |
| } |
| |
| OMX_ERRORTYPE SoftAMR::internalGetParameter( |
| OMX_INDEXTYPE index, OMX_PTR params) { |
| switch (index) { |
| case OMX_IndexParamAudioAmr: |
| { |
| OMX_AUDIO_PARAM_AMRTYPE *amrParams = |
| (OMX_AUDIO_PARAM_AMRTYPE *)params; |
| |
| if (amrParams->nPortIndex != 0) { |
| return OMX_ErrorUndefined; |
| } |
| |
| amrParams->nChannels = 1; |
| amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff; |
| amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; |
| |
| if (!isConfigured()) { |
| amrParams->nBitRate = 0; |
| amrParams->eAMRBandMode = OMX_AUDIO_AMRBandModeUnused; |
| } else { |
| amrParams->nBitRate = 0; |
| amrParams->eAMRBandMode = |
| mMode == MODE_NARROW |
| ? OMX_AUDIO_AMRBandModeNB0 : OMX_AUDIO_AMRBandModeWB0; |
| } |
| |
| return OMX_ErrorNone; |
| } |
| |
| case OMX_IndexParamAudioPcm: |
| { |
| OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = |
| (OMX_AUDIO_PARAM_PCMMODETYPE *)params; |
| |
| if (pcmParams->nPortIndex != 1) { |
| return OMX_ErrorUndefined; |
| } |
| |
| pcmParams->nChannels = 1; |
| pcmParams->eNumData = OMX_NumericalDataSigned; |
| pcmParams->eEndian = OMX_EndianBig; |
| pcmParams->bInterleaved = OMX_TRUE; |
| pcmParams->nBitPerSample = 16; |
| |
| pcmParams->nSamplingRate = |
| (mMode == MODE_NARROW) ? kSampleRateNB : kSampleRateWB; |
| |
| pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; |
| pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF; |
| pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF; |
| |
| return OMX_ErrorNone; |
| } |
| |
| default: |
| return SimpleSoftOMXComponent::internalGetParameter(index, params); |
| } |
| } |
| |
| OMX_ERRORTYPE SoftAMR::internalSetParameter( |
| OMX_INDEXTYPE index, const OMX_PTR params) { |
| switch (index) { |
| case OMX_IndexParamStandardComponentRole: |
| { |
| const OMX_PARAM_COMPONENTROLETYPE *roleParams = |
| (const OMX_PARAM_COMPONENTROLETYPE *)params; |
| |
| if (mMode == MODE_NARROW) { |
| if (strncmp((const char *)roleParams->cRole, |
| "audio_decoder.amrnb", |
| OMX_MAX_STRINGNAME_SIZE - 1)) { |
| return OMX_ErrorUndefined; |
| } |
| } else { |
| if (strncmp((const char *)roleParams->cRole, |
| "audio_decoder.amrwb", |
| OMX_MAX_STRINGNAME_SIZE - 1)) { |
| return OMX_ErrorUndefined; |
| } |
| } |
| |
| return OMX_ErrorNone; |
| } |
| |
| case OMX_IndexParamAudioAmr: |
| { |
| const OMX_AUDIO_PARAM_AMRTYPE *aacParams = |
| (const OMX_AUDIO_PARAM_AMRTYPE *)params; |
| |
| if (aacParams->nPortIndex != 0) { |
| 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 SoftAMR::isConfigured() const { |
| return mInputBufferCount > 0; |
| } |
| |
| static size_t getFrameSize(unsigned FT) { |
| static const size_t kFrameSizeWB[10] = { |
| 132, 177, 253, 285, 317, 365, 397, 461, 477, 40 |
| }; |
| |
| if (FT >= 10) { |
| return 1; |
| } |
| |
| size_t frameSize = kFrameSizeWB[FT]; |
| |
| // Round up bits to bytes and add 1 for the header byte. |
| frameSize = (frameSize + 7) / 8 + 1; |
| |
| return frameSize; |
| } |
| |
| void SoftAMR::onQueueFilled(OMX_U32 /* portIndex */) { |
| List<BufferInfo *> &inQueue = getPortQueue(0); |
| List<BufferInfo *> &outQueue = getPortQueue(1); |
| |
| if (mSignalledError || mOutputPortSettingsChange != NONE) { |
| return; |
| } |
| |
| while (!inQueue.empty() && !outQueue.empty()) { |
| BufferInfo *inInfo = *inQueue.begin(); |
| OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; |
| |
| if (inHeader->nFilledLen == 0) { |
| inInfo->mOwnedByUs = false; |
| inQueue.erase(inQueue.begin()); |
| notifyEmptyBufferDone(inHeader); |
| continue; |
| } |
| |
| BufferInfo *outInfo = *outQueue.begin(); |
| OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; |
| |
| if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { |
| inQueue.erase(inQueue.begin()); |
| inInfo->mOwnedByUs = false; |
| notifyEmptyBufferDone(inHeader); |
| |
| outHeader->nFilledLen = 0; |
| outHeader->nFlags = OMX_BUFFERFLAG_EOS; |
| |
| outQueue.erase(outQueue.begin()); |
| outInfo->mOwnedByUs = false; |
| notifyFillBufferDone(outHeader); |
| return; |
| } |
| |
| if (inHeader->nOffset == 0) { |
| mAnchorTimeUs = inHeader->nTimeStamp; |
| mNumSamplesOutput = 0; |
| } |
| |
| const uint8_t *inputPtr = inHeader->pBuffer + inHeader->nOffset; |
| int32_t numBytesRead; |
| |
| if (mMode == MODE_NARROW) { |
| if (outHeader->nAllocLen < kNumSamplesPerFrameNB * sizeof(int16_t)) { |
| ALOGE("b/27662364: NB expected output buffer %zu bytes vs %u", |
| kNumSamplesPerFrameNB * sizeof(int16_t), outHeader->nAllocLen); |
| android_errorWriteLog(0x534e4554, "27662364"); |
| notify(OMX_EventError, OMX_ErrorOverflow, 0, NULL); |
| mSignalledError = true; |
| return; |
| } |
| |
| int16 mode = ((inputPtr[0] >> 3) & 0x0f); |
| // for WMF since MIME_IETF is used when calling AMRDecode. |
| size_t frameSize = WmfDecBytesPerFrame[mode] + 1; |
| |
| if (inHeader->nFilledLen < frameSize) { |
| ALOGE("b/27662364: expected %zu bytes vs %u", frameSize, inHeader->nFilledLen); |
| notify(OMX_EventError, OMX_ErrorStreamCorrupt, 0, NULL); |
| mSignalledError = true; |
| return; |
| } |
| |
| numBytesRead = |
| AMRDecode(mState, |
| (Frame_Type_3GPP)((inputPtr[0] >> 3) & 0x0f), |
| (UWord8 *)&inputPtr[1], |
| reinterpret_cast<int16_t *>(outHeader->pBuffer), |
| MIME_IETF); |
| |
| if (numBytesRead == -1) { |
| ALOGE("PV AMR decoder AMRDecode() call failed"); |
| |
| notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); |
| mSignalledError = true; |
| |
| return; |
| } |
| |
| ++numBytesRead; // Include the frame type header byte. |
| |
| if (static_cast<size_t>(numBytesRead) > inHeader->nFilledLen) { |
| // This is bad, should never have happened, but did. Abort now. |
| |
| notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); |
| mSignalledError = true; |
| |
| return; |
| } |
| } else { |
| if (outHeader->nAllocLen < kNumSamplesPerFrameWB * sizeof(int16_t)) { |
| ALOGE("b/27662364: WB expected output buffer %zu bytes vs %u", |
| kNumSamplesPerFrameWB * sizeof(int16_t), outHeader->nAllocLen); |
| android_errorWriteLog(0x534e4554, "27662364"); |
| notify(OMX_EventError, OMX_ErrorOverflow, 0, NULL); |
| mSignalledError = true; |
| return; |
| } |
| |
| int16 mode = ((inputPtr[0] >> 3) & 0x0f); |
| |
| if (mode >= 10 && mode <= 13) { |
| ALOGE("encountered illegal frame type %d in AMR WB content.", |
| mode); |
| |
| notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL); |
| mSignalledError = true; |
| |
| return; |
| } |
| |
| size_t frameSize = getFrameSize(mode); |
| if (inHeader->nFilledLen < frameSize) { |
| ALOGE("b/27662364: expected %zu bytes vs %u", frameSize, inHeader->nFilledLen); |
| notify(OMX_EventError, OMX_ErrorStreamCorrupt, 0, NULL); |
| mSignalledError = true; |
| return; |
| } |
| |
| int16_t *outPtr = (int16_t *)outHeader->pBuffer; |
| |
| if (mode >= 9) { |
| // Produce silence instead of comfort noise and for |
| // speech lost/no data. |
| memset(outPtr, 0, kNumSamplesPerFrameWB * sizeof(int16_t)); |
| } else if (mode < 9) { |
| int16 frameType; |
| RX_State_wb rx_state; |
| mime_unsorting( |
| const_cast<uint8_t *>(&inputPtr[1]), |
| mInputSampleBuffer, |
| &frameType, &mode, 1, &rx_state); |
| |
| int16_t numSamplesOutput; |
| pvDecoder_AmrWb( |
| mode, mInputSampleBuffer, |
| outPtr, |
| &numSamplesOutput, |
| mDecoderBuf, frameType, mDecoderCookie); |
| |
| CHECK_EQ((int)numSamplesOutput, (int)kNumSamplesPerFrameWB); |
| |
| for (int i = 0; i < kNumSamplesPerFrameWB; ++i) { |
| /* Delete the 2 LSBs (14-bit output) */ |
| outPtr[i] &= 0xfffC; |
| } |
| } |
| |
| numBytesRead = frameSize; |
| } |
| |
| inHeader->nOffset += numBytesRead; |
| inHeader->nFilledLen -= numBytesRead; |
| |
| outHeader->nFlags = 0; |
| outHeader->nOffset = 0; |
| |
| if (mMode == MODE_NARROW) { |
| outHeader->nFilledLen = kNumSamplesPerFrameNB * sizeof(int16_t); |
| |
| outHeader->nTimeStamp = |
| mAnchorTimeUs |
| + (mNumSamplesOutput * 1000000ll) / kSampleRateNB; |
| |
| mNumSamplesOutput += kNumSamplesPerFrameNB; |
| } else { |
| outHeader->nFilledLen = kNumSamplesPerFrameWB * sizeof(int16_t); |
| |
| outHeader->nTimeStamp = |
| mAnchorTimeUs |
| + (mNumSamplesOutput * 1000000ll) / kSampleRateWB; |
| |
| mNumSamplesOutput += kNumSamplesPerFrameWB; |
| } |
| |
| if (inHeader->nFilledLen == 0) { |
| inInfo->mOwnedByUs = false; |
| inQueue.erase(inQueue.begin()); |
| inInfo = NULL; |
| notifyEmptyBufferDone(inHeader); |
| inHeader = NULL; |
| } |
| |
| outInfo->mOwnedByUs = false; |
| outQueue.erase(outQueue.begin()); |
| outInfo = NULL; |
| notifyFillBufferDone(outHeader); |
| outHeader = NULL; |
| |
| ++mInputBufferCount; |
| } |
| } |
| |
| void SoftAMR::onPortFlushCompleted(OMX_U32 portIndex) { |
| ALOGV("onPortFlushCompleted portindex %d, resetting frame ", portIndex); |
| if (portIndex == 0) { |
| if (mMode == MODE_NARROW) { |
| Speech_Decode_Frame_reset(mState); |
| } else { |
| pvDecoder_AmrWb_Reset(mState, 0 /* reset_all */); |
| } |
| } |
| } |
| |
| void SoftAMR::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; |
| } |
| } |
| } |
| |
| void SoftAMR::onReset() { |
| mSignalledError = false; |
| mOutputPortSettingsChange = NONE; |
| } |
| |
| } // namespace android |
| |
| android::SoftOMXComponent *createSoftOMXComponent( |
| const char *name, const OMX_CALLBACKTYPE *callbacks, |
| OMX_PTR appData, OMX_COMPONENTTYPE **component) { |
| return new android::SoftAMR(name, callbacks, appData, component); |
| } |
| |