From 3876a193fd5aa46373bfbc8b8ba41bf8c93d3dba Mon Sep 17 00:00:00 2001 From: Ludovico de Nittis <ludovico.denittis@collabora.com> Date: Thu, 26 Nov 2020 15:40:04 +0100 Subject: [PATCH] check-va-api: Decode H264 and fallback to MPEG2 and VAProfileNone Some Intel GPUs don't have the profile "VAProfileNone". For this reason we try "VAProfileH264Main" instead, that should be supported since Intel GMA 4500 and Radeon HD 2000 and GeForce 8. For additional coverage we also fallback to "VAProfileMPEG2Simple", that has a similar GPU compatibility and lastly to "VAProfileNone". Fixes: #24 Signed-off-by: Ludovico de Nittis <ludovico.denittis@collabora.com> --- helpers/check-va-api.c | 270 ++++++++++++++++++++++++++++++++++------- 1 file changed, 229 insertions(+), 41 deletions(-) diff --git a/helpers/check-va-api.c b/helpers/check-va-api.c index b7ccc506e..b337f51f6 100644 --- a/helpers/check-va-api.c +++ b/helpers/check-va-api.c @@ -52,6 +52,94 @@ struct option long_options[] = { NULL, 0, NULL, 0 } }; +/* Pseudo-randomly generated MPEG2 video clip with one I-frame */ +static unsigned char clip_mpeg2[] = +{ + 0x28, 0x00, 0x2f, 0x64, 0x36, 0x00, 0x2d, 0xd0, 0x40, 0x00, 0x2d, 0x60, + 0x28, 0x00, 0x2f, 0x64, 0x36, 0x00, 0x2d, 0xd0, 0x40, 0x00, 0x2d, 0x60, + 0x28, 0x00, 0x2f, 0x64, 0x36, 0x00, 0x2d, 0xd0, 0x40, 0x00, 0x2d, 0x60, + 0x28, 0x00, 0x2f, 0x64, 0x36, 0x00, 0x2d, 0xd0, 0x40, 0x00, 0x2d, 0x60, + 0x28, 0x00, 0x2f, 0x64, 0x36, 0x00, 0x2d, 0xd0, 0x40, 0x00, 0x2d, 0x60, + 0x28, 0x00, 0x2f, 0x64, 0x36, 0x00, 0x2d, 0xd0, 0x40, 0x00, 0x2d, 0x60, + 0x28, 0x00, 0x2f, 0x64, 0x36, 0x00, 0x2d, 0xd0, 0x40, 0x00, 0x2d, 0x60, + 0x28, 0x00, 0x2f, 0x64, 0x36, 0x00, 0x2d, 0xd0, 0x40, 0x00, 0x2d, 0x60, + 0x28, 0x00, 0x2f, 0x64, 0x36, 0x00, 0x2d, 0xd0, 0x40, 0x00, 0x2d, 0x60, + 0x28, 0x00, 0x2f, 0x64, 0x36, 0x00, 0x2d, 0xd0, 0x40, 0x00, 0x2d, 0x60, + 0x12, 0x00, 0x2a, 0x61, 0x20, 0x00, 0x3e, 0x65, +}; + +#define CLIP_SIZE_MPEG2 sizeof(clip_mpeg2) / sizeof(clip_mpeg2[0]) + +static VAPictureParameterBufferMPEG2 pic_param_mpeg2 = +{ + /* Limit the picture buffer to 16x16 */ + .horizontal_size = 16, + .vertical_size = 16, + .picture_coding_type = 1, /* I-frame */ +}; + +static VAIQMatrixBufferMPEG2 iq_matrix_mpeg2 = +{ + /* Do not do anything particular here */ + .load_intra_quantiser_matrix = 1, + .load_non_intra_quantiser_matrix = 1, + .load_chroma_intra_quantiser_matrix = 0, + .load_chroma_non_intra_quantiser_matrix = 0, + .intra_quantiser_matrix = {0}, + .non_intra_quantiser_matrix = {0}, + .chroma_intra_quantiser_matrix = {0}, + .chroma_non_intra_quantiser_matrix = {0}, +}; + +static VASliceParameterBufferMPEG2 slice_param_mpeg2 = +{ + .slice_data_size = CLIP_SIZE_MPEG2, + .slice_data_offset = 0, + .slice_data_flag = 0, + /* Assume a slice with a 64bit header */ + .macroblock_offset = 64, +}; + +/* Pseudo-randomly generated H264 video clip with one I-frame */ +static unsigned int clip_h264[] = +{ + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, + 0xca123456, 0xe2255446, 0x9a61c747, 0xe10133c7, 0x71ccf20f, 0xfd2e5af3, +}; + +#define CLIP_SIZE_H264 sizeof(clip_h264) / sizeof(clip_h264[0]) + +static VAPictureParameterBufferH264 pic_param_h264 = +{ + /* The size has been arbitrarily chosen */ + .picture_width_in_mbs_minus1 = 10, + .picture_height_in_mbs_minus1 = 10, + .num_ref_frames = 1, +}; + +static VAIQMatrixBufferH264 iq_matrix_h264 = +{ + /* Do not do anything particular here */ + .ScalingList4x4 = {{0}}, + .ScalingList8x8 = {{0}}, +}; + +static VASliceParameterBufferH264 slice_param_h264 = +{ + .slice_data_size = CLIP_SIZE_H264, + .slice_data_offset = 0, + .slice_data_flag = 0, +}; + static void usage (int code) __attribute__((__noreturn__)); /* @@ -92,6 +180,7 @@ main (int argc, #define do_vaapi_or_exit(expr) if (! _do_vaapi (#expr, expr)) goto out; bool verbose = false; + bool decode_available = false; int opt; int surfaces_count = 2; int ret = 1; @@ -112,6 +201,10 @@ main (int argc, VAContextID context = VA_INVALID_ID; VABufferID misc_buf_id = VA_INVALID_ID; VABufferID pipeline_param_buf_id = VA_INVALID_ID; + VABufferID pic_param_buf = VA_INVALID_ID; + VABufferID iq_matrix_buf = VA_INVALID_ID; + VABufferID slice_param_buf = VA_INVALID_ID; + VABufferID slice_data_buf = VA_INVALID_ID; VAEncMiscParameterBuffer *misc_param_buffer; VAEncMiscParameterBufferQualityLevel *buffer_quality_level; VAProcPipelineParameterBuffer pipeline_param_buf; @@ -152,11 +245,11 @@ main (int argc, attr.type = VASurfaceAttribPixelFormat; attr.flags = VA_SURFACE_ATTRIB_SETTABLE; attr.value.type = VAGenericValueTypeInteger; - /* Arbitrarily use the 8-bit RGBA, assuming to be widely supported. + /* Arbitrarily use the 8-bit YUV 4:2:0, assuming to be widely supported. * In case the current system doesn't support it, vaCreateSurfaces will fail * and this test will exit returning 1 */ - attr.value.value.i = VA_FOURCC_RGBA; - image_format.fourcc = VA_FOURCC_RGBA; + attr.value.value.i = VA_FOURCC_I420; + image_format.fourcc = VA_FOURCC_I420; image_format.byte_order = VA_LSB_FIRST; image_format.bits_per_pixel = 32; @@ -225,7 +318,7 @@ main (int argc, } /* Test the creation of two surfaces and an image */ - do_vaapi_or_exit (vaCreateSurfaces (va_display, VA_RT_FORMAT_RGB32, width, height, + do_vaapi_or_exit (vaCreateSurfaces (va_display, VA_RT_FORMAT_YUV420, width, height, surfaces, surfaces_count, &attr, 1)); do_vaapi_or_exit (vaCreateImage (va_display, &image_format, width, height, &img)); @@ -241,44 +334,131 @@ main (int argc, do_vaapi_or_exit (vaSyncSurface (va_display, surfaces[1])); - /* Assume VAProfileNone to be available. If it isn't vaCreateConfig will not return - * "VA_STATUS_SUCCESS" and this test will exit returning 1 */ - do_vaapi_or_exit (vaCreateConfig (va_display, VAProfileNone, VAEntrypointVideoProc, - NULL, 0, &config)); - - do_vaapi_or_exit (vaCreateContext (va_display, config, width, height, 0, surfaces, - surfaces_count, &context)); - - - /* Try to render a picture, tuning its encode quality */ - do_vaapi_or_exit (vaCreateBuffer (va_display, context, VAEncMiscParameterBufferType, - sizeof (VAEncMiscParameterBuffer) + sizeof (VAEncMiscParameterBufferQualityLevel), - 1, NULL, &misc_buf_id)); - do_vaapi_or_exit (vaMapBuffer (va_display, misc_buf_id, (void **)&misc_param_buffer)); - misc_param_buffer->type = VAEncMiscParameterTypeQualityLevel; - buffer_quality_level = (VAEncMiscParameterBufferQualityLevel *)misc_param_buffer->data; - /* 1 is always the highest possible quality level, we don't need to check VAConfigAttribEncQualityRange */ - buffer_quality_level->quality_level = 1; - do_vaapi_or_exit (vaUnmapBuffer (va_display, misc_buf_id)); - do_vaapi_or_exit (vaBeginPicture (va_display, context, surfaces[1])); - do_vaapi_or_exit (vaRenderPicture (va_display, context, &misc_buf_id, 1)); - do_vaapi_or_exit (vaSyncSurface (va_display, surfaces[1])); - + /* We assume to have at least one of VAProfileH264Main, VAProfileMPEG2Simple + * or VAProfileNone */ + if (_do_vaapi ("Testing ability to decode VAProfileH264Main", + vaCreateConfig (va_display, VAProfileH264Main, + VAEntrypointVLD, NULL, 0, &config))) + { + decode_available = true; + + do_vaapi_or_exit (vaCreateBuffer (va_display, context, + VAPictureParameterBufferType, + sizeof (VAPictureParameterBufferH264), + 1, &pic_param_h264, + &pic_param_buf)); + + do_vaapi_or_exit (vaCreateBuffer (va_display, context, + VAIQMatrixBufferType, + sizeof (VAIQMatrixBufferH264), + 1, &iq_matrix_h264, + &iq_matrix_buf)); + + do_vaapi_or_exit (vaCreateBuffer (va_display, context, + VASliceParameterBufferType, + sizeof (VASliceParameterBufferH264), + 1, &slice_param_h264, + &slice_param_buf)); + + do_vaapi_or_exit (vaCreateBuffer (va_display, context, + VASliceDataBufferType, + CLIP_SIZE_H264, 1, + clip_h264, &slice_data_buf)); + } + else if (_do_vaapi ("Testing ability to decode VAProfileMPEG2Simple", + vaCreateConfig (va_display, VAProfileMPEG2Simple, + VAEntrypointVLD, NULL, 0, &config))) + { + decode_available = true; + + do_vaapi_or_exit (vaCreateBuffer (va_display, context, + VAPictureParameterBufferType, + sizeof (VAPictureParameterBufferMPEG2), + 1, &pic_param_mpeg2, + &pic_param_buf)); + + do_vaapi_or_exit (vaCreateBuffer (va_display, context, + VAIQMatrixBufferType, + sizeof (VAIQMatrixBufferMPEG2), + 1, &iq_matrix_mpeg2, + &iq_matrix_buf)); + + do_vaapi_or_exit (vaCreateBuffer (va_display, context, + VASliceParameterBufferType, + sizeof (VASliceParameterBufferMPEG2), + 1, &slice_param_mpeg2, + &slice_param_buf)); + + do_vaapi_or_exit (vaCreateBuffer (va_display, context, + VASliceDataBufferType, + CLIP_SIZE_MPEG2, 1, + clip_mpeg2, &slice_data_buf)); + } + else if (_do_vaapi ("Testing ability to use VAProfileNone video pre/post processing", + vaCreateConfig (va_display, VAProfileNone, + VAEntrypointVideoProc, NULL, 0, &config))) + { + do_vaapi_or_exit (vaCreateContext (va_display, config, width, height, 0, + surfaces, surfaces_count, &context)); + + /* Try to render a picture, tuning its encode quality */ + do_vaapi_or_exit (vaCreateBuffer (va_display, context, + VAEncMiscParameterBufferType, + sizeof (VAEncMiscParameterBuffer) + + sizeof (VAEncMiscParameterBufferQualityLevel), + 1, NULL, &misc_buf_id)); + do_vaapi_or_exit (vaMapBuffer (va_display, misc_buf_id, (void **)&misc_param_buffer)); + misc_param_buffer->type = VAEncMiscParameterTypeQualityLevel; + buffer_quality_level = (VAEncMiscParameterBufferQualityLevel *)misc_param_buffer->data; + /* 1 is always the highest possible quality level, we don't need to check + * VAConfigAttribEncQualityRange */ + buffer_quality_level->quality_level = 1; + do_vaapi_or_exit (vaUnmapBuffer (va_display, misc_buf_id)); + do_vaapi_or_exit (vaBeginPicture (va_display, context, surfaces[1])); + do_vaapi_or_exit (vaRenderPicture (va_display, context, &misc_buf_id, 1)); + do_vaapi_or_exit (vaSyncSurface (va_display, surfaces[1])); + + /* Try to render a picture from the first surface to the second, + * applying a crop to it */ + memset (&pipeline_param_buf, 0, sizeof (pipeline_param_buf)); + pipeline_param_buf.surface = surfaces[0]; + pipeline_param_buf.surface_region = &input_region; + pipeline_param_buf.output_region = &output_region; + /* Set a green background */ + pipeline_param_buf.output_background_color = 0xff00ff00; + pipeline_param_buf.output_color_standard = VAProcColorStandardNone; + do_vaapi_or_exit (vaCreateBuffer (va_display, context, + VAProcPipelineParameterBufferType, + sizeof (pipeline_param_buf), 1, + &pipeline_param_buf, &pipeline_param_buf_id)); + do_vaapi_or_exit (vaBeginPicture (va_display, context, surfaces[1])); + do_vaapi_or_exit (vaRenderPicture (va_display, context, &pipeline_param_buf_id, 1)); + do_vaapi_or_exit (vaEndPicture (va_display, context)); + do_vaapi_or_exit (vaSyncSurface (va_display, surfaces[1])); + } + else + { + goto out; + } - /* Try to render a picture from the first surface to the second, applying a crop to it */ - memset (&pipeline_param_buf, 0, sizeof (pipeline_param_buf)); - pipeline_param_buf.surface = surfaces[0]; - pipeline_param_buf.surface_region = &input_region; - pipeline_param_buf.output_region = &output_region; - /* Set a green background */ - pipeline_param_buf.output_background_color = 0xff00ff00; - pipeline_param_buf.output_color_standard = VAProcColorStandardNone; - do_vaapi_or_exit (vaCreateBuffer (va_display, context, VAProcPipelineParameterBufferType, - sizeof (pipeline_param_buf), 1, &pipeline_param_buf, &pipeline_param_buf_id)); - do_vaapi_or_exit (vaBeginPicture (va_display, context, surfaces[1])); - do_vaapi_or_exit (vaRenderPicture (va_display, context, &pipeline_param_buf_id, 1)); - do_vaapi_or_exit (vaEndPicture (va_display, context)); - do_vaapi_or_exit (vaSyncSurface (va_display, surfaces[1])); + if (decode_available) + { + do_vaapi_or_exit (vaCreateContext (va_display, config, width, height, + VA_PROGRESSIVE, surfaces, + surfaces_count, &context)); + + do_vaapi_or_exit (vaBeginPicture (va_display, context, surfaces[1])); + /* Send the buffers to the server */ + do_vaapi_or_exit (vaRenderPicture (va_display, context, &pic_param_buf, 1)); + do_vaapi_or_exit (vaRenderPicture (va_display, context, &iq_matrix_buf, 1)); + do_vaapi_or_exit (vaRenderPicture (va_display, context, &slice_param_buf, 1)); + do_vaapi_or_exit (vaRenderPicture (va_display, context, &slice_data_buf, 1)); + /* We are done with the sending, now the server will start to process all + * pending operations */ + do_vaapi_or_exit (vaEndPicture (va_display, context)); + /* Blocks until all pending operations ends */ + do_vaapi_or_exit (vaSyncSurface (va_display, surfaces[1])); + } ret = 0; @@ -297,6 +477,14 @@ out: vaDestroyConfig(va_display, config); if (surfaces) vaDestroySurfaces (va_display, surfaces, surfaces_count); + if (pic_param_buf != VA_INVALID_ID) + vaDestroyBuffer (va_display, pic_param_buf); + if (iq_matrix_buf != VA_INVALID_ID) + vaDestroyBuffer (va_display, iq_matrix_buf); + if (slice_param_buf != VA_INVALID_ID) + vaDestroyBuffer (va_display, slice_param_buf); + if (slice_data_buf != VA_INVALID_ID) + vaDestroyBuffer (va_display, slice_data_buf); vaTerminate (va_display); } -- GitLab