SoftAMRNBEncoder.cpp revision 3d34fa7fe4df6fb66215303f721cc87ce4619cdc
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 (formatParams->nPortIndex > 1) { 124 return OMX_ErrorUndefined; 125 } 126 127 if (formatParams->nIndex > 0) { 128 return OMX_ErrorNoMore; 129 } 130 131 formatParams->eEncoding = 132 (formatParams->nPortIndex == 0) 133 ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingAMR; 134 135 return OMX_ErrorNone; 136 } 137 138 case OMX_IndexParamAudioAmr: 139 { 140 OMX_AUDIO_PARAM_AMRTYPE *amrParams = 141 (OMX_AUDIO_PARAM_AMRTYPE *)params; 142 143 if (amrParams->nPortIndex != 1) { 144 return OMX_ErrorUndefined; 145 } 146 147 amrParams->nChannels = 1; 148 amrParams->nBitRate = mBitRate; 149 amrParams->eAMRBandMode = (OMX_AUDIO_AMRBANDMODETYPE)(mMode + 1); 150 amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff; 151 amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; 152 153 return OMX_ErrorNone; 154 } 155 156 case OMX_IndexParamAudioPcm: 157 { 158 OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = 159 (OMX_AUDIO_PARAM_PCMMODETYPE *)params; 160 161 if (pcmParams->nPortIndex != 0) { 162 return OMX_ErrorUndefined; 163 } 164 165 pcmParams->eNumData = OMX_NumericalDataSigned; 166 pcmParams->eEndian = OMX_EndianBig; 167 pcmParams->bInterleaved = OMX_TRUE; 168 pcmParams->nBitPerSample = 16; 169 pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear; 170 pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelCF; 171 172 pcmParams->nChannels = 1; 173 pcmParams->nSamplingRate = kSampleRate; 174 175 return OMX_ErrorNone; 176 } 177 178 default: 179 return SimpleSoftOMXComponent::internalGetParameter(index, params); 180 } 181} 182 183OMX_ERRORTYPE SoftAMRNBEncoder::internalSetParameter( 184 OMX_INDEXTYPE index, const OMX_PTR params) { 185 switch (index) { 186 case OMX_IndexParamStandardComponentRole: 187 { 188 const OMX_PARAM_COMPONENTROLETYPE *roleParams = 189 (const OMX_PARAM_COMPONENTROLETYPE *)params; 190 191 if (strncmp((const char *)roleParams->cRole, 192 "audio_encoder.amrnb", 193 OMX_MAX_STRINGNAME_SIZE - 1)) { 194 return OMX_ErrorUndefined; 195 } 196 197 return OMX_ErrorNone; 198 } 199 200 case OMX_IndexParamAudioPortFormat: 201 { 202 const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams = 203 (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params; 204 205 if (formatParams->nPortIndex > 1) { 206 return OMX_ErrorUndefined; 207 } 208 209 if (formatParams->nIndex > 0) { 210 return OMX_ErrorNoMore; 211 } 212 213 if ((formatParams->nPortIndex == 0 214 && formatParams->eEncoding != OMX_AUDIO_CodingPCM) 215 || (formatParams->nPortIndex == 1 216 && formatParams->eEncoding != OMX_AUDIO_CodingAMR)) { 217 return OMX_ErrorUndefined; 218 } 219 220 return OMX_ErrorNone; 221 } 222 223 case OMX_IndexParamAudioAmr: 224 { 225 OMX_AUDIO_PARAM_AMRTYPE *amrParams = 226 (OMX_AUDIO_PARAM_AMRTYPE *)params; 227 228 if (amrParams->nPortIndex != 1) { 229 return OMX_ErrorUndefined; 230 } 231 232 if (amrParams->nChannels != 1 233 || amrParams->eAMRDTXMode != OMX_AUDIO_AMRDTXModeOff 234 || amrParams->eAMRFrameFormat 235 != OMX_AUDIO_AMRFrameFormatFSF 236 || amrParams->eAMRBandMode < OMX_AUDIO_AMRBandModeNB0 237 || amrParams->eAMRBandMode > OMX_AUDIO_AMRBandModeNB7) { 238 return OMX_ErrorUndefined; 239 } 240 241 mBitRate = amrParams->nBitRate; 242 mMode = amrParams->eAMRBandMode - 1; 243 244 amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff; 245 amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; 246 247 return OMX_ErrorNone; 248 } 249 250 case OMX_IndexParamAudioPcm: 251 { 252 OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = 253 (OMX_AUDIO_PARAM_PCMMODETYPE *)params; 254 255 if (pcmParams->nPortIndex != 0) { 256 return OMX_ErrorUndefined; 257 } 258 259 if (pcmParams->nChannels != 1 260 || pcmParams->nSamplingRate != (OMX_U32)kSampleRate) { 261 return OMX_ErrorUndefined; 262 } 263 264 return OMX_ErrorNone; 265 } 266 267 268 default: 269 return SimpleSoftOMXComponent::internalSetParameter(index, params); 270 } 271} 272 273void SoftAMRNBEncoder::onQueueFilled(OMX_U32 portIndex) { 274 if (mSignalledError) { 275 return; 276 } 277 278 List<BufferInfo *> &inQueue = getPortQueue(0); 279 List<BufferInfo *> &outQueue = getPortQueue(1); 280 281 size_t numBytesPerInputFrame = kNumSamplesPerFrame * sizeof(int16_t); 282 283 for (;;) { 284 // We do the following until we run out of buffers. 285 286 while (mInputSize < numBytesPerInputFrame) { 287 // As long as there's still input data to be read we 288 // will drain "kNumSamplesPerFrame" samples 289 // into the "mInputFrame" buffer and then encode those 290 // as a unit into an output buffer. 291 292 if (mSawInputEOS || inQueue.empty()) { 293 return; 294 } 295 296 BufferInfo *inInfo = *inQueue.begin(); 297 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; 298 299 const void *inData = inHeader->pBuffer + inHeader->nOffset; 300 301 size_t copy = numBytesPerInputFrame - mInputSize; 302 if (copy > inHeader->nFilledLen) { 303 copy = inHeader->nFilledLen; 304 } 305 306 if (mInputSize == 0) { 307 mInputTimeUs = inHeader->nTimeStamp; 308 } 309 310 memcpy((uint8_t *)mInputFrame + mInputSize, inData, copy); 311 mInputSize += copy; 312 313 inHeader->nOffset += copy; 314 inHeader->nFilledLen -= copy; 315 316 // "Time" on the input buffer has in effect advanced by the 317 // number of audio frames we just advanced nOffset by. 318 inHeader->nTimeStamp += 319 (copy * 1000000ll / kSampleRate) / sizeof(int16_t); 320 321 if (inHeader->nFilledLen == 0) { 322 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { 323 ALOGV("saw input EOS"); 324 mSawInputEOS = true; 325 326 // Pad any remaining data with zeroes. 327 memset((uint8_t *)mInputFrame + mInputSize, 328 0, 329 numBytesPerInputFrame - mInputSize); 330 331 mInputSize = numBytesPerInputFrame; 332 } 333 334 inQueue.erase(inQueue.begin()); 335 inInfo->mOwnedByUs = false; 336 notifyEmptyBufferDone(inHeader); 337 338 inData = NULL; 339 inHeader = NULL; 340 inInfo = NULL; 341 } 342 } 343 344 // At this point we have all the input data necessary to encode 345 // a single frame, all we need is an output buffer to store the result 346 // in. 347 348 if (outQueue.empty()) { 349 return; 350 } 351 352 BufferInfo *outInfo = *outQueue.begin(); 353 OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; 354 355 uint8_t *outPtr = outHeader->pBuffer + outHeader->nOffset; 356 size_t outAvailable = outHeader->nAllocLen - outHeader->nOffset; 357 358 Frame_Type_3GPP frameType; 359 int res = AMREncode( 360 mEncState, mSidState, (Mode)mMode, 361 mInputFrame, outPtr, &frameType, AMR_TX_WMF); 362 363 CHECK_GE(res, 0); 364 CHECK_LE((size_t)res, outAvailable); 365 366 // Convert header byte from WMF to IETF format. 367 outPtr[0] = ((outPtr[0] << 3) | 4) & 0x7c; 368 369 outHeader->nFilledLen = res; 370 outHeader->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; 371 372 if (mSawInputEOS) { 373 // We also tag this output buffer with EOS if it corresponds 374 // to the final input buffer. 375 outHeader->nFlags = OMX_BUFFERFLAG_EOS; 376 } 377 378 outHeader->nTimeStamp = mInputTimeUs; 379 380#if 0 381 ALOGI("sending %d bytes of data (time = %lld us, flags = 0x%08lx)", 382 nOutputBytes, mInputTimeUs, outHeader->nFlags); 383 384 hexdump(outHeader->pBuffer + outHeader->nOffset, outHeader->nFilledLen); 385#endif 386 387 outQueue.erase(outQueue.begin()); 388 outInfo->mOwnedByUs = false; 389 notifyFillBufferDone(outHeader); 390 391 outHeader = NULL; 392 outInfo = NULL; 393 394 mInputSize = 0; 395 } 396} 397 398} // namespace android 399 400android::SoftOMXComponent *createSoftOMXComponent( 401 const char *name, const OMX_CALLBACKTYPE *callbacks, 402 OMX_PTR appData, OMX_COMPONENTTYPE **component) { 403 return new android::SoftAMRNBEncoder(name, callbacks, appData, component); 404} 405