SoftAMRNBEncoder.cpp revision 84333e0475bc911adc16417f4ca327c975cf6c36
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "SoftAMRNBEncoder"
19#include <utils/Log.h>
20
21#include "SoftAMRNBEncoder.h"
22
23#include "gsmamr_enc.h"
24
25#include <media/stagefright/foundation/ADebug.h>
26#include <media/stagefright/foundation/hexdump.h>
27
28namespace android {
29
30static const int32_t kSampleRate = 8000;
31
32template<class T>
33static void InitOMXParams(T *params) {
34    params->nSize = sizeof(T);
35    params->nVersion.s.nVersionMajor = 1;
36    params->nVersion.s.nVersionMinor = 0;
37    params->nVersion.s.nRevision = 0;
38    params->nVersion.s.nStep = 0;
39}
40
41SoftAMRNBEncoder::SoftAMRNBEncoder(
42        const char *name,
43        const OMX_CALLBACKTYPE *callbacks,
44        OMX_PTR appData,
45        OMX_COMPONENTTYPE **component)
46    : SimpleSoftOMXComponent(name, callbacks, appData, component),
47      mEncState(NULL),
48      mSidState(NULL),
49      mBitRate(0),
50      mMode(MR475),
51      mInputSize(0),
52      mInputTimeUs(-1ll),
53      mSawInputEOS(false),
54      mSignalledError(false) {
55    initPorts();
56    CHECK_EQ(initEncoder(), (status_t)OK);
57}
58
59SoftAMRNBEncoder::~SoftAMRNBEncoder() {
60    if (mEncState != NULL) {
61        AMREncodeExit(&mEncState, &mSidState);
62        mEncState = mSidState = NULL;
63    }
64}
65
66void SoftAMRNBEncoder::initPorts() {
67    OMX_PARAM_PORTDEFINITIONTYPE def;
68    InitOMXParams(&def);
69
70    def.nPortIndex = 0;
71    def.eDir = OMX_DirInput;
72    def.nBufferCountMin = kNumBuffers;
73    def.nBufferCountActual = def.nBufferCountMin;
74    def.nBufferSize = kNumSamplesPerFrame * sizeof(int16_t);
75    def.bEnabled = OMX_TRUE;
76    def.bPopulated = OMX_FALSE;
77    def.eDomain = OMX_PortDomainAudio;
78    def.bBuffersContiguous = OMX_FALSE;
79    def.nBufferAlignment = 1;
80
81    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
82    def.format.audio.pNativeRender = NULL;
83    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
84    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
85
86    addPort(def);
87
88    def.nPortIndex = 1;
89    def.eDir = OMX_DirOutput;
90    def.nBufferCountMin = kNumBuffers;
91    def.nBufferCountActual = def.nBufferCountMin;
92    def.nBufferSize = 8192;
93    def.bEnabled = OMX_TRUE;
94    def.bPopulated = OMX_FALSE;
95    def.eDomain = OMX_PortDomainAudio;
96    def.bBuffersContiguous = OMX_FALSE;
97    def.nBufferAlignment = 2;
98
99    def.format.audio.cMIMEType = const_cast<char *>("audio/3gpp");
100    def.format.audio.pNativeRender = NULL;
101    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
102    def.format.audio.eEncoding = OMX_AUDIO_CodingAMR;
103
104    addPort(def);
105}
106
107status_t SoftAMRNBEncoder::initEncoder() {
108    if (AMREncodeInit(&mEncState, &mSidState, false /* dtx_enable */) != 0) {
109        return UNKNOWN_ERROR;
110    }
111
112    return OK;
113}
114
115OMX_ERRORTYPE SoftAMRNBEncoder::internalGetParameter(
116        OMX_INDEXTYPE index, OMX_PTR params) {
117    switch (index) {
118        case OMX_IndexParamAudioPortFormat:
119        {
120            OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
121                (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
122
123            if (formatParams->nPortIndex > 1) {
124                return OMX_ErrorUndefined;
125            }
126
127            if (formatParams->nIndex > 0) {
128                return OMX_ErrorNoMore;
129            }
130
131            formatParams->eEncoding =
132                (formatParams->nPortIndex == 0)
133                    ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAMR;
134
135            return OMX_ErrorNone;
136        }
137
138        case OMX_IndexParamAudioAmr:
139        {
140            OMX_AUDIO_PARAM_AMRTYPE *amrParams =
141                (OMX_AUDIO_PARAM_AMRTYPE *)params;
142
143            if (amrParams->nPortIndex != 1) {
144                return OMX_ErrorUndefined;
145            }
146
147            amrParams->nChannels = 1;
148            amrParams->nBitRate = mBitRate;
149            amrParams->eAMRBandMode = (OMX_AUDIO_AMRBANDMODETYPE)(mMode + 1);
150            amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
151            amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
152
153            return OMX_ErrorNone;
154        }
155
156        case OMX_IndexParamAudioPcm:
157        {
158            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
159                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
160
161            if (pcmParams->nPortIndex != 0) {
162                return OMX_ErrorUndefined;
163            }
164
165            pcmParams->eNumData = OMX_NumericalDataSigned;
166            pcmParams->eEndian = OMX_EndianBig;
167            pcmParams->bInterleaved = OMX_TRUE;
168            pcmParams->nBitPerSample = 16;
169            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
170            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelCF;
171
172            pcmParams->nChannels = 1;
173            pcmParams->nSamplingRate = kSampleRate;
174
175            return OMX_ErrorNone;
176        }
177
178        default:
179            return SimpleSoftOMXComponent::internalGetParameter(index, params);
180    }
181}
182
183OMX_ERRORTYPE SoftAMRNBEncoder::internalSetParameter(
184        OMX_INDEXTYPE index, const OMX_PTR params) {
185    switch (index) {
186        case OMX_IndexParamStandardComponentRole:
187        {
188            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
189                (const OMX_PARAM_COMPONENTROLETYPE *)params;
190
191            if (strncmp((const char *)roleParams->cRole,
192                        "audio_encoder.amrnb",
193                        OMX_MAX_STRINGNAME_SIZE - 1)) {
194                return OMX_ErrorUndefined;
195            }
196
197            return OMX_ErrorNone;
198        }
199
200        case OMX_IndexParamAudioPortFormat:
201        {
202            const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
203                (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
204
205            if (formatParams->nPortIndex > 1) {
206                return OMX_ErrorUndefined;
207            }
208
209            if (formatParams->nIndex > 0) {
210                return OMX_ErrorNoMore;
211            }
212
213            if ((formatParams->nPortIndex == 0
214                        && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
215                || (formatParams->nPortIndex == 1
216                        && formatParams->eEncoding != OMX_AUDIO_CodingAMR)) {
217                return OMX_ErrorUndefined;
218            }
219
220            return OMX_ErrorNone;
221        }
222
223        case OMX_IndexParamAudioAmr:
224        {
225            OMX_AUDIO_PARAM_AMRTYPE *amrParams =
226                (OMX_AUDIO_PARAM_AMRTYPE *)params;
227
228            if (amrParams->nPortIndex != 1) {
229                return OMX_ErrorUndefined;
230            }
231
232            if (amrParams->nChannels != 1
233                    || amrParams->eAMRDTXMode != OMX_AUDIO_AMRDTXModeOff
234                    || amrParams->eAMRFrameFormat
235                            != OMX_AUDIO_AMRFrameFormatFSF
236                    || amrParams->eAMRBandMode < OMX_AUDIO_AMRBandModeNB0
237                    || amrParams->eAMRBandMode > OMX_AUDIO_AMRBandModeNB7) {
238                return OMX_ErrorUndefined;
239            }
240
241            mBitRate = amrParams->nBitRate;
242            mMode = amrParams->eAMRBandMode - 1;
243
244            amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
245            amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
246
247            return OMX_ErrorNone;
248        }
249
250        case OMX_IndexParamAudioPcm:
251        {
252            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
253                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
254
255            if (pcmParams->nPortIndex != 0) {
256                return OMX_ErrorUndefined;
257            }
258
259            if (pcmParams->nChannels != 1
260                    || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) {
261                return OMX_ErrorUndefined;
262            }
263
264            return OMX_ErrorNone;
265        }
266
267
268        default:
269            return SimpleSoftOMXComponent::internalSetParameter(index, params);
270    }
271}
272
273void SoftAMRNBEncoder::onQueueFilled(OMX_U32 /* portIndex */) {
274    if (mSignalledError) {
275        return;
276    }
277
278    List<BufferInfo *> &inQueue = getPortQueue(0);
279    List<BufferInfo *> &outQueue = getPortQueue(1);
280
281    size_t numBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
282
283    for (;;) {
284        // We do the following until we run out of buffers.
285
286        while (mInputSize < numBytesPerInputFrame) {
287            // As long as there's still input data to be read we
288            // will drain "kNumSamplesPerFrame" samples
289            // into the "mInputFrame" buffer and then encode those
290            // as a unit into an output buffer.
291
292            if (mSawInputEOS || inQueue.empty()) {
293                return;
294            }
295
296            BufferInfo *inInfo = *inQueue.begin();
297            OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
298
299            const void *inData = inHeader->pBuffer + inHeader->nOffset;
300
301            size_t copy = numBytesPerInputFrame - mInputSize;
302            if (copy > inHeader->nFilledLen) {
303                copy = inHeader->nFilledLen;
304            }
305
306            if (mInputSize == 0) {
307                mInputTimeUs = inHeader->nTimeStamp;
308            }
309
310            memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy);
311            mInputSize += copy;
312
313            inHeader->nOffset += copy;
314            inHeader->nFilledLen -= copy;
315
316            // "Time" on the input buffer has in effect advanced by the
317            // number of audio frames we just advanced nOffset by.
318            inHeader->nTimeStamp +=
319                (copy * 1000000ll / kSampleRate) / sizeof(int16_t);
320
321            if (inHeader->nFilledLen == 0) {
322                if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
323                    ALOGV("saw input EOS");
324                    mSawInputEOS = true;
325
326                    // Pad any remaining data with zeroes.
327                    memset((uint8_t *)mInputFrame + mInputSize,
328                           0,
329                           numBytesPerInputFrame - mInputSize);
330
331                    mInputSize = numBytesPerInputFrame;
332                }
333
334                inQueue.erase(inQueue.begin());
335                inInfo->mOwnedByUs = false;
336                notifyEmptyBufferDone(inHeader);
337
338                inData = NULL;
339                inHeader = NULL;
340                inInfo = NULL;
341            }
342        }
343
344        // At this  point we have all the input data necessary to encode
345        // a single frame, all we need is an output buffer to store the result
346        // in.
347
348        if (outQueue.empty()) {
349            return;
350        }
351
352        BufferInfo *outInfo = *outQueue.begin();
353        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
354
355        uint8_t *outPtr = outHeader->pBuffer + outHeader->nOffset;
356        size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset;
357
358        Frame_Type_3GPP frameType;
359        int res = AMREncode(
360                mEncState, mSidState, (Mode)mMode,
361                mInputFrame, outPtr, &frameType, AMR_TX_WMF);
362
363        CHECK_GE(res, 0);
364        CHECK_LE((size_t)res, outAvailable);
365
366        // Convert header byte from WMF to IETF format.
367        outPtr[0] = ((outPtr[0] << 3) | 4) & 0x7c;
368
369        outHeader->nFilledLen = res;
370        outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
371
372        if (mSawInputEOS) {
373            // We also tag this output buffer with EOS if it corresponds
374            // to the final input buffer.
375            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
376        }
377
378        outHeader->nTimeStamp = mInputTimeUs;
379
380#if 0
381        ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)",
382              nOutputBytes, mInputTimeUs, outHeader->nFlags);
383
384        hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen);
385#endif
386
387        outQueue.erase(outQueue.begin());
388        outInfo->mOwnedByUs = false;
389        notifyFillBufferDone(outHeader);
390
391        outHeader = NULL;
392        outInfo = NULL;
393
394        mInputSize = 0;
395    }
396}
397
398}  // namespace android
399
400android::SoftOMXComponent *createSoftOMXComponent(
401        const char *name, const OMX_CALLBACKTYPE *callbacks,
402        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
403    return new android::SoftAMRNBEncoder(name, callbacks, appData, component);
404}
405