Home | History | Annotate | Download | only in base
      1 /*
      2  * Copyright (C) 2012 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 #include <cutils/properties.h>
     18 #include <system/graphics.h>
     19 #include "isv_worker.h"
     20 #ifndef TARGET_VPP_USE_GEN
     21 #include <hal_public.h>
     22 #else
     23 #include <ufo/graphics.h>
     24 #endif
     25 
     26 //#define LOG_NDEBUG 0
     27 #undef LOG_TAG
     28 #define LOG_TAG "isv-omxil"
     29 
     30 #define CHECK_VASTATUS(str) \
     31     do { \
     32         if (vaStatus != VA_STATUS_SUCCESS) { \
     33                 ALOGE("%s failed\n", str); \
     34                 return STATUS_ERROR;}   \
     35         }while(0);
     36 
     37 enum STRENGTH {
     38     STRENGTH_LOW = 0,
     39     STRENGTH_MEDIUM,
     40     STRENGTH_HIGH
     41 };
     42 
     43 #define DENOISE_DEBLOCK_STRENGTH STRENGTH_MEDIUM
     44 #define COLOR_STRENGTH STRENGTH_MEDIUM
     45 #ifdef TARGET_VPP_USE_GEN
     46 #define COLOR_NUM 4
     47 #else
     48 #define COLOR_NUM 2
     49 #endif
     50 
     51 #define MAX_FRC_OUTPUT 4 /*for frcx4*/
     52 
     53 using namespace android;
     54 
     55 ISVWorker::ISVWorker()
     56     :mNumForwardReferences(0),
     57     mVAContext(VA_INVALID_ID),
     58     mWidth(0), mHeight(0),
     59     mDisplay(NULL), mVADisplay(NULL),
     60     mVAConfig(VA_INVALID_ID),
     61     mForwardReferences(NULL),
     62     mPrevInput(0), mPrevOutput(0),
     63     mNumFilterBuffers(0),
     64     mFilterFrc(VA_INVALID_ID), mFilters(0),
     65     mInputIndex(0), mOutputIndex(0),
     66     mOutputCount(0) {
     67     memset(&mFilterBuffers, VA_INVALID_ID, VAProcFilterCount * sizeof(VABufferID));
     68     memset(&mFilterParam, 0, sizeof(mFilterParam));
     69 }
     70 
     71 bool ISVWorker::isSupport() const {
     72     bool support = false;
     73 
     74     int num_entrypoints = vaMaxNumEntrypoints(mVADisplay);
     75     VAEntrypoint * entrypoints = (VAEntrypoint *)malloc(num_entrypoints * sizeof(VAEntrypoint));
     76     if (entrypoints == NULL) {
     77         ALOGE("failed to malloc entrypoints array\n");
     78         return false;
     79     }
     80 
     81     // check if it contains VPP entry point VAEntrypointVideoProc
     82     VAStatus vaStatus = vaQueryConfigEntrypoints(mVADisplay, VAProfileNone, entrypoints, &num_entrypoints);
     83     if (vaStatus != VA_STATUS_SUCCESS) {
     84         ALOGE("vaQueryConfigEntrypoints failed");
     85         return false;
     86     }
     87     for (int i = 0; !support && i < num_entrypoints; i++) {
     88         support = entrypoints[i] == VAEntrypointVideoProc;
     89     }
     90     free(entrypoints);
     91     entrypoints = NULL;
     92 
     93     return support;
     94 }
     95 
     96 uint32_t ISVWorker::getProcBufCount() {
     97     return getOutputBufCount(mInputIndex);
     98 }
     99 
    100 uint32_t ISVWorker::getFillBufCount() {
    101         return getOutputBufCount(mOutputIndex);
    102 }
    103 
    104 uint32_t ISVWorker::getOutputBufCount(uint32_t index) {
    105     uint32_t bufCount = 1;
    106     if (((mFilters & FilterFrameRateConversion) != 0)
    107             && index > 0)
    108             bufCount = mFilterParam.frcRate - (((mFilterParam.frcRate == FRC_RATE_2_5X) ? (index & 1): 0));
    109     return bufCount;
    110 }
    111 
    112 
    113 status_t ISVWorker::init(uint32_t width, uint32_t height) {
    114     ALOGV("init");
    115 
    116     if (mDisplay != NULL) {
    117         ALOGE("VA is particially started");
    118         return STATUS_ERROR;
    119     }
    120     mDisplay = new Display;
    121     *mDisplay = ANDROID_DISPLAY_HANDLE;
    122 
    123     mVADisplay = vaGetDisplay(mDisplay);
    124     if (mVADisplay == NULL) {
    125         ALOGE("vaGetDisplay failed");
    126         return STATUS_ERROR;
    127     }
    128 
    129     int majorVersion, minorVersion;
    130     VAStatus vaStatus = vaInitialize(mVADisplay, &majorVersion, &minorVersion);
    131     CHECK_VASTATUS("vaInitialize");
    132 
    133     // Check if VPP entry point is supported
    134     if (!isSupport()) {
    135         ALOGE("VPP is not supported on current platform");
    136         return STATUS_NOT_SUPPORT;
    137     }
    138 
    139     // Find out the format for the target
    140     VAConfigAttrib attrib;
    141     attrib.type = VAConfigAttribRTFormat;
    142     vaStatus = vaGetConfigAttributes(mVADisplay, VAProfileNone, VAEntrypointVideoProc, &attrib, 1);
    143     CHECK_VASTATUS("vaGetConfigAttributes");
    144 
    145     if ((attrib.value & VA_RT_FORMAT_YUV420) == 0) {
    146         ALOGE("attribute is %x vs wanted %x", attrib.value, VA_RT_FORMAT_YUV420);
    147         return STATUS_NOT_SUPPORT;
    148     }
    149 
    150     ALOGV("ready to create config");
    151     // Create the configuration
    152     vaStatus = vaCreateConfig(mVADisplay, VAProfileNone, VAEntrypointVideoProc, &attrib, 1, &mVAConfig);
    153     CHECK_VASTATUS("vaCreateConfig");
    154 
    155 
    156     // Create Context
    157     ALOGV("ready to create context");
    158     mWidth = width;
    159     mHeight = height;
    160     vaStatus = vaCreateContext(mVADisplay, mVAConfig, mWidth, mHeight, 0, NULL, 0, &mVAContext);
    161     CHECK_VASTATUS("vaCreateContext");
    162 
    163     ALOGV("VA has been successfully started");
    164     return STATUS_OK;
    165 }
    166 
    167 status_t ISVWorker::deinit() {
    168     {
    169         Mutex::Autolock autoLock(mPipelineBufferLock);
    170         while (!mPipelineBuffers.isEmpty()) {
    171             VABufferID pipelineBuffer = mPipelineBuffers.itemAt(0);
    172             if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, pipelineBuffer))
    173                 ALOGW("%s: failed to destroy va buffer id %d", __func__, pipelineBuffer);
    174             mPipelineBuffers.removeAt(0);
    175         }
    176     }
    177 
    178     if (mNumFilterBuffers != 0) {
    179         for (uint32_t i = 0; i < mNumFilterBuffers; i++) {
    180             if(VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, mFilterBuffers[i]))
    181                 ALOGW("%s: failed to destroy va buffer id %d", __func__, mFilterBuffers[i]);
    182         }
    183         mNumFilterBuffers = 0;
    184         memset(&mFilterBuffers, VA_INVALID_ID, VAProcFilterCount * sizeof(VABufferID));
    185         mFilterFrc = VA_INVALID_ID;
    186     }
    187 
    188     if (mForwardReferences != NULL) {
    189         free(mForwardReferences);
    190         mForwardReferences = NULL;
    191         mNumForwardReferences = 0;
    192     }
    193 
    194     if (mVAContext != VA_INVALID_ID) {
    195          vaDestroyContext(mVADisplay, mVAContext);
    196          mVAContext = VA_INVALID_ID;
    197     }
    198 
    199     if (mVAConfig != VA_INVALID_ID) {
    200         vaDestroyConfig(mVADisplay, mVAConfig);
    201         mVAConfig = VA_INVALID_ID;
    202     }
    203 
    204     if (mVADisplay) {
    205         vaTerminate(mVADisplay);
    206         mVADisplay = NULL;
    207     }
    208 
    209     if (mDisplay) {
    210         delete mDisplay;
    211         mDisplay = NULL;
    212     }
    213 
    214     return STATUS_OK;
    215 }
    216 
    217 status_t ISVWorker::allocSurface(uint32_t* width, uint32_t* height,
    218         uint32_t stride, uint32_t format, unsigned long handle, int32_t* surfaceId)
    219 {
    220     if (mWidth == 0 || mHeight == 0) {
    221         ALOGE("%s: isv worker has not been initialized.", __func__);
    222         return STATUS_ERROR;
    223     }
    224 
    225 #ifndef TARGET_VPP_USE_GEN
    226     *width = mWidth;
    227     *height = mHeight;
    228 #endif
    229     // Create VASurfaces
    230     VASurfaceAttrib attribs[3];
    231     VASurfaceAttribExternalBuffers vaExtBuf;
    232 
    233     memset(&vaExtBuf, 0, sizeof(VASurfaceAttribExternalBuffers));
    234     switch(format) {
    235         case HAL_PIXEL_FORMAT_YV12:
    236             vaExtBuf.pixel_format = VA_FOURCC_YV12;
    237             vaExtBuf.num_planes = 3;
    238             vaExtBuf.pitches[0] = stride;
    239             vaExtBuf.pitches[1] = stride / 2;
    240             vaExtBuf.pitches[2] = stride / 2;
    241             vaExtBuf.pitches[3] = 0;
    242             vaExtBuf.offsets[0] = 0;
    243             vaExtBuf.offsets[1] = stride * *height;
    244             vaExtBuf.offsets[2] = vaExtBuf.offsets[1] + (stride / 2) * (*height / 2);
    245             vaExtBuf.offsets[3] = 0;
    246             break;
    247         case HAL_PIXEL_FORMAT_INTEL_YV12:
    248             vaExtBuf.pixel_format = VA_FOURCC_YV12;
    249             vaExtBuf.num_planes = 3;
    250             vaExtBuf.pitches[0] = stride;
    251             vaExtBuf.pitches[1] = stride / 2;
    252             vaExtBuf.pitches[2] = stride / 2;
    253             vaExtBuf.pitches[3] = 0;
    254             vaExtBuf.offsets[0] = 0;
    255             // The height of HAL_PIXEL_FORMAT_INTEL_YV12 gralloc buffer has been aligned with 32 pixels.
    256             vaExtBuf.offsets[1] = stride * ((*height + 31) & ~31);
    257             vaExtBuf.offsets[2] = vaExtBuf.offsets[1] + (stride / 2) * (((*height + 31) & ~31) / 2);
    258             vaExtBuf.offsets[3] = 0;
    259             break;
    260 #ifdef TARGET_VPP_USE_GEN
    261         case HAL_PIXEL_FORMAT_NV12_Y_TILED_INTEL:
    262         case HAL_PIXEL_FORMAT_NV12_X_TILED_INTEL:
    263         //it will be removed in future, it indicate the same format with HAL_PIXEL_FORMAT_NV12_Y_TILED_INTEL
    264         case HAL_PIXEL_FORMAT_YUV420PackedSemiPlanar_Tiled_INTEL:
    265 #else
    266         case HAL_PIXEL_FORMAT_NV12_VED:
    267         case HAL_PIXEL_FORMAT_NV12_VEDT:
    268 #endif
    269             vaExtBuf.pixel_format = VA_FOURCC_NV12;
    270             vaExtBuf.num_planes = 2;
    271             vaExtBuf.pitches[0] = stride;
    272             vaExtBuf.pitches[1] = stride;
    273             vaExtBuf.pitches[2] = 0;
    274             vaExtBuf.pitches[3] = 0;
    275             vaExtBuf.offsets[0] = 0;
    276             vaExtBuf.offsets[1] = stride * *height;
    277             vaExtBuf.offsets[2] = 0;
    278             vaExtBuf.offsets[3] = 0;
    279             break;
    280         default:
    281             ALOGE("%s: can't support this format 0x%08x", __func__, format);
    282             return STATUS_ERROR;
    283     }
    284     vaExtBuf.width = *width;
    285     vaExtBuf.height = *height;
    286     vaExtBuf.data_size = stride * *height * 1.5;
    287     vaExtBuf.num_buffers = 1;
    288 #ifndef TARGET_VPP_USE_GEN
    289     if (format == HAL_PIXEL_FORMAT_NV12_VEDT) {
    290         ALOGV("set TILING flag");
    291         vaExtBuf.flags |= VA_SURFACE_EXTBUF_DESC_ENABLE_TILING;
    292     }
    293 #endif
    294     vaExtBuf.flags |= VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;
    295     vaExtBuf.buffers = (long unsigned int*)&handle;
    296 
    297     attribs[0].type = (VASurfaceAttribType)VASurfaceAttribMemoryType;
    298     attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE;
    299     attribs[0].value.type = VAGenericValueTypeInteger;
    300     attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_ANDROID_GRALLOC;
    301 
    302     attribs[1].type = (VASurfaceAttribType)VASurfaceAttribExternalBufferDescriptor;
    303     attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE;
    304     attribs[1].value.type = VAGenericValueTypePointer;
    305     attribs[1].value.value.p = &vaExtBuf;
    306 
    307     attribs[2].type = (VASurfaceAttribType)VASurfaceAttribUsageHint;
    308     attribs[2].flags = VA_SURFACE_ATTRIB_SETTABLE;
    309     attribs[2].value.type = VAGenericValueTypeInteger;
    310     attribs[2].value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_READ;
    311 
    312     ALOGV("%s: Ext buffer: width %d, height %d, data_size %d, pitch %d", __func__,
    313             vaExtBuf.width, vaExtBuf.height, vaExtBuf.data_size, vaExtBuf.pitches[0]);
    314     VAStatus vaStatus = vaCreateSurfaces(mVADisplay, VA_RT_FORMAT_YUV420, vaExtBuf.width,
    315                                  vaExtBuf.height, (VASurfaceID*)surfaceId, 1, attribs, 3);
    316     CHECK_VASTATUS("vaCreateSurfaces");
    317 
    318     return (vaStatus == VA_STATUS_SUCCESS) ? STATUS_OK : STATUS_ERROR;
    319 }
    320 
    321 status_t ISVWorker::freeSurface(int32_t* surfaceId)
    322 {
    323     VAStatus vaStatus = VA_STATUS_SUCCESS;
    324     vaDestroySurfaces(mVADisplay, (VASurfaceID*)surfaceId, 1);
    325     CHECK_VASTATUS("vaDestroySurfaces");
    326 
    327     return (vaStatus == VA_STATUS_SUCCESS) ? STATUS_OK : STATUS_ERROR;
    328 }
    329 
    330 status_t ISVWorker::configFilters(uint32_t filters,
    331                                   const FilterParam* filterParam)
    332 {
    333     status_t ret = STATUS_OK;
    334 
    335     if (!filterParam) {
    336         ALOGE("%s: invalid filterParam", __func__);
    337         return STATUS_ERROR;
    338     }
    339 
    340     if (filters != 0) {
    341         mFilterParam.srcWidth = filterParam->srcWidth;
    342         mFilterParam.srcHeight = filterParam->srcHeight;
    343         mFilterParam.dstWidth = filterParam->dstWidth;
    344         mFilterParam.dstHeight = filterParam->dstHeight;
    345         mFilterParam.frameRate = filterParam->frameRate;
    346         mFilterParam.frcRate = filterParam->frcRate;
    347     }
    348 
    349     if (mFilters != filters) {
    350         mFilters = filters;
    351         ALOGI("%s: mFilters 0x%x, fps %d, frc rate %d", __func__, mFilters, mFilterParam.frameRate, mFilterParam.frcRate);
    352         ret = setupFilters();
    353     }
    354 
    355     return ret;
    356 }
    357 
    358 bool ISVWorker::isFpsSupport(int32_t fps, int32_t *fpsSet, int32_t fpsSetCnt) {
    359     bool ret = false;
    360     for (int32_t i = 0; i < fpsSetCnt; i++) {
    361         if (fps == fpsSet[i]) {
    362             ret = true;
    363             break;
    364         }
    365     }
    366 
    367     return ret;
    368 }
    369 
    370 status_t ISVWorker::setupFilters() {
    371     ALOGV("setupFilters");
    372     VAProcFilterParameterBuffer deblock, denoise, sharpen, stde;
    373     VAProcFilterParameterBufferDeinterlacing deint;
    374     VAProcFilterParameterBufferColorBalance color[COLOR_NUM];
    375     VAProcFilterParameterBufferFrameRateConversion frc;
    376     VABufferID deblockId, denoiseId, deintId, sharpenId, colorId, frcId, stdeId;
    377     uint32_t numCaps;
    378     VAProcFilterCap deblockCaps, denoiseCaps, sharpenCaps, frcCaps, stdeCaps;
    379     VAProcFilterCapDeinterlacing deinterlacingCaps[VAProcDeinterlacingCount];
    380     VAProcFilterCapColorBalance colorCaps[COLOR_NUM];
    381     VAStatus vaStatus;
    382     uint32_t numSupportedFilters = VAProcFilterCount;
    383     VAProcFilterType supportedFilters[VAProcFilterCount];
    384 
    385     if (mNumFilterBuffers != 0) {
    386         for (uint32_t i = 0; i < mNumFilterBuffers; i++) {
    387             if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, mFilterBuffers[i]))
    388                 ALOGW("%s: failed to destroy va buffer %d", __func__, mFilterBuffers[i]);
    389                 //return STATUS_ERROR;
    390         }
    391         memset(&mFilterBuffers, VA_INVALID_ID, VAProcFilterCount * sizeof(VABufferID));
    392         mFilterFrc = VA_INVALID_ID;
    393         mNumFilterBuffers = 0;
    394     }
    395 
    396     // query supported filters
    397     vaStatus = vaQueryVideoProcFilters(mVADisplay, mVAContext, supportedFilters, &numSupportedFilters);
    398     CHECK_VASTATUS("vaQueryVideoProcFilters");
    399 
    400     // create filter buffer for each filter
    401     for (uint32_t i = 0; i < numSupportedFilters; i++) {
    402         switch (supportedFilters[i]) {
    403             case VAProcFilterDeblocking:
    404                 if ((mFilters & FilterDeblocking) != 0) {
    405                     // check filter caps
    406                     numCaps = 1;
    407                     vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
    408                             VAProcFilterDeblocking,
    409                             &deblockCaps,
    410                             &numCaps);
    411                     CHECK_VASTATUS("vaQueryVideoProcFilterCaps for deblocking");
    412                     // create parameter buffer
    413                     deblock.type = VAProcFilterDeblocking;
    414                     deblock.value = deblockCaps.range.min_value + DENOISE_DEBLOCK_STRENGTH * deblockCaps.range.step;
    415                     vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
    416                         VAProcFilterParameterBufferType, sizeof(deblock), 1,
    417                         &deblock, &deblockId);
    418                     CHECK_VASTATUS("vaCreateBuffer for deblocking");
    419                     mFilterBuffers[mNumFilterBuffers] = deblockId;
    420                     mNumFilterBuffers++;
    421                 }
    422                 break;
    423             case VAProcFilterNoiseReduction:
    424                 if((mFilters & FilterNoiseReduction) != 0) {
    425                     // check filter caps
    426                     numCaps = 1;
    427                     vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
    428                             VAProcFilterNoiseReduction,
    429                             &denoiseCaps,
    430                             &numCaps);
    431                     CHECK_VASTATUS("vaQueryVideoProcFilterCaps for denoising");
    432                     // create parameter buffer
    433                     denoise.type = VAProcFilterNoiseReduction;
    434 #ifdef TARGET_VPP_USE_GEN
    435                     char propValueString[PROPERTY_VALUE_MAX];
    436 
    437                     // placeholder for vpg driver: can't support denoise factor auto adjust, so leave config to user.
    438                     property_get("vpp.filter.denoise.factor", propValueString, "64.0");
    439                     denoise.value = atof(propValueString);
    440                     denoise.value = (denoise.value < 0.0f) ? 0.0f : denoise.value;
    441                     denoise.value = (denoise.value > 64.0f) ? 64.0f : denoise.value;
    442 #else
    443                     denoise.value = denoiseCaps.range.min_value + DENOISE_DEBLOCK_STRENGTH * denoiseCaps.range.step;
    444 #endif
    445                     vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
    446                         VAProcFilterParameterBufferType, sizeof(denoise), 1,
    447                         &denoise, &denoiseId);
    448                     CHECK_VASTATUS("vaCreateBuffer for denoising");
    449                     mFilterBuffers[mNumFilterBuffers] = denoiseId;
    450                     mNumFilterBuffers++;
    451                 }
    452                 break;
    453             case VAProcFilterDeinterlacing:
    454                 if ((mFilters & FilterDeinterlacing) != 0) {
    455                     numCaps = VAProcDeinterlacingCount;
    456                     vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
    457                             VAProcFilterDeinterlacing,
    458                             &deinterlacingCaps[0],
    459                             &numCaps);
    460                     CHECK_VASTATUS("vaQueryVideoProcFilterCaps for deinterlacing");
    461                     for (uint32_t i = 0; i < numCaps; i++)
    462                     {
    463                         VAProcFilterCapDeinterlacing * const cap = &deinterlacingCaps[i];
    464                         if (cap->type != VAProcDeinterlacingBob) // desired Deinterlacing Type
    465                             continue;
    466 
    467                         deint.type = VAProcFilterDeinterlacing;
    468                         deint.algorithm = VAProcDeinterlacingBob;
    469                         vaStatus = vaCreateBuffer(mVADisplay,
    470                                 mVAContext,
    471                                 VAProcFilterParameterBufferType,
    472                                 sizeof(deint), 1,
    473                                 &deint, &deintId);
    474                         CHECK_VASTATUS("vaCreateBuffer for deinterlacing");
    475                         mFilterBuffers[mNumFilterBuffers] = deintId;
    476                         mNumFilterBuffers++;
    477                     }
    478                 }
    479                 break;
    480             case VAProcFilterSharpening:
    481                 if((mFilters & FilterSharpening) != 0) {
    482                     // check filter caps
    483                     numCaps = 1;
    484                     vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
    485                             VAProcFilterSharpening,
    486                             &sharpenCaps,
    487                             &numCaps);
    488                     CHECK_VASTATUS("vaQueryVideoProcFilterCaps for sharpening");
    489                     // create parameter buffer
    490                     sharpen.type = VAProcFilterSharpening;
    491 #ifdef TARGET_VPP_USE_GEN
    492                     char propValueString[PROPERTY_VALUE_MAX];
    493 
    494                     // placeholder for vpg driver: can't support sharpness factor auto adjust, so leave config to user.
    495                     property_get("vpp.filter.sharpen.factor", propValueString, "8.0");
    496                     sharpen.value = atof(propValueString);
    497                     sharpen.value = (sharpen.value < 0.0f) ? 0.0f : sharpen.value;
    498                     sharpen.value = (sharpen.value > 64.0f) ? 64.0f : sharpen.value;
    499 #else
    500                     sharpen.value = sharpenCaps.range.default_value;
    501 #endif
    502                     vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
    503                         VAProcFilterParameterBufferType, sizeof(sharpen), 1,
    504                         &sharpen, &sharpenId);
    505                     CHECK_VASTATUS("vaCreateBuffer for sharpening");
    506                     mFilterBuffers[mNumFilterBuffers] = sharpenId;
    507                     mNumFilterBuffers++;
    508                 }
    509                 break;
    510             case VAProcFilterColorBalance:
    511                 if((mFilters & FilterColorBalance) != 0) {
    512                     uint32_t featureCount = 0;
    513                     // check filter caps
    514                     // FIXME: it's not used at all!
    515                     numCaps = COLOR_NUM;
    516                     vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
    517                             VAProcFilterColorBalance,
    518                             colorCaps,
    519                             &numCaps);
    520                     CHECK_VASTATUS("vaQueryVideoProcFilterCaps for color balance");
    521                     // create parameter buffer
    522                     for (uint32_t i = 0; i < numCaps; i++) {
    523                         if (colorCaps[i].type == VAProcColorBalanceAutoSaturation) {
    524                             color[i].type = VAProcFilterColorBalance;
    525                             color[i].attrib = VAProcColorBalanceAutoSaturation;
    526                             color[i].value = colorCaps[i].range.min_value + COLOR_STRENGTH * colorCaps[i].range.step;
    527                             featureCount++;
    528                         }
    529                         else if (colorCaps[i].type == VAProcColorBalanceAutoBrightness) {
    530                             color[i].type = VAProcFilterColorBalance;
    531                             color[i].attrib = VAProcColorBalanceAutoBrightness;
    532                             color[i].value = colorCaps[i].range.min_value + COLOR_STRENGTH * colorCaps[i].range.step;
    533                             featureCount++;
    534                         }
    535                     }
    536 #ifdef TARGET_VPP_USE_GEN
    537                     //TODO: VPG need to support check input value by colorCaps.
    538                     enum {kHue = 0, kSaturation, kBrightness, kContrast};
    539                     char propValueString[PROPERTY_VALUE_MAX];
    540                     color[kHue].type = VAProcFilterColorBalance;
    541                     color[kHue].attrib = VAProcColorBalanceHue;
    542 
    543                     // placeholder for vpg driver: can't support auto color balance, so leave config to user.
    544                     property_get("vpp.filter.procamp.hue", propValueString, "179.0");
    545                     color[kHue].value = atof(propValueString);
    546                     color[kHue].value = (color[kHue].value < -180.0f) ? -180.0f : color[kHue].value;
    547                     color[kHue].value = (color[kHue].value > 180.0f) ? 180.0f : color[kHue].value;
    548                     featureCount++;
    549 
    550                     color[kSaturation].type   = VAProcFilterColorBalance;
    551                     color[kSaturation].attrib = VAProcColorBalanceSaturation;
    552                     property_get("vpp.filter.procamp.saturation", propValueString, "1.0");
    553                     color[kSaturation].value = atof(propValueString);
    554                     color[kSaturation].value = (color[kSaturation].value < 0.0f) ? 0.0f : color[kSaturation].value;
    555                     color[kSaturation].value = (color[kSaturation].value > 10.0f) ? 10.0f : color[kSaturation].value;
    556                     featureCount++;
    557 
    558                     color[kBrightness].type   = VAProcFilterColorBalance;
    559                     color[kBrightness].attrib = VAProcColorBalanceBrightness;
    560                     property_get("vpp.filter.procamp.brightness", propValueString, "0.0");
    561                     color[kBrightness].value = atof(propValueString);
    562                     color[kBrightness].value = (color[kBrightness].value < -100.0f) ? -100.0f : color[kBrightness].value;
    563                     color[kBrightness].value = (color[kBrightness].value > 100.0f) ? 100.0f : color[kBrightness].value;
    564                     featureCount++;
    565 
    566                     color[kContrast].type   = VAProcFilterColorBalance;
    567                     color[kContrast].attrib = VAProcColorBalanceContrast;
    568                     property_get("vpp.filter.procamp.contrast", propValueString, "1.0");
    569                     color[kContrast].value = atof(propValueString);
    570                     color[kContrast].value = (color[kContrast].value < 0.0f) ? 0.0f : color[kContrast].value;
    571                     color[kContrast].value = (color[kContrast].value > 10.0f) ? 10.0f : color[kContrast].value;
    572                     featureCount++;
    573 #endif
    574                     vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
    575                         VAProcFilterParameterBufferType, sizeof(*color), featureCount,
    576                         color, &colorId);
    577                     CHECK_VASTATUS("vaCreateBuffer for color balance");
    578                     mFilterBuffers[mNumFilterBuffers] = colorId;
    579                     mNumFilterBuffers++;
    580                 }
    581                 break;
    582             case VAProcFilterFrameRateConversion:
    583                 if((mFilters & FilterFrameRateConversion) != 0) {
    584                     frc.type = VAProcFilterFrameRateConversion;
    585                     frc.input_fps = mFilterParam.frameRate;
    586                     switch (mFilterParam.frcRate){
    587                         case FRC_RATE_1X:
    588                             frc.output_fps = frc.input_fps;
    589                             break;
    590                         case FRC_RATE_2X:
    591                             frc.output_fps = frc.input_fps * 2;
    592                             break;
    593                         case FRC_RATE_2_5X:
    594                             frc.output_fps = frc.input_fps * 5/2;
    595                             break;
    596                         case FRC_RATE_4X:
    597                             frc.output_fps = frc.input_fps * 4;
    598                             break;
    599                     }
    600                     vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
    601                         VAProcFilterParameterBufferType, sizeof(frc), 1,
    602                         &frc, &frcId);
    603                     CHECK_VASTATUS("vaCreateBuffer for frc");
    604                     mFilterBuffers[mNumFilterBuffers] = frcId;
    605                     mNumFilterBuffers++;
    606                     mFilterFrc = frcId;
    607                 }
    608                 break;
    609             case VAProcFilterSkinToneEnhancement:
    610                 if((mFilters & FilterSkinToneEnhancement) != 0) {
    611                     // check filter caps
    612                     numCaps = 1;
    613                     vaStatus = vaQueryVideoProcFilterCaps(mVADisplay, mVAContext,
    614                             VAProcFilterSkinToneEnhancement,
    615                             &stdeCaps,
    616                             &numCaps);
    617                     CHECK_VASTATUS("vaQueryVideoProcFilterCaps for skintone");
    618                     // create parameter buffer
    619                     stde.type = VAProcFilterSkinToneEnhancement;
    620 #ifdef TARGET_VPP_USE_GEN
    621                     char propValueString[PROPERTY_VALUE_MAX];
    622 
    623                     // placeholder for vpg driver: can't support skintone factor auto adjust, so leave config to user.
    624                     property_get("vpp.filter.skintone.factor", propValueString, "8.0");
    625                     stde.value = atof(propValueString);
    626                     stde.value = (stde.value < 0.0f) ? 0.0f : stde.value;
    627                     stde.value = (stde.value > 8.0f) ? 8.0f : stde.value;
    628 #else
    629                     stde.value = stdeCaps.range.default_value;
    630 #endif
    631                     vaStatus = vaCreateBuffer(mVADisplay, mVAContext,
    632                         VAProcFilterParameterBufferType, sizeof(stde), 1,
    633                         &stde, &stdeId);
    634                     CHECK_VASTATUS("vaCreateBuffer for skintone");
    635                     mFilterBuffers[mNumFilterBuffers] = stdeId;
    636                     mNumFilterBuffers++;
    637                 }
    638                 break;
    639             default:
    640                 ALOGW("%s: Not supported filter 0x%08x", __func__, supportedFilters[i]);
    641                 break;
    642         }
    643     }
    644 
    645     return setupPipelineCaps();
    646 }
    647 
    648 status_t ISVWorker::setupPipelineCaps() {
    649     ALOGV("setupPipelineCaps");
    650     //TODO color standards
    651     VAProcPipelineCaps pipelineCaps;
    652     VAStatus vaStatus;
    653     pipelineCaps.input_color_standards = in_color_standards;
    654     pipelineCaps.num_input_color_standards = VAProcColorStandardCount;
    655     pipelineCaps.output_color_standards = out_color_standards;
    656     pipelineCaps.num_output_color_standards = VAProcColorStandardCount;
    657 
    658     vaStatus = vaQueryVideoProcPipelineCaps(mVADisplay, mVAContext,
    659         mFilterBuffers, mNumFilterBuffers,
    660         &pipelineCaps);
    661     CHECK_VASTATUS("vaQueryVideoProcPipelineCaps");
    662 
    663     if (mForwardReferences != NULL) {
    664         free(mForwardReferences);
    665         mForwardReferences = NULL;
    666         mNumForwardReferences = 0;
    667     }
    668 
    669     mNumForwardReferences = pipelineCaps.num_forward_references;
    670     if (mNumForwardReferences > 0) {
    671         mForwardReferences = (VASurfaceID*)malloc(mNumForwardReferences * sizeof(VASurfaceID));
    672         if (mForwardReferences == NULL)
    673             return STATUS_ALLOCATION_ERROR;
    674         memset(mForwardReferences, 0, mNumForwardReferences * sizeof(VASurfaceID));
    675     }
    676     return STATUS_OK;
    677 }
    678 
    679 status_t ISVWorker::process(ISVBuffer* inputBuffer, Vector<ISVBuffer*> outputBuffer,
    680                              uint32_t outputCount, bool isEOS, uint32_t flags) {
    681     ALOGV("process: outputCount=%d, mInputIndex=%d", outputCount, mInputIndex);
    682     VASurfaceID input;
    683     VASurfaceID output[MAX_FRC_OUTPUT];
    684     VABufferID pipelineId;
    685     VAProcPipelineParameterBuffer *pipeline;
    686     VAProcFilterParameterBufferFrameRateConversion *frc;
    687     VAStatus vaStatus = STATUS_OK;
    688     uint32_t i = 0;
    689 
    690     if (isEOS) {
    691         if (mInputIndex == 0) {
    692             ALOGV("%s: don't need to flush VSP", __func__);
    693             return STATUS_OK;
    694         }
    695         input = VA_INVALID_SURFACE;
    696         outputCount = 1;
    697         output[0] = mPrevOutput;
    698     } else {
    699         if (!inputBuffer || outputBuffer.size() != outputCount) {
    700             ALOGE("%s: invalid input/output buffer", __func__);
    701             return STATUS_ERROR;
    702         }
    703 
    704         if (outputCount < 1 || outputCount > 4) {
    705             ALOGE("%s: invalid outputCount", __func__);
    706             return STATUS_ERROR;
    707         }
    708 
    709         input = inputBuffer->getSurface();
    710         for (i = 0; i < outputCount; i++) {
    711             output[i] = outputBuffer[i]->getSurface();
    712             if (output[i] == VA_INVALID_SURFACE) {
    713                 ALOGE("invalid output buffer");
    714                 return STATUS_ERROR;
    715             }
    716         }
    717     }
    718 
    719     // reference frames setting
    720     if (mNumForwardReferences > 0) {
    721         /* add previous frame into reference array*/
    722         for (i = 1; i < mNumForwardReferences; i++) {
    723             mForwardReferences[i - 1] = mForwardReferences[i];
    724         }
    725 
    726         //make last reference to input
    727         mForwardReferences[mNumForwardReferences - 1] = mPrevInput;
    728     }
    729 
    730     mPrevInput = input;
    731 
    732     // create pipeline parameter buffer
    733     vaStatus = vaCreateBuffer(mVADisplay,
    734             mVAContext,
    735             VAProcPipelineParameterBufferType,
    736             sizeof(VAProcPipelineParameterBuffer),
    737             1,
    738             NULL,
    739             &pipelineId);
    740     CHECK_VASTATUS("vaCreateBuffer for VAProcPipelineParameterBufferType");
    741 
    742     ALOGV("before vaBeginPicture");
    743     vaStatus = vaBeginPicture(mVADisplay, mVAContext, output[0]);
    744     CHECK_VASTATUS("vaBeginPicture");
    745 
    746     // map pipeline paramter buffer
    747     vaStatus = vaMapBuffer(mVADisplay, pipelineId, (void**)&pipeline);
    748     CHECK_VASTATUS("vaMapBuffer for pipeline parameter buffer");
    749 
    750     // frc pamameter setting
    751     if ((mFilters & FilterFrameRateConversion) != 0) {
    752         vaStatus = vaMapBuffer(mVADisplay, mFilterFrc, (void **)&frc);
    753         CHECK_VASTATUS("vaMapBuffer for frc parameter buffer");
    754         if (isEOS)
    755             frc->num_output_frames = 0;
    756         else
    757             frc->num_output_frames = outputCount - 1;
    758         frc->output_frames = output + 1;
    759     }
    760 
    761     // pipeline parameter setting
    762     VARectangle dst_region;
    763     dst_region.x = 0;
    764     dst_region.y = 0;
    765     dst_region.width = mFilterParam.dstWidth;
    766     dst_region.height = mFilterParam.dstHeight;
    767 
    768     VARectangle src_region;
    769     src_region.x = 0;
    770     src_region.y = 0;
    771     src_region.width = mFilterParam.srcWidth;
    772     src_region.height = mFilterParam.srcHeight;
    773 
    774     if (isEOS) {
    775         pipeline->surface = 0;
    776         pipeline->pipeline_flags = VA_PIPELINE_FLAG_END;
    777     }
    778     else {
    779         pipeline->surface = input;
    780         pipeline->pipeline_flags = 0;
    781     }
    782 #ifdef TARGET_VPP_USE_GEN
    783     pipeline->surface_region = &src_region;
    784     pipeline->output_region = &dst_region;
    785     pipeline->surface_color_standard = VAProcColorStandardBT601;
    786     pipeline->output_color_standard = VAProcColorStandardBT601;
    787 #else
    788     pipeline->surface_region = NULL;
    789     pipeline->output_region = NULL;//&output_region;
    790     pipeline->surface_color_standard = VAProcColorStandardNone;
    791     pipeline->output_color_standard = VAProcColorStandardNone;
    792     /* real rotate state will be decided in psb video */
    793     pipeline->rotation_state = 0;
    794 #endif
    795     /* FIXME: set more meaningful background color */
    796     pipeline->output_background_color = 0;
    797     pipeline->filters = mFilterBuffers;
    798     pipeline->num_filters = mNumFilterBuffers;
    799     pipeline->forward_references = mForwardReferences;
    800     pipeline->num_forward_references = mNumForwardReferences;
    801     pipeline->backward_references = NULL;
    802     pipeline->num_backward_references = 0;
    803 
    804     //currently, we only transfer TOP field to frame, no frame rate change.
    805     if (flags & (OMX_BUFFERFLAG_TFF | OMX_BUFFERFLAG_BFF)) {
    806         pipeline->filter_flags = VA_TOP_FIELD;
    807     } else {
    808         pipeline->filter_flags = VA_FRAME_PICTURE;
    809     }
    810 
    811     if ((mFilters & FilterFrameRateConversion) != 0) {
    812         vaStatus = vaUnmapBuffer(mVADisplay, mFilterFrc);
    813         CHECK_VASTATUS("vaUnmapBuffer for frc parameter buffer");
    814     }
    815 
    816     vaStatus = vaUnmapBuffer(mVADisplay, pipelineId);
    817     CHECK_VASTATUS("vaUnmapBuffer for pipeline parameter buffer");
    818 
    819     ALOGV("before vaRenderPicture");
    820     // Send parameter to driver
    821     vaStatus = vaRenderPicture(mVADisplay, mVAContext, &pipelineId, 1);
    822     CHECK_VASTATUS("vaRenderPicture");
    823 
    824     ALOGV("before vaEndPicture");
    825     vaStatus = vaEndPicture(mVADisplay, mVAContext);
    826     CHECK_VASTATUS("vaEndPicture");
    827 
    828     if (isEOS) {
    829         vaStatus = vaSyncSurface(mVADisplay, mPrevOutput);
    830         CHECK_VASTATUS("vaSyncSurface");
    831         if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, pipelineId)) {
    832             ALOGE("%s: failed to destroy va buffer %d", __func__, pipelineId);
    833             return STATUS_ERROR;
    834         }
    835         return STATUS_OK;
    836     }
    837 
    838     mPrevOutput = output[0];
    839     mInputIndex++;
    840 
    841     Mutex::Autolock autoLock(mPipelineBufferLock);
    842     mPipelineBuffers.push_back(pipelineId);
    843 
    844     ALOGV("process, exit");
    845     return STATUS_OK;
    846 }
    847 
    848 status_t ISVWorker::fill(Vector<ISVBuffer*> outputBuffer, uint32_t outputCount) {
    849     ALOGV("fill, outputCount=%d, mOutputIndex=%d",outputCount, mOutputIndex);
    850     // get output surface
    851     VASurfaceID output[MAX_FRC_OUTPUT];
    852     VAStatus vaStatus;
    853     VASurfaceStatus surStatus;
    854 
    855     if (outputCount < 1)
    856         return STATUS_ERROR;
    857     // map GraphicBuffer to VASurface
    858     for (uint32_t i = 0; i < outputCount; i++) {
    859 
    860         output[i] = outputBuffer[i]->getSurface();
    861         if (output[i] == VA_INVALID_SURFACE) {
    862             ALOGE("invalid output buffer");
    863             return STATUS_ERROR;
    864         }
    865         //FIXME: only enable sync mode
    866 #if 0
    867         vaStatus = vaQuerySurfaceStatus(mVADisplay, output[i],&surStatus);
    868         CHECK_VASTATUS("vaQuerySurfaceStatus");
    869         if (surStatus == VASurfaceRendering) {
    870             ALOGV("Rendering %d", i);
    871             /* The behavior of driver is: all output of one process task are return in one interruption.
    872                The whole outputs of one FRC task are all ready or none of them is ready.
    873                If the behavior changed, it hurts the performance.
    874             */
    875             if (0 != i) {
    876                 ALOGW("*****Driver behavior changed. The performance is hurt.");
    877                 ALOGW("Please check driver behavior: all output of one task return in one interruption.");
    878             }
    879             vaStatus = STATUS_DATA_RENDERING;
    880             break;
    881         }
    882 
    883         if ((surStatus != VASurfaceRendering) && (surStatus != VASurfaceReady)) {
    884             ALOGE("surface statu Error %d", surStatus);
    885             vaStatus = STATUS_ERROR;
    886         }
    887 #endif
    888         vaStatus = vaSyncSurface(mVADisplay, output[i]);
    889         CHECK_VASTATUS("vaSyncSurface");
    890         vaStatus = STATUS_OK;
    891         mOutputCount++;
    892         //dumpYUVFrameData(output[i]);
    893     }
    894 
    895     {
    896         Mutex::Autolock autoLock(mPipelineBufferLock);
    897         if (vaStatus == STATUS_OK) {
    898             VABufferID pipelineBuffer = mPipelineBuffers.itemAt(0);
    899             if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, pipelineBuffer)) {
    900                 ALOGE("%s: failed to destroy va buffer %d", __func__, pipelineBuffer);
    901                 return STATUS_ERROR;
    902             }
    903             mPipelineBuffers.removeAt(0);
    904             mOutputIndex++;
    905         }
    906     }
    907 
    908     ALOGV("fill, exit");
    909     return vaStatus;
    910 }
    911 
    912 // Debug only
    913 #define FRAME_OUTPUT_FILE_NV12 "/storage/sdcard0/vpp_output.nv12"
    914 status_t ISVWorker::dumpYUVFrameData(VASurfaceID surfaceID) {
    915     status_t ret;
    916     if (surfaceID == VA_INVALID_SURFACE)
    917         return STATUS_ERROR;
    918 
    919     VAStatus vaStatus;
    920     VAImage image;
    921     unsigned char *data_ptr;
    922 
    923     vaStatus = vaDeriveImage(mVADisplay,
    924             surfaceID,
    925             &image);
    926     CHECK_VASTATUS("vaDeriveImage");
    927 
    928     vaStatus = vaMapBuffer(mVADisplay, image.buf, (void **)&data_ptr);
    929     CHECK_VASTATUS("vaMapBuffer");
    930 
    931     ret = writeNV12(mFilterParam.srcWidth, mFilterParam.srcHeight, data_ptr, image.pitches[0], image.pitches[1]);
    932     if (ret != STATUS_OK) {
    933         ALOGV("writeNV12 error");
    934         return STATUS_ERROR;
    935     }
    936 
    937     vaStatus = vaUnmapBuffer(mVADisplay, image.buf);
    938     CHECK_VASTATUS("vaUnMapBuffer");
    939 
    940     vaStatus = vaDestroyImage(mVADisplay,image.image_id);
    941     CHECK_VASTATUS("vaDestroyImage");
    942 
    943     return STATUS_OK;
    944 }
    945 
    946 status_t ISVWorker::reset() {
    947     status_t ret;
    948     ALOGV("reset");
    949     if (mOutputCount > 0) {
    950         ALOGI("======mVPPInputCount=%d, mVPPRenderCount=%d======",
    951                 mInputIndex, mOutputCount);
    952     }
    953     mInputIndex = 0;
    954     mOutputIndex = 0;
    955     mOutputCount = 0;
    956 
    957     {
    958         Mutex::Autolock autoLock(mPipelineBufferLock);
    959         while (!mPipelineBuffers.isEmpty()) {
    960             VABufferID pipelineBuffer = mPipelineBuffers.itemAt(0);
    961             if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, pipelineBuffer)) {
    962                 ALOGE("%s: failed to destroy va buffer %d", __func__, pipelineBuffer);
    963                 return STATUS_ERROR;
    964             }
    965             mPipelineBuffers.removeAt(0);
    966         }
    967     }
    968 
    969     if (mNumFilterBuffers != 0) {
    970         for (uint32_t i = 0; i < mNumFilterBuffers; i++) {
    971             if (VA_STATUS_SUCCESS != vaDestroyBuffer(mVADisplay, mFilterBuffers[i]))
    972                 ALOGW("%s: failed to destroy va buffer %d", __func__, mFilterBuffers[i]);
    973                 //return STATUS_ERROR;
    974         }
    975         mNumFilterBuffers = 0;
    976         memset(&mFilterBuffers, VA_INVALID_ID, VAProcFilterCount * sizeof(VABufferID));
    977         mFilterFrc = VA_INVALID_ID;
    978     }
    979 
    980     // we need to clear the cache for reference surfaces
    981     if (mForwardReferences != NULL) {
    982         free(mForwardReferences);
    983         mForwardReferences = NULL;
    984         mNumForwardReferences = 0;
    985     }
    986 
    987     if (mVAContext != VA_INVALID_ID) {
    988          vaDestroyContext(mVADisplay, mVAContext);
    989          mVAContext = VA_INVALID_ID;
    990     }
    991     VAStatus vaStatus = vaCreateContext(mVADisplay, mVAConfig, mWidth, mHeight, 0, NULL, 0, &mVAContext);
    992     CHECK_VASTATUS("vaCreateContext");
    993 
    994     return setupFilters();
    995 }
    996 
    997 uint32_t ISVWorker::getVppOutputFps() {
    998     uint32_t outputFps;
    999     //mFilterParam.frcRate is 1 if FRC is disabled or input FPS is not changed by VPP.
   1000     if (FRC_RATE_2_5X == mFilterParam.frcRate) {
   1001         outputFps = mFilterParam.frameRate * 5 / 2;
   1002     } else {
   1003         outputFps = mFilterParam.frameRate * mFilterParam.frcRate;
   1004     }
   1005 
   1006     ALOGV("vpp is on in settings %d %d %d", outputFps,  mFilterParam.frameRate, mFilterParam.frcRate);
   1007     return outputFps;
   1008 }
   1009 
   1010 
   1011 status_t ISVWorker::writeNV12(int width, int height, unsigned char *out_buf, int y_pitch, int uv_pitch) {
   1012     size_t result;
   1013     int frame_size;
   1014     unsigned char *y_start, *uv_start;
   1015     int h;
   1016 
   1017     FILE *ofile = fopen(FRAME_OUTPUT_FILE_NV12, "ab");
   1018     if(ofile == NULL) {
   1019         ALOGE("Open %s failed!", FRAME_OUTPUT_FILE_NV12);
   1020         return STATUS_ERROR;
   1021     }
   1022 
   1023     if (out_buf == NULL)
   1024     {
   1025         fclose(ofile);
   1026         return STATUS_ERROR;
   1027     }
   1028     if ((width % 2) || (height % 2))
   1029     {
   1030         fclose(ofile);
   1031         return STATUS_ERROR;
   1032     }
   1033     // Set frame size
   1034     frame_size = height * width * 3/2;
   1035 
   1036     /* write y */
   1037     y_start = out_buf;
   1038     for (h = 0; h < height; ++h) {
   1039         result = fwrite(y_start, sizeof(unsigned char), width, ofile);
   1040         y_start += y_pitch;
   1041     }
   1042 
   1043     /* write uv */
   1044     uv_start = out_buf + uv_pitch * height;
   1045     for (h = 0; h < height / 2; ++h) {
   1046         result = fwrite(uv_start, sizeof(unsigned char), width, ofile);
   1047         uv_start += uv_pitch;
   1048     }
   1049     // Close file
   1050     fclose(ofile);
   1051     return STATUS_OK;
   1052 }
   1053 
   1054