Home | History | Annotate | Download | only in videodecoder
      1 /*
      2 * Copyright (c) 2009-2011 Intel Corporation.  All rights reserved.
      3 *
      4 * Licensed under the Apache License, Version 2.0 (the "License");
      5 * you may not use this file except in compliance with the License.
      6 * You may obtain a copy of the License at
      7 *
      8 * http://www.apache.org/licenses/LICENSE-2.0
      9 *
     10 * Unless required by applicable law or agreed to in writing, software
     11 * distributed under the License is distributed on an "AS IS" BASIS,
     12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 * See the License for the specific language governing permissions and
     14 * limitations under the License.
     15 */
     16 
     17 #include "VideoDecoderWMV.h"
     18 #include "VideoDecoderTrace.h"
     19 #include <string.h>
     20 
     21 VideoDecoderWMV::VideoDecoderWMV(const char *mimeType)
     22     : VideoDecoderBase(mimeType, VBP_VC1),
     23       mBufferIDs(NULL),
     24       mNumBufferIDs(0),
     25       mConfigDataParsed(false),
     26       mRangeMapped(false),
     27       mDeblockedCurrPicIndex(0),
     28       mDeblockedLastPicIndex(1),
     29       mDeblockedForwardPicIndex(2) {
     30 }
     31 
     32 
     33 VideoDecoderWMV::~VideoDecoderWMV() {
     34     stop();
     35 }
     36 
     37 Decode_Status VideoDecoderWMV::start(VideoConfigBuffer *buffer) {
     38     Decode_Status status;
     39 
     40     status = VideoDecoderBase::start(buffer);
     41     CHECK_STATUS("VideoDecoderBase::start");
     42 
     43     if (buffer->data == NULL || buffer->size == 0) {
     44         WTRACE("No config data to start VA.");
     45         return DECODE_SUCCESS;
     46     }
     47 
     48     vbp_data_vc1 *data = NULL;
     49     status = parseBuffer(buffer->data, buffer->size, &data);
     50     CHECK_STATUS("parseBuffer");
     51 
     52     status = startVA(data);
     53     return status;
     54 }
     55 
     56 void VideoDecoderWMV::stop(void) {
     57     if (mBufferIDs) {
     58         delete [] mBufferIDs;
     59         mBufferIDs = NULL;
     60     }
     61     mNumBufferIDs = 0;
     62     mConfigDataParsed = false;
     63     mRangeMapped = false;
     64 
     65     mDeblockedCurrPicIndex = 0;
     66     mDeblockedLastPicIndex = 1;
     67     mDeblockedForwardPicIndex = 2;
     68 
     69     VideoDecoderBase::stop();
     70 }
     71 
     72 void VideoDecoderWMV::flush(void) {
     73     VideoDecoderBase::flush();
     74 
     75     mRangeMapped = false;
     76     mDeblockedCurrPicIndex = 0;
     77     mDeblockedLastPicIndex = 1;
     78     mDeblockedForwardPicIndex = 2;
     79 }
     80 
     81 Decode_Status VideoDecoderWMV::decode(VideoDecodeBuffer *buffer) {
     82     Decode_Status status;
     83     vbp_data_vc1 *data = NULL;
     84     bool useGraphicbuffer = mConfigBuffer.flag & USE_NATIVE_GRAPHIC_BUFFER;
     85     if (buffer == NULL) {
     86         return DECODE_INVALID_DATA;
     87     }
     88 
     89     status = parseBuffer(buffer->data, buffer->size, &data);
     90     CHECK_STATUS("parseBuffer");
     91 
     92     if (!mVAStarted) {
     93         status = startVA(data);
     94         CHECK_STATUS("startVA");
     95     }
     96 
     97     if (mSizeChanged && !useGraphicbuffer) {
     98         mSizeChanged = false;
     99         return DECODE_FORMAT_CHANGE;
    100     }
    101 
    102     if ((mVideoFormatInfo.width != data->se_data->CODED_WIDTH ||
    103         mVideoFormatInfo.height != data->se_data->CODED_HEIGHT) &&
    104         data->se_data->CODED_WIDTH &&
    105         data->se_data->CODED_HEIGHT) {
    106         ITRACE("video size is changed from %dx%d to %dx%d", mVideoFormatInfo.width, mVideoFormatInfo.height,
    107         data->se_data->CODED_WIDTH, data->se_data->CODED_HEIGHT);
    108         if (useGraphicbuffer && mStoreMetaData) {
    109             pthread_mutex_lock(&mFormatLock);
    110         }
    111         mVideoFormatInfo.width = data->se_data->CODED_WIDTH;
    112         mVideoFormatInfo.height = data->se_data->CODED_HEIGHT;
    113         bool needFlush = false;
    114         if (useGraphicbuffer) {
    115             if (mStoreMetaData) {
    116                 needFlush = true;
    117 
    118                 mVideoFormatInfo.valid = false;
    119                 pthread_mutex_unlock(&mFormatLock);
    120             } else {
    121                 needFlush = (mVideoFormatInfo.width > mVideoFormatInfo.surfaceWidth)
    122                          || (mVideoFormatInfo.height > mVideoFormatInfo.surfaceHeight);
    123             }
    124         }
    125 
    126         setRenderRect();
    127 
    128         if (needFlush) {
    129             if (mStoreMetaData) {
    130                 status = endDecodingFrame(false);
    131                 CHECK_STATUS("endDecodingFrame");
    132             } else {
    133                 flushSurfaceBuffers();
    134             }
    135             mSizeChanged = false;
    136             return DECODE_FORMAT_CHANGE;
    137         } else {
    138             mSizeChanged = true;
    139         }
    140     } else {
    141         if (useGraphicbuffer && mStoreMetaData) {
    142             mVideoFormatInfo.valid = true;
    143         }
    144     }
    145 
    146     status = decodeFrame(buffer, data);
    147     CHECK_STATUS("decodeFrame");
    148     return status;
    149 }
    150 
    151 Decode_Status VideoDecoderWMV::decodeFrame(VideoDecodeBuffer* buffer, vbp_data_vc1 *data) {
    152     Decode_Status status;
    153     mCurrentPTS = buffer->timeStamp;
    154     if (0 == data->num_pictures || NULL == data->pic_data) {
    155         WTRACE("Number of pictures is 0, buffer contains configuration data only?");
    156         return DECODE_SUCCESS;
    157     }
    158 
    159     if (data->pic_data[0].picture_is_skipped == VC1_PTYPE_SKIPPED) {
    160 
    161         // Do nothing for skip frame as the last frame will be rendered agian by natively
    162         // No needs to handle reference frame neither
    163         return DECODE_SUCCESS;
    164 #if 0
    165         //use the last P or I frame surface for skipped frame and treat it as P frame
    166         if (mLastReference == NULL) {
    167             // TODO: handle this case
    168             WTRACE("The last reference is unavailable to construct skipped frame.");
    169             return DECODE_SUCCESS;
    170         }
    171 
    172         status = acquireSurfaceBuffer();
    173         CHECK_STATUS("acquireSurfaceBuffer");
    174         mAcquiredBuffer->renderBuffer.timeStamp = mCurrentPTS;
    175         mAcquiredBuffer->renderBuffer.flag = 0;
    176         mAcquiredBuffer->renderBuffer.scanFormat = mLastReference->renderBuffer.scanFormat;
    177         mAcquiredBuffer->renderBuffer.surface = mLastReference->renderBuffer.surface;
    178         // No need to update mappedData for HW decoding
    179         //mAcquiredBuffer->mappedData.data = mLastReference->mappedData.data;
    180         mAcquiredBuffer->referenceFrame = true;
    181         // let outputSurfaceBuffer handle "asReference" for VC1
    182         status = outputSurfaceBuffer();
    183         return status;
    184 #endif
    185     }
    186 
    187     status = acquireSurfaceBuffer();
    188     CHECK_STATUS("acquireSurfaceBuffer");
    189 
    190     mAcquiredBuffer->renderBuffer.timeStamp = buffer->timeStamp;
    191     if (buffer->flag & HAS_DISCONTINUITY) {
    192         mAcquiredBuffer->renderBuffer.flag |= HAS_DISCONTINUITY;
    193     }
    194     if (buffer->flag & WANT_DECODE_ONLY) {
    195         mAcquiredBuffer->renderBuffer.flag |= WANT_DECODE_ONLY;
    196     }
    197     if (mSizeChanged) {
    198         mSizeChanged = false;
    199         mAcquiredBuffer->renderBuffer.flag |= IS_RESOLUTION_CHANGE;
    200     }
    201 
    202     if (data->num_pictures > 1) {
    203         if (data->pic_data[0].pic_parms->picture_fields.bits.is_first_field) {
    204             mAcquiredBuffer->renderBuffer.scanFormat = VA_TOP_FIELD;
    205         } else {
    206             mAcquiredBuffer->renderBuffer.scanFormat = VA_BOTTOM_FIELD;
    207         }
    208     } else {
    209         mAcquiredBuffer->renderBuffer.scanFormat = VA_FRAME_PICTURE;
    210     }
    211 
    212     mRangeMapped = (data->se_data->RANGE_MAPY_FLAG || data->se_data->RANGE_MAPUV_FLAG || data->se_data->RANGERED);
    213 
    214     int frameType = data->pic_data[0].pic_parms->picture_fields.bits.picture_type;
    215     mAcquiredBuffer->referenceFrame = (frameType == VC1_PTYPE_I || frameType == VC1_PTYPE_P);
    216 
    217     // TODO: handle multiple frames parsed from a sample buffer
    218     int numPictures = (data->num_pictures > 1) ? 2 : 1;
    219 
    220     for (int index = 0; index < numPictures; index++) {
    221         status = decodePicture(data, index);
    222         if (status != DECODE_SUCCESS) {
    223             endDecodingFrame(true);
    224             return status;
    225         }
    226     }
    227 
    228     if (mRangeMapped) {
    229         updateDeblockedPicIndexes(frameType);
    230     }
    231 
    232     // let outputSurfaceBuffer handle "asReference" for VC1
    233     status = outputSurfaceBuffer();
    234     return status;
    235 }
    236 
    237 
    238 Decode_Status VideoDecoderWMV::decodePicture(vbp_data_vc1 *data, int32_t picIndex) {
    239     VAStatus vaStatus = VA_STATUS_SUCCESS;
    240     Decode_Status status;
    241     int32_t bufferIDCount = 0;
    242     vbp_picture_data_vc1 *picData = &(data->pic_data[picIndex]);
    243     VAPictureParameterBufferVC1 *picParams = picData->pic_parms;
    244 
    245     if (picParams == NULL) {
    246         return DECODE_PARSER_FAIL;
    247     }
    248 
    249     status = allocateVABufferIDs(picData->num_slices * 2 + 2);
    250     CHECK_STATUS("allocateVABufferIDs");
    251 
    252     status = setReference(picParams, picIndex, mAcquiredBuffer->renderBuffer.surface);
    253     CHECK_STATUS("setReference");
    254 
    255     if (mRangeMapped) {
    256         // keep the destination surface for the picture after decoding and in-loop filtering
    257         picParams->inloop_decoded_picture = mExtraSurfaces[mDeblockedCurrPicIndex];
    258     } else {
    259         picParams->inloop_decoded_picture = VA_INVALID_SURFACE;
    260     }
    261 
    262     vaStatus = vaBeginPicture(mVADisplay, mVAContext, mAcquiredBuffer->renderBuffer.surface);
    263     CHECK_VA_STATUS("vaBeginPicture");
    264     // setting mDecodingFrame to true so vaEndPicture will be invoked to end the picture decoding.
    265     mDecodingFrame = true;
    266 
    267     vaStatus = vaCreateBuffer(
    268             mVADisplay,
    269             mVAContext,
    270             VAPictureParameterBufferType,
    271             sizeof(VAPictureParameterBufferVC1),
    272             1,
    273             picParams,
    274             &mBufferIDs[bufferIDCount]);
    275     CHECK_VA_STATUS("vaCreatePictureParameterBuffer");
    276     bufferIDCount++;
    277 
    278     if (picParams->bitplane_present.value) {
    279         vaStatus = vaCreateBuffer(
    280                 mVADisplay,
    281                 mVAContext,
    282                 VABitPlaneBufferType,
    283                 picData->size_bitplanes,
    284                 1,
    285                 picData->packed_bitplanes,
    286                 &mBufferIDs[bufferIDCount]);
    287         CHECK_VA_STATUS("vaCreateBitPlaneBuffer");
    288         bufferIDCount++;
    289     }
    290 
    291     for (uint32_t i = 0; i < picData->num_slices; i++) {
    292         vaStatus = vaCreateBuffer(
    293                 mVADisplay,
    294                 mVAContext,
    295                 VASliceParameterBufferType,
    296                 sizeof(VASliceParameterBufferVC1),
    297                 1,
    298                 &(picData->slc_data[i].slc_parms),
    299                 &mBufferIDs[bufferIDCount]);
    300         CHECK_VA_STATUS("vaCreateSliceParameterBuffer");
    301         bufferIDCount++;
    302 
    303         vaStatus = vaCreateBuffer(
    304                 mVADisplay,
    305                 mVAContext,
    306                 VASliceDataBufferType,
    307                 //size
    308                 picData->slc_data[i].slice_size,
    309                 //num_elements
    310                 1,
    311                 //slice data buffer pointer
    312                 //Note that this is the original data buffer ptr;
    313                 // offset to the actual slice data is provided in
    314                 // slice_data_offset in VASliceParameterBufferVC1
    315                 picData->slc_data[i].buffer_addr + picData->slc_data[i].slice_offset,
    316                 &mBufferIDs[bufferIDCount]);
    317         CHECK_VA_STATUS("vaCreateSliceDataBuffer");
    318         bufferIDCount++;
    319     }
    320 
    321     vaStatus = vaRenderPicture(
    322             mVADisplay,
    323             mVAContext,
    324             mBufferIDs,
    325             bufferIDCount);
    326     CHECK_VA_STATUS("vaRenderPicture");
    327 
    328     vaStatus = vaEndPicture(mVADisplay, mVAContext);
    329     mDecodingFrame = false;
    330     CHECK_VA_STATUS("vaRenderPicture");
    331 
    332     return DECODE_SUCCESS;
    333 }
    334 
    335 
    336 Decode_Status VideoDecoderWMV::setReference(
    337         VAPictureParameterBufferVC1 *params,
    338         int32_t picIndex,
    339         VASurfaceID current) {
    340     int frameType = params->picture_fields.bits.picture_type;
    341     switch (frameType) {
    342         case VC1_PTYPE_I:
    343             params->forward_reference_picture = current;
    344             params->backward_reference_picture = current;
    345             break;
    346         case VC1_PTYPE_P:
    347             // check REFDIST in the picture parameter buffer
    348             if (0 != params->reference_fields.bits.reference_distance_flag &&
    349                 0 != params->reference_fields.bits.reference_distance) {
    350                 /* The previous decoded frame (distance is up to 16 but not 0) is used
    351                             for reference. Not supported here.
    352                             */
    353                 return DECODE_NO_REFERENCE;
    354             }
    355             if (1 == picIndex) {
    356                 // handle interlace field coding case
    357                 if (1 == params->reference_fields.bits.num_reference_pictures ||
    358                     1 == params->reference_fields.bits.reference_field_pic_indicator) {
    359                     /*
    360                                     two reference fields or the second closest I/P field is used for
    361                                     prediction. Set forward reference picture to INVALID so it will be
    362                                     updated to a valid previous reconstructed reference frame later.
    363                                     */
    364                     params->forward_reference_picture = VA_INVALID_SURFACE;
    365                 } else {
    366                    /* the closest I/P is used for reference so it must be the
    367                                   complementary field in the same surface.
    368                                  */
    369                     params->forward_reference_picture = current;
    370                 }
    371             }
    372             if (VA_INVALID_SURFACE == params->forward_reference_picture) {
    373                 if (mLastReference == NULL) {
    374                     return DECODE_NO_REFERENCE;
    375                 }
    376                 params->forward_reference_picture = mLastReference->renderBuffer.surface;
    377             }
    378             params->backward_reference_picture = VA_INVALID_SURFACE;
    379             break;
    380         case VC1_PTYPE_B:
    381             if (mForwardReference == NULL || mLastReference == NULL) {
    382                 return DECODE_NO_REFERENCE;
    383             }
    384             params->forward_reference_picture = mForwardReference->renderBuffer.surface;
    385             params->backward_reference_picture = mLastReference->renderBuffer.surface;
    386             break;
    387         case VC1_PTYPE_BI:
    388             params->forward_reference_picture = VA_INVALID_SURFACE;
    389             params->backward_reference_picture = VA_INVALID_SURFACE;
    390             break;
    391         case VC1_PTYPE_SKIPPED:
    392             //Will never happen here
    393             break;
    394         default:
    395             break;
    396     }
    397     return DECODE_SUCCESS;
    398 }
    399 
    400 void VideoDecoderWMV::updateDeblockedPicIndexes(int frameType) {
    401     int32_t curPicIndex = mDeblockedCurrPicIndex;
    402 
    403     /* Out Loop (range map) buffers */
    404     if (frameType != VC1_PTYPE_SKIPPED) {
    405         if ((frameType == VC1_PTYPE_I) || (frameType == VC1_PTYPE_P)) {
    406             mDeblockedCurrPicIndex = mDeblockedLastPicIndex;
    407             mDeblockedLastPicIndex = curPicIndex;
    408         } else {
    409             mDeblockedCurrPicIndex = mDeblockedForwardPicIndex;
    410             mDeblockedForwardPicIndex = curPicIndex;
    411         }
    412     }
    413 }
    414 
    415 Decode_Status VideoDecoderWMV::updateConfigData(
    416         uint8_t *configData,
    417         int32_t configDataLen,
    418         uint8_t **newConfigData,
    419         int32_t* newConfigDataLen) {
    420     int32_t i = 0;
    421     uint8_t *p = configData;
    422 
    423     /* Check for start codes.  If one exist, then this is VC-1 and not WMV. */
    424     while (i < configDataLen - 2) {
    425         if ((p[i] == 0) &&
    426             (p[i + 1] == 0) &&
    427             (p[i + 2] == 1)) {
    428             *newConfigData = NULL;
    429             *newConfigDataLen = 0;
    430             return DECODE_SUCCESS;
    431         }
    432         i++;
    433     }
    434 
    435     *newConfigDataLen = configDataLen + 9;
    436     p = *newConfigData = new uint8_t [*newConfigDataLen];
    437     if (!p) {
    438        return DECODE_MEMORY_FAIL;
    439     }
    440 
    441     /* If we get here we have 4+ bytes of codec data that must be formatted */
    442     /* to pass through as an RCV sequence header. */
    443     p[0] = 0;
    444     p[1] = 0;
    445     p[2] = 1;
    446     p[3] = 0x0f;  /* Start code. */
    447     p[4] = (mVideoFormatInfo.width >> 8) & 0x0ff;
    448     p[5] = mVideoFormatInfo.width & 0x0ff;
    449     p[6] = (mVideoFormatInfo.height >> 8) & 0x0ff;
    450     p[7] = mVideoFormatInfo.height & 0x0ff;
    451 
    452     memcpy(p + 8, configData, configDataLen);
    453     *(p + configDataLen + 8) = 0x80;
    454 
    455     return DECODE_SUCCESS;
    456 }
    457 
    458 Decode_Status VideoDecoderWMV::startVA(vbp_data_vc1 *data) {
    459     updateFormatInfo(data);
    460 
    461     VAProfile vaProfile;
    462     switch (data->se_data->PROFILE) {
    463         case 0:
    464         vaProfile = VAProfileVC1Simple;
    465         break;
    466         case 1:
    467         vaProfile = VAProfileVC1Main;
    468         break;
    469         default:
    470         vaProfile = VAProfileVC1Advanced;
    471         break;
    472     }
    473 
    474     return VideoDecoderBase::setupVA(VC1_SURFACE_NUMBER, vaProfile, VC1_EXTRA_SURFACE_NUMBER);
    475 }
    476 
    477 void VideoDecoderWMV::updateFormatInfo(vbp_data_vc1 *data) {
    478     ITRACE("updateFormatInfo: current size: %d x %d, new size: %d x %d",
    479         mVideoFormatInfo.width, mVideoFormatInfo.height,
    480         data->se_data->CODED_WIDTH, data->se_data->CODED_HEIGHT);
    481 
    482     mVideoFormatInfo.cropBottom = data->se_data->CODED_HEIGHT > mVideoFormatInfo.height ?
    483                                                                            data->se_data->CODED_HEIGHT - mVideoFormatInfo.height : 0;
    484     mVideoFormatInfo.cropRight = data->se_data->CODED_WIDTH > mVideoFormatInfo.width ?
    485                                                                       data->se_data->CODED_WIDTH - mVideoFormatInfo.width : 0;
    486 
    487      if ((mVideoFormatInfo.width != data->se_data->CODED_WIDTH ||
    488         mVideoFormatInfo.height != data->se_data->CODED_HEIGHT) &&
    489         data->se_data->CODED_WIDTH &&
    490         data->se_data->CODED_HEIGHT) {
    491         // encoded image size
    492         mVideoFormatInfo.width = data->se_data->CODED_WIDTH;
    493         mVideoFormatInfo.height = data->se_data->CODED_HEIGHT;
    494         mSizeChanged = true;
    495         ITRACE("Video size is changed.");
    496     }
    497 
    498     // video_range has default value of 0. Y ranges from 16 to 235.
    499     mVideoFormatInfo.videoRange = 0;
    500 
    501     switch (data->se_data->MATRIX_COEF) {
    502         case 1:
    503             mVideoFormatInfo.colorMatrix = VA_SRC_BT709;
    504             break;
    505         // ITU-R BT.1700, ITU-R BT.601-5, and SMPTE 293M-1996.
    506         case 6:
    507             mVideoFormatInfo.colorMatrix = VA_SRC_BT601;
    508             break;
    509         default:
    510             // unknown color matrix, set to 0 so color space flag will not be set.
    511             mVideoFormatInfo.colorMatrix = 0;
    512             break;
    513     }
    514 
    515     mVideoFormatInfo.aspectX = data->se_data->ASPECT_HORIZ_SIZE;
    516     mVideoFormatInfo.aspectY = data->se_data->ASPECT_VERT_SIZE;
    517     mVideoFormatInfo.bitrate = 0; //data->se_data->bitrate;
    518     mVideoFormatInfo.valid = true;
    519 
    520     setRenderRect();
    521     setColorSpaceInfo(mVideoFormatInfo.colorMatrix, mVideoFormatInfo.videoRange);
    522 }
    523 
    524 Decode_Status VideoDecoderWMV::allocateVABufferIDs(int32_t number) {
    525     if (mNumBufferIDs > number) {
    526         return DECODE_SUCCESS;
    527     }
    528     if (mBufferIDs) {
    529         delete [] mBufferIDs;
    530     }
    531     mBufferIDs = NULL;
    532     mNumBufferIDs = 0;
    533     mBufferIDs = new VABufferID [number];
    534     if (mBufferIDs == NULL) {
    535         return DECODE_MEMORY_FAIL;
    536     }
    537     mNumBufferIDs = number;
    538     return DECODE_SUCCESS;
    539 }
    540 
    541 Decode_Status VideoDecoderWMV::parseBuffer(uint8_t *data, int32_t size, vbp_data_vc1 **vbpData) {
    542     Decode_Status status;
    543 
    544     if (data == NULL || size == 0) {
    545         return DECODE_INVALID_DATA;
    546     }
    547 
    548     if (mConfigDataParsed) {
    549         status = VideoDecoderBase::parseBuffer(data, size, false, (void**)vbpData);
    550         CHECK_STATUS("VideoDecoderBase::parseBuffer");
    551     } else {
    552         uint8_t *newData = NULL;
    553         int32_t newSize = 0;
    554         status = updateConfigData(data, size, &newData, &newSize);
    555         CHECK_STATUS("updateConfigData");
    556 
    557         if (newSize) {
    558             status = VideoDecoderBase::parseBuffer(newData, newSize, true, (void**)vbpData);
    559             delete [] newData;
    560         } else {
    561             status = VideoDecoderBase::parseBuffer(data, size, true, (void**)vbpData);
    562         }
    563         CHECK_STATUS("VideoDecoderBase::parseBuffer");
    564         mConfigDataParsed = true;
    565     }
    566     return DECODE_SUCCESS;
    567 }
    568 
    569 
    570 Decode_Status VideoDecoderWMV::checkHardwareCapability() {
    571 #ifndef USE_GEN_HW
    572     VAStatus vaStatus;
    573     VAConfigAttrib cfgAttribs[2];
    574     cfgAttribs[0].type = VAConfigAttribMaxPictureWidth;
    575     cfgAttribs[1].type = VAConfigAttribMaxPictureHeight;
    576     vaStatus = vaGetConfigAttributes(mVADisplay, VAProfileVC1Advanced,
    577             VAEntrypointVLD, cfgAttribs, 2);
    578     CHECK_VA_STATUS("vaGetConfigAttributes");
    579     if (cfgAttribs[0].value * cfgAttribs[1].value < (uint32_t)mVideoFormatInfo.width * (uint32_t)mVideoFormatInfo.height) {
    580         ETRACE("hardware supports resolution %d * %d smaller than the clip resolution %d * %d",
    581                 cfgAttribs[0].value, cfgAttribs[1].value, mVideoFormatInfo.width, mVideoFormatInfo.height);
    582         return DECODE_DRIVER_FAIL;
    583     }
    584 #endif
    585     return DECODE_SUCCESS;
    586 }
    587 
    588 
    589