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