SoftAMRNBEncoder.cpp revision 2720c8b094cfa58de314daa8e5e4fb4fa81fe3b2
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 (!isValidOMXParam(formatParams)) {
124                return OMX_ErrorBadParameter;
125            }
126
127            if (formatParams->nPortIndex > 1) {
128                return OMX_ErrorUndefined;
129            }
130
131            if (formatParams->nIndex > 0) {
132                return OMX_ErrorNoMore;
133            }
134
135            formatParams->eEncoding =
136                (formatParams->nPortIndex == 0)
137                    ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAMR;
138
139            return OMX_ErrorNone;
140        }
141
142        case OMX_IndexParamAudioAmr:
143        {
144            OMX_AUDIO_PARAM_AMRTYPE *amrParams =
145                (OMX_AUDIO_PARAM_AMRTYPE *)params;
146
147            if (!isValidOMXParam(amrParams)) {
148                return OMX_ErrorBadParameter;
149            }
150
151            if (amrParams->nPortIndex != 1) {
152                return OMX_ErrorUndefined;
153            }
154
155            amrParams->nChannels = 1;
156            amrParams->nBitRate = mBitRate;
157            amrParams->eAMRBandMode = (OMX_AUDIO_AMRBANDMODETYPE)(mMode + 1);
158            amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
159            amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
160
161            return OMX_ErrorNone;
162        }
163
164        case OMX_IndexParamAudioPcm:
165        {
166            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
167                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
168
169            if (!isValidOMXParam(pcmParams)) {
170                return OMX_ErrorBadParameter;
171            }
172
173            if (pcmParams->nPortIndex != 0) {
174                return OMX_ErrorUndefined;
175            }
176
177            pcmParams->eNumData = OMX_NumericalDataSigned;
178            pcmParams->eEndian = OMX_EndianBig;
179            pcmParams->bInterleaved = OMX_TRUE;
180            pcmParams->nBitPerSample = 16;
181            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
182            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelCF;
183
184            pcmParams->nChannels = 1;
185            pcmParams->nSamplingRate = kSampleRate;
186
187            return OMX_ErrorNone;
188        }
189
190        default:
191            return SimpleSoftOMXComponent::internalGetParameter(index, params);
192    }
193}
194
195OMX_ERRORTYPE SoftAMRNBEncoder::internalSetParameter(
196        OMX_INDEXTYPE index, const OMX_PTR params) {
197    switch (index) {
198        case OMX_IndexParamStandardComponentRole:
199        {
200            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
201                (const OMX_PARAM_COMPONENTROLETYPE *)params;
202
203            if (!isValidOMXParam(roleParams)) {
204                return OMX_ErrorBadParameter;
205            }
206
207            if (strncmp((const char *)roleParams->cRole,
208                        "audio_encoder.amrnb",
209                        OMX_MAX_STRINGNAME_SIZE - 1)) {
210                return OMX_ErrorUndefined;
211            }
212
213            return OMX_ErrorNone;
214        }
215
216        case OMX_IndexParamAudioPortFormat:
217        {
218            const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
219                (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
220
221            if (!isValidOMXParam(formatParams)) {
222                return OMX_ErrorBadParameter;
223            }
224
225            if (formatParams->nPortIndex > 1) {
226                return OMX_ErrorUndefined;
227            }
228
229            if (formatParams->nIndex > 0) {
230                return OMX_ErrorNoMore;
231            }
232
233            if ((formatParams->nPortIndex == 0
234                        && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
235                || (formatParams->nPortIndex == 1
236                        && formatParams->eEncoding != OMX_AUDIO_CodingAMR)) {
237                return OMX_ErrorUndefined;
238            }
239
240            return OMX_ErrorNone;
241        }
242
243        case OMX_IndexParamAudioAmr:
244        {
245            OMX_AUDIO_PARAM_AMRTYPE *amrParams =
246                (OMX_AUDIO_PARAM_AMRTYPE *)params;
247
248            if (!isValidOMXParam(amrParams)) {
249                return OMX_ErrorBadParameter;
250            }
251
252            if (amrParams->nPortIndex != 1) {
253                return OMX_ErrorUndefined;
254            }
255
256            if (amrParams->nChannels != 1
257                    || amrParams->eAMRDTXMode != OMX_AUDIO_AMRDTXModeOff
258                    || amrParams->eAMRFrameFormat
259                            != OMX_AUDIO_AMRFrameFormatFSF
260                    || amrParams->eAMRBandMode < OMX_AUDIO_AMRBandModeNB0
261                    || amrParams->eAMRBandMode > OMX_AUDIO_AMRBandModeNB7) {
262                return OMX_ErrorUndefined;
263            }
264
265            mBitRate = amrParams->nBitRate;
266            mMode = amrParams->eAMRBandMode - 1;
267
268            amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
269            amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
270
271            return OMX_ErrorNone;
272        }
273
274        case OMX_IndexParamAudioPcm:
275        {
276            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
277                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
278
279            if (!isValidOMXParam(pcmParams)) {
280                return OMX_ErrorBadParameter;
281            }
282
283            if (pcmParams->nPortIndex != 0) {
284                return OMX_ErrorUndefined;
285            }
286
287            if (pcmParams->nChannels != 1
288                    || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) {
289                return OMX_ErrorUndefined;
290            }
291
292            return OMX_ErrorNone;
293        }
294
295
296        default:
297            return SimpleSoftOMXComponent::internalSetParameter(index, params);
298    }
299}
300
301void SoftAMRNBEncoder::onQueueFilled(OMX_U32 /* portIndex */) {
302    if (mSignalledError) {
303        return;
304    }
305
306    List<BufferInfo *> &inQueue = getPortQueue(0);
307    List<BufferInfo *> &outQueue = getPortQueue(1);
308
309    size_t numBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
310
311    for (;;) {
312        // We do the following until we run out of buffers.
313
314        while (mInputSize < numBytesPerInputFrame) {
315            // As long as there's still input data to be read we
316            // will drain "kNumSamplesPerFrame" samples
317            // into the "mInputFrame" buffer and then encode those
318            // as a unit into an output buffer.
319
320            if (mSawInputEOS || inQueue.empty()) {
321                return;
322            }
323
324            BufferInfo *inInfo = *inQueue.begin();
325            OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
326
327            const void *inData = inHeader->pBuffer + inHeader->nOffset;
328
329            size_t copy = numBytesPerInputFrame - mInputSize;
330            if (copy > inHeader->nFilledLen) {
331                copy = inHeader->nFilledLen;
332            }
333
334            if (mInputSize == 0) {
335                mInputTimeUs = inHeader->nTimeStamp;
336            }
337
338            memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy);
339            mInputSize += copy;
340
341            inHeader->nOffset += copy;
342            inHeader->nFilledLen -= copy;
343
344            // "Time" on the input buffer has in effect advanced by the
345            // number of audio frames we just advanced nOffset by.
346            inHeader->nTimeStamp +=
347                (copy * 1000000ll / kSampleRate) / sizeof(int16_t);
348
349            if (inHeader->nFilledLen == 0) {
350                if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
351                    ALOGV("saw input EOS");
352                    mSawInputEOS = true;
353
354                    // Pad any remaining data with zeroes.
355                    memset((uint8_t *)mInputFrame + mInputSize,
356                           0,
357                           numBytesPerInputFrame - mInputSize);
358
359                    mInputSize = numBytesPerInputFrame;
360                }
361
362                inQueue.erase(inQueue.begin());
363                inInfo->mOwnedByUs = false;
364                notifyEmptyBufferDone(inHeader);
365
366                inData = NULL;
367                inHeader = NULL;
368                inInfo = NULL;
369            }
370        }
371
372        // At this  point we have all the input data necessary to encode
373        // a single frame, all we need is an output buffer to store the result
374        // in.
375
376        if (outQueue.empty()) {
377            return;
378        }
379
380        BufferInfo *outInfo = *outQueue.begin();
381        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
382
383        uint8_t *outPtr = outHeader->pBuffer + outHeader->nOffset;
384        size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset;
385
386        Frame_Type_3GPP frameType;
387        int res = AMREncode(
388                mEncState, mSidState, (Mode)mMode,
389                mInputFrame, outPtr, &frameType, AMR_TX_WMF);
390
391        CHECK_GE(res, 0);
392        CHECK_LE((size_t)res, outAvailable);
393
394        // Convert header byte from WMF to IETF format.
395        outPtr[0] = ((outPtr[0] << 3) | 4) & 0x7c;
396
397        outHeader->nFilledLen = res;
398        outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
399
400        if (mSawInputEOS) {
401            // We also tag this output buffer with EOS if it corresponds
402            // to the final input buffer.
403            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
404        }
405
406        outHeader->nTimeStamp = mInputTimeUs;
407
408#if 0
409        ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)",
410              nOutputBytes, mInputTimeUs, outHeader->nFlags);
411
412        hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen);
413#endif
414
415        outQueue.erase(outQueue.begin());
416        outInfo->mOwnedByUs = false;
417        notifyFillBufferDone(outHeader);
418
419        outHeader = NULL;
420        outInfo = NULL;
421
422        mInputSize = 0;
423    }
424}
425
426}  // namespace android
427
428android::SoftOMXComponent *createSoftOMXComponent(
429        const char *name, const OMX_CALLBACKTYPE *callbacks,
430        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
431    return new android::SoftAMRNBEncoder(name, callbacks, appData, component);
432}
433