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> 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 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 60 SoftAMRWBEncoder::~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 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 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 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 (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 221 OMX_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 320 void 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 455 android::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 } 460