xref: /aosp_15_r20/frameworks/av/media/libstagefright/codecs/amrnb/enc/SoftAMRNBEncoder.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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 
28 namespace android {
29 
30 static const int32_t kSampleRate = 8000;
31 
32 template<class T>
InitOMXParams(T * params)33 static 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 
SoftAMRNBEncoder(const char * name,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)41 SoftAMRNBEncoder::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 
~SoftAMRNBEncoder()59 SoftAMRNBEncoder::~SoftAMRNBEncoder() {
60     if (mEncState != NULL) {
61         AMREncodeExit(&mEncState, &mSidState);
62         mEncState = mSidState = NULL;
63     }
64 }
65 
initPorts()66 void 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 
initEncoder()107 status_t SoftAMRNBEncoder::initEncoder() {
108     if (AMREncodeInit(&mEncState, &mSidState, false /* dtx_enable */) != 0) {
109         return UNKNOWN_ERROR;
110     }
111 
112     return OK;
113 }
114 
internalGetParameter(OMX_INDEXTYPE index,OMX_PTR params)115 OMX_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 
internalSetParameter(OMX_INDEXTYPE index,const OMX_PTR params)195 OMX_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->nPortIndex == 0
230                         && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
231                 || (formatParams->nPortIndex == 1
232                         && formatParams->eEncoding != OMX_AUDIO_CodingAMR)) {
233                 return OMX_ErrorUndefined;
234             }
235 
236             return OMX_ErrorNone;
237         }
238 
239         case OMX_IndexParamAudioAmr:
240         {
241             OMX_AUDIO_PARAM_AMRTYPE *amrParams =
242                 (OMX_AUDIO_PARAM_AMRTYPE *)params;
243 
244             if (!isValidOMXParam(amrParams)) {
245                 return OMX_ErrorBadParameter;
246             }
247 
248             if (amrParams->nPortIndex != 1) {
249                 return OMX_ErrorUndefined;
250             }
251 
252             if (amrParams->nChannels != 1
253                     || amrParams->eAMRDTXMode != OMX_AUDIO_AMRDTXModeOff
254                     || amrParams->eAMRFrameFormat
255                             != OMX_AUDIO_AMRFrameFormatFSF
256                     || amrParams->eAMRBandMode < OMX_AUDIO_AMRBandModeNB0
257                     || amrParams->eAMRBandMode > OMX_AUDIO_AMRBandModeNB7) {
258                 return OMX_ErrorUndefined;
259             }
260 
261             mBitRate = amrParams->nBitRate;
262             mMode = amrParams->eAMRBandMode - 1;
263 
264             amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
265             amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
266 
267             return OMX_ErrorNone;
268         }
269 
270         case OMX_IndexParamAudioPcm:
271         {
272             OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
273                 (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
274 
275             if (!isValidOMXParam(pcmParams)) {
276                 return OMX_ErrorBadParameter;
277             }
278 
279             if (pcmParams->nPortIndex != 0) {
280                 return OMX_ErrorUndefined;
281             }
282 
283             if (pcmParams->nChannels != 1
284                     || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) {
285                 return OMX_ErrorUndefined;
286             }
287 
288             return OMX_ErrorNone;
289         }
290 
291 
292         default:
293             return SimpleSoftOMXComponent::internalSetParameter(index, params);
294     }
295 }
296 
onQueueFilled(OMX_U32)297 void SoftAMRNBEncoder::onQueueFilled(OMX_U32 /* portIndex */) {
298     if (mSignalledError) {
299         return;
300     }
301 
302     List<BufferInfo *> &inQueue = getPortQueue(0);
303     List<BufferInfo *> &outQueue = getPortQueue(1);
304 
305     size_t numBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
306 
307     for (;;) {
308         // We do the following until we run out of buffers.
309 
310         while (mInputSize < numBytesPerInputFrame) {
311             // As long as there's still input data to be read we
312             // will drain "kNumSamplesPerFrame" samples
313             // into the "mInputFrame" buffer and then encode those
314             // as a unit into an output buffer.
315 
316             if (mSawInputEOS || inQueue.empty()) {
317                 return;
318             }
319 
320             BufferInfo *inInfo = *inQueue.begin();
321             OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
322 
323             const void *inData = inHeader->pBuffer + inHeader->nOffset;
324 
325             size_t copy = numBytesPerInputFrame - mInputSize;
326             if (copy > inHeader->nFilledLen) {
327                 copy = inHeader->nFilledLen;
328             }
329 
330             if (mInputSize == 0) {
331                 mInputTimeUs = inHeader->nTimeStamp;
332             }
333 
334             memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy);
335             mInputSize += copy;
336 
337             inHeader->nOffset += copy;
338             inHeader->nFilledLen -= copy;
339 
340             // "Time" on the input buffer has in effect advanced by the
341             // number of audio frames we just advanced nOffset by.
342             inHeader->nTimeStamp +=
343                 (copy * 1000000LL / kSampleRate) / sizeof(int16_t);
344 
345             if (inHeader->nFilledLen == 0) {
346                 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
347                     ALOGV("saw input EOS");
348                     mSawInputEOS = true;
349 
350                     // Pad any remaining data with zeroes.
351                     memset((uint8_t *)mInputFrame + mInputSize,
352                            0,
353                            numBytesPerInputFrame - mInputSize);
354 
355                     mInputSize = numBytesPerInputFrame;
356                 }
357 
358                 inQueue.erase(inQueue.begin());
359                 inInfo->mOwnedByUs = false;
360                 notifyEmptyBufferDone(inHeader);
361 
362                 inData = NULL;
363                 inHeader = NULL;
364                 inInfo = NULL;
365             }
366         }
367 
368         // At this  point we have all the input data necessary to encode
369         // a single frame, all we need is an output buffer to store the result
370         // in.
371 
372         if (outQueue.empty()) {
373             return;
374         }
375 
376         BufferInfo *outInfo = *outQueue.begin();
377         OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
378 
379         uint8_t *outPtr = outHeader->pBuffer + outHeader->nOffset;
380         size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset;
381 
382         Frame_Type_3GPP frameType;
383         int res = AMREncode(
384                 mEncState, mSidState, (Mode)mMode,
385                 mInputFrame, outPtr, &frameType, AMR_TX_WMF);
386 
387         CHECK_GE(res, 0);
388         CHECK_LE((size_t)res, outAvailable);
389 
390         // Convert header byte from WMF to IETF format.
391         outPtr[0] = ((outPtr[0] << 3) | 4) & 0x7c;
392 
393         outHeader->nFilledLen = res;
394         outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
395 
396         if (mSawInputEOS) {
397             // We also tag this output buffer with EOS if it corresponds
398             // to the final input buffer.
399             outHeader->nFlags = OMX_BUFFERFLAG_EOS;
400         }
401 
402         outHeader->nTimeStamp = mInputTimeUs;
403 
404 #if 0
405         ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)",
406               nOutputBytes, mInputTimeUs, outHeader->nFlags);
407 
408         hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen);
409 #endif
410 
411         outQueue.erase(outQueue.begin());
412         outInfo->mOwnedByUs = false;
413         notifyFillBufferDone(outHeader);
414 
415         outHeader = NULL;
416         outInfo = NULL;
417 
418         mInputSize = 0;
419     }
420 }
421 
422 }  // namespace android
423 
424 __attribute__((cfi_canonical_jump_table))
createSoftOMXComponent(const char * name,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)425 android::SoftOMXComponent *createSoftOMXComponent(
426         const char *name, const OMX_CALLBACKTYPE *callbacks,
427         OMX_PTR appData, OMX_COMPONENTTYPE **component) {
428     return new android::SoftAMRNBEncoder(name, callbacks, appData, component);
429 }
430