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 
VideoDecoderWMV(const char * mimeType)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 
~VideoDecoderWMV()33 VideoDecoderWMV::~VideoDecoderWMV() {
34     stop();
35 }
36 
start(VideoConfigBuffer * buffer)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 
stop(void)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 
flush(void)72 void VideoDecoderWMV::flush(void) {
73     VideoDecoderBase::flush();
74 
75     mRangeMapped = false;
76     mDeblockedCurrPicIndex = 0;
77     mDeblockedLastPicIndex = 1;
78     mDeblockedForwardPicIndex = 2;
79 }
80 
decode(VideoDecodeBuffer * buffer)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 
decodeFrame(VideoDecodeBuffer * buffer,vbp_data_vc1 * data)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 
decodePicture(vbp_data_vc1 * data,int32_t picIndex)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 
setReference(VAPictureParameterBufferVC1 * params,int32_t picIndex,VASurfaceID current)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 
updateDeblockedPicIndexes(int frameType)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 
updateConfigData(uint8_t * configData,int32_t configDataLen,uint8_t ** newConfigData,int32_t * newConfigDataLen)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 
startVA(vbp_data_vc1 * data)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 
updateFormatInfo(vbp_data_vc1 * data)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 
allocateVABufferIDs(int32_t number)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 
parseBuffer(uint8_t * data,int32_t size,vbp_data_vc1 ** vbpData)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 
checkHardwareCapability()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