1 /*
2 * Copyright (C) 2011 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 "SoftG711"
19 #include <utils/Log.h>
20
21 #include "SoftG711.h"
22
23 #include <media/stagefright/foundation/ADebug.h>
24 #include <media/stagefright/MediaDefs.h>
25
26 #define MAX_CHANNEL_COUNT 6 /* maximum number of audio channels that can be decoded */
27
28 namespace android {
29
30 template<class T>
InitOMXParams(T * params)31 static void InitOMXParams(T *params) {
32 params->nSize = sizeof(T);
33 params->nVersion.s.nVersionMajor = 1;
34 params->nVersion.s.nVersionMinor = 0;
35 params->nVersion.s.nRevision = 0;
36 params->nVersion.s.nStep = 0;
37 }
38
SoftG711(const char * name,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)39 SoftG711::SoftG711(
40 const char *name,
41 const OMX_CALLBACKTYPE *callbacks,
42 OMX_PTR appData,
43 OMX_COMPONENTTYPE **component)
44 : SimpleSoftOMXComponent(name, callbacks, appData, component),
45 mIsMLaw(true),
46 mSignalledError(false),
47 mNumChannels(1),
48 mSamplingRate(8000) {
49 if (!strcmp(name, "OMX.google.g711.alaw.decoder")) {
50 mIsMLaw = false;
51 } else {
52 CHECK(!strcmp(name, "OMX.google.g711.mlaw.decoder"));
53 }
54
55 initPorts();
56 }
57
~SoftG711()58 SoftG711::~SoftG711() {
59 }
60
initPorts()61 void SoftG711::initPorts() {
62 OMX_PARAM_PORTDEFINITIONTYPE def;
63 InitOMXParams(&def);
64
65 def.nPortIndex = 0;
66 def.eDir = OMX_DirInput;
67 def.nBufferCountMin = kNumBuffers;
68 def.nBufferCountActual = def.nBufferCountMin;
69 def.nBufferSize = 8192;
70 def.bEnabled = OMX_TRUE;
71 def.bPopulated = OMX_FALSE;
72 def.eDomain = OMX_PortDomainAudio;
73 def.bBuffersContiguous = OMX_FALSE;
74 def.nBufferAlignment = 1;
75
76 def.format.audio.cMIMEType =
77 const_cast<char *>(
78 mIsMLaw
79 ? MEDIA_MIMETYPE_AUDIO_G711_MLAW
80 : MEDIA_MIMETYPE_AUDIO_G711_ALAW);
81
82 def.format.audio.pNativeRender = NULL;
83 def.format.audio.bFlagErrorConcealment = OMX_FALSE;
84 def.format.audio.eEncoding = OMX_AUDIO_CodingG711;
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 = kMaxNumSamplesPerFrame * sizeof(int16_t);
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/raw");
100 def.format.audio.pNativeRender = NULL;
101 def.format.audio.bFlagErrorConcealment = OMX_FALSE;
102 def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
103
104 addPort(def);
105 }
106
internalGetParameter(OMX_INDEXTYPE index,OMX_PTR params)107 OMX_ERRORTYPE SoftG711::internalGetParameter(
108 OMX_INDEXTYPE index, OMX_PTR params) {
109 switch (index) {
110 case OMX_IndexParamAudioPortFormat:
111 {
112 OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
113 (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
114
115 if (!isValidOMXParam(formatParams)) {
116 return OMX_ErrorBadParameter;
117 }
118
119 if (formatParams->nPortIndex > 1) {
120 return OMX_ErrorUndefined;
121 }
122
123 if (formatParams->nIndex > 0) {
124 return OMX_ErrorNoMore;
125 }
126
127 formatParams->eEncoding =
128 (formatParams->nPortIndex == 0)
129 ? OMX_AUDIO_CodingG711 : OMX_AUDIO_CodingPCM;
130
131 return OMX_ErrorNone;
132 }
133
134 case OMX_IndexParamAudioPcm:
135 {
136 OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
137 (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
138
139 if (!isValidOMXParam(pcmParams)) {
140 return OMX_ErrorBadParameter;
141 }
142
143 if (pcmParams->nPortIndex > 1) {
144 return OMX_ErrorUndefined;
145 }
146
147 pcmParams->eNumData = OMX_NumericalDataSigned;
148 pcmParams->eEndian = OMX_EndianBig;
149 pcmParams->bInterleaved = OMX_TRUE;
150 pcmParams->nBitPerSample = 16;
151 if (pcmParams->nPortIndex == 0) {
152 // input port
153 pcmParams->ePCMMode = mIsMLaw ? OMX_AUDIO_PCMModeMULaw
154 : OMX_AUDIO_PCMModeALaw;
155 } else {
156 // output port
157 pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
158 }
159 pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
160 pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
161
162 pcmParams->nChannels = mNumChannels;
163 pcmParams->nSamplingRate = mSamplingRate;
164
165 return OMX_ErrorNone;
166 }
167
168 default:
169 return SimpleSoftOMXComponent::internalGetParameter(index, params);
170 }
171 }
172
internalSetParameter(OMX_INDEXTYPE index,const OMX_PTR params)173 OMX_ERRORTYPE SoftG711::internalSetParameter(
174 OMX_INDEXTYPE index, const OMX_PTR params) {
175 switch (index) {
176 case OMX_IndexParamAudioPcm:
177 {
178 OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
179 (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
180
181 if (!isValidOMXParam(pcmParams)) {
182 return OMX_ErrorBadParameter;
183 }
184
185 if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) {
186 return OMX_ErrorUndefined;
187 }
188
189 if (pcmParams->nChannels < 1 || pcmParams->nChannels > MAX_CHANNEL_COUNT) {
190 return OMX_ErrorUndefined;
191 }
192
193 if(pcmParams->nPortIndex == 0) {
194 mNumChannels = pcmParams->nChannels;
195 }
196
197 mSamplingRate = pcmParams->nSamplingRate;
198
199 return OMX_ErrorNone;
200 }
201
202 case OMX_IndexParamAudioPortFormat:
203 {
204 const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
205 (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
206
207 if (!isValidOMXParam(formatParams)) {
208 return OMX_ErrorBadParameter;
209 }
210
211 if (formatParams->nPortIndex > 1) {
212 return OMX_ErrorUndefined;
213 }
214
215 if ((formatParams->nPortIndex == 0
216 && formatParams->eEncoding != OMX_AUDIO_CodingG711)
217 || (formatParams->nPortIndex == 1
218 && formatParams->eEncoding != OMX_AUDIO_CodingPCM)) {
219 return OMX_ErrorUndefined;
220 }
221
222 return OMX_ErrorNone;
223 }
224
225 case OMX_IndexParamStandardComponentRole:
226 {
227 const OMX_PARAM_COMPONENTROLETYPE *roleParams =
228 (const OMX_PARAM_COMPONENTROLETYPE *)params;
229
230 if (!isValidOMXParam(roleParams)) {
231 return OMX_ErrorBadParameter;
232 }
233
234 if (mIsMLaw) {
235 if (strncmp((const char *)roleParams->cRole,
236 "audio_decoder.g711mlaw",
237 OMX_MAX_STRINGNAME_SIZE - 1)) {
238 return OMX_ErrorUndefined;
239 }
240 } else {
241 if (strncmp((const char *)roleParams->cRole,
242 "audio_decoder.g711alaw",
243 OMX_MAX_STRINGNAME_SIZE - 1)) {
244 return OMX_ErrorUndefined;
245 }
246 }
247
248 return OMX_ErrorNone;
249 }
250
251 default:
252 return SimpleSoftOMXComponent::internalSetParameter(index, params);
253 }
254 }
255
onQueueFilled(OMX_U32)256 void SoftG711::onQueueFilled(OMX_U32 /* portIndex */) {
257 if (mSignalledError) {
258 return;
259 }
260
261 List<BufferInfo *> &inQueue = getPortQueue(0);
262 List<BufferInfo *> &outQueue = getPortQueue(1);
263
264 while (!inQueue.empty() && !outQueue.empty()) {
265 BufferInfo *inInfo = *inQueue.begin();
266 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
267
268 BufferInfo *outInfo = *outQueue.begin();
269 OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
270
271 if ((inHeader->nFlags & OMX_BUFFERFLAG_EOS) && inHeader->nFilledLen == 0) {
272 inQueue.erase(inQueue.begin());
273 inInfo->mOwnedByUs = false;
274 notifyEmptyBufferDone(inHeader);
275
276 outHeader->nFilledLen = 0;
277 outHeader->nFlags = OMX_BUFFERFLAG_EOS;
278
279 outQueue.erase(outQueue.begin());
280 outInfo->mOwnedByUs = false;
281 notifyFillBufferDone(outHeader);
282 return;
283 }
284
285 if (inHeader->nFilledLen > kMaxNumSamplesPerFrame) {
286 ALOGE("input buffer too large (%d).", inHeader->nFilledLen);
287
288 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
289 mSignalledError = true;
290 }
291
292 if (inHeader->nFilledLen * sizeof(int16_t) > outHeader->nAllocLen) {
293 ALOGE("output buffer too small (%d).", outHeader->nAllocLen);
294 android_errorWriteLog(0x534e4554, "27793163");
295
296 notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
297 mSignalledError = true;
298 return;
299 }
300
301 const uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset;
302
303 if (mIsMLaw) {
304 DecodeMLaw(
305 reinterpret_cast<int16_t *>(outHeader->pBuffer),
306 inputptr, inHeader->nFilledLen);
307 } else {
308 DecodeALaw(
309 reinterpret_cast<int16_t *>(outHeader->pBuffer),
310 inputptr, inHeader->nFilledLen);
311 }
312
313 outHeader->nTimeStamp = inHeader->nTimeStamp;
314 outHeader->nOffset = 0;
315 outHeader->nFilledLen = inHeader->nFilledLen * sizeof(int16_t);
316 outHeader->nFlags = 0;
317
318 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
319 inHeader->nFilledLen = 0;
320 } else {
321 inInfo->mOwnedByUs = false;
322 inQueue.erase(inQueue.begin());
323 inInfo = NULL;
324 notifyEmptyBufferDone(inHeader);
325 inHeader = NULL;
326 }
327
328 outInfo->mOwnedByUs = false;
329 outQueue.erase(outQueue.begin());
330 outInfo = NULL;
331 notifyFillBufferDone(outHeader);
332 outHeader = NULL;
333 }
334 }
335
336 // static
DecodeALaw(int16_t * out,const uint8_t * in,size_t inSize)337 void SoftG711::DecodeALaw(
338 int16_t *out, const uint8_t *in, size_t inSize) {
339 while (inSize > 0) {
340 inSize--;
341 int32_t x = *in++;
342
343 int32_t ix = x ^ 0x55;
344 ix &= 0x7f;
345
346 int32_t iexp = ix >> 4;
347 int32_t mant = ix & 0x0f;
348
349 if (iexp > 0) {
350 mant += 16;
351 }
352
353 mant = (mant << 4) + 8;
354
355 if (iexp > 1) {
356 mant = mant << (iexp - 1);
357 }
358
359 *out++ = (x > 127) ? mant : -mant;
360 }
361 }
362
363 // static
DecodeMLaw(int16_t * out,const uint8_t * in,size_t inSize)364 void SoftG711::DecodeMLaw(
365 int16_t *out, const uint8_t *in, size_t inSize) {
366 while (inSize > 0) {
367 inSize--;
368 int32_t x = *in++;
369
370 int32_t mantissa = ~x;
371 int32_t exponent = (mantissa >> 4) & 7;
372 int32_t segment = exponent + 1;
373 mantissa &= 0x0f;
374
375 int32_t step = 4 << segment;
376
377 int32_t abs = (0x80L << exponent) + step * mantissa + step / 2 - 4 * 33;
378
379 *out++ = (x < 0x80) ? -abs : abs;
380 }
381 }
382
383 } // namespace android
384
385 __attribute__((cfi_canonical_jump_table))
createSoftOMXComponent(const char * name,const OMX_CALLBACKTYPE * callbacks,OMX_PTR appData,OMX_COMPONENTTYPE ** component)386 android::SoftOMXComponent *createSoftOMXComponent(
387 const char *name, const OMX_CALLBACKTYPE *callbacks,
388 OMX_PTR appData, OMX_COMPONENTTYPE **component) {
389 return new android::SoftG711(name, callbacks, appData, component);
390 }
391
392