blob: 9ccb49c6b8b8da3a897e97a053612ee0c48c42b9 [file] [log] [blame]
Andreas Huber83f70f42012-02-01 11:47:54 -08001/*
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
28namespace android {
29
30static const int32_t kSampleRate = 16000;
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
41SoftAMRWBEncoder::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
60SoftAMRWBEncoder::~SoftAMRWBEncoder() {
61 if (mEncoderHandle != NULL) {
62 CHECK_EQ(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
73void 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
114status_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
150OMX_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 (formatParams->nPortIndex > 1) {
159 return OMX_ErrorUndefined;
160 }
161
162 if (formatParams->nIndex > 0) {
163 return OMX_ErrorNoMore;
164 }
165
166 formatParams->eEncoding =
167 (formatParams->nPortIndex == 0)
168 ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAMR;
169
170 return OMX_ErrorNone;
171 }
172
173 case OMX_IndexParamAudioAmr:
174 {
175 OMX_AUDIO_PARAM_AMRTYPE *amrParams =
176 (OMX_AUDIO_PARAM_AMRTYPE *)params;
177
178 if (amrParams->nPortIndex != 1) {
179 return OMX_ErrorUndefined;
180 }
181
182 amrParams->nChannels = 1;
183 amrParams->nBitRate = mBitRate;
184
185 amrParams->eAMRBandMode =
186 (OMX_AUDIO_AMRBANDMODETYPE)(mMode + OMX_AUDIO_AMRBandModeWB0);
187
188 amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
189 amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
190
191 return OMX_ErrorNone;
192 }
193
194 case OMX_IndexParamAudioPcm:
195 {
196 OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
197 (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
198
199 if (pcmParams->nPortIndex != 0) {
200 return OMX_ErrorUndefined;
201 }
202
203 pcmParams->eNumData = OMX_NumericalDataSigned;
204 pcmParams->eEndian = OMX_EndianBig;
205 pcmParams->bInterleaved = OMX_TRUE;
206 pcmParams->nBitPerSample = 16;
207 pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
208 pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelCF;
209
210 pcmParams->nChannels = 1;
211 pcmParams->nSamplingRate = kSampleRate;
212
213 return OMX_ErrorNone;
214 }
215
216 default:
217 return SimpleSoftOMXComponent::internalGetParameter(index, params);
218 }
219}
220
221OMX_ERRORTYPE SoftAMRWBEncoder::internalSetParameter(
222 OMX_INDEXTYPE index, const OMX_PTR params) {
223 switch (index) {
224 case OMX_IndexParamStandardComponentRole:
225 {
226 const OMX_PARAM_COMPONENTROLETYPE *roleParams =
227 (const OMX_PARAM_COMPONENTROLETYPE *)params;
228
229 if (strncmp((const char *)roleParams->cRole,
230 "audio_encoder.amrwb",
231 OMX_MAX_STRINGNAME_SIZE - 1)) {
232 return OMX_ErrorUndefined;
233 }
234
235 return OMX_ErrorNone;
236 }
237
238 case OMX_IndexParamAudioPortFormat:
239 {
240 const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
241 (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
242
243 if (formatParams->nPortIndex > 1) {
244 return OMX_ErrorUndefined;
245 }
246
247 if (formatParams->nIndex > 0) {
248 return OMX_ErrorNoMore;
249 }
250
251 if ((formatParams->nPortIndex == 0
252 && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
253 || (formatParams->nPortIndex == 1
254 && formatParams->eEncoding != OMX_AUDIO_CodingAMR)) {
255 return OMX_ErrorUndefined;
256 }
257
258 return OMX_ErrorNone;
259 }
260
261 case OMX_IndexParamAudioAmr:
262 {
263 OMX_AUDIO_PARAM_AMRTYPE *amrParams =
264 (OMX_AUDIO_PARAM_AMRTYPE *)params;
265
266 if (amrParams->nPortIndex != 1) {
267 return OMX_ErrorUndefined;
268 }
269
270 if (amrParams->nChannels != 1
271 || amrParams->eAMRDTXMode != OMX_AUDIO_AMRDTXModeOff
272 || amrParams->eAMRFrameFormat
273 != OMX_AUDIO_AMRFrameFormatFSF
274 || amrParams->eAMRBandMode < OMX_AUDIO_AMRBandModeWB0
275 || amrParams->eAMRBandMode > OMX_AUDIO_AMRBandModeWB8) {
276 return OMX_ErrorUndefined;
277 }
278
279 mBitRate = amrParams->nBitRate;
280
281 mMode = (VOAMRWBMODE)(
282 amrParams->eAMRBandMode - OMX_AUDIO_AMRBandModeWB0);
283
284 amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
285 amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
286
287 if (VO_ERR_NONE !=
288 mApiHandle->SetParam(
289 mEncoderHandle, VO_PID_AMRWB_MODE, &mMode)) {
290 ALOGE("Failed to set AMRWB encoder mode to %d", mMode);
291 return OMX_ErrorUndefined;
292 }
293
294 return OMX_ErrorNone;
295 }
296
297 case OMX_IndexParamAudioPcm:
298 {
299 OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
300 (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
301
302 if (pcmParams->nPortIndex != 0) {
303 return OMX_ErrorUndefined;
304 }
305
306 if (pcmParams->nChannels != 1
307 || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) {
308 return OMX_ErrorUndefined;
309 }
310
311 return OMX_ErrorNone;
312 }
313
314
315 default:
316 return SimpleSoftOMXComponent::internalSetParameter(index, params);
317 }
318}
319
320void SoftAMRWBEncoder::onQueueFilled(OMX_U32 portIndex) {
321 if (mSignalledError) {
322 return;
323 }
324
325 List<BufferInfo *> &inQueue = getPortQueue(0);
326 List<BufferInfo *> &outQueue = getPortQueue(1);
327
328 size_t numBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t);
329
330 for (;;) {
331 // We do the following until we run out of buffers.
332
333 while (mInputSize < numBytesPerInputFrame) {
334 // As long as there's still input data to be read we
335 // will drain "kNumSamplesPerFrame" samples
336 // into the "mInputFrame" buffer and then encode those
337 // as a unit into an output buffer.
338
339 if (mSawInputEOS || inQueue.empty()) {
340 return;
341 }
342
343 BufferInfo *inInfo = *inQueue.begin();
344 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
345
346 const void *inData = inHeader->pBuffer + inHeader->nOffset;
347
348 size_t copy = numBytesPerInputFrame - mInputSize;
349 if (copy > inHeader->nFilledLen) {
350 copy = inHeader->nFilledLen;
351 }
352
353 if (mInputSize == 0) {
354 mInputTimeUs = inHeader->nTimeStamp;
355 }
356
357 memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy);
358 mInputSize += copy;
359
360 inHeader->nOffset += copy;
361 inHeader->nFilledLen -= copy;
362
363 // "Time" on the input buffer has in effect advanced by the
364 // number of audio frames we just advanced nOffset by.
365 inHeader->nTimeStamp +=
366 (copy * 1000000ll / kSampleRate) / sizeof(int16_t);
367
368 if (inHeader->nFilledLen == 0) {
369 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
370 ALOGV("saw input EOS");
371 mSawInputEOS = true;
372
373 // Pad any remaining data with zeroes.
374 memset((uint8_t *)mInputFrame + mInputSize,
375 0,
376 numBytesPerInputFrame - mInputSize);
377
378 mInputSize = numBytesPerInputFrame;
379 }
380
381 inQueue.erase(inQueue.begin());
382 inInfo->mOwnedByUs = false;
383 notifyEmptyBufferDone(inHeader);
384
385 inData = NULL;
386 inHeader = NULL;
387 inInfo = NULL;
388 }
389 }
390
391 // At this point we have all the input data necessary to encode
392 // a single frame, all we need is an output buffer to store the result
393 // in.
394
395 if (outQueue.empty()) {
396 return;
397 }
398
399 BufferInfo *outInfo = *outQueue.begin();
400 OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
401
402 uint8_t *outPtr = outHeader->pBuffer + outHeader->nOffset;
403 size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset;
404
405 VO_CODECBUFFER inputData;
406 memset(&inputData, 0, sizeof(inputData));
407 inputData.Buffer = (unsigned char *) mInputFrame;
408 inputData.Length = mInputSize;
409
410 CHECK_EQ(VO_ERR_NONE,
411 mApiHandle->SetInputData(mEncoderHandle, &inputData));
412
413 VO_CODECBUFFER outputData;
414 memset(&outputData, 0, sizeof(outputData));
415 VO_AUDIO_OUTPUTINFO outputInfo;
416 memset(&outputInfo, 0, sizeof(outputInfo));
417
418 outputData.Buffer = outPtr;
419 outputData.Length = outAvailable;
420 VO_U32 ret = mApiHandle->GetOutputData(
421 mEncoderHandle, &outputData, &outputInfo);
422 CHECK(ret == VO_ERR_NONE || ret == VO_ERR_INPUT_BUFFER_SMALL);
423
424 outHeader->nFilledLen = outputData.Length;
425 outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME;
426
427 if (mSawInputEOS) {
428 // We also tag this output buffer with EOS if it corresponds
429 // to the final input buffer.
430 outHeader->nFlags = OMX_BUFFERFLAG_EOS;
431 }
432
433 outHeader->nTimeStamp = mInputTimeUs;
434
435#if 0
436 ALOGI("sending %ld bytes of data (time = %lld us, flags = 0x%08lx)",
437 outHeader->nFilledLen, mInputTimeUs, outHeader->nFlags);
438
439 hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen);
440#endif
441
442 outQueue.erase(outQueue.begin());
443 outInfo->mOwnedByUs = false;
444 notifyFillBufferDone(outHeader);
445
446 outHeader = NULL;
447 outInfo = NULL;
448
449 mInputSize = 0;
450 }
451}
452
453} // namespace android
454
455android::SoftOMXComponent *createSoftOMXComponent(
456 const char *name, const OMX_CALLBACKTYPE *callbacks,
457 OMX_PTR appData, OMX_COMPONENTTYPE **component) {
458 return new android::SoftAMRWBEncoder(name, callbacks, appData, component);
459}