xref: /aosp_15_r20/frameworks/av/media/libstagefright/codecs/amrwbenc/SoftAMRWBEncoder.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 "SoftAMRWBEncoder"
19 #include <utils/Log.h>
20 
21 #include "SoftAMRWBEncoder.h"
22 
23 #include "cmnMemory.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 = 16000;
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 
SoftAMRWBEncoder(const char * name,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)41 SoftAMRWBEncoder::SoftAMRWBEncoder(
42         const char *name,
43         const OMX_CALLBACKTYPE *callbacks,
44         OMX_PTR appData,
45         OMX_COMPONENTTYPE **component)
46     : SimpleSoftOMXComponent(name, callbacks, appData, component),
47       mEncoderHandle(NULL),
48       mApiHandle(NULL),
49       mMemOperator(NULL),
50       mBitRate(0),
51       mMode(VOAMRWB_MD66),
52       mInputSize(0),
53       mInputTimeUs(-1LL),
54       mSawInputEOS(false),
55       mSignalledError(false) {
56     initPorts();
57     CHECK_EQ(initEncoder(), (status_t)OK);
58 }
59 
~SoftAMRWBEncoder()60 SoftAMRWBEncoder::~SoftAMRWBEncoder() {
61     if (mEncoderHandle != NULL) {
62         CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
63         mEncoderHandle = NULL;
64     }
65 
66     delete mApiHandle;
67     mApiHandle = NULL;
68 
69     delete mMemOperator;
70     mMemOperator = NULL;
71 }
72 
initPorts()73 void SoftAMRWBEncoder::initPorts() {
74     OMX_PARAM_PORTDEFINITIONTYPE def;
75     InitOMXParams(&def);
76 
77     def.nPortIndex = 0;
78     def.eDir = OMX_DirInput;
79     def.nBufferCountMin = kNumBuffers;
80     def.nBufferCountActual = def.nBufferCountMin;
81     def.nBufferSize = kNumSamplesPerFrame * sizeof(int16_t);
82     def.bEnabled = OMX_TRUE;
83     def.bPopulated = OMX_FALSE;
84     def.eDomain = OMX_PortDomainAudio;
85     def.bBuffersContiguous = OMX_FALSE;
86     def.nBufferAlignment = 1;
87 
88     def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
89     def.format.audio.pNativeRender = NULL;
90     def.format.audio.bFlagErrorConcealment = OMX_FALSE;
91     def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
92 
93     addPort(def);
94 
95     def.nPortIndex = 1;
96     def.eDir = OMX_DirOutput;
97     def.nBufferCountMin = kNumBuffers;
98     def.nBufferCountActual = def.nBufferCountMin;
99     def.nBufferSize = 8192;
100     def.bEnabled = OMX_TRUE;
101     def.bPopulated = OMX_FALSE;
102     def.eDomain = OMX_PortDomainAudio;
103     def.bBuffersContiguous = OMX_FALSE;
104     def.nBufferAlignment = 2;
105 
106     def.format.audio.cMIMEType = const_cast<char *>("audio/amr-wb");
107     def.format.audio.pNativeRender = NULL;
108     def.format.audio.bFlagErrorConcealment = OMX_FALSE;
109     def.format.audio.eEncoding = OMX_AUDIO_CodingAMR;
110 
111     addPort(def);
112 }
113 
initEncoder()114 status_t SoftAMRWBEncoder::initEncoder() {
115     mApiHandle = new VO_AUDIO_CODECAPI;
116 
117     if (VO_ERR_NONE != voGetAMRWBEncAPI(mApiHandle)) {
118         ALOGE("Failed to get api handle");
119         return UNKNOWN_ERROR;
120     }
121 
122     mMemOperator = new VO_MEM_OPERATOR;
123     mMemOperator->Alloc = cmnMemAlloc;
124     mMemOperator->Copy = cmnMemCopy;
125     mMemOperator->Free = cmnMemFree;
126     mMemOperator->Set = cmnMemSet;
127     mMemOperator->Check = cmnMemCheck;
128 
129     VO_CODEC_INIT_USERDATA userData;
130     memset(&userData, 0, sizeof(userData));
131     userData.memflag = VO_IMF_USERMEMOPERATOR;
132     userData.memData = (VO_PTR) mMemOperator;
133 
134     if (VO_ERR_NONE != mApiHandle->Init(
135                 &mEncoderHandle, VO_AUDIO_CodingAMRWB, &userData)) {
136         ALOGE("Failed to init AMRWB encoder");
137         return UNKNOWN_ERROR;
138     }
139 
140     VOAMRWBFRAMETYPE type = VOAMRWB_RFC3267;
141     if (VO_ERR_NONE != mApiHandle->SetParam(
142                 mEncoderHandle, VO_PID_AMRWB_FRAMETYPE, &type)) {
143         ALOGE("Failed to set AMRWB encoder frame type to %d", type);
144         return UNKNOWN_ERROR;
145     }
146 
147     return OK;
148 }
149 
internalGetParameter(OMX_INDEXTYPE index,OMX_PTR params)150 OMX_ERRORTYPE SoftAMRWBEncoder::internalGetParameter(
151         OMX_INDEXTYPE index, OMX_PTR params) {
152     switch (index) {
153         case OMX_IndexParamAudioPortFormat:
154         {
155             OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
156                 (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
157 
158             if (!isValidOMXParam(formatParams)) {
159                 return OMX_ErrorBadParameter;
160             }
161 
162             if (formatParams->nPortIndex > 1) {
163                 return OMX_ErrorUndefined;
164             }
165 
166             if (formatParams->nIndex > 0) {
167                 return OMX_ErrorNoMore;
168             }
169 
170             formatParams->eEncoding =
171                 (formatParams->nPortIndex == 0)
172                     ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAMR;
173 
174             return OMX_ErrorNone;
175         }
176 
177         case OMX_IndexParamAudioAmr:
178         {
179             OMX_AUDIO_PARAM_AMRTYPE *amrParams =
180                 (OMX_AUDIO_PARAM_AMRTYPE *)params;
181 
182             if (!isValidOMXParam(amrParams)) {
183                 return OMX_ErrorBadParameter;
184             }
185 
186             if (amrParams->nPortIndex != 1) {
187                 return OMX_ErrorUndefined;
188             }
189 
190             amrParams->nChannels = 1;
191             amrParams->nBitRate = mBitRate;
192 
193             amrParams->eAMRBandMode =
194                 (OMX_AUDIO_AMRBANDMODETYPE)(mMode + OMX_AUDIO_AMRBandModeWB0);
195 
196             amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
197             amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
198 
199             return OMX_ErrorNone;
200         }
201 
202         case OMX_IndexParamAudioPcm:
203         {
204             OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
205                 (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
206 
207             if (!isValidOMXParam(pcmParams)) {
208                 return OMX_ErrorBadParameter;
209             }
210 
211             if (pcmParams->nPortIndex != 0) {
212                 return OMX_ErrorUndefined;
213             }
214 
215             pcmParams->eNumData = OMX_NumericalDataSigned;
216             pcmParams->eEndian = OMX_EndianBig;
217             pcmParams->bInterleaved = OMX_TRUE;
218             pcmParams->nBitPerSample = 16;
219             pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
220             pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelCF;
221 
222             pcmParams->nChannels = 1;
223             pcmParams->nSamplingRate = kSampleRate;
224 
225             return OMX_ErrorNone;
226         }
227 
228         default:
229             return SimpleSoftOMXComponent::internalGetParameter(index, params);
230     }
231 }
232 
internalSetParameter(OMX_INDEXTYPE index,const OMX_PTR params)233 OMX_ERRORTYPE SoftAMRWBEncoder::internalSetParameter(
234         OMX_INDEXTYPE index, const OMX_PTR params) {
235     switch (index) {
236         case OMX_IndexParamStandardComponentRole:
237         {
238             const OMX_PARAM_COMPONENTROLETYPE *roleParams =
239                 (const OMX_PARAM_COMPONENTROLETYPE *)params;
240 
241             if (!isValidOMXParam(roleParams)) {
242                 return OMX_ErrorBadParameter;
243             }
244 
245             if (strncmp((const char *)roleParams->cRole,
246                         "audio_encoder.amrwb",
247                         OMX_MAX_STRINGNAME_SIZE - 1)) {
248                 return OMX_ErrorUndefined;
249             }
250 
251             return OMX_ErrorNone;
252         }
253 
254         case OMX_IndexParamAudioPortFormat:
255         {
256             const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
257                 (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
258 
259             if (!isValidOMXParam(formatParams)) {
260                 return OMX_ErrorBadParameter;
261             }
262 
263             if (formatParams->nPortIndex > 1) {
264                 return OMX_ErrorUndefined;
265             }
266 
267             if ((formatParams->nPortIndex == 0
268                         && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
269                 || (formatParams->nPortIndex == 1
270                         && formatParams->eEncoding != OMX_AUDIO_CodingAMR)) {
271                 return OMX_ErrorUndefined;
272             }
273 
274             return OMX_ErrorNone;
275         }
276 
277         case OMX_IndexParamAudioAmr:
278         {
279             OMX_AUDIO_PARAM_AMRTYPE *amrParams =
280                 (OMX_AUDIO_PARAM_AMRTYPE *)params;
281 
282             if (!isValidOMXParam(amrParams)) {
283                 return OMX_ErrorBadParameter;
284             }
285 
286             if (amrParams->nPortIndex != 1) {
287                 return OMX_ErrorUndefined;
288             }
289 
290             if (amrParams->nChannels != 1
291                     || amrParams->eAMRDTXMode != OMX_AUDIO_AMRDTXModeOff
292                     || amrParams->eAMRFrameFormat
293                             != OMX_AUDIO_AMRFrameFormatFSF
294                     || amrParams->eAMRBandMode < OMX_AUDIO_AMRBandModeWB0
295                     || amrParams->eAMRBandMode > OMX_AUDIO_AMRBandModeWB8) {
296                 return OMX_ErrorUndefined;
297             }
298 
299             mBitRate = amrParams->nBitRate;
300 
301             mMode = (VOAMRWBMODE)(
302                     amrParams->eAMRBandMode - OMX_AUDIO_AMRBandModeWB0);
303 
304             amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
305             amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
306 
307             if (VO_ERR_NONE !=
308                     mApiHandle->SetParam(
309                         mEncoderHandle, VO_PID_AMRWB_MODE,  &mMode)) {
310                 ALOGE("Failed to set AMRWB encoder mode to %d", mMode);
311                 return OMX_ErrorUndefined;
312             }
313 
314             return OMX_ErrorNone;
315         }
316 
317         case OMX_IndexParamAudioPcm:
318         {
319             OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
320                 (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
321 
322             if (!isValidOMXParam(pcmParams)) {
323                 return OMX_ErrorBadParameter;
324             }
325 
326             if (pcmParams->nPortIndex != 0) {
327                 return OMX_ErrorUndefined;
328             }
329 
330             if (pcmParams->nChannels != 1
331                     || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) {
332                 return OMX_ErrorUndefined;
333             }
334 
335             return OMX_ErrorNone;
336         }
337 
338 
339         default:
340             return SimpleSoftOMXComponent::internalSetParameter(index, params);
341     }
342 }
343 
onQueueFilled(OMX_U32)344 void SoftAMRWBEncoder::onQueueFilled(OMX_U32 /* portIndex */) {
345     if (mSignalledError) {
346         return;
347     }
348 
349     List<BufferInfo *> &inQueue = getPortQueue(0);
350     List<BufferInfo *> &outQueue = getPortQueue(1);
351 
352     size_t numBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
353 
354     for (;;) {
355         // We do the following until we run out of buffers.
356 
357         while (mInputSize < numBytesPerInputFrame) {
358             // As long as there's still input data to be read we
359             // will drain "kNumSamplesPerFrame" samples
360             // into the "mInputFrame" buffer and then encode those
361             // as a unit into an output buffer.
362 
363             if (mSawInputEOS || inQueue.empty()) {
364                 return;
365             }
366 
367             BufferInfo *inInfo = *inQueue.begin();
368             OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
369 
370             const void *inData = inHeader->pBuffer + inHeader->nOffset;
371 
372             size_t copy = numBytesPerInputFrame - mInputSize;
373             if (copy > inHeader->nFilledLen) {
374                 copy = inHeader->nFilledLen;
375             }
376 
377             if (mInputSize == 0) {
378                 mInputTimeUs = inHeader->nTimeStamp;
379             }
380 
381             memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy);
382             mInputSize += copy;
383 
384             inHeader->nOffset += copy;
385             inHeader->nFilledLen -= copy;
386 
387             // "Time" on the input buffer has in effect advanced by the
388             // number of audio frames we just advanced nOffset by.
389             inHeader->nTimeStamp +=
390                 (copy * 1000000LL / kSampleRate) / sizeof(int16_t);
391 
392             if (inHeader->nFilledLen == 0) {
393                 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
394                     ALOGV("saw input EOS");
395                     mSawInputEOS = true;
396 
397                     // Pad any remaining data with zeroes.
398                     memset((uint8_t *)mInputFrame + mInputSize,
399                            0,
400                            numBytesPerInputFrame - mInputSize);
401 
402                     mInputSize = numBytesPerInputFrame;
403                 }
404 
405                 inQueue.erase(inQueue.begin());
406                 inInfo->mOwnedByUs = false;
407                 notifyEmptyBufferDone(inHeader);
408 
409                 inData = NULL;
410                 inHeader = NULL;
411                 inInfo = NULL;
412             }
413         }
414 
415         // At this  point we have all the input data necessary to encode
416         // a single frame, all we need is an output buffer to store the result
417         // in.
418 
419         if (outQueue.empty()) {
420             return;
421         }
422 
423         BufferInfo *outInfo = *outQueue.begin();
424         OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
425 
426         uint8_t *outPtr = outHeader->pBuffer + outHeader->nOffset;
427         size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset;
428 
429         VO_CODECBUFFER inputData;
430         memset(&inputData, 0, sizeof(inputData));
431         inputData.Buffer = (unsigned char *) mInputFrame;
432         inputData.Length = mInputSize;
433 
434         CHECK_EQ((VO_U32)VO_ERR_NONE,
435                  mApiHandle->SetInputData(mEncoderHandle, &inputData));
436 
437         VO_CODECBUFFER outputData;
438         memset(&outputData, 0, sizeof(outputData));
439         VO_AUDIO_OUTPUTINFO outputInfo;
440         memset(&outputInfo, 0, sizeof(outputInfo));
441 
442         outputData.Buffer = outPtr;
443         outputData.Length = outAvailable;
444         VO_U32 ret = mApiHandle->GetOutputData(
445                 mEncoderHandle, &outputData, &outputInfo);
446         CHECK(ret == VO_ERR_NONE || ret == VO_ERR_INPUT_BUFFER_SMALL);
447 
448         outHeader->nFilledLen = outputData.Length;
449         outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
450 
451         if (mSawInputEOS) {
452             // We also tag this output buffer with EOS if it corresponds
453             // to the final input buffer.
454             outHeader->nFlags = OMX_BUFFERFLAG_EOS;
455         }
456 
457         outHeader->nTimeStamp = mInputTimeUs;
458 
459 #if 0
460         ALOGI("sending %ld bytes of data (time = %lld us, flags = 0x%08lx)",
461               outHeader->nFilledLen, mInputTimeUs, outHeader->nFlags);
462 
463         hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen);
464 #endif
465 
466         outQueue.erase(outQueue.begin());
467         outInfo->mOwnedByUs = false;
468         notifyFillBufferDone(outHeader);
469 
470         outHeader = NULL;
471         outInfo = NULL;
472 
473         mInputSize = 0;
474     }
475 }
476 
477 }  // namespace android
478 
479 __attribute__((cfi_canonical_jump_table))
createSoftOMXComponent(const char * name,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)480 android::SoftOMXComponent *createSoftOMXComponent(
481         const char *name, const OMX_CALLBACKTYPE *callbacks,
482         OMX_PTR appData, OMX_COMPONENTTYPE **component) {
483     return new android::SoftAMRWBEncoder(name, callbacks, appData, component);
484 }
485