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