1 /*
2 * Copyright (c) 2011 Intel Corporation. All Rights Reserved.
3 * Copyright (c) Imagination Technologies Limited, UK
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 * Elaine Wang <elaine.wang@intel.com>
27 * Zeng Li <zeng.li@intel.com>
28 *
29 */
30
31 #include "psb_def.h"
32 #include "psb_drv_debug.h"
33 #include "psb_surface.h"
34 #include "psb_cmdbuf.h"
35 #include "pnw_MPEG4ES.h"
36 #include "pnw_hostcode.h"
37 #include "pnw_hostheader.h"
38
39 #include <stdlib.h>
40 #include <stdint.h>
41 #include <string.h>
42
43
44 #define TOPAZ_MPEG4_MAX_BITRATE 16000000
45
46 #define INIT_CONTEXT_MPEG4ES context_ENC_p ctx = (context_ENC_p) obj_context->format_data
47 #define SURFACE(id) ((object_surface_p) object_heap_lookup( &ctx->obj_context->driver_data->surface_heap, id ))
48 #define BUFFER(id) ((object_buffer_p) object_heap_lookup( &ctx->obj_context->driver_data->buffer_heap, id ))
49
50
51
pnw_MPEG4ES_QueryConfigAttributes(VAProfile __maybe_unused profile,VAEntrypoint __maybe_unused entrypoint,VAConfigAttrib * attrib_list,int num_attribs)52 static void pnw_MPEG4ES_QueryConfigAttributes(
53 VAProfile __maybe_unused profile,
54 VAEntrypoint __maybe_unused entrypoint,
55 VAConfigAttrib * attrib_list,
56 int num_attribs)
57 {
58 int i;
59
60 drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_QueryConfigAttributes\n");
61
62 /* RateControl attributes */
63 for (i = 0; i < num_attribs; i++) {
64 switch (attrib_list[i].type) {
65 case VAConfigAttribRTFormat:
66 break;
67
68 case VAConfigAttribRateControl:
69 attrib_list[i].value = VA_RC_NONE | VA_RC_CBR | VA_RC_VBR;
70 break;
71
72 default:
73 attrib_list[i].value = VA_ATTRIB_NOT_SUPPORTED;
74 break;
75 }
76 }
77
78 return;
79 }
80
81
pnw_MPEG4ES_ValidateConfig(object_config_p obj_config)82 static VAStatus pnw_MPEG4ES_ValidateConfig(
83 object_config_p obj_config)
84 {
85 int i;
86 /* Check all attributes */
87 for (i = 0; i < obj_config->attrib_count; i++) {
88 switch (obj_config->attrib_list[i].type) {
89 case VAConfigAttribRTFormat:
90 /* Ignore */
91 break;
92 case VAConfigAttribRateControl:
93 break;
94 default:
95 return VA_STATUS_ERROR_ATTR_NOT_SUPPORTED;
96 }
97 }
98
99 return VA_STATUS_SUCCESS;
100 }
101
102
pnw_MPEG4ES_CreateContext(object_context_p obj_context,object_config_p obj_config)103 static VAStatus pnw_MPEG4ES_CreateContext(
104 object_context_p obj_context,
105 object_config_p obj_config)
106 {
107 VAStatus vaStatus = VA_STATUS_SUCCESS;
108 context_ENC_p ctx;
109 int i;
110 unsigned int eRCmode;
111
112 drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_CreateContext\n");
113
114 vaStatus = pnw_CreateContext(obj_context, obj_config, 0);
115 if (VA_STATUS_SUCCESS != vaStatus)
116 return VA_STATUS_ERROR_ALLOCATION_FAILED;
117
118 ctx = (context_ENC_p) obj_context->format_data;
119
120 for (i = 0; i < obj_config->attrib_count; i++) {
121 if (obj_config->attrib_list[i].type == VAConfigAttribRateControl)
122 break;
123 }
124
125 if (i >= obj_config->attrib_count)
126 eRCmode = VA_RC_NONE;
127 else
128 eRCmode = obj_config->attrib_list[i].value;
129
130
131 if (eRCmode == VA_RC_VBR) {
132 ctx->eCodec = IMG_CODEC_MPEG4_VBR;
133 ctx->sRCParams.RCEnable = IMG_TRUE;
134 } else if (eRCmode == VA_RC_CBR) {
135 ctx->eCodec = IMG_CODEC_MPEG4_CBR;
136 ctx->sRCParams.RCEnable = IMG_TRUE;
137 } else if (eRCmode == VA_RC_NONE) {
138 ctx->eCodec = IMG_CODEC_MPEG4_NO_RC;
139 ctx->sRCParams.RCEnable = IMG_FALSE;
140 } else
141 return VA_STATUS_ERROR_UNSUPPORTED_RT_FORMAT;
142 ctx->eFormat = IMG_CODEC_PL12;
143
144 ctx->Slices = 1;
145 ctx->ParallelCores = 1;
146
147 ctx->IPEControl = pnw__get_ipe_control(ctx->eCodec);
148
149 switch (obj_config->profile) {
150 case VAProfileMPEG4Simple:
151 ctx->profile_idc = 2;
152 break;
153 case VAProfileMPEG4AdvancedSimple:
154 ctx->profile_idc = 3;
155 break;
156 default:
157 ctx->profile_idc = 2;
158 break;
159 }
160
161 return vaStatus;
162 }
163
164
pnw_MPEG4ES_DestroyContext(object_context_p obj_context)165 static void pnw_MPEG4ES_DestroyContext(
166 object_context_p obj_context)
167 {
168 drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_DestroyPicture\n");
169
170 pnw_DestroyContext(obj_context);
171 }
172
pnw_MPEG4ES_BeginPicture(object_context_p obj_context)173 static VAStatus pnw_MPEG4ES_BeginPicture(
174 object_context_p obj_context)
175 {
176 INIT_CONTEXT_MPEG4ES;
177 VAStatus vaStatus = VA_STATUS_SUCCESS;
178
179 drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_BeginPicture\n");
180
181 vaStatus = pnw_BeginPicture(ctx);
182
183 return vaStatus;
184 }
185
pnw__MPEG4ES_process_sequence_param(context_ENC_p ctx,object_buffer_p obj_buffer)186 static VAStatus pnw__MPEG4ES_process_sequence_param(context_ENC_p ctx, object_buffer_p obj_buffer)
187 {
188 VAStatus vaStatus = VA_STATUS_SUCCESS;
189 VAEncSequenceParameterBufferMPEG4 *seq_params;
190 pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
191 MPEG4_PROFILE_TYPE profile;
192 int i, vop_time_increment_resolution;
193 unsigned frame_size;
194
195 ASSERT(obj_buffer->type == VAEncSequenceParameterBufferType);
196 ASSERT(obj_buffer->num_elements == 1);
197 ASSERT(obj_buffer->size == sizeof(VAEncSequenceParameterBufferMPEG4));
198
199 //initialize the frame_rate and qp
200 ctx->sRCParams.InitialQp = 15;
201 ctx->sRCParams.MinQP = 1;
202 ctx->sRCParams.FrameRate = 30;
203
204 if ((obj_buffer->num_elements != 1) ||
205 (obj_buffer->size != sizeof(VAEncSequenceParameterBufferMPEG4))) {
206 return VA_STATUS_ERROR_UNKNOWN;
207 }
208
209 seq_params = (VAEncSequenceParameterBufferMPEG4 *) obj_buffer->buffer_data;
210 obj_buffer->buffer_data = NULL;
211 obj_buffer->size = 0;
212
213 if (seq_params->bits_per_second > TOPAZ_MPEG4_MAX_BITRATE) {
214 ctx->sRCParams.BitsPerSecond = TOPAZ_MPEG4_MAX_BITRATE;
215 drv_debug_msg(VIDEO_DEBUG_GENERAL, " bits_per_second(%d) exceeds \
216 the maximum bitrate, set it with %d\n",
217 seq_params->bits_per_second,
218 TOPAZ_MPEG4_MAX_BITRATE);
219 } else
220 ctx->sRCParams.BitsPerSecond = seq_params->bits_per_second;
221
222 ctx->sRCParams.FrameRate = (seq_params->frame_rate < 1) ?
223 1 : ((65535 < seq_params->frame_rate) ? 65535 : seq_params->frame_rate);
224 ctx->sRCParams.InitialQp = seq_params->initial_qp;
225 ctx->sRCParams.MinQP = seq_params->min_qp;
226 ctx->sRCParams.BUSize = 0; /* default 0, and will be set in pnw__setup_busize */
227
228 ctx->sRCParams.Slices = 1;
229 ctx->sRCParams.QCPOffset = 0;/* FIXME */
230
231 if (ctx->sRCParams.IntraFreq != seq_params->intra_period
232 && ctx->raw_frame_count != 0
233 && ctx->sRCParams.IntraFreq != 0
234 && ((ctx->obj_context->frame_count + 1) % ctx->sRCParams.IntraFreq) != 0
235 && (!ctx->sRCParams.bDisableFrameSkipping)) {
236 drv_debug_msg(VIDEO_DEBUG_ERROR,
237 "Changing intra period value in the middle of a GOP is\n"
238 "not allowed if frame skip isn't disabled.\n"
239 "it can cause I frame been skipped\n");
240 free(seq_params);
241 return VA_STATUS_ERROR_INVALID_PARAMETER;
242 }
243 else
244 ctx->sRCParams.IntraFreq = seq_params->intra_period;
245
246 ctx->sRCParams.IntraFreq = seq_params->intra_period;
247
248 frame_size = ctx->sRCParams.BitsPerSecond / ctx->sRCParams.FrameRate;
249
250 ctx->sRCParams.BufferSize = ctx->sRCParams.BitsPerSecond;
251 /* Header buffersize is specified in 16384 units, so ensure conformance
252 of parameters. InitialLevel in units of 64, assured by this */
253
254 ctx->sRCParams.BufferSize /= 16384;
255 ctx->sRCParams.BufferSize *= 16384;
256
257 ctx->sRCParams.InitialLevel = (3 * ctx->sRCParams.BufferSize) >> 4;
258 /* Aligned with target frame size */
259 ctx->sRCParams.InitialLevel += (frame_size / 2);
260 ctx->sRCParams.InitialLevel /= frame_size;
261 ctx->sRCParams.InitialLevel *= frame_size;
262 ctx->sRCParams.InitialDelay = ctx->sRCParams.BufferSize - ctx->sRCParams.InitialLevel;
263 ctx->buffer_size = ctx->sRCParams.BufferSize;
264
265 if (ctx->raw_frame_count == 0) { /* Add Register IO behind begin Picture */
266 for (i = (ctx->ParallelCores - 1); i >= 0; i--)
267 pnw_set_bias(ctx, i);
268 }
269
270 cmdbuf = ctx->obj_context->pnw_cmdbuf;
271
272 switch (ctx->profile_idc) {
273 case 2:
274 profile = SP;
275 break;
276 case 3:
277 profile = ASP;
278 break;
279 default:
280 profile = SP;
281 break;
282 }
283
284 memset(cmdbuf->header_mem_p + ctx->seq_header_ofs,
285 0,
286 HEADER_SIZE);
287
288 vop_time_increment_resolution = (seq_params->vop_time_increment_resolution < 1) ? 1 :
289 ((65535 < seq_params->vop_time_increment_resolution) ? 65535 : seq_params->vop_time_increment_resolution);
290 pnw__MPEG4_prepare_sequence_header(
291 cmdbuf->header_mem_p + ctx->seq_header_ofs,
292 0, /* BFrame? */
293 profile, /* sProfile */
294 seq_params->profile_and_level_indication, /* */
295 seq_params->fixed_vop_time_increment, /*3,*/ /* sFixed_vop_time_increment */
296 seq_params->video_object_layer_width,/* Picture_Width_Pixels */
297 seq_params->video_object_layer_height, /* Picture_Height_Pixels */
298 NULL,
299 vop_time_increment_resolution); /* VopTimeResolution */
300
301 ctx->MPEG4_vop_time_increment_resolution = vop_time_increment_resolution;
302
303 pnw_cmdbuf_insert_command_package(ctx->obj_context,
304 ctx->ParallelCores - 1, /* Send to the last core as this will complete first */
305 MTX_CMDID_DO_HEADER,
306 &cmdbuf->header_mem,
307 ctx->seq_header_ofs);
308
309 free(seq_params);
310 return vaStatus;
311 }
312
313
pnw__MPEG4ES_process_picture_param(context_ENC_p ctx,object_buffer_p obj_buffer)314 static VAStatus pnw__MPEG4ES_process_picture_param(context_ENC_p ctx, object_buffer_p obj_buffer)
315 {
316 VAStatus vaStatus = VA_STATUS_SUCCESS;
317 VAEncPictureParameterBufferMPEG4 *pBuffer;
318 pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
319 unsigned int *pPictureHeaderMem;
320 MTX_HEADER_PARAMS *psPicHeader;
321 int i;
322 IMG_BOOL bIsVOPCoded = IMG_TRUE;
323
324 ASSERT(obj_buffer->type == VAEncPictureParameterBufferType);
325
326 if ((obj_buffer->num_elements != 1) ||
327 (obj_buffer->size != sizeof(VAEncPictureParameterBufferMPEG4))) {
328 return VA_STATUS_ERROR_UNKNOWN;
329 }
330
331 /* Transfer ownership of VAEncPictureParameterBufferMPEG4 data */
332 pBuffer = (VAEncPictureParameterBufferMPEG4 *) obj_buffer->buffer_data;
333 obj_buffer->buffer_data = NULL;
334 obj_buffer->size = 0;
335
336 ctx->ref_surface = SURFACE(pBuffer->reference_picture);
337 ctx->dest_surface = SURFACE(pBuffer->reconstructed_picture);
338 ctx->coded_buf = BUFFER(pBuffer->coded_buf);
339
340 ASSERT(ctx->Width == pBuffer->picture_width);
341 ASSERT(ctx->Height == pBuffer->picture_height);
342
343 /*if (ctx->sRCParams.RCEnable && ctx->sRCParams.FrameSkip)
344 bIsVOPCoded = IMG_FALSE;*/
345
346 ctx->FCode = 4 - 1; /* 4 is default value of "ui8Search_range" */
347
348 pPictureHeaderMem = (unsigned int *)(cmdbuf->header_mem_p + ctx->pic_header_ofs);
349 psPicHeader = (MTX_HEADER_PARAMS *)pPictureHeaderMem;
350
351 memset(pPictureHeaderMem, 0, HEADER_SIZE);
352
353 pnw__MPEG4_prepare_vop_header((unsigned char *)pPictureHeaderMem,
354 bIsVOPCoded,
355 pBuffer->vop_time_increment, /* In testbench, this should be FrameNum */
356 4,/* default value is 4,search range */
357 pBuffer->picture_type,
358 ctx->MPEG4_vop_time_increment_resolution/* defaule value */);
359
360 /* Mark this header as a complex header */
361 psPicHeader->Elements |= 0x100;
362 pPictureHeaderMem += ((HEADER_SIZE) >> 3);
363
364 pnw__MPEG4_prepare_vop_header((unsigned char *)pPictureHeaderMem,
365 IMG_FALSE,
366 pBuffer->vop_time_increment, /* In testbench, this should be FrameNum */
367 4,/* default value is 4,search range */
368 pBuffer->picture_type,
369 ctx->MPEG4_vop_time_increment_resolution/* defaule value */);
370
371 pnw_cmdbuf_insert_command_package(ctx->obj_context,
372 ctx->ParallelCores - 1, /* Send to the last core as this will complete first */
373 MTX_CMDID_DO_HEADER,
374 &cmdbuf->header_mem,
375 ctx->pic_header_ofs);
376
377 /* Prepare START_PICTURE params */
378 for (i = (ctx->ParallelCores - 1); i >= 0; i--)
379 vaStatus = pnw_RenderPictureParameter(ctx, i);
380
381 free(pBuffer);
382 return vaStatus;
383 }
384
pnw__MPEG4ES_process_slice_param(context_ENC_p ctx,object_buffer_p obj_buffer)385 static VAStatus pnw__MPEG4ES_process_slice_param(context_ENC_p ctx, object_buffer_p obj_buffer)
386 {
387 VAStatus vaStatus = VA_STATUS_SUCCESS;
388 VAEncSliceParameterBuffer *pBuffer;
389 pnw_cmdbuf_p cmdbuf = ctx->obj_context->pnw_cmdbuf;
390 PIC_PARAMS *psPicParams = (PIC_PARAMS *)(cmdbuf->pic_params_p);
391 unsigned int i;
392 int slice_param_idx;
393
394 ASSERT(obj_buffer->type == VAEncSliceParameterBufferType);
395
396 pBuffer = (VAEncSliceParameterBuffer *) obj_buffer->buffer_data;
397
398 /*In case the slice number changes*/
399 if ((ctx->slice_param_cache != NULL) && (obj_buffer->num_elements != ctx->slice_param_num)) {
400 drv_debug_msg(VIDEO_DEBUG_GENERAL, "Slice number changes. Previous value is %d. Now it's %d\n",
401 ctx->slice_param_num, obj_buffer->num_elements);
402 free(ctx->slice_param_cache);
403 ctx->slice_param_cache = NULL;
404 ctx->slice_param_num = 0;
405 }
406
407 if (NULL == ctx->slice_param_cache) {
408 drv_debug_msg(VIDEO_DEBUG_GENERAL, "Allocate %d VAEncSliceParameterBuffer cache buffers\n", 2 * ctx->slice_param_num);
409 ctx->slice_param_num = obj_buffer->num_elements;
410 ctx->slice_param_cache = calloc(2 * ctx->slice_param_num, sizeof(VAEncSliceParameterBuffer));
411 if (NULL == ctx->slice_param_cache) {
412 drv_debug_msg(VIDEO_DEBUG_ERROR, "Run out of memory!\n");
413 free(obj_buffer->buffer_data);
414 return VA_STATUS_ERROR_ALLOCATION_FAILED;
415 }
416 }
417
418
419 for (i = 0; i < obj_buffer->num_elements; i++) {
420
421 unsigned char deblock_idc;
422
423 deblock_idc = pBuffer->slice_flags.bits.disable_deblocking_filter_idc;
424
425 if ((pBuffer->start_row_number == 0) && pBuffer->slice_flags.bits.is_intra) {
426 pnw_reset_encoder_params(ctx);
427 ctx->BelowParamsBufIdx = (ctx->BelowParamsBufIdx + 1) & 0x1;
428 }
429
430 /*The corresponding slice buffer cache*/
431 slice_param_idx = (pBuffer->slice_flags.bits.is_intra ? 0 : 1) * ctx->slice_param_num + i;
432
433 if (VAEncSliceParameter_Equal(&ctx->slice_param_cache[slice_param_idx], pBuffer) == 0) {
434 /* cache current param parameters */
435 memcpy(&ctx->slice_param_cache[slice_param_idx],
436 pBuffer, sizeof(VAEncSliceParameterBuffer));
437
438 /* Setup InParams value*/
439 pnw_setup_slice_params(ctx,
440 pBuffer->start_row_number * 16,
441 pBuffer->slice_height * 16,
442 pBuffer->slice_flags.bits.is_intra,
443 ctx->obj_context->frame_count > 0,
444 psPicParams->sInParams.SeInitQP);
445 }
446
447 pnw__send_encode_slice_params(ctx,
448 pBuffer->slice_flags.bits.is_intra,
449 pBuffer->start_row_number * 16,
450 deblock_idc,
451 ctx->obj_context->frame_count,
452 pBuffer->slice_height * 16,
453 ctx->obj_context->slice_count);
454
455 drv_debug_msg(VIDEO_DEBUG_GENERAL, "Now frame_count/slice_count is %d/%d\n",
456 ctx->obj_context->frame_count, ctx->obj_context->slice_count);
457
458 ctx->obj_context->slice_count++;
459 pBuffer++;
460
461 ASSERT(ctx->obj_context->slice_count < MAX_SLICES_PER_PICTURE);
462 }
463
464 free(obj_buffer->buffer_data);
465 obj_buffer->buffer_data = NULL;
466
467 return vaStatus;
468 }
469
470
pnw_MPEG4ES_RenderPicture(object_context_p obj_context,object_buffer_p * buffers,int num_buffers)471 static VAStatus pnw_MPEG4ES_RenderPicture(
472 object_context_p obj_context,
473 object_buffer_p *buffers,
474 int num_buffers)
475 {
476 INIT_CONTEXT_MPEG4ES;
477 VAStatus vaStatus = VA_STATUS_SUCCESS;
478 int i;
479
480 drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_RenderPicture\n");
481
482 for (i = 0; i < num_buffers; i++) {
483 object_buffer_p obj_buffer = buffers[i];
484
485 switch (obj_buffer->type) {
486 case VAEncSequenceParameterBufferType:
487 drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_RenderPicture got VAEncSequenceParameterBufferType\n");
488 vaStatus = pnw__MPEG4ES_process_sequence_param(ctx, obj_buffer);
489 DEBUG_FAILURE;
490 break;
491
492 case VAEncPictureParameterBufferType:
493 drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_RenderPicture got VAEncPictureParameterBufferType\n");
494 vaStatus = pnw__MPEG4ES_process_picture_param(ctx, obj_buffer);
495 DEBUG_FAILURE;
496 break;
497
498 case VAEncSliceParameterBufferType:
499 drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_RenderPicture got VAEncSliceParameterBufferType\n");
500 vaStatus = pnw__MPEG4ES_process_slice_param(ctx, obj_buffer);
501 DEBUG_FAILURE;
502 break;
503 default:
504 vaStatus = VA_STATUS_ERROR_UNKNOWN;
505 DEBUG_FAILURE;
506 }
507 }
508
509 return vaStatus;
510 }
511
pnw_MPEG4ES_EndPicture(object_context_p obj_context)512 static VAStatus pnw_MPEG4ES_EndPicture(
513 object_context_p obj_context)
514 {
515 INIT_CONTEXT_MPEG4ES;
516
517 drv_debug_msg(VIDEO_DEBUG_GENERAL, "pnw_MPEG4ES_EndPicture\n");
518 return pnw_EndPicture(ctx);
519 }
520
521
522 struct format_vtable_s pnw_MPEG4ES_vtable = {
523 queryConfigAttributes:
524 pnw_MPEG4ES_QueryConfigAttributes,
525 validateConfig:
526 pnw_MPEG4ES_ValidateConfig,
527 createContext:
528 pnw_MPEG4ES_CreateContext,
529 destroyContext:
530 pnw_MPEG4ES_DestroyContext,
531 beginPicture:
532 pnw_MPEG4ES_BeginPicture,
533 renderPicture:
534 pnw_MPEG4ES_RenderPicture,
535 endPicture:
536 pnw_MPEG4ES_EndPicture
537 };
538