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