Home | History | Annotate | Download | only in videoencoder
      1 /*
      2 * Copyright (c) 2009-2011 Intel Corporation.  All rights reserved.
      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 "PVSoftMPEG4Encoder"
     19 #include <wrs_omxil_core/log.h>
     20 
     21 #include "mp4enc_api.h"
     22 #include "OMX_Video.h"
     23 
     24 #include <media/stagefright/foundation/ADebug.h>
     25 #include <media/stagefright/MediaDefs.h>
     26 #include <media/stagefright/MediaErrors.h>
     27 #include <media/stagefright/MetaData.h>
     28 #include <media/stagefright/Utils.h>
     29 
     30 #include <ui/GraphicBufferMapper.h>
     31 #include <ui/Rect.h>
     32 
     33 #include "PVSoftMPEG4Encoder.h"
     34 #include "VideoEncoderLog.h"
     35 
     36 #define ALIGN(x, align)                  (((x) + (align) - 1) & (~((align) - 1)))
     37 
     38 inline static void ConvertYUV420SemiPlanarToYUV420Planar(
     39         uint8_t *inyuv, uint8_t* outyuv,
     40         int32_t width, int32_t height) {
     41 
     42     int32_t outYsize = width * height;
     43     uint32_t *outy =  (uint32_t *) outyuv;
     44     uint16_t *outcb = (uint16_t *) (outyuv + outYsize);
     45     uint16_t *outcr = (uint16_t *) (outyuv + outYsize + (outYsize >> 2));
     46 
     47     /* Y copying */
     48     memcpy(outy, inyuv, outYsize);
     49 
     50     /* U & V copying */
     51     uint32_t *inyuv_4 = (uint32_t *) (inyuv + outYsize);
     52     for (int32_t i = height >> 1; i > 0; --i) {
     53         for (int32_t j = width >> 2; j > 0; --j) {
     54             uint32_t temp = *inyuv_4++;
     55             uint32_t tempU = temp & 0xFF;
     56             tempU = tempU | ((temp >> 8) & 0xFF00);
     57 
     58             uint32_t tempV = (temp >> 8) & 0xFF;
     59             tempV = tempV | ((temp >> 16) & 0xFF00);
     60 
     61             // Flip U and V
     62             *outcb++ = tempU;
     63             *outcr++ = tempV;
     64         }
     65     }
     66 }
     67 
     68 inline static void trimBuffer(uint8_t *dataIn, uint8_t *dataOut,
     69         int32_t width, int32_t height,
     70         int32_t alignedHeight, int32_t stride) {
     71     int32_t h;
     72     uint8_t *y_start, *uv_start, *_y_start, *_uv_start;
     73     y_start = dataOut;
     74     uv_start = dataOut + width * height;
     75     _y_start = dataIn;
     76     _uv_start = dataIn + stride * alignedHeight;
     77 
     78     for (h = 0; h < height; h++)
     79         memcpy(y_start + h * width, _y_start + h * stride, width);
     80     for (h = 0; h < height / 2; h++)
     81         memcpy(uv_start + h * width,
     82                 _uv_start + h * stride, width);
     83 }
     84 
     85 PVSoftMPEG4Encoder::PVSoftMPEG4Encoder(const char *name)
     86     :  mEncodeMode(COMBINE_MODE_WITH_ERR_RES),
     87       mVideoWidth(176),
     88       mVideoHeight(144),
     89       mVideoFrameRate(30),
     90       mVideoBitRate(192000),
     91       mVideoColorFormat(OMX_COLOR_FormatYUV420SemiPlanar),
     92       mStoreMetaDataInBuffers(false),
     93       mIDRFrameRefreshIntervalInSec(1),
     94       mNumInputFrames(-1),
     95       mStarted(false),
     96       mSawInputEOS(false),
     97       mSignalledError(false),
     98       mHandle(new tagvideoEncControls),
     99       mEncParams(new tagvideoEncOptions),
    100       mInputFrameData(NULL)
    101 {
    102 
    103     if (!strcmp(name, "OMX.google.h263.encoder")) {
    104         mEncodeMode = H263_MODE;
    105         LOG_I("construct h263 encoder");
    106     } else {
    107         CHECK(!strcmp(name, "OMX.google.mpeg4.encoder"));
    108         LOG_I("construct mpeg4 encoder");
    109     }
    110 
    111     setDefaultParams();
    112 #if NO_BUFFER_SHARE
    113     mVASurfaceMappingAction |= MAPACT_COPY;
    114 #endif
    115 
    116     LOG_I("Construct PVSoftMPEG4Encoder");
    117 
    118 }
    119 
    120 PVSoftMPEG4Encoder::~PVSoftMPEG4Encoder() {
    121     LOG_I("Destruct PVSoftMPEG4Encoder");
    122     releaseEncoder();
    123 
    124 }
    125 
    126 void PVSoftMPEG4Encoder::setDefaultParams() {
    127 
    128     // Set default value for input parameters
    129     mComParams.profile = VAProfileH264Baseline;
    130     mComParams.level = 41;
    131     mComParams.rawFormat = RAW_FORMAT_NV12;
    132     mComParams.frameRate.frameRateNum = 30;
    133     mComParams.frameRate.frameRateDenom = 1;
    134     mComParams.resolution.width = 0;
    135     mComParams.resolution.height = 0;
    136     mComParams.intraPeriod = 30;
    137     mComParams.rcMode = RATE_CONTROL_NONE;
    138     mComParams.rcParams.initQP = 15;
    139     mComParams.rcParams.minQP = 0;
    140     mComParams.rcParams.bitRate = 640000;
    141     mComParams.rcParams.targetPercentage= 0;
    142     mComParams.rcParams.windowSize = 0;
    143     mComParams.rcParams.disableFrameSkip = 0;
    144     mComParams.rcParams.disableBitsStuffing = 1;
    145     mComParams.cyclicFrameInterval = 30;
    146     mComParams.refreshType = VIDEO_ENC_NONIR;
    147     mComParams.airParams.airMBs = 0;
    148     mComParams.airParams.airThreshold = 0;
    149     mComParams.airParams.airAuto = 1;
    150     mComParams.disableDeblocking = 2;
    151     mComParams.syncEncMode = false;
    152     mComParams.codedBufNum = 2;
    153 
    154 }
    155 
    156 Encode_Status PVSoftMPEG4Encoder::initEncParams() {
    157     CHECK(mHandle != NULL);
    158     memset(mHandle, 0, sizeof(tagvideoEncControls));
    159 
    160     CHECK(mEncParams != NULL);
    161     memset(mEncParams, 0, sizeof(tagvideoEncOptions));
    162     if (!PVGetDefaultEncOption(mEncParams, 0)) {
    163         LOG_E("Failed to get default encoding parameters");
    164         return ENCODE_FAIL;
    165     }
    166     mEncParams->encMode = mEncodeMode;
    167     mEncParams->encWidth[0] = mVideoWidth;
    168     mEncParams->encHeight[0] = mVideoHeight;
    169     mEncParams->encFrameRate[0] = mVideoFrameRate;
    170     mEncParams->rcType = VBR_1;
    171     mEncParams->vbvDelay = 5.0f;
    172 
    173     // FIXME:
    174     // Add more profile and level support for MPEG4 encoder
    175     mEncParams->profile_level = CORE_PROFILE_LEVEL2;
    176     mEncParams->packetSize = 32;
    177     mEncParams->rvlcEnable = PV_OFF;
    178     mEncParams->numLayers = 1;
    179     mEncParams->timeIncRes = 1000;
    180     mEncParams->tickPerSrc = mEncParams->timeIncRes / mVideoFrameRate;
    181 
    182     mEncParams->bitRate[0] = mVideoBitRate <= 2000000 ? mVideoBitRate : 2000000;
    183     mEncParams->iQuant[0] = 15;
    184     mEncParams->pQuant[0] = 12;
    185     mEncParams->quantType[0] = 0;
    186     mEncParams->noFrameSkipped = PV_OFF;
    187 
    188     mTrimedInputData =
    189         (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1);
    190     CHECK(mTrimedInputData != NULL);
    191 
    192     if (mVideoColorFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
    193         // Color conversion is needed.
    194         CHECK(mInputFrameData == NULL);
    195         mInputFrameData =
    196             (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1);
    197         CHECK(mInputFrameData != NULL);
    198     }
    199 
    200     // PV's MPEG4 encoder requires the video dimension of multiple
    201     if (mVideoWidth % 16 != 0 || mVideoHeight % 16 != 0) {
    202         LOG_E("Video frame size %dx%d must be a multiple of 16",
    203             mVideoWidth, mVideoHeight);
    204         return ENCODE_INVALID_PARAMS;
    205     }
    206 
    207     // Set IDR frame refresh interval
    208     if (mIDRFrameRefreshIntervalInSec < 0) {
    209         mEncParams->intraPeriod = -1;
    210     } else if (mIDRFrameRefreshIntervalInSec == 0) {
    211         mEncParams->intraPeriod = 1;  // All I frames
    212     } else {
    213         mEncParams->intraPeriod =
    214             (mIDRFrameRefreshIntervalInSec * mVideoFrameRate);
    215     }
    216 
    217     mEncParams->numIntraMB = 0;
    218     mEncParams->sceneDetect = PV_ON;
    219     mEncParams->searchRange = 16;
    220     mEncParams->mv8x8Enable = PV_OFF;
    221     mEncParams->gobHeaderInterval = 0;
    222     mEncParams->useACPred = PV_ON;
    223     mEncParams->intraDCVlcTh = 0;
    224 
    225     return ENCODE_SUCCESS;
    226 }
    227 
    228 Encode_Status PVSoftMPEG4Encoder::initEncoder() {
    229     LOG_V("Begin\n");
    230 
    231     CHECK(!mStarted);
    232 
    233     Encode_Status ret = ENCODE_SUCCESS;
    234     if (ENCODE_SUCCESS != (ret = initEncParams())) {
    235         LOG_E("Failed to initialized encoder params");
    236         mSignalledError = true;
    237         return ret;
    238     }
    239 
    240     if (!PVInitVideoEncoder(mHandle, mEncParams)) {
    241         LOG_E("Failed to initialize the encoder");
    242         mSignalledError = true;
    243         return ENCODE_FAIL;
    244     }
    245 
    246     mNumInputFrames = -1;  // 1st buffer for codec specific data
    247     mStarted = true;
    248     mCurTimestampUs = 0;
    249     mLastTimestampUs = 0;
    250     mVolHeaderLength = 256;
    251 
    252     LOG_V("End\n");
    253 
    254     return ENCODE_SUCCESS;
    255 }
    256 
    257 Encode_Status PVSoftMPEG4Encoder::releaseEncoder() {
    258     LOG_V("Begin\n");
    259 
    260     if (!mStarted) {
    261         return ENCODE_SUCCESS;
    262     }
    263 
    264     PVCleanUpVideoEncoder(mHandle);
    265 
    266     delete mTrimedInputData;
    267     mTrimedInputData = NULL;
    268 
    269     delete mInputFrameData;
    270     mInputFrameData = NULL;
    271 
    272     delete mEncParams;
    273     mEncParams = NULL;
    274 
    275     delete mHandle;
    276     mHandle = NULL;
    277 
    278     mStarted = false;
    279 
    280     LOG_V("End\n");
    281 
    282     return ENCODE_SUCCESS;
    283 }
    284 
    285 Encode_Status PVSoftMPEG4Encoder::setParameters(
    286         VideoParamConfigSet *videoEncParams)
    287 {
    288 
    289     Encode_Status ret = ENCODE_SUCCESS;
    290     CHECK_NULL_RETURN_IFFAIL(videoEncParams);
    291     LOG_I("Config type = %d\n", (int)videoEncParams->type);
    292 
    293     if (mStarted) {
    294         LOG_E("Encoder has been initialized, should use setConfig to change configurations\n");
    295         return ENCODE_ALREADY_INIT;
    296     }
    297 
    298     switch (videoEncParams->type) {
    299         case VideoParamsTypeCommon: {
    300 
    301             VideoParamsCommon *paramsCommon =
    302                     reinterpret_cast <VideoParamsCommon *> (videoEncParams);
    303             if (paramsCommon->size != sizeof (VideoParamsCommon)) {
    304                 return ENCODE_INVALID_PARAMS;
    305             }
    306             if(paramsCommon->codedBufNum < 2)
    307                 paramsCommon->codedBufNum =2;
    308             mComParams = *paramsCommon;
    309 
    310             mVideoWidth = mComParams.resolution.width;
    311             mVideoHeight = mComParams.resolution.height;
    312             mVideoFrameRate =  mComParams.frameRate.frameRateNum / \
    313                                mComParams.frameRate.frameRateDenom;
    314             mVideoBitRate = mComParams.rcParams.bitRate;
    315             mVideoColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
    316             break;
    317         }
    318 
    319         case VideoParamsTypeStoreMetaDataInBuffers: {
    320             VideoParamsStoreMetaDataInBuffers *metadata =
    321                     reinterpret_cast <VideoParamsStoreMetaDataInBuffers *> (videoEncParams);
    322 
    323             if (metadata->size != sizeof (VideoParamsStoreMetaDataInBuffers)) {
    324                 return ENCODE_INVALID_PARAMS;
    325             }
    326 
    327             mStoreMetaDataInBuffers = metadata->isEnabled;
    328 
    329             break;
    330         }
    331 
    332         default: {
    333             LOG_I ("Wrong ParamType here\n");
    334             break;
    335         }
    336     }
    337 
    338     return ret;
    339 }
    340 
    341 Encode_Status PVSoftMPEG4Encoder::getParameters(
    342         VideoParamConfigSet *videoEncParams) {
    343 
    344     Encode_Status ret = ENCODE_SUCCESS;
    345     CHECK_NULL_RETURN_IFFAIL(videoEncParams);
    346     LOG_I("Config type = %d\n", (int)videoEncParams->type);
    347 
    348     switch (videoEncParams->type) {
    349         case VideoParamsTypeCommon: {
    350 
    351             VideoParamsCommon *paramsCommon =
    352                     reinterpret_cast <VideoParamsCommon *> (videoEncParams);
    353 
    354             if (paramsCommon->size != sizeof (VideoParamsCommon)) {
    355                 return ENCODE_INVALID_PARAMS;
    356             }
    357             *paramsCommon = mComParams;
    358             break;
    359         }
    360 
    361         case VideoParamsTypeStoreMetaDataInBuffers: {
    362             VideoParamsStoreMetaDataInBuffers *metadata =
    363                     reinterpret_cast <VideoParamsStoreMetaDataInBuffers *> (videoEncParams);
    364 
    365             if (metadata->size != sizeof (VideoParamsStoreMetaDataInBuffers)) {
    366                 return ENCODE_INVALID_PARAMS;
    367             }
    368 
    369             metadata->isEnabled = mStoreMetaDataInBuffers;
    370 
    371             break;
    372         }
    373 
    374         default: {
    375             LOG_I ("Wrong ParamType here\n");
    376             break;
    377         }
    378 
    379     }
    380     return ret;
    381 }
    382 
    383 Encode_Status PVSoftMPEG4Encoder::encode(VideoEncRawBuffer *inBuffer, uint32_t timeout)
    384 {
    385     LOG_V("Begin\n");
    386 
    387     Encode_Status ret = ENCODE_SUCCESS;
    388 
    389     if (mCurTimestampUs <= inBuffer->timeStamp) {
    390         mLastTimestampUs = mCurTimestampUs;
    391         mCurTimestampUs = inBuffer->timeStamp;
    392     }
    393 
    394     if (mNumInputFrames < 0) {
    395         if (!PVGetVolHeader(mHandle, mVolHeader, &mVolHeaderLength, 0)) {
    396             LOG_E("Failed to get VOL header");
    397             mSignalledError = true;
    398             return ENCODE_FAIL;
    399         }
    400         LOG_I("Output VOL header: %d bytes", mVolHeaderLength);
    401         mNumInputFrames++;
    402         //return ENCODE_SUCCESS;
    403     }
    404 
    405     if (mStoreMetaDataInBuffers) {
    406         IntelMetadataBuffer imb;
    407         int32_t type;
    408         int32_t value;
    409         uint8_t *img;
    410         const android::Rect rect(mVideoWidth, mVideoHeight);
    411         android::status_t res;
    412         ValueInfo vinfo;
    413         ValueInfo *pvinfo = &vinfo;
    414         CHECK(IMB_SUCCESS == imb.UnSerialize(inBuffer->data, inBuffer->size));
    415         imb.GetType((::IntelMetadataBufferType&)type);
    416         imb.GetValue(value);
    417         imb.GetValueInfo(pvinfo);
    418         if(pvinfo == NULL) {
    419             res = android::GraphicBufferMapper::get().lock((buffer_handle_t)value,
    420                     GRALLOC_USAGE_SW_READ_MASK,
    421                     rect, (void**)&img);
    422         } else {
    423             img = (uint8_t*)value;
    424         }
    425         if (pvinfo != NULL)
    426             trimBuffer(img, mTrimedInputData, pvinfo->width, pvinfo->height,
    427                    pvinfo->height, pvinfo->lumaStride);
    428         else {
    429             //NV12 Y-TILED
    430             trimBuffer(img, mTrimedInputData, mVideoWidth, mVideoHeight,
    431                     ALIGN(mVideoHeight, 32), ALIGN(mVideoWidth, 128));
    432             android::GraphicBufferMapper::get().unlock((buffer_handle_t)value);
    433         }
    434     } else {
    435         memcpy(mTrimedInputData, inBuffer->data,
    436                 (mVideoWidth * mVideoHeight * 3 ) >> 1);
    437     }
    438 
    439     if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) {
    440         ConvertYUV420SemiPlanarToYUV420Planar(
    441                 mTrimedInputData, mInputFrameData, mVideoWidth, mVideoHeight);
    442     } else {
    443         memcpy(mTrimedInputData, mInputFrameData,
    444                 (mVideoWidth * mVideoHeight * 3 ) >> 1);
    445     }
    446 
    447     LOG_V("End\n");
    448 
    449     return ret;
    450 }
    451 
    452 Encode_Status PVSoftMPEG4Encoder::getOutput(VideoEncOutputBuffer *outBuffer, uint32_t timeout)
    453 {
    454     LOG_V("Begin\n");
    455 
    456     Encode_Status ret = ENCODE_SUCCESS;
    457     uint8_t *outPtr = outBuffer->data;
    458     int32_t dataLength = outBuffer->bufferSize;
    459     outBuffer->flag = 0;
    460 
    461     if ((mEncodeMode == COMBINE_MODE_WITH_ERR_RES) &&
    462             (outBuffer->format == OUTPUT_CODEC_DATA)) {
    463         memcpy(outPtr, mVolHeader, mVolHeaderLength);
    464         ++mNumInputFrames;
    465         outBuffer->flag |= ENCODE_BUFFERFLAG_CODECCONFIG;
    466         outBuffer->flag |= ENCODE_BUFFERFLAG_ENDOFFRAME;
    467         outBuffer->flag |= ENCODE_BUFFERFLAG_SYNCFRAME;
    468         outBuffer->dataSize = mVolHeaderLength;
    469         outBuffer->remainingSize = 0;
    470         return ENCODE_SUCCESS;
    471     }
    472 
    473     outBuffer->timeStamp = mCurTimestampUs;
    474     LOG_I("info.mTimeUs %lld\n", outBuffer->timeStamp);
    475 
    476     VideoEncFrameIO vin, vout;
    477     memset(&vin, 0, sizeof(vin));
    478     memset(&vout, 0, sizeof(vout));
    479     vin.height = ((mVideoHeight  + 15) >> 4) << 4;
    480     vin.pitch = ((mVideoWidth + 15) >> 4) << 4;
    481     vin.timestamp = (outBuffer->timeStamp + 500) / 1000;  // in ms
    482     vin.yChan = mInputFrameData;
    483     vin.uChan = vin.yChan + vin.height * vin.pitch;
    484     vin.vChan = vin.uChan + ((vin.height * vin.pitch) >> 2);
    485 
    486     unsigned long modTimeMs = 0;
    487     int32_t nLayer = 0;
    488     MP4HintTrack hintTrack;
    489     if (!PVEncodeVideoFrame(mHandle, &vin, &vout,
    490                 &modTimeMs, outPtr, &dataLength, &nLayer) ||
    491             !PVGetHintTrack(mHandle, &hintTrack)) {
    492         LOG_E("Failed to encode frame or get hink track at frame %lld",
    493                 mNumInputFrames);
    494         mSignalledError = true;
    495         hintTrack.CodeType = 0;
    496         ret = ENCODE_FAIL;
    497     }
    498     LOG_I("dataLength %d\n", dataLength);
    499     CHECK(NULL == PVGetOverrunBuffer(mHandle));
    500     if (hintTrack.CodeType == 0) {  // I-frame serves as sync frame
    501         outBuffer->flag |= ENCODE_BUFFERFLAG_SYNCFRAME;
    502     }
    503 
    504     ++mNumInputFrames;
    505 
    506     outBuffer->flag |= ENCODE_BUFFERFLAG_ENDOFFRAME;
    507     outBuffer->dataSize = dataLength;
    508 
    509     LOG_V("End\n");
    510 
    511     return ret;
    512 }
    513 
    514