1 /*
2 * Copyright (c) 2022-2023, Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 */
22 //!
23 //! \file ddi_decode_avc_specific.cpp
24 //! \brief AVC class definition for DDI media decoder
25 //!
26
27 #include "ddi_decode_functions.h"
28 #include "media_libva_util_next.h"
29 #include "media_libva_interface_next.h"
30 #include "ddi_decode_avc_specific.h"
31 #include "ddi_decode_trace_specific.h"
32
33 namespace decode
34 {
35
ParseSliceParams(DDI_MEDIA_CONTEXT * mediaCtx,VASliceParameterBufferH264 * slcParam,uint32_t numSlices)36 VAStatus DdiDecodeAvc::ParseSliceParams(
37 DDI_MEDIA_CONTEXT *mediaCtx,
38 VASliceParameterBufferH264 *slcParam,
39 uint32_t numSlices)
40 {
41 DDI_CODEC_FUNC_ENTER;
42
43 PCODEC_AVC_SLICE_PARAMS avcSliceParams;
44 avcSliceParams = (PCODEC_AVC_SLICE_PARAMS)(m_decodeCtx->DecodeParams.m_sliceParams);
45 avcSliceParams += m_decodeCtx->DecodeParams.m_numSlices;
46
47 if ((slcParam == nullptr) || (avcSliceParams == nullptr))
48 {
49 DDI_CODEC_ASSERTMESSAGE("Invalid Parameter for Parsing AVC Slice parameter\n");
50 return VA_STATUS_ERROR_INVALID_PARAMETER;
51 }
52 VASliceParameterBufferH264 *slc;
53 slc = (VASliceParameterBufferH264 *)slcParam;
54
55 VASliceParameterBufferBase *slcBase;
56 slcBase = (VASliceParameterBufferBase *)slcParam;
57
58 PCODEC_AVC_PIC_PARAMS avcPicParams;
59 avcPicParams = (PCODEC_AVC_PIC_PARAMS)(m_decodeCtx->DecodeParams.m_picParams);
60 avcPicParams->pic_fields.IntraPicFlag = (slc->slice_type == 2) ? 1 : 0;
61
62 bool useCABAC = (bool)(avcPicParams->pic_fields.entropy_coding_mode_flag);
63
64 uint32_t sliceBaseOffset;
65 sliceBaseOffset = GetBsBufOffset(m_groupIndex);
66
67 uint32_t i, slcCount, refCount;
68 for (slcCount = 0; slcCount < numSlices; slcCount++)
69 {
70 if (m_decodeCtx->bShortFormatInUse)
71 {
72 avcSliceParams->slice_data_size = slcBase->slice_data_size;
73 avcSliceParams->slice_data_offset = sliceBaseOffset +
74 slcBase->slice_data_offset;
75 if (slcBase->slice_data_flag)
76 {
77 DDI_CODEC_NORMALMESSAGE("The whole slice is not in the bitstream buffer for this Execute call");
78 }
79 slcBase++;
80 }
81 else
82 {
83 if (useCABAC)
84 {
85 // add the alignment bit
86 slc->slice_data_bit_offset = MOS_ALIGN_CEIL(slc->slice_data_bit_offset, 8);
87 }
88
89 // remove 1 byte of NAL unit code
90 slc->slice_data_bit_offset = slc->slice_data_bit_offset - 8;
91
92 avcSliceParams->slice_data_size = slc->slice_data_size;
93 avcSliceParams->slice_data_offset = sliceBaseOffset + slc->slice_data_offset;
94
95 if (slc->slice_data_flag)
96 {
97 DDI_CODEC_NORMALMESSAGE("The whole slice is not in the bitstream buffer for this Execute call");
98 }
99
100 avcSliceParams->slice_data_bit_offset = slc->slice_data_bit_offset;
101 avcSliceParams->first_mb_in_slice = slc->first_mb_in_slice;
102 avcSliceParams->NumMbsForSlice = 0; // not in LibVA slc->NumMbsForSlice;
103 avcSliceParams->slice_type = slc->slice_type;
104 avcSliceParams->direct_spatial_mv_pred_flag = slc->direct_spatial_mv_pred_flag;
105 avcSliceParams->num_ref_idx_l0_active_minus1 = slc->num_ref_idx_l0_active_minus1;
106 avcSliceParams->num_ref_idx_l1_active_minus1 = slc->num_ref_idx_l1_active_minus1;
107
108 if (slcCount == 0)
109 {
110 avcPicParams->num_ref_idx_l0_active_minus1 = avcSliceParams->num_ref_idx_l0_active_minus1;
111 avcPicParams->num_ref_idx_l1_active_minus1 = avcSliceParams->num_ref_idx_l1_active_minus1;
112 }
113 avcSliceParams->cabac_init_idc = slc->cabac_init_idc;
114 avcSliceParams->slice_qp_delta = slc->slice_qp_delta;
115 avcSliceParams->disable_deblocking_filter_idc = slc->disable_deblocking_filter_idc;
116 avcSliceParams->slice_alpha_c0_offset_div2 = slc->slice_alpha_c0_offset_div2;
117 avcSliceParams->slice_beta_offset_div2 = slc->slice_beta_offset_div2;
118 // reference list 0
119 refCount = std::min(avcSliceParams->num_ref_idx_l0_active_minus1 + 1, CODEC_MAX_NUM_REF_FIELD);
120 for (i = 0; i < refCount; i++)
121 {
122 SetupCodecPicture(
123 mediaCtx,
124 &(m_decodeCtx->RTtbl),
125 &(avcSliceParams->RefPicList[0][i]),
126 slc->RefPicList0[i],
127 avcPicParams->pic_fields.field_pic_flag,
128 false,
129 true);
130
131 GetSlcRefIdx(&(avcPicParams->RefFrameList[0]), &(avcSliceParams->RefPicList[0][i]));
132 }
133 // reference list 1
134 refCount = std::min(avcSliceParams->num_ref_idx_l1_active_minus1 + 1, CODEC_MAX_NUM_REF_FIELD);
135 for (i = 0; i < refCount; i++)
136 {
137 SetupCodecPicture(
138 mediaCtx,
139 &(m_decodeCtx->RTtbl),
140 &(avcSliceParams->RefPicList[1][i]),
141 slc->RefPicList1[i],
142 avcPicParams->pic_fields.field_pic_flag,
143 false,
144 true);
145
146 GetSlcRefIdx(&(avcPicParams->RefFrameList[0]), &(avcSliceParams->RefPicList[1][i]));
147 }
148
149 avcSliceParams->luma_log2_weight_denom = slc->luma_log2_weight_denom;
150 avcSliceParams->chroma_log2_weight_denom = slc->chroma_log2_weight_denom;
151 for (i = 0; i < 32; i++)
152 {
153 // list 0
154 avcSliceParams->Weights[0][i][0][0] = slc->luma_weight_l0[i]; // Y weight
155 avcSliceParams->Weights[0][i][0][1] = slc->luma_offset_l0[i]; // Y offset
156
157 avcSliceParams->Weights[0][i][1][0] = slc->chroma_weight_l0[i][0]; // Cb weight
158 avcSliceParams->Weights[0][i][1][1] = slc->chroma_offset_l0[i][0]; // Cb offset
159
160 avcSliceParams->Weights[0][i][2][0] = slc->chroma_weight_l0[i][1]; // Cr weight
161 avcSliceParams->Weights[0][i][2][1] = slc->chroma_offset_l0[i][1]; // Cr offset
162
163 // list 1
164 avcSliceParams->Weights[1][i][0][0] = slc->luma_weight_l1[i]; // Y weight
165 avcSliceParams->Weights[1][i][0][1] = slc->luma_offset_l1[i]; // Y offset
166
167 avcSliceParams->Weights[1][i][1][0] = slc->chroma_weight_l1[i][0]; // Cb weight
168 avcSliceParams->Weights[1][i][1][1] = slc->chroma_offset_l1[i][0]; // Cb offset
169
170 avcSliceParams->Weights[1][i][2][0] = slc->chroma_weight_l1[i][1]; // Cr weight
171 avcSliceParams->Weights[1][i][2][1] = slc->chroma_offset_l1[i][1]; // Cr offset
172 }
173 slc++;
174 }
175 avcSliceParams->slice_id = 0;
176 avcSliceParams++;
177 }
178
179 return VA_STATUS_SUCCESS;
180 }
181
ParsePicParams(DDI_MEDIA_CONTEXT * mediaCtx,VAPictureParameterBufferH264 * picParam)182 VAStatus DdiDecodeAvc::ParsePicParams(
183 DDI_MEDIA_CONTEXT *mediaCtx,
184 VAPictureParameterBufferH264 *picParam)
185 {
186 DDI_CODEC_FUNC_ENTER;
187
188 PCODEC_AVC_PIC_PARAMS avcPicParams;
189 avcPicParams = (PCODEC_AVC_PIC_PARAMS)(m_decodeCtx->DecodeParams.m_picParams);
190
191 if ((picParam == nullptr) ||
192 (avcPicParams == nullptr))
193 return VA_STATUS_ERROR_INVALID_PARAMETER;
194
195 SetupCodecPicture(mediaCtx,
196 &(m_decodeCtx->RTtbl),
197 &avcPicParams->CurrPic,
198 picParam->CurrPic,
199 picParam->pic_fields.bits.field_pic_flag,
200 false,
201 false);
202
203 // Check the current frame index
204 // Add the invalid surface id to RecList
205 if (avcPicParams->CurrPic.FrameIdx < CODEC_AVC_NUM_UNCOMPRESSED_SURFACE)
206 {
207 m_decodeCtx->RecListSurfaceID[avcPicParams->CurrPic.FrameIdx] =
208 picParam->CurrPic.picture_id;
209 }
210
211 uint32_t i;
212 uint32_t j;
213 avcPicParams->UsedForReferenceFlags = 0x0;
214 for (i = 0; i < CODEC_MAX_NUM_REF_FRAME; i++)
215 {
216 if (picParam->ReferenceFrames[i].picture_id != VA_INVALID_SURFACE)
217 {
218 UpdateRegisteredRTSurfaceFlag(&(m_decodeCtx->RTtbl),
219 MediaLibvaCommonNext::GetSurfaceFromVASurfaceID(mediaCtx,
220 picParam->ReferenceFrames[i].picture_id));
221 }
222
223 SetupCodecPicture(
224 mediaCtx,
225 &(m_decodeCtx->RTtbl),
226 &(avcPicParams->RefFrameList[i]),
227 picParam->ReferenceFrames[i],
228 picParam->pic_fields.bits.field_pic_flag,
229 true,
230 false);
231
232 if ((picParam->ReferenceFrames[i].flags & VA_PICTURE_H264_SHORT_TERM_REFERENCE) ||
233 (picParam->ReferenceFrames[i].flags & VA_PICTURE_H264_LONG_TERM_REFERENCE))
234 {
235 if (!m_decodeCtx->bShortFormatInUse)
236 {
237 avcPicParams->UsedForReferenceFlags = avcPicParams->UsedForReferenceFlags | (3 << (i * 2));
238 }
239 else if ((picParam->ReferenceFrames[i].flags & VA_PICTURE_H264_BOTTOM_FIELD) ||
240 (picParam->ReferenceFrames[i].flags & VA_PICTURE_H264_TOP_FIELD))
241 {
242 if (picParam->ReferenceFrames[i].flags & VA_PICTURE_H264_BOTTOM_FIELD)
243 avcPicParams->UsedForReferenceFlags = avcPicParams->UsedForReferenceFlags | (2 << (i * 2));
244
245 if (picParam->ReferenceFrames[i].flags & VA_PICTURE_H264_TOP_FIELD)
246 avcPicParams->UsedForReferenceFlags = avcPicParams->UsedForReferenceFlags | (1 << (i * 2));
247 }
248 else
249 {
250 avcPicParams->UsedForReferenceFlags = avcPicParams->UsedForReferenceFlags | (3 << (i * 2));
251 }
252 }
253 }
254
255 // Accoding to RecList, if the surface id is invalid, set PicFlags equal to PICTURE_INVALID
256 for (i = 0; i < CODEC_MAX_NUM_REF_FRAME; i++)
257 {
258 // Check the surface id of reference list
259 if (avcPicParams->RefFrameList[i].FrameIdx < CODEC_AVC_NUM_UNCOMPRESSED_SURFACE &&
260 VA_INVALID_ID == m_decodeCtx->RecListSurfaceID[avcPicParams->RefFrameList[i].FrameIdx])
261 {
262 // Set invalid flag
263 avcPicParams->RefFrameList[i].PicFlags = PICTURE_INVALID;
264 }
265 }
266
267 avcPicParams->pic_width_in_mbs_minus1 = picParam->picture_width_in_mbs_minus1;
268 avcPicParams->pic_height_in_mbs_minus1 = picParam->picture_height_in_mbs_minus1;
269 avcPicParams->bit_depth_luma_minus8 = picParam->bit_depth_luma_minus8;
270 avcPicParams->bit_depth_chroma_minus8 = picParam->bit_depth_chroma_minus8;
271 avcPicParams->num_ref_frames = picParam->num_ref_frames;
272 avcPicParams->CurrFieldOrderCnt[0] = picParam->CurrPic.TopFieldOrderCnt;
273 avcPicParams->CurrFieldOrderCnt[1] = picParam->CurrPic.BottomFieldOrderCnt;
274 for (i = 0; i < CODEC_MAX_NUM_REF_FRAME; i++)
275 {
276 avcPicParams->FieldOrderCntList[i][0] = picParam->ReferenceFrames[i].TopFieldOrderCnt;
277 avcPicParams->FieldOrderCntList[i][1] = picParam->ReferenceFrames[i].BottomFieldOrderCnt;
278 }
279
280 avcPicParams->seq_fields.chroma_format_idc = picParam->seq_fields.bits.chroma_format_idc;
281 avcPicParams->seq_fields.residual_colour_transform_flag = picParam->seq_fields.bits.residual_colour_transform_flag;
282 avcPicParams->seq_fields.frame_mbs_only_flag = picParam->seq_fields.bits.frame_mbs_only_flag;
283 avcPicParams->seq_fields.mb_adaptive_frame_field_flag = picParam->seq_fields.bits.mb_adaptive_frame_field_flag;
284 avcPicParams->seq_fields.direct_8x8_inference_flag = picParam->seq_fields.bits.direct_8x8_inference_flag;
285 avcPicParams->seq_fields.log2_max_frame_num_minus4 = picParam->seq_fields.bits.log2_max_frame_num_minus4;
286 avcPicParams->seq_fields.pic_order_cnt_type = picParam->seq_fields.bits.pic_order_cnt_type;
287 avcPicParams->seq_fields.log2_max_pic_order_cnt_lsb_minus4 = picParam->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4;
288 avcPicParams->seq_fields.delta_pic_order_always_zero_flag = picParam->seq_fields.bits.delta_pic_order_always_zero_flag;
289
290 avcPicParams->num_slice_groups_minus1 = 0;
291 avcPicParams->slice_group_map_type = 0;
292 avcPicParams->slice_group_change_rate_minus1 = 0;
293 avcPicParams->pic_init_qp_minus26 = picParam->pic_init_qp_minus26;
294 avcPicParams->chroma_qp_index_offset = picParam->chroma_qp_index_offset;
295 avcPicParams->second_chroma_qp_index_offset = picParam->second_chroma_qp_index_offset;
296
297 avcPicParams->pic_fields.entropy_coding_mode_flag = picParam->pic_fields.bits.entropy_coding_mode_flag;
298 avcPicParams->pic_fields.weighted_pred_flag = picParam->pic_fields.bits.weighted_pred_flag;
299 avcPicParams->pic_fields.weighted_bipred_idc = picParam->pic_fields.bits.weighted_bipred_idc;
300 avcPicParams->pic_fields.transform_8x8_mode_flag = picParam->pic_fields.bits.transform_8x8_mode_flag;
301 avcPicParams->pic_fields.field_pic_flag = picParam->pic_fields.bits.field_pic_flag;
302 avcPicParams->pic_fields.constrained_intra_pred_flag = picParam->pic_fields.bits.constrained_intra_pred_flag;
303 avcPicParams->pic_fields.pic_order_present_flag = picParam->pic_fields.bits.pic_order_present_flag;
304 avcPicParams->pic_fields.deblocking_filter_control_present_flag = picParam->pic_fields.bits.deblocking_filter_control_present_flag;
305 avcPicParams->pic_fields.redundant_pic_cnt_present_flag = picParam->pic_fields.bits.redundant_pic_cnt_present_flag;
306 avcPicParams->pic_fields.reference_pic_flag = picParam->pic_fields.bits.reference_pic_flag;
307
308 for (i = 0; i < CODEC_MAX_NUM_REF_FRAME; i++)
309 {
310 avcPicParams->FrameNumList[i] = picParam->ReferenceFrames[i].frame_idx;
311 }
312
313 avcPicParams->frame_num = picParam->frame_num;
314
315 #if MOS_EVENT_TRACE_DUMP_SUPPORTED
316 // Picture Info
317 DECODE_EVENTDATA_INFO_PICTUREVA eventData = {0};
318 eventData.CodecFormat = m_decodeCtx->wMode;
319 eventData.FrameType = avcPicParams->pic_fields.IntraPicFlag == 1 ? I_TYPE : MIXED_TYPE;
320 eventData.PicStruct = avcPicParams->CurrPic.PicFlags; // 1-Top; 2-Bottom; 3-Frame
321 eventData.Width = (avcPicParams->pic_width_in_mbs_minus1 + 1) * MACROBLOCK_WIDTH;
322 eventData.Height = (avcPicParams->pic_height_in_mbs_minus1 + 1) * MACROBLOCK_HEIGHT;
323 eventData.Bitdepth = avcPicParams->bit_depth_luma_minus8 + 8;
324 eventData.ChromaFormat = avcPicParams->seq_fields.chroma_format_idc; // 0-4:0:0; 1-4:2:0; 2-4:2:2; 3-4:4:4
325 MOS_TraceEvent(EVENT_DECODE_INFO_PICTUREVA, EVENT_TYPE_INFO, &eventData, sizeof(eventData), NULL, 0);
326 #endif
327
328 return VA_STATUS_SUCCESS;
329 }
330
ParseIQMatrix(DDI_MEDIA_CONTEXT * mediaCtx,VAIQMatrixBufferH264 * matrix)331 VAStatus DdiDecodeAvc::ParseIQMatrix(
332 DDI_MEDIA_CONTEXT *mediaCtx,
333 VAIQMatrixBufferH264 *matrix)
334 {
335 DDI_CODEC_FUNC_ENTER;
336
337 PCODEC_AVC_IQ_MATRIX_PARAMS avcIqMatrix;
338
339 avcIqMatrix = (PCODEC_AVC_IQ_MATRIX_PARAMS)(m_decodeCtx->DecodeParams.m_iqMatrixBuffer);
340
341 if ((matrix == nullptr) || (avcIqMatrix == nullptr))
342 {
343 DDI_CODEC_ASSERTMESSAGE("Invalid Parameter for Parsing AVC IQMatrix parameter\n");
344 return VA_STATUS_ERROR_INVALID_PARAMETER;
345 }
346 // 4x4 block
347 int32_t i;
348 for (i = 0; i < 6; i++)
349 {
350 MOS_SecureMemcpy(avcIqMatrix->ScalingList4x4[i],
351 16,
352 matrix->ScalingList4x4[i],
353 16);
354 }
355 // 8x8 block
356 for (i = 0; i < 2; i++)
357 {
358 MOS_SecureMemcpy(avcIqMatrix->ScalingList8x8[i],
359 64,
360 matrix->ScalingList8x8[i],
361 64);
362 }
363 return VA_STATUS_SUCCESS;
364 }
365
AllocSliceParamContext(uint32_t numSlices)366 VAStatus DdiDecodeAvc::AllocSliceParamContext(
367 uint32_t numSlices)
368 {
369 DDI_CODEC_FUNC_ENTER;
370
371 uint32_t baseSize = sizeof(CODEC_AVC_SLICE_PARAMS);
372
373 if (m_sliceParamBufNum < (m_decodeCtx->DecodeParams.m_numSlices + numSlices))
374 {
375 // in order to avoid that the buffer is reallocated multi-times,
376 // extra 10 slices are added.
377 uint32_t extraSlices = numSlices + 10;
378 m_decodeCtx->DecodeParams.m_sliceParams = realloc(m_decodeCtx->DecodeParams.m_sliceParams,
379 baseSize * (m_sliceParamBufNum + extraSlices));
380
381 if (m_decodeCtx->DecodeParams.m_sliceParams == nullptr)
382 {
383 return VA_STATUS_ERROR_ALLOCATION_FAILED;
384 }
385
386 memset((void *)((uint8_t *)m_decodeCtx->DecodeParams.m_sliceParams + baseSize * m_sliceParamBufNum),
387 0,
388 baseSize * extraSlices);
389
390 m_sliceParamBufNum += extraSlices;
391 }
392
393 return VA_STATUS_SUCCESS;
394 }
395
RenderPicture(VADriverContextP ctx,VAContextID context,VABufferID * buffers,int32_t numBuffers)396 VAStatus DdiDecodeAvc::RenderPicture(
397 VADriverContextP ctx,
398 VAContextID context,
399 VABufferID *buffers,
400 int32_t numBuffers)
401 {
402 DDI_CODEC_FUNC_ENTER;
403
404 VAStatus va = VA_STATUS_SUCCESS;
405
406 PDDI_MEDIA_CONTEXT mediaCtx = GetMediaContext(ctx);
407 void *data = nullptr;
408 for (int32_t i = 0; i < numBuffers; i++)
409 {
410 DDI_MEDIA_BUFFER *buf = MediaLibvaCommonNext::GetBufferFromVABufferID(mediaCtx, buffers[i]);
411 if (nullptr == buf)
412 {
413 return VA_STATUS_ERROR_INVALID_BUFFER;
414 }
415 uint32_t dataSize = buf->iSize;
416
417 MediaLibvaInterfaceNext::MapBuffer(ctx, buffers[i], &data);
418
419 if (data == nullptr)
420 {
421 return VA_STATUS_ERROR_INVALID_BUFFER;
422 }
423
424 switch ((int32_t)buf->uiType)
425 {
426 case VASliceDataBufferType:
427 {
428 int32_t index = GetBitstreamBufIndexFromBuffer(&m_decodeCtx->BufMgr, buf);
429 if (index == DDI_CODEC_INVALID_BUFFER_INDEX)
430 {
431 return VA_STATUS_ERROR_INVALID_BUFFER;
432 }
433
434 MediaLibvaCommonNext::MediaBufferToMosResource(m_decodeCtx->BufMgr.pBitStreamBuffObject[index], &m_decodeCtx->BufMgr.resBitstreamBuffer);
435 m_decodeCtx->DecodeParams.m_dataSize += dataSize;
436
437 break;
438 }
439 case VASliceParameterBufferType:
440 {
441 VASliceParameterBufferH264 *slcInfoH264;
442 if (buf->uiNumElements == 0)
443 {
444 return VA_STATUS_ERROR_INVALID_BUFFER;
445 }
446
447 slcInfoH264 = (VASliceParameterBufferH264 *)data;
448 uint32_t numSlices = buf->uiNumElements;
449 DDI_CHK_RET(AllocSliceParamContext(numSlices),"AllocSliceParamContext failed!");
450 DDI_CHK_RET(ParseSliceParams(mediaCtx, slcInfoH264, numSlices),"ParseSliceParams failed!");
451 m_decodeCtx->DecodeParams.m_numSlices += numSlices;
452 m_groupIndex++;
453 break;
454 }
455 case VAIQMatrixBufferType:
456 {
457 VAIQMatrixBufferH264 *imxBuf = (VAIQMatrixBufferH264 *)data;
458 DDI_CHK_RET(ParseIQMatrix(mediaCtx, imxBuf),"ParseIQMatrix failed!");
459
460 break;
461 }
462 case VAPictureParameterBufferType:
463 {
464 VAPictureParameterBufferH264 *picParam;
465 picParam = (VAPictureParameterBufferH264 *)data;
466 DDI_CHK_RET(ParsePicParams(mediaCtx, picParam),"ParsePicParams failed!");
467 break;
468 }
469 case VAProcPipelineParameterBufferType:
470 {
471 DDI_CHK_RET(ParseProcessingBuffer(mediaCtx, data),"ParseProcessingBuffer failed!");
472 break;
473 }
474 case VADecodeStreamoutBufferType:
475 {
476 MediaLibvaCommonNext::MediaBufferToMosResource(buf, &m_decodeCtx->BufMgr.resExternalStreamOutBuffer);
477 m_streamOutEnabled = true;
478 break;
479 }
480
481 default:
482 va = m_decodeCtx->pCpDdiInterfaceNext->RenderCencPicture(ctx, context, buf, data);
483 break;
484 }
485 MediaLibvaInterfaceNext::UnmapBuffer(ctx, buffers[i]);
486 }
487
488 return va;
489 }
490
SetDecodeParams()491 VAStatus DdiDecodeAvc::SetDecodeParams()
492 {
493 DDI_CODEC_FUNC_ENTER;
494
495 DDI_CHK_RET(DdiDecodeBase::SetDecodeParams(),"SetDecodeParams failed!");
496 #ifdef _DECODE_PROCESSING_SUPPORTED
497 // Bridge the SFC input with VDBOX output
498 if (m_decProcessingType == VA_DEC_PROCESSING)
499 {
500 auto procParams = (DecodeProcessingParams *)m_decodeCtx->DecodeParams.m_procParams;
501 procParams->m_inputSurface = (&m_decodeCtx->DecodeParams)->m_destSurface;
502 // codechal_decode_sfc.c expects Input Width/Height information.
503 procParams->m_inputSurface->dwWidth = procParams->m_inputSurface->OsResource.iWidth;
504 procParams->m_inputSurface->dwHeight = procParams->m_inputSurface->OsResource.iHeight;
505 procParams->m_inputSurface->dwPitch = procParams->m_inputSurface->OsResource.iPitch;
506 procParams->m_inputSurface->Format = procParams->m_inputSurface->OsResource.Format;
507
508 if (m_requireInputRegion)
509 {
510 procParams->m_inputSurfaceRegion.m_x = 0;
511 procParams->m_inputSurfaceRegion.m_y = 0;
512 procParams->m_inputSurfaceRegion.m_width = procParams->m_inputSurface->dwWidth;
513 procParams->m_inputSurfaceRegion.m_height = procParams->m_inputSurface->dwHeight;
514 }
515 }
516 #endif
517
518 return VA_STATUS_SUCCESS;
519 }
520
DestroyContext(VADriverContextP ctx)521 void DdiDecodeAvc::DestroyContext(
522 VADriverContextP ctx)
523 {
524 DDI_CODEC_FUNC_ENTER;
525
526 FreeResourceBuffer();
527 // explicitly call the base function to do the further clean-up
528 DdiDecodeBase::DestroyContext(ctx);
529
530 return;
531 }
532
ContextInit(int32_t picWidth,int32_t picHeight)533 void DdiDecodeAvc::ContextInit(int32_t picWidth, int32_t picHeight)
534 {
535 DDI_CODEC_FUNC_ENTER;
536
537 // call the function in base class to initialize it.
538 DdiDecodeBase::ContextInit(picWidth, picHeight);
539
540 if (m_ddiDecodeAttr->componentData.data.sliceMode == VA_DEC_SLICE_MODE_BASE)
541 {
542 m_decodeCtx->bShortFormatInUse = true;
543 }
544 m_decodeCtx->wMode = CODECHAL_DECODE_MODE_AVCVLD;
545
546 return;
547 }
548
GetPicParamBuf(DDI_CODEC_COM_BUFFER_MGR * bufMgr)549 uint8_t* DdiDecodeAvc::GetPicParamBuf(
550 DDI_CODEC_COM_BUFFER_MGR *bufMgr)
551 {
552 return (uint8_t*)(&(bufMgr->Codec_Param.Codec_Param_H264.PicParam264));
553 }
554
AllocSliceControlBuffer(DDI_MEDIA_BUFFER * buf)555 VAStatus DdiDecodeAvc::AllocSliceControlBuffer(
556 DDI_MEDIA_BUFFER *buf)
557 {
558 DDI_CODEC_FUNC_ENTER;
559
560 DDI_CODEC_COM_BUFFER_MGR *bufMgr = nullptr;
561 uint32_t availSize = 0;
562 uint32_t newSize = 0;
563
564 bufMgr = &(m_decodeCtx->BufMgr);
565 availSize = m_sliceCtrlBufNum - bufMgr->dwNumSliceControl;
566
567 if (m_decodeCtx->bShortFormatInUse)
568 {
569 if (availSize < buf->uiNumElements)
570 {
571 newSize = sizeof(VASliceParameterBufferBase) * (m_sliceCtrlBufNum - availSize + buf->uiNumElements);
572 bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264Base = (VASliceParameterBufferBase *)realloc(bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264Base, newSize);
573 if (bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264Base == nullptr)
574 {
575 return VA_STATUS_ERROR_ALLOCATION_FAILED;
576 }
577 MOS_ZeroMemory(bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264Base + m_sliceCtrlBufNum, sizeof(VASliceParameterBufferBase) * (buf->uiNumElements - availSize));
578 m_sliceCtrlBufNum = m_sliceCtrlBufNum - availSize + buf->uiNumElements;
579 }
580 buf->pData = (uint8_t*)bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264Base;
581 buf->uiOffset = bufMgr->dwNumSliceControl * sizeof(VASliceParameterBufferBase);
582 }
583 else
584 {
585 if (availSize < buf->uiNumElements)
586 {
587 newSize = sizeof(VASliceParameterBufferH264) * (m_sliceCtrlBufNum - availSize + buf->uiNumElements);
588 bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264 = (VASliceParameterBufferH264 *)realloc(bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264, newSize);
589 if (bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264 == nullptr)
590 {
591 return VA_STATUS_ERROR_ALLOCATION_FAILED;
592 }
593 MOS_ZeroMemory(bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264 + m_sliceCtrlBufNum, sizeof(VASliceParameterBufferH264) * (buf->uiNumElements - availSize));
594 m_sliceCtrlBufNum = m_sliceCtrlBufNum - availSize + buf->uiNumElements;
595 }
596 buf->pData = (uint8_t*)bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264;
597 buf->uiOffset = bufMgr->dwNumSliceControl * sizeof(VASliceParameterBufferH264);
598 }
599
600 bufMgr->dwNumSliceControl += buf->uiNumElements;
601
602 return VA_STATUS_SUCCESS;
603 }
604
CodecHalInit(DDI_MEDIA_CONTEXT * mediaCtx,void * ptr)605 VAStatus DdiDecodeAvc::CodecHalInit(
606 DDI_MEDIA_CONTEXT *mediaCtx,
607 void *ptr)
608 {
609 DDI_CODEC_FUNC_ENTER;
610
611 VAStatus vaStatus = VA_STATUS_SUCCESS;
612 MOS_CONTEXT *mosCtx = (MOS_CONTEXT *)ptr;
613
614 m_codechalSettings->shortFormatInUse = m_decodeCtx->bShortFormatInUse;
615
616 CODECHAL_FUNCTION codecFunction = CODECHAL_FUNCTION_DECODE;
617
618 CODECHAL_STANDARD_INFO standardInfo;
619 memset(&standardInfo, 0, sizeof(standardInfo));
620
621 standardInfo.CodecFunction = codecFunction;
622 standardInfo.Mode = (CODECHAL_MODE)m_decodeCtx->wMode;
623
624 m_codechalSettings->codecFunction = codecFunction;
625 m_codechalSettings->width = m_width;
626 m_codechalSettings->height = m_height;
627 // For Avc Decoding:
628 // If the slice header contains the emulation_prevention_three_byte, we need to set bIntelEntrypointInUse to false.
629 // Because in this case, driver can not get the correct BsdStartAddress by itself. We need to turn to GEN to calculate the correct address.
630 m_codechalSettings->intelEntrypointInUse = false;
631
632 m_codechalSettings->lumaChromaDepth = CODECHAL_LUMA_CHROMA_DEPTH_8_BITS;
633
634 m_codechalSettings->mode = CODECHAL_DECODE_MODE_AVCVLD;
635 m_codechalSettings->standard = CODECHAL_AVC;
636
637 m_decodeCtx->pCpDdiInterfaceNext->SetCpParams(m_ddiDecodeAttr->componentData.data.encryptType, m_codechalSettings);
638
639 m_decodeCtx->DecodeParams.m_iqMatrixBuffer = (void*)MOS_AllocAndZeroMemory(sizeof(CODEC_AVC_IQ_MATRIX_PARAMS));
640 if (m_decodeCtx->DecodeParams.m_iqMatrixBuffer == nullptr)
641 {
642 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
643 FreeResource();
644 return vaStatus;
645 }
646 m_decodeCtx->DecodeParams.m_picParams = (void*)MOS_AllocAndZeroMemory(sizeof(CODEC_AVC_PIC_PARAMS));
647 if (m_decodeCtx->DecodeParams.m_picParams == nullptr)
648 {
649 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
650 FreeResource();
651 return vaStatus;
652 }
653
654 m_sliceParamBufNum = m_picHeightInMB;
655 m_decodeCtx->DecodeParams.m_sliceParams = (void*)MOS_AllocAndZeroMemory(m_sliceParamBufNum * sizeof(CODEC_AVC_SLICE_PARAMS));
656 if (m_decodeCtx->DecodeParams.m_sliceParams == nullptr)
657 {
658 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
659 FreeResource();
660 return vaStatus;
661 }
662
663 #ifdef _DECODE_PROCESSING_SUPPORTED
664 if (m_decProcessingType == VA_DEC_PROCESSING)
665 {
666 DecodeProcessingParams *procParams = nullptr;
667
668 m_codechalSettings->downsamplingHinted = true;
669
670 procParams = (DecodeProcessingParams *)MOS_AllocAndZeroMemory(sizeof(DecodeProcessingParams));
671 if (procParams == nullptr)
672 {
673 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
674 FreeResource();
675 return vaStatus;
676 }
677
678 m_decodeCtx->DecodeParams.m_procParams = procParams;
679 procParams->m_outputSurface = (PMOS_SURFACE)MOS_AllocAndZeroMemory(sizeof(MOS_SURFACE));
680 if (procParams->m_outputSurface == nullptr)
681 {
682 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
683 FreeResource();
684 return vaStatus;
685 }
686 }
687 #endif
688 vaStatus = CreateCodecHal(mediaCtx, ptr, &standardInfo);
689
690 if (vaStatus != VA_STATUS_SUCCESS)
691 {
692 FreeResource();
693 return vaStatus;
694 }
695
696 if (InitResourceBuffer() != VA_STATUS_SUCCESS)
697 {
698 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
699 FreeResource();
700 return vaStatus;
701 }
702
703 return vaStatus;
704 }
705
FreeResource()706 void DdiDecodeAvc::FreeResource()
707 {
708 DDI_CODEC_FUNC_ENTER;
709
710 FreeResourceBuffer();
711
712 if (m_decodeCtx->pCodecHal)
713 {
714 m_decodeCtx->pCodecHal->Destroy();
715 MOS_Delete(m_decodeCtx->pCodecHal);
716 m_decodeCtx->pCodecHal = nullptr;
717 }
718 MOS_FreeMemory(m_decodeCtx->DecodeParams.m_iqMatrixBuffer);
719 m_decodeCtx->DecodeParams.m_iqMatrixBuffer = nullptr;
720 MOS_FreeMemory(m_decodeCtx->DecodeParams.m_picParams);
721 m_decodeCtx->DecodeParams.m_picParams = nullptr;
722 MOS_FreeMemory(m_decodeCtx->DecodeParams.m_huffmanTable);
723 m_decodeCtx->DecodeParams.m_huffmanTable = nullptr;
724 MOS_FreeMemory(m_decodeCtx->DecodeParams.m_sliceParams);
725 m_decodeCtx->DecodeParams.m_sliceParams = nullptr;
726
727 #ifdef _DECODE_PROCESSING_SUPPORTED
728 if (m_decodeCtx->DecodeParams.m_procParams)
729 {
730 auto procParams =
731 (DecodeProcessingParams *)m_decodeCtx->DecodeParams.m_procParams;
732 MOS_FreeMemory(procParams->m_outputSurface);
733
734 MOS_FreeMemory(m_decodeCtx->DecodeParams.m_procParams);
735 m_decodeCtx->DecodeParams.m_procParams = nullptr;
736 }
737 #endif
738 return;
739 }
740
InitResourceBuffer()741 VAStatus DdiDecodeAvc::InitResourceBuffer()
742 {
743 DDI_CODEC_FUNC_ENTER;
744
745 VAStatus vaStatus = VA_STATUS_SUCCESS;
746 DDI_CODEC_COM_BUFFER_MGR *bufMgr = nullptr;
747
748 bufMgr = &(m_decodeCtx->BufMgr);
749
750 bufMgr->pSliceData = nullptr;
751 bufMgr->ui64BitstreamOrder = 0;
752
753 if (m_width * m_height < CODEC_720P_MAX_PIC_WIDTH * CODEC_720P_MAX_PIC_HEIGHT)
754 {
755 bufMgr->dwMaxBsSize = m_width * m_height * 3 / 2;
756 }
757 else if (m_width * m_height < CODEC_4K_MAX_PIC_WIDTH * CODEC_4K_MAX_PIC_HEIGHT)
758 {
759 bufMgr->dwMaxBsSize = m_width * m_height * 3 / 8;
760 }
761 else
762 {
763 bufMgr->dwMaxBsSize = m_width * m_height * 3 / 16;
764 }
765
766 // minimal 10k bytes for some special case. Will refractor this later
767 if (bufMgr->dwMaxBsSize < DDI_CODEC_MIN_VALUE_OF_MAX_BS_SIZE)
768 {
769 bufMgr->dwMaxBsSize = DDI_CODEC_MIN_VALUE_OF_MAX_BS_SIZE;
770 }
771
772 int32_t i, count;
773 // init decode bitstream buffer object
774 for (i = 0; i < DDI_CODEC_MAX_BITSTREAM_BUFFER; i++)
775 {
776 bufMgr->pBitStreamBuffObject[i] = (DDI_MEDIA_BUFFER *)MOS_AllocAndZeroMemory(sizeof(DDI_MEDIA_BUFFER));
777 if (bufMgr->pBitStreamBuffObject[i] == nullptr)
778 {
779 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
780 FreeResourceBuffer();
781 return vaStatus;
782 }
783 bufMgr->pBitStreamBuffObject[i]->iSize = bufMgr->dwMaxBsSize;
784 bufMgr->pBitStreamBuffObject[i]->uiType = VASliceDataBufferType;
785 bufMgr->pBitStreamBuffObject[i]->format = Media_Format_Buffer;
786 bufMgr->pBitStreamBuffObject[i]->uiOffset = 0;
787 bufMgr->pBitStreamBuffObject[i]->bo = nullptr;
788 bufMgr->pBitStreamBase[i] = nullptr;
789 }
790
791 // The pSliceData can be allocated on demand. So the default size is wPicHeightInMB.
792 bufMgr->m_maxNumSliceData = m_picHeightInMB;
793 bufMgr->pSliceData = (DDI_CODEC_BITSTREAM_BUFFER_INFO*)MOS_AllocAndZeroMemory(sizeof(bufMgr->pSliceData[0]) * bufMgr->m_maxNumSliceData);
794
795 if (bufMgr->pSliceData == nullptr)
796 {
797 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
798 FreeResourceBuffer();
799 return vaStatus;
800 }
801
802 bufMgr->dwNumSliceData = 0;
803 bufMgr->dwNumSliceControl = 0;
804
805 m_sliceCtrlBufNum = m_picHeightInMB;
806 if (m_decodeCtx->bShortFormatInUse)
807 {
808 bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264Base =
809 (VASliceParameterBufferBase *)MOS_AllocAndZeroMemory(sizeof(VASliceParameterBufferBase) * m_sliceCtrlBufNum);
810 if (bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264Base == nullptr)
811 {
812 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
813 FreeResourceBuffer();
814 return vaStatus;
815 }
816 }
817 else
818 {
819 bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264 =
820 (VASliceParameterBufferH264 *)MOS_AllocAndZeroMemory(sizeof(VASliceParameterBufferH264) * m_sliceCtrlBufNum);
821 if (bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264 == nullptr)
822 {
823 vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
824 FreeResourceBuffer();
825 return vaStatus;
826 }
827 }
828
829 return VA_STATUS_SUCCESS;
830 }
831
FreeResourceBuffer()832 void DdiDecodeAvc::FreeResourceBuffer()
833 {
834 DDI_CODEC_FUNC_ENTER;
835
836 DDI_CODEC_COM_BUFFER_MGR *bufMgr;
837 int32_t i;
838
839 bufMgr = &(m_decodeCtx->BufMgr);
840
841 for (i = 0; i < DDI_CODEC_MAX_BITSTREAM_BUFFER; i++)
842 {
843 if (bufMgr->pBitStreamBase[i])
844 {
845 MediaLibvaUtilNext::UnlockBuffer(bufMgr->pBitStreamBuffObject[i]);
846 bufMgr->pBitStreamBase[i] = nullptr;
847 }
848 if (bufMgr->pBitStreamBuffObject[i])
849 {
850 MediaLibvaUtilNext::FreeBuffer(bufMgr->pBitStreamBuffObject[i]);
851 MOS_FreeMemory(bufMgr->pBitStreamBuffObject[i]);
852 bufMgr->pBitStreamBuffObject[i] = nullptr;
853 }
854 }
855
856 if (bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264)
857 {
858 MOS_FreeMemory(bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264);
859 bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264 = nullptr;
860 }
861 if (bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264Base)
862 {
863 MOS_FreeMemory(bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264Base);
864 bufMgr->Codec_Param.Codec_Param_H264.pVASliceParaBufH264Base = nullptr;
865 }
866
867 // free decode bitstream buffer object
868 MOS_FreeMemory(bufMgr->pSliceData);
869 bufMgr->pSliceData = nullptr;
870
871 return;
872 }
873
GetSlcRefIdx(CODEC_PICTURE * picReference,CODEC_PICTURE * slcReference)874 void DdiDecodeAvc::GetSlcRefIdx(CODEC_PICTURE *picReference, CODEC_PICTURE *slcReference)
875 {
876 DDI_CODEC_FUNC_ENTER;
877
878 if (nullptr == picReference|| nullptr == slcReference)
879 {
880 return;
881 }
882
883 int32_t i = 0;
884 if (slcReference->FrameIdx != CODEC_AVC_NUM_UNCOMPRESSED_SURFACE)
885 {
886 for (i = 0; i < CODEC_MAX_NUM_REF_FRAME; i++)
887 {
888 if (slcReference->FrameIdx == picReference[i].FrameIdx)
889 {
890 slcReference->FrameIdx = i;
891 break;
892 }
893 }
894 if (i == CODEC_MAX_NUM_REF_FRAME)
895 {
896 slcReference->FrameIdx = CODEC_AVC_NUM_UNCOMPRESSED_SURFACE;
897 }
898 }
899 return;
900 }
901
SetupCodecPicture(DDI_MEDIA_CONTEXT * mediaCtx,DDI_CODEC_RENDER_TARGET_TABLE * rtTbl,CODEC_PICTURE * codecHalPic,VAPictureH264 vaPic,bool fieldPicFlag,bool picReference,bool sliceReference)902 void DdiDecodeAvc::SetupCodecPicture(
903 DDI_MEDIA_CONTEXT *mediaCtx,
904 DDI_CODEC_RENDER_TARGET_TABLE *rtTbl,
905 CODEC_PICTURE *codecHalPic,
906 VAPictureH264 vaPic,
907 bool fieldPicFlag,
908 bool picReference,
909 bool sliceReference)
910 {
911 DDI_CODEC_FUNC_ENTER;
912
913 if (vaPic.picture_id != DDI_CODEC_INVALID_FRAME_INDEX)
914 {
915 DDI_MEDIA_SURFACE *surface = MediaLibvaCommonNext::GetSurfaceFromVASurfaceID(mediaCtx, vaPic.picture_id);
916 vaPic.frame_idx = GetRenderTargetID(rtTbl, surface);
917 if (vaPic.frame_idx == DDI_CODEC_INVALID_FRAME_INDEX)
918 {
919 codecHalPic->FrameIdx = CODEC_AVC_NUM_UNCOMPRESSED_SURFACE - 1;
920 }
921 else
922 {
923 codecHalPic->FrameIdx = (uint8_t)vaPic.frame_idx;
924 }
925 }
926 else
927 {
928 vaPic.frame_idx = DDI_CODEC_INVALID_FRAME_INDEX;
929 codecHalPic->FrameIdx = CODEC_AVC_NUM_UNCOMPRESSED_SURFACE - 1;
930 }
931
932 if (picReference)
933 {
934 if (vaPic.frame_idx == DDI_CODEC_INVALID_FRAME_INDEX)
935 {
936 codecHalPic->PicFlags = PICTURE_INVALID;
937 }
938 else if ((vaPic.flags&VA_PICTURE_H264_LONG_TERM_REFERENCE) == VA_PICTURE_H264_LONG_TERM_REFERENCE)
939 {
940 codecHalPic->PicFlags = PICTURE_LONG_TERM_REFERENCE;
941 }
942 else
943 {
944 codecHalPic->PicFlags = PICTURE_SHORT_TERM_REFERENCE;
945 }
946 }
947 else
948 {
949 if (fieldPicFlag)
950 {
951 if ((vaPic.flags&VA_PICTURE_H264_BOTTOM_FIELD) == VA_PICTURE_H264_BOTTOM_FIELD)
952 {
953 codecHalPic->PicFlags = PICTURE_BOTTOM_FIELD;
954 }
955 else
956 {
957 codecHalPic->PicFlags = PICTURE_TOP_FIELD;
958 }
959 }
960 else
961 {
962 codecHalPic->PicFlags = PICTURE_FRAME;
963 }
964 }
965
966 if (sliceReference && (vaPic.picture_id == VA_INVALID_ID)) // VA_INVALID_ID is used to indicate invalide picture in LIBVA.
967 {
968 codecHalPic->PicFlags = PICTURE_INVALID;
969 }
970
971 return;
972 }
973
974 } // namespace decode
975