SoftMPEG4Encoder.cpp revision 3e08ca6709b5a75dc3b779cf3eba8196b08a0245
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 "SoftMPEG4Encoder" 19#include <utils/Log.h> 20#include <utils/misc.h> 21 22#include "mp4enc_api.h" 23#include "OMX_Video.h" 24 25#include <HardwareAPI.h> 26#include <MetadataBufferType.h> 27#include <media/stagefright/foundation/ADebug.h> 28#include <media/stagefright/foundation/AUtils.h> 29#include <media/stagefright/MediaDefs.h> 30#include <media/stagefright/MediaErrors.h> 31#include <media/stagefright/MetaData.h> 32#include <media/stagefright/Utils.h> 33#include <ui/Rect.h> 34#include <ui/GraphicBufferMapper.h> 35 36#include "SoftMPEG4Encoder.h" 37 38#include <inttypes.h> 39 40namespace android { 41 42template<class T> 43static void InitOMXParams(T *params) { 44 params->nSize = sizeof(T); 45 params->nVersion.s.nVersionMajor = 1; 46 params->nVersion.s.nVersionMinor = 0; 47 params->nVersion.s.nRevision = 0; 48 params->nVersion.s.nStep = 0; 49} 50 51static const CodecProfileLevel kMPEG4ProfileLevels[] = { 52 { OMX_VIDEO_MPEG4ProfileCore, OMX_VIDEO_MPEG4Level2 }, 53}; 54 55static const CodecProfileLevel kH263ProfileLevels[] = { 56 { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level45 }, 57}; 58 59SoftMPEG4Encoder::SoftMPEG4Encoder( 60 const char *name, 61 const char *componentRole, 62 OMX_VIDEO_CODINGTYPE codingType, 63 const char *mime, 64 const CodecProfileLevel *profileLevels, 65 size_t numProfileLevels, 66 const OMX_CALLBACKTYPE *callbacks, 67 OMX_PTR appData, 68 OMX_COMPONENTTYPE **component) 69 : SoftVideoEncoderOMXComponent( 70 name, componentRole, codingType, 71 profileLevels, numProfileLevels, 72 176 /* width */, 144 /* height */, 73 callbacks, appData, component), 74 mEncodeMode(COMBINE_MODE_WITH_ERR_RES), 75 mIDRFrameRefreshIntervalInSec(1), 76 mNumInputFrames(-1), 77 mStarted(false), 78 mSawInputEOS(false), 79 mSignalledError(false), 80 mHandle(new tagvideoEncControls), 81 mEncParams(new tagvideoEncOptions), 82 mInputFrameData(NULL) { 83 84 if (codingType == OMX_VIDEO_CodingH263) { 85 mEncodeMode = H263_MODE; 86 } 87 88 // 256 * 1024 is a magic number for PV's encoder, not sure why 89 const size_t kOutputBufferSize = 256 * 1024; 90 91 initPorts(kNumBuffers, kNumBuffers, kOutputBufferSize, mime); 92 93 ALOGI("Construct SoftMPEG4Encoder"); 94} 95 96SoftMPEG4Encoder::~SoftMPEG4Encoder() { 97 ALOGV("Destruct SoftMPEG4Encoder"); 98 releaseEncoder(); 99 List<BufferInfo *> &outQueue = getPortQueue(1); 100 List<BufferInfo *> &inQueue = getPortQueue(0); 101 CHECK(outQueue.empty()); 102 CHECK(inQueue.empty()); 103} 104 105OMX_ERRORTYPE SoftMPEG4Encoder::initEncParams() { 106 CHECK(mHandle != NULL); 107 memset(mHandle, 0, sizeof(tagvideoEncControls)); 108 109 CHECK(mEncParams != NULL); 110 memset(mEncParams, 0, sizeof(tagvideoEncOptions)); 111 if (!PVGetDefaultEncOption(mEncParams, 0)) { 112 ALOGE("Failed to get default encoding parameters"); 113 return OMX_ErrorUndefined; 114 } 115 mEncParams->encMode = mEncodeMode; 116 mEncParams->encWidth[0] = mWidth; 117 mEncParams->encHeight[0] = mHeight; 118 mEncParams->encFrameRate[0] = mFramerate >> 16; // mFramerate is in Q16 format 119 mEncParams->rcType = VBR_1; 120 mEncParams->vbvDelay = 5.0f; 121 122 // FIXME: 123 // Add more profile and level support for MPEG4 encoder 124 mEncParams->profile_level = CORE_PROFILE_LEVEL2; 125 mEncParams->packetSize = 32; 126 mEncParams->rvlcEnable = PV_OFF; 127 mEncParams->numLayers = 1; 128 mEncParams->timeIncRes = 1000; 129 mEncParams->tickPerSrc = ((int64_t)mEncParams->timeIncRes << 16) / mFramerate; 130 131 mEncParams->bitRate[0] = mBitrate; 132 mEncParams->iQuant[0] = 15; 133 mEncParams->pQuant[0] = 12; 134 mEncParams->quantType[0] = 0; 135 mEncParams->noFrameSkipped = PV_OFF; 136 137 if (mColorFormat != OMX_COLOR_FormatYUV420Planar || mInputDataIsMeta) { 138 // Color conversion is needed. 139 free(mInputFrameData); 140 mInputFrameData = 141 (uint8_t *) malloc((mWidth * mHeight * 3 ) >> 1); 142 CHECK(mInputFrameData != NULL); 143 } 144 145 // PV's MPEG4 encoder requires the video dimension of multiple 146 if (mWidth % 16 != 0 || mHeight % 16 != 0) { 147 ALOGE("Video frame size %dx%d must be a multiple of 16", 148 mWidth, mHeight); 149 return OMX_ErrorBadParameter; 150 } 151 152 // Set IDR frame refresh interval 153 if (mIDRFrameRefreshIntervalInSec < 0) { 154 mEncParams->intraPeriod = -1; 155 } else if (mIDRFrameRefreshIntervalInSec == 0) { 156 mEncParams->intraPeriod = 1; // All I frames 157 } else { 158 mEncParams->intraPeriod = 159 (mIDRFrameRefreshIntervalInSec * mFramerate) >> 16; 160 } 161 162 mEncParams->numIntraMB = 0; 163 mEncParams->sceneDetect = PV_ON; 164 mEncParams->searchRange = 16; 165 mEncParams->mv8x8Enable = PV_OFF; 166 mEncParams->gobHeaderInterval = 0; 167 mEncParams->useACPred = PV_ON; 168 mEncParams->intraDCVlcTh = 0; 169 170 return OMX_ErrorNone; 171} 172 173OMX_ERRORTYPE SoftMPEG4Encoder::initEncoder() { 174 CHECK(!mStarted); 175 176 OMX_ERRORTYPE errType = OMX_ErrorNone; 177 if (OMX_ErrorNone != (errType = initEncParams())) { 178 ALOGE("Failed to initialized encoder params"); 179 mSignalledError = true; 180 notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); 181 return errType; 182 } 183 184 if (!PVInitVideoEncoder(mHandle, mEncParams)) { 185 ALOGE("Failed to initialize the encoder"); 186 mSignalledError = true; 187 notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); 188 return OMX_ErrorUndefined; 189 } 190 191 mNumInputFrames = -1; // 1st buffer for codec specific data 192 mStarted = true; 193 194 return OMX_ErrorNone; 195} 196 197OMX_ERRORTYPE SoftMPEG4Encoder::releaseEncoder() { 198 if (!mStarted) { 199 return OMX_ErrorNone; 200 } 201 202 PVCleanUpVideoEncoder(mHandle); 203 204 free(mInputFrameData); 205 mInputFrameData = NULL; 206 207 delete mEncParams; 208 mEncParams = NULL; 209 210 delete mHandle; 211 mHandle = NULL; 212 213 mStarted = false; 214 215 return OMX_ErrorNone; 216} 217 218OMX_ERRORTYPE SoftMPEG4Encoder::internalGetParameter( 219 OMX_INDEXTYPE index, OMX_PTR params) { 220 switch (index) { 221 case OMX_IndexParamVideoBitrate: 222 { 223 OMX_VIDEO_PARAM_BITRATETYPE *bitRate = 224 (OMX_VIDEO_PARAM_BITRATETYPE *) params; 225 226 if (bitRate->nPortIndex != 1) { 227 return OMX_ErrorUndefined; 228 } 229 230 bitRate->eControlRate = OMX_Video_ControlRateVariable; 231 bitRate->nTargetBitrate = mBitrate; 232 return OMX_ErrorNone; 233 } 234 235 case OMX_IndexParamVideoH263: 236 { 237 OMX_VIDEO_PARAM_H263TYPE *h263type = 238 (OMX_VIDEO_PARAM_H263TYPE *)params; 239 240 if (h263type->nPortIndex != 1) { 241 return OMX_ErrorUndefined; 242 } 243 244 h263type->nAllowedPictureTypes = 245 (OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP); 246 h263type->eProfile = OMX_VIDEO_H263ProfileBaseline; 247 h263type->eLevel = OMX_VIDEO_H263Level45; 248 h263type->bPLUSPTYPEAllowed = OMX_FALSE; 249 h263type->bForceRoundingTypeToZero = OMX_FALSE; 250 h263type->nPictureHeaderRepetition = 0; 251 h263type->nGOBHeaderInterval = 0; 252 253 return OMX_ErrorNone; 254 } 255 256 case OMX_IndexParamVideoMpeg4: 257 { 258 OMX_VIDEO_PARAM_MPEG4TYPE *mpeg4type = 259 (OMX_VIDEO_PARAM_MPEG4TYPE *)params; 260 261 if (mpeg4type->nPortIndex != 1) { 262 return OMX_ErrorUndefined; 263 } 264 265 mpeg4type->eProfile = OMX_VIDEO_MPEG4ProfileCore; 266 mpeg4type->eLevel = OMX_VIDEO_MPEG4Level2; 267 mpeg4type->nAllowedPictureTypes = 268 (OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP); 269 mpeg4type->nBFrames = 0; 270 mpeg4type->nIDCVLCThreshold = 0; 271 mpeg4type->bACPred = OMX_TRUE; 272 mpeg4type->nMaxPacketSize = 256; 273 mpeg4type->nTimeIncRes = 1000; 274 mpeg4type->nHeaderExtension = 0; 275 mpeg4type->bReversibleVLC = OMX_FALSE; 276 277 return OMX_ErrorNone; 278 } 279 280 default: 281 return SoftVideoEncoderOMXComponent::internalGetParameter(index, params); 282 } 283} 284 285OMX_ERRORTYPE SoftMPEG4Encoder::internalSetParameter( 286 OMX_INDEXTYPE index, const OMX_PTR params) { 287 int32_t indexFull = index; 288 289 switch (indexFull) { 290 case OMX_IndexParamVideoBitrate: 291 { 292 OMX_VIDEO_PARAM_BITRATETYPE *bitRate = 293 (OMX_VIDEO_PARAM_BITRATETYPE *) params; 294 295 if (bitRate->nPortIndex != 1 || 296 bitRate->eControlRate != OMX_Video_ControlRateVariable) { 297 return OMX_ErrorUndefined; 298 } 299 300 mBitrate = bitRate->nTargetBitrate; 301 return OMX_ErrorNone; 302 } 303 304 case OMX_IndexParamVideoH263: 305 { 306 OMX_VIDEO_PARAM_H263TYPE *h263type = 307 (OMX_VIDEO_PARAM_H263TYPE *)params; 308 309 if (h263type->nPortIndex != 1) { 310 return OMX_ErrorUndefined; 311 } 312 313 if (h263type->eProfile != OMX_VIDEO_H263ProfileBaseline || 314 h263type->eLevel != OMX_VIDEO_H263Level45 || 315 (h263type->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) || 316 h263type->bPLUSPTYPEAllowed != OMX_FALSE || 317 h263type->bForceRoundingTypeToZero != OMX_FALSE || 318 h263type->nPictureHeaderRepetition != 0 || 319 h263type->nGOBHeaderInterval != 0) { 320 return OMX_ErrorUndefined; 321 } 322 323 return OMX_ErrorNone; 324 } 325 326 case OMX_IndexParamVideoMpeg4: 327 { 328 OMX_VIDEO_PARAM_MPEG4TYPE *mpeg4type = 329 (OMX_VIDEO_PARAM_MPEG4TYPE *)params; 330 331 if (mpeg4type->nPortIndex != 1) { 332 return OMX_ErrorUndefined; 333 } 334 335 if (mpeg4type->eProfile != OMX_VIDEO_MPEG4ProfileCore || 336 mpeg4type->eLevel != OMX_VIDEO_MPEG4Level2 || 337 (mpeg4type->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) || 338 mpeg4type->nBFrames != 0 || 339 mpeg4type->nIDCVLCThreshold != 0 || 340 mpeg4type->bACPred != OMX_TRUE || 341 mpeg4type->nMaxPacketSize != 256 || 342 mpeg4type->nTimeIncRes != 1000 || 343 mpeg4type->nHeaderExtension != 0 || 344 mpeg4type->bReversibleVLC != OMX_FALSE) { 345 return OMX_ErrorUndefined; 346 } 347 348 return OMX_ErrorNone; 349 } 350 351 default: 352 return SoftVideoEncoderOMXComponent::internalSetParameter(index, params); 353 } 354} 355 356void SoftMPEG4Encoder::onQueueFilled(OMX_U32 /* portIndex */) { 357 if (mSignalledError || mSawInputEOS) { 358 return; 359 } 360 361 if (!mStarted) { 362 if (OMX_ErrorNone != initEncoder()) { 363 return; 364 } 365 } 366 367 List<BufferInfo *> &inQueue = getPortQueue(0); 368 List<BufferInfo *> &outQueue = getPortQueue(1); 369 370 while (!mSawInputEOS && !inQueue.empty() && !outQueue.empty()) { 371 BufferInfo *inInfo = *inQueue.begin(); 372 OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; 373 BufferInfo *outInfo = *outQueue.begin(); 374 OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; 375 376 outHeader->nTimeStamp = 0; 377 outHeader->nFlags = 0; 378 outHeader->nOffset = 0; 379 outHeader->nFilledLen = 0; 380 outHeader->nOffset = 0; 381 382 uint8_t *outPtr = (uint8_t *) outHeader->pBuffer; 383 int32_t dataLength = outHeader->nAllocLen; 384 385 if (mNumInputFrames < 0) { 386 if (!PVGetVolHeader(mHandle, outPtr, &dataLength, 0)) { 387 ALOGE("Failed to get VOL header"); 388 mSignalledError = true; 389 notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); 390 return; 391 } 392 ALOGV("Output VOL header: %d bytes", dataLength); 393 ++mNumInputFrames; 394 outHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG; 395 outHeader->nFilledLen = dataLength; 396 outQueue.erase(outQueue.begin()); 397 outInfo->mOwnedByUs = false; 398 notifyFillBufferDone(outHeader); 399 return; 400 } 401 402 // Save the input buffer info so that it can be 403 // passed to an output buffer 404 InputBufferInfo info; 405 info.mTimeUs = inHeader->nTimeStamp; 406 info.mFlags = inHeader->nFlags; 407 mInputBufferInfoVec.push(info); 408 409 if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { 410 mSawInputEOS = true; 411 } 412 413 if (inHeader->nFilledLen > 0) { 414 const uint8_t *inputData = NULL; 415 if (mInputDataIsMeta) { 416 inputData = 417 extractGraphicBuffer( 418 mInputFrameData, (mWidth * mHeight * 3) >> 1, 419 inHeader->pBuffer + inHeader->nOffset, inHeader->nFilledLen, 420 mWidth, mHeight); 421 if (inputData == NULL) { 422 ALOGE("Unable to extract gralloc buffer in metadata mode"); 423 mSignalledError = true; 424 notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); 425 return; 426 } 427 } else { 428 inputData = (const uint8_t *)inHeader->pBuffer + inHeader->nOffset; 429 if (mColorFormat != OMX_COLOR_FormatYUV420Planar) { 430 ConvertYUV420SemiPlanarToYUV420Planar( 431 inputData, mInputFrameData, mWidth, mHeight); 432 inputData = mInputFrameData; 433 } 434 } 435 436 CHECK(inputData != NULL); 437 438 VideoEncFrameIO vin, vout; 439 memset(&vin, 0, sizeof(vin)); 440 memset(&vout, 0, sizeof(vout)); 441 vin.height = align(mHeight, 16); 442 vin.pitch = align(mWidth, 16); 443 vin.timestamp = (inHeader->nTimeStamp + 500) / 1000; // in ms 444 vin.yChan = (uint8_t *)inputData; 445 vin.uChan = vin.yChan + vin.height * vin.pitch; 446 vin.vChan = vin.uChan + ((vin.height * vin.pitch) >> 2); 447 448 ULong modTimeMs = 0; 449 int32_t nLayer = 0; 450 MP4HintTrack hintTrack; 451 if (!PVEncodeVideoFrame(mHandle, &vin, &vout, 452 &modTimeMs, outPtr, &dataLength, &nLayer) || 453 !PVGetHintTrack(mHandle, &hintTrack)) { 454 ALOGE("Failed to encode frame or get hink track at frame %" PRId64, 455 mNumInputFrames); 456 mSignalledError = true; 457 notify(OMX_EventError, OMX_ErrorUndefined, 0, 0); 458 } 459 CHECK(NULL == PVGetOverrunBuffer(mHandle)); 460 if (hintTrack.CodeType == 0) { // I-frame serves as sync frame 461 outHeader->nFlags |= OMX_BUFFERFLAG_SYNCFRAME; 462 } 463 464 ++mNumInputFrames; 465 } else { 466 dataLength = 0; 467 } 468 469 inQueue.erase(inQueue.begin()); 470 inInfo->mOwnedByUs = false; 471 notifyEmptyBufferDone(inHeader); 472 473 outQueue.erase(outQueue.begin()); 474 CHECK(!mInputBufferInfoVec.empty()); 475 InputBufferInfo *inputBufInfo = mInputBufferInfoVec.begin(); 476 outHeader->nTimeStamp = inputBufInfo->mTimeUs; 477 outHeader->nFlags |= (inputBufInfo->mFlags | OMX_BUFFERFLAG_ENDOFFRAME); 478 outHeader->nFilledLen = dataLength; 479 mInputBufferInfoVec.erase(mInputBufferInfoVec.begin()); 480 outInfo->mOwnedByUs = false; 481 notifyFillBufferDone(outHeader); 482 } 483} 484 485} // namespace android 486 487android::SoftOMXComponent *createSoftOMXComponent( 488 const char *name, const OMX_CALLBACKTYPE *callbacks, 489 OMX_PTR appData, OMX_COMPONENTTYPE **component) { 490 using namespace android; 491 if (!strcmp(name, "OMX.google.h263.encoder")) { 492 return new android::SoftMPEG4Encoder( 493 name, "video_encoder.h263", OMX_VIDEO_CodingH263, MEDIA_MIMETYPE_VIDEO_H263, 494 kH263ProfileLevels, NELEM(kH263ProfileLevels), 495 callbacks, appData, component); 496 } else if (!strcmp(name, "OMX.google.mpeg4.encoder")) { 497 return new android::SoftMPEG4Encoder( 498 name, "video_encoder.mpeg4", OMX_VIDEO_CodingMPEG4, MEDIA_MIMETYPE_VIDEO_MPEG4, 499 kMPEG4ProfileLevels, NELEM(kMPEG4ProfileLevels), 500 callbacks, appData, component); 501 } else { 502 CHECK(!"Unknown component"); 503 } 504 return NULL; 505} 506