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