tests: add vulkan AV1 encode test

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8841>
This commit is contained in:
Stéphane Cerveau 2025-08-08 16:46:48 +02:00 committed by GStreamer Marge Bot
parent 167e41c343
commit 34a009d85c
2 changed files with 831 additions and 0 deletions

View File

@ -0,0 +1,830 @@
/* GStreamer
*
* Copyright (C) 2025 Igalia, S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/codecparsers/gstav1parser.h>
#include "vkvideoencodebase.c"
GstAV1Parser *parser = NULL;
#define MAX_ORDER_HINT 7
#define FRAME_ID_BITS 15
#define DELTA_FRAME_ID_BITS 14
typedef struct
{
GstVulkanEncoderPicture picture;
gboolean is_ref;
gint pic_num;
gint pic_order_cnt;
VkVideoEncodeAV1PictureInfoKHR enc_pic_info;
StdVideoEncodeAV1PictureInfo pic_info;
StdVideoEncodeAV1ReferenceInfo ref_info;
VkVideoEncodeAV1DpbSlotInfoKHR dpb_slot_info;
VkVideoEncodeAV1RateControlInfoKHR rc_info;
} GstVulkanAV1EncoderFrame;
static GstAV1OBUType
check_av1_obu (guint8 * bitstream, gsize size, GstAV1OBU * obu)
{
GstAV1ParserResult res = GST_AV1_PARSER_OK;
guint32 consumed;
guint32 offset = 0;
if (!parser) {
parser = gst_av1_parser_new ();
}
while (offset < size) {
res =
gst_av1_parser_identify_one_obu (parser, bitstream + offset, size, obu,
&consumed);
assert_equals_int (res, GST_AV1_PARSER_OK);
switch (obu->obu_type) {
case GST_AV1_OBU_TEMPORAL_DELIMITER:
{
res = gst_av1_parser_parse_temporal_delimiter_obu (parser, obu);
assert_equals_int (res, GST_AV1_PARSER_OK);
break;
}
case GST_AV1_OBU_SEQUENCE_HEADER:
{
GstAV1SequenceHeaderOBU seq_header;
res =
gst_av1_parser_parse_sequence_header_obu (parser, obu, &seq_header);
assert_equals_int (res, GST_AV1_PARSER_OK);
break;
}
case GST_AV1_OBU_FRAME_HEADER:
{
GstAV1FrameHeaderOBU frame_header;
res =
gst_av1_parser_parse_frame_header_obu (parser, obu, &frame_header);
assert_equals_int (res, GST_AV1_PARSER_OK);
break;
}
case GST_AV1_OBU_FRAME:
{
GstAV1FrameOBU frame;
res = gst_av1_parser_parse_frame_obu (parser, obu, &frame);
assert_equals_int (res, GST_AV1_PARSER_OK);
break;
}
default:
fail_unless (0);
break;
}
offset += consumed;
}
return obu->obu_type;
}
static void
check_av1_obu_frame (GstAV1OBU * obu, GstAV1FrameType frame_type)
{
GstAV1FrameOBU frame;
GstAV1ParserResult res = GST_AV1_PARSER_OK;
res = gst_av1_parser_parse_frame_obu (parser, obu, &frame);
assert_equals_int (res, GST_AV1_PARSER_OK);
assert_equals_int (frame.frame_header.frame_type, frame_type);
}
static gint
_av1_helper_msb (guint n)
{
int log = 0;
guint value = n;
int i;
g_assert (n != 0);
for (i = 4; i >= 0; --i) {
const gint shift = (1 << i);
const guint x = value >> shift;
if (x != 0) {
value = x;
log += shift;
}
}
return log;
}
static void
check_av1_session_params (GstVulkanEncoder * enc)
{
GError *err = NULL;
guint8 *bitstream = NULL;
gsize bitstream_size = 0;
GstAV1OBU obu;
fail_unless (gst_vulkan_encoder_video_session_parameters_overrides (enc,
NULL, NULL, &bitstream_size, (gpointer *) & bitstream, &err));
assert_equals_int (check_av1_obu (bitstream, bitstream_size, &obu),
GST_AV1_OBU_SEQUENCE_HEADER);
g_free (bitstream);
}
static GstVulkanAV1EncoderFrame *
_av1_encode_frame_new (GstVulkanEncoder * enc, GstBuffer * img_buffer,
gsize size, gboolean is_ref)
{
GstVulkanAV1EncoderFrame *frame;
frame = g_new (GstVulkanAV1EncoderFrame, 1);
fail_unless (gst_vulkan_encoder_picture_init (&frame->picture, enc,
img_buffer, size));
frame->is_ref = is_ref;
return frame;
}
static void
_av1_encode_frame_free (GstVulkanEncoder * enc, gpointer pframe)
{
GstVulkanAV1EncoderFrame *frame = (GstVulkanAV1EncoderFrame *) pframe;
gst_vulkan_encoder_picture_clear (&frame->picture, enc);
g_free (frame);
}
static GstVulkanAV1EncoderFrame *
allocate_av1_frame (GstVulkanEncoder * enc, int width, int height,
gboolean is_ref)
{
GstVulkanAV1EncoderFrame *frame;
GstBuffer *in_buffer, *img_buffer;
in_buffer = generate_input_buffer (buffer_pool, width, height);
fail_unless (in_buffer);
fail_unless (upload_buffer_to_image (img_pool, in_buffer,
&img_buffer) == GST_FLOW_OK);
frame = _av1_encode_frame_new (enc, img_buffer, width * height * 3, is_ref);
fail_unless (frame);
gst_buffer_unref (in_buffer);
gst_buffer_unref (img_buffer);
return frame;
}
static void
setup_codec_pic (GstVulkanEncoderPicture * pic, VkVideoEncodeInfoKHR * info,
gpointer data)
{
GstVulkanAV1EncoderFrame *frame = (GstVulkanAV1EncoderFrame *) pic;
info->pNext = &frame->enc_pic_info;
pic->dpb_slot.pNext = &frame->dpb_slot_info;
/* *INDENT-OFF* */
frame->dpb_slot_info = (VkVideoEncodeAV1DpbSlotInfoKHR) {
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_AV1_DPB_SLOT_INFO_KHR,
.pNext = NULL,
.pStdReferenceInfo = &frame->ref_info,
};
/* *INDENT-ON* */
if (frame->pic_info.frame_type == STD_VIDEO_AV1_FRAME_TYPE_KEY) {
frame->pic_info.refresh_frame_flags = 0xff;
} else {
frame->pic_info.refresh_frame_flags =
1 << frame->picture.dpb_slot.slotIndex;
}
}
static void
setup_rc_codec (GstVulkanEncoderPicture * pic,
VkVideoEncodeRateControlInfoKHR * rc_info,
VkVideoEncodeRateControlLayerInfoKHR * rc_layer, gpointer data)
{
GstVulkanAV1EncoderFrame *frame = (GstVulkanAV1EncoderFrame *) pic;
/* *INDENT-OFF* */
frame->rc_info = (VkVideoEncodeAV1RateControlInfoKHR) {
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_AV1_RATE_CONTROL_INFO_KHR,
.pNext = NULL,
.flags = VK_VIDEO_ENCODE_AV1_RATE_CONTROL_REFERENCE_PATTERN_FLAT_BIT_KHR |
VK_VIDEO_ENCODE_AV1_RATE_CONTROL_REGULAR_GOP_BIT_KHR,
.gopFrameCount = 1,
.keyFramePeriod = 1,
.consecutiveBipredictiveFrameCount = 0,
.temporalLayerCount = 0,
};
/* *INDENT-ON* */
rc_info->pNext = &frame->rc_info;
}
static GstVulkanEncoder *
setup_av1_encoder (guint32 width, gint32 height, int gop_size)
{
GstVulkanEncoder *enc = NULL;
GError *err = NULL;
GstVulkanVideoProfile profile;
GstVulkanEncoderParameters enc_params;
StdVideoAV1SequenceHeader av1_seq_header;
StdVideoAV1Profile av1_profile = STD_VIDEO_AV1_PROFILE_MAIN;
StdVideoAV1ColorConfig av1_color_config;
StdVideoEncodeAV1DecoderModelInfo av1_model_info;
StdVideoEncodeAV1OperatingPointInfo av1_operating_point_info;
GstVulkanEncoderQualityProperties quality_props;
/* *INDENT-OFF* */
profile = (GstVulkanVideoProfile) {
.profile = {
.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR,
.pNext = &profile.usage.encode,
.videoCodecOperation = VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR,
.chromaSubsampling = VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR,
.lumaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR,
.chromaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR,
},
.usage.encode = {
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_USAGE_INFO_KHR,
.pNext = &profile.codec,
.videoUsageHints = VK_VIDEO_ENCODE_USAGE_DEFAULT_KHR,
.videoContentHints = VK_VIDEO_ENCODE_CONTENT_DEFAULT_KHR,
.tuningMode = VK_VIDEO_ENCODE_TUNING_MODE_DEFAULT_KHR,
},
.codec.av1enc = {
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_AV1_PROFILE_INFO_KHR,
.stdProfile = av1_profile,
}
};
quality_props = (GstVulkanEncoderQualityProperties) {
.quality_level = -1,
.codec.av1 = {
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_AV1_QUALITY_LEVEL_PROPERTIES_KHR,
},
};
/* *INDENT-ON* */
setup_queue (VK_QUEUE_VIDEO_ENCODE_BIT_KHR,
VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR);
if (!video_queue) {
GST_WARNING ("Unable to find encoding queue");
return NULL;
}
if (!graphics_queue) {
GST_WARNING ("Unable to find graphics queue");
return NULL;
}
enc = gst_vulkan_encoder_create_from_queue (video_queue,
VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR);
if (!enc) {
GST_WARNING ("Unable to create a vulkan encoder, queue=%p", video_queue);
return NULL;
}
fail_unless (gst_vulkan_encoder_quality_level (enc) == -1);
fail_unless (gst_vulkan_encoder_start (enc, &profile, &quality_props, &err));
fail_unless (gst_vulkan_encoder_quality_level (enc) > -1);
fail_unless (gst_vulkan_encoder_is_started (enc));
/* *INDENT-OFF* */
av1_color_config = (StdVideoAV1ColorConfig) {
.flags = (StdVideoAV1ColorConfigFlags) {
.mono_chrome = 0,
.color_range = 0,
.separate_uv_delta_q = 0,
.color_description_present_flag = 0,
},
.BitDepth = 8, /* VK_FORMAT_G8_B8R8_2PLANE_420_UNORM */
.subsampling_x = 1,
.subsampling_y = 1,
.color_primaries = STD_VIDEO_AV1_COLOR_PRIMARIES_BT_UNSPECIFIED,
.transfer_characteristics = STD_VIDEO_AV1_TRANSFER_CHARACTERISTICS_UNSPECIFIED,
.matrix_coefficients = STD_VIDEO_AV1_MATRIX_COEFFICIENTS_UNSPECIFIED,
.chroma_sample_position = STD_VIDEO_AV1_CHROMA_SAMPLE_POSITION_UNKNOWN,
};
av1_seq_header = (StdVideoAV1SequenceHeader) {
.flags = (StdVideoAV1SequenceHeaderFlags) {
.still_picture = 0,
.reduced_still_picture_header = 0,
.use_128x128_superblock = 0,
.enable_filter_intra = 0,
.enable_intra_edge_filter = 0,
.enable_interintra_compound = 0,
.enable_masked_compound = 0,
.enable_warped_motion = 0,
.enable_dual_filter = 0,
.enable_order_hint = 1,
.enable_jnt_comp = 0,
.enable_ref_frame_mvs = 0,
.frame_id_numbers_present_flag = 0,
.enable_superres = 0,
.enable_cdef = 0,
.enable_restoration = 0,
.film_grain_params_present = 0,
.timing_info_present_flag = 0,
.initial_display_delay_present_flag = 0,
},
.seq_profile = av1_profile,
.frame_width_bits_minus_1 = _av1_helper_msb (width),
.frame_height_bits_minus_1 = _av1_helper_msb (height),
.max_frame_width_minus_1 = width - 1,
.max_frame_height_minus_1 = height - 1,
.delta_frame_id_length_minus_2 = DELTA_FRAME_ID_BITS - 2, /* Comes from vk_video_samples */
.additional_frame_id_length_minus_1 = FRAME_ID_BITS - DELTA_FRAME_ID_BITS - 1, /* Comes from vk_video_samples */
.order_hint_bits_minus_1 = MAX (_av1_helper_msb(gop_size), MAX_ORDER_HINT - 1), /* Should be ceil log2 of the gop size with MAX_ORDER_HINT as max value */
.seq_force_integer_mv = 0,
.seq_force_screen_content_tools = 0,
.pColorConfig = &av1_color_config,
.pTimingInfo = NULL,
};
av1_model_info = (StdVideoEncodeAV1DecoderModelInfo) {
.buffer_delay_length_minus_1 = 0,
.buffer_removal_time_length_minus_1 = 0,
.frame_presentation_time_length_minus_1 = 0,
.num_units_in_decoding_tick = 0,
};
av1_operating_point_info = (StdVideoEncodeAV1OperatingPointInfo) {
.flags = (StdVideoEncodeAV1OperatingPointInfoFlags) {
.decoder_model_present_for_this_op = 0,
.low_delay_mode_flag = 0,
.initial_display_delay_present_for_this_op = 0,
},
.operating_point_idc = 0,
.seq_level_idx = 0,
.seq_tier = 0,
.decoder_buffer_delay = 0,
.encoder_buffer_delay = 0,
.initial_display_delay_minus_1 = 0,
};
enc_params.av1 = (VkVideoEncodeAV1SessionParametersCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_AV1_SESSION_PARAMETERS_CREATE_INFO_KHR,
.pNext = NULL,
.pStdSequenceHeader = &av1_seq_header,
.pStdDecoderModelInfo = &av1_model_info,
.stdOperatingPointCount = 1,
.pStdOperatingPoints = &av1_operating_point_info,
};
/* *INDENT-ON* */
fail_unless (gst_vulkan_encoder_update_video_session_parameters (enc,
&enc_params, &err));
check_av1_session_params (enc);
return enc;
}
static void
encode_frame (GstVulkanEncoder * enc, GstVulkanAV1EncoderFrame * frame,
StdVideoAV1FrameType frame_type, guint frame_num,
GstVulkanAV1EncoderFrame ** list0, gint list0_num,
GstVulkanAV1EncoderFrame ** list1, gint list1_num)
{
GstVulkanVideoCapabilities enc_caps;
int i, ref_pics_num = 0;
GstVulkanEncoderPicture *ref_pics[16] = { NULL, };
GstVulkanEncoderPicture *picture = &frame->picture;
GstVulkanEncoderCallbacks cb = { setup_codec_pic, setup_rc_codec };
GST_DEBUG ("Encoding frame num:%d", frame_num);
fail_unless (gst_vulkan_encoder_caps (enc, &enc_caps));
gst_vulkan_encoder_set_callbacks (enc, &cb, &enc_caps, NULL);
/* *INDENT-OFF* */
frame->pic_info = (StdVideoEncodeAV1PictureInfo) {
.flags = (StdVideoEncodeAV1PictureInfoFlags) {
.error_resilient_mode = (frame_type == STD_VIDEO_AV1_FRAME_TYPE_KEY),
.disable_cdf_update = 0,
.use_superres = 0,
.render_and_frame_size_different = 0,
.allow_screen_content_tools = 0,
.is_filter_switchable = 0,
.force_integer_mv = 0,
.frame_size_override_flag = 0,
.buffer_removal_time_present_flag = 0,
.allow_intrabc = 0,
.frame_refs_short_signaling = 0,
.allow_high_precision_mv = 0,
.is_motion_mode_switchable = 0,
.use_ref_frame_mvs = 0,
.disable_frame_end_update_cdf = 0,
.allow_warped_motion = 0,
.reduced_tx_set = 0,
.skip_mode_present = 0,
.delta_q_present = 0,
.delta_lf_present = 0,
.delta_lf_multi = 0,
.segmentation_enabled = 0,
.segmentation_update_map = 0,
.segmentation_temporal_update = 0,
.segmentation_update_data = 0,
.UsesLr = 0,
.usesChromaLr = 0,
.show_frame = (frame->pic_order_cnt <= frame->pic_num),
.showable_frame = (frame_type != STD_VIDEO_AV1_FRAME_TYPE_KEY),
},
.frame_type = frame_type,
.frame_presentation_time = 0,
.current_frame_id = frame_num,
.order_hint = frame->pic_order_cnt % (1 << MAX_ORDER_HINT),
.primary_ref_frame = STD_VIDEO_AV1_PRIMARY_REF_NONE,
.refresh_frame_flags = 0xff, /* set during `setup_codec_pic` callback */
.coded_denom = 0,
.render_width_minus_1 = GST_VIDEO_INFO_WIDTH (&out_info) - 1,
.render_height_minus_1 = GST_VIDEO_INFO_HEIGHT (&out_info) - 1,
.interpolation_filter = STD_VIDEO_AV1_INTERPOLATION_FILTER_EIGHTTAP,
.TxMode = STD_VIDEO_AV1_TX_MODE_ONLY_4X4,
.delta_q_res = 0,
.delta_lf_res = 0,
.pTileInfo = NULL,
.pQuantization = NULL,
.pSegmentation = NULL,
.pLoopFilter = NULL,
.pCDEF = NULL,
.pLoopRestoration = NULL,
.pGlobalMotion = NULL,
.pExtensionHeader = NULL,
.pBufferRemovalTimes = NULL,
};
frame->enc_pic_info = (VkVideoEncodeAV1PictureInfoKHR) {
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_AV1_PICTURE_INFO_KHR,
.pNext = NULL,
.predictionMode = VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_INTRA_ONLY_KHR,
.rateControlGroup = VK_VIDEO_ENCODE_AV1_RATE_CONTROL_GROUP_INTRA_KHR,
.constantQIndex = 64,
.pStdPictureInfo = &frame->pic_info,
.primaryReferenceCdfOnly = VK_FALSE,
.generateObuExtensionHeader = VK_FALSE,
};
/* *INDENT-ON* */
memset (frame->pic_info.ref_order_hint, 0, STD_VIDEO_AV1_NUM_REF_FRAMES);
memset (frame->pic_info.ref_frame_idx, 0, STD_VIDEO_AV1_REFS_PER_FRAME);
memset (frame->pic_info.delta_frame_id_minus_1, 0,
STD_VIDEO_AV1_REFS_PER_FRAME * sizeof (uint32_t));
if (frame_type != STD_VIDEO_AV1_FRAME_TYPE_KEY) {
if (list1_num) { /* Bi-directional frame */
frame->enc_pic_info.predictionMode =
VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_BIDIRECTIONAL_COMPOUND_KHR;
frame->enc_pic_info.rateControlGroup =
VK_VIDEO_ENCODE_AV1_RATE_CONTROL_GROUP_BIPREDICTIVE_KHR;
frame->pic_info.refresh_frame_flags = 0;
} else {
if (enc_caps.encoder.codec.av1.maxUnidirectionalCompoundReferenceCount
&& list0_num > 1) {
frame->enc_pic_info.predictionMode =
VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_UNIDIRECTIONAL_COMPOUND_KHR;
} else {
frame->enc_pic_info.predictionMode =
VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_SINGLE_REFERENCE_KHR;
}
frame->enc_pic_info.rateControlGroup =
VK_VIDEO_ENCODE_AV1_RATE_CONTROL_GROUP_PREDICTIVE_KHR;
}
}
if (frame_type != STD_VIDEO_AV1_FRAME_TYPE_KEY) {
if (list1_num != 0) {
frame->enc_pic_info.predictionMode =
VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_BIDIRECTIONAL_COMPOUND_KHR;
frame->enc_pic_info.rateControlGroup =
VK_VIDEO_ENCODE_AV1_RATE_CONTROL_GROUP_BIPREDICTIVE_KHR;
} else {
frame->enc_pic_info.predictionMode =
VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_SINGLE_REFERENCE_KHR;
frame->enc_pic_info.rateControlGroup =
VK_VIDEO_ENCODE_AV1_RATE_CONTROL_GROUP_PREDICTIVE_KHR;
}
} else {
frame->enc_pic_info.predictionMode =
VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_INTRA_ONLY_KHR;
frame->enc_pic_info.rateControlGroup =
VK_VIDEO_ENCODE_AV1_RATE_CONTROL_GROUP_INTRA_KHR;
}
/* Cause a crash in NVIDIA driver if the referenceNameSlotIndices are not all
* -1 by default. */
memset (frame->enc_pic_info.referenceNameSlotIndices, -1,
STD_VIDEO_AV1_REFS_PER_FRAME * sizeof (int32_t));
/* *INDENT-OFF* */
frame->ref_info = (StdVideoEncodeAV1ReferenceInfo) {
.flags = (StdVideoEncodeAV1ReferenceInfoFlags) {
.disable_frame_end_update_cdf = 0,
.segmentation_enabled = 0,
},
.RefFrameId = 0, /* FIXME Vulkan Video Samples value is 0 too */
.frame_type = frame_type,
.OrderHint = frame->pic_order_cnt % (1 << MAX_ORDER_HINT),
.pExtensionHeader = NULL,
};
/* *INDENT-ON* */
for (i = 0; i < list0_num; i++) {
ref_pics[i] = &list0[i]->picture;
frame->enc_pic_info.referenceNameSlotIndices[i] =
list0[i]->picture.dpb_slot.slotIndex;
ref_pics_num++;
}
for (i = 0; i < list1_num; i++) {
ref_pics[i + list0_num] = &list1[i]->picture;
frame->enc_pic_info.referenceNameSlotIndices[STD_VIDEO_AV1_REFS_PER_FRAME -
1] = list1[i]->picture.dpb_slot.slotIndex;
ref_pics_num++;
}
fail_unless (gst_vulkan_encoder_encode (enc, &in_info, picture, ref_pics_num,
ref_pics));
}
static void
tear_down_encoder (GstVulkanEncoder * enc)
{
if (enc) {
fail_unless (gst_vulkan_encoder_stop (enc));
gst_object_unref (enc);
}
if (exec) {
if (!gst_vulkan_operation_wait (exec)) {
GST_WARNING
("Failed to wait for all fences to complete before shutting down");
}
gst_object_unref (exec);
exec = NULL;
}
gst_clear_object (&video_queue);
gst_clear_object (&graphics_queue);
gst_av1_parser_free (parser);
parser = NULL;
}
static void
check_encoded_frame (GstVulkanAV1EncoderFrame * frame,
GstAV1FrameType frame_type)
{
GstMapInfo info;
GstAV1OBU obu;
fail_unless (frame->picture.out_buffer != NULL);
gst_buffer_map (frame->picture.out_buffer, &info, GST_MAP_READ);
fail_unless (info.size);
GST_MEMDUMP ("out buffer", info.data, info.size);
assert_equals_int (check_av1_obu (info.data, info.size, &obu),
GST_AV1_OBU_FRAME);
check_av1_obu_frame (&obu, frame_type);
gst_buffer_unmap (frame->picture.out_buffer, &info);
}
#define N_BUFFERS STD_VIDEO_AV1_NUM_REF_FRAMES + 1
#define FRAME_WIDTH 720
#define FRAME_HEIGHT 480
GST_START_TEST (test_encoder_av1_key)
{
GstVulkanEncoder *enc;
uint32_t width = FRAME_WIDTH;
uint32_t height = FRAME_HEIGHT;
GstVulkanAV1EncoderFrame *frame;
int frame_num = 0;
int i;
/* Create and setup an AV1 encoder with its initial session parameters */
enc = setup_av1_encoder (width, height, N_BUFFERS);
if (!enc) {
GST_WARNING ("Unable to initialize AV1 encoder");
return;
}
buffer_pool = allocate_buffer_pool (enc, width, height);
img_pool = allocate_image_buffer_pool (enc, width, height);
/* Encode N_BUFFERS of I-Frames */
for (i = 0; i < N_BUFFERS; i++) {
frame = allocate_av1_frame (enc, width, height, TRUE);
encode_frame (enc, frame, STD_VIDEO_AV1_FRAME_TYPE_KEY,
frame_num, NULL, 0, NULL, 0);
check_encoded_frame (frame, GST_AV1_KEY_FRAME);
frame_num++;
_av1_encode_frame_free (enc, frame);
}
fail_unless (gst_buffer_pool_set_active (buffer_pool, FALSE));
gst_object_unref (buffer_pool);
fail_unless (gst_buffer_pool_set_active (img_pool, FALSE));
gst_object_unref (img_pool);
tear_down_encoder (enc);
}
GST_END_TEST;
GST_START_TEST (test_encoder_av1_inter)
{
GstVulkanEncoder *enc;
uint32_t width = FRAME_WIDTH;
uint32_t height = FRAME_HEIGHT;
GstVulkanAV1EncoderFrame *frame;
GstVulkanAV1EncoderFrame *list0[2] = { NULL, };
int frame_num = 0;
int i;
/* Create and setup an AV1 encoder with its initial session parameters */
enc = setup_av1_encoder (width, height, N_BUFFERS);
if (!enc) {
GST_WARNING ("Unable to initialize AV1 encoder");
return;
}
buffer_pool = allocate_buffer_pool (enc, width, height);
img_pool = allocate_image_buffer_pool (enc, width, height);
frame = allocate_av1_frame (enc, width, height, TRUE);
encode_frame (enc, frame, STD_VIDEO_AV1_FRAME_TYPE_KEY,
frame_num, NULL, 0, NULL, 0);
check_encoded_frame (frame, GST_AV1_KEY_FRAME);
list0[0] = frame;
frame_num++;
/* Encode N_BUFFERS of Inter-Frames */
for (i = 1; i < N_BUFFERS; i++) {
frame = allocate_av1_frame (enc, width, height, TRUE);
frame->pic_num = frame_num;
frame->pic_order_cnt = frame_num;
encode_frame (enc, frame, STD_VIDEO_AV1_FRAME_TYPE_INTER,
frame_num, list0, 1, NULL, 0);
check_encoded_frame (frame, GST_AV1_INTER_FRAME);
_av1_encode_frame_free (enc, list0[0]);
list0[0] = frame;
frame_num++;
}
_av1_encode_frame_free (enc, frame);
fail_unless (gst_buffer_pool_set_active (buffer_pool, FALSE));
gst_object_unref (buffer_pool);
fail_unless (gst_buffer_pool_set_active (img_pool, FALSE));
gst_object_unref (img_pool);
tear_down_encoder (enc);
}
GST_END_TEST;
GST_START_TEST (test_encoder_av1_inter_bi)
{
GstVulkanEncoder *enc;
uint32_t width = FRAME_WIDTH;
uint32_t height = FRAME_HEIGHT;
GstVulkanAV1EncoderFrame *frame;
GstVulkanAV1EncoderFrame *list0[STD_VIDEO_AV1_NUM_REF_FRAMES] = { NULL, };
GstVulkanAV1EncoderFrame *list1[STD_VIDEO_AV1_NUM_REF_FRAMES] = { NULL, };
gint list0_num = 0;
gint list1_num = 0;
int frame_num = 0;
GstVulkanVideoCapabilities enc_caps;
/* Create and setup an AV1 encoder with its initial session parameters */
enc = setup_av1_encoder (width, height, 4);
if (!enc) {
GST_WARNING ("Unable to initialize AV1 encoder");
return;
}
fail_unless (gst_vulkan_encoder_caps (enc, &enc_caps));
if (!enc_caps.encoder.codec.av1.maxBidirectionalCompoundReferenceCount) {
GST_WARNING ("Driver does not support bi-directional frames");
goto beach;
}
buffer_pool = allocate_buffer_pool (enc, width, height);
img_pool = allocate_image_buffer_pool (enc, width, height);
/* Encode 1st picture as an IDR-Frame */
frame = allocate_av1_frame (enc, width, height, TRUE);
encode_frame (enc, frame, STD_VIDEO_AV1_FRAME_TYPE_KEY,
frame_num, NULL, 0, NULL, 0);
check_encoded_frame (frame, GST_AV1_KEY_FRAME);
list0[0] = frame;
list0_num++;
frame_num++;
/* Encode 4th picture as a P-Frame */
frame = allocate_av1_frame (enc, width, height, TRUE);
frame->pic_num = frame_num; /* Encode order */
frame->pic_order_cnt = 3; /* Display order */
encode_frame (enc, frame, STD_VIDEO_AV1_FRAME_TYPE_INTER,
frame_num, list0, list0_num, list1, list1_num);
check_encoded_frame (frame, GST_AV1_INTER_FRAME);
list1[0] = frame;
list1_num++;
frame_num++;
/* Encode 2nd picture as a B-Frame */
frame = allocate_av1_frame (enc, width, height, FALSE);
frame->pic_num = frame_num;
frame->pic_order_cnt = 1;
encode_frame (enc, frame, STD_VIDEO_AV1_FRAME_TYPE_INTER,
frame_num, list0, list0_num, list1, list1_num);
check_encoded_frame (frame, GST_AV1_INTER_FRAME);
frame_num++;
_av1_encode_frame_free (enc, frame);
/* Encode 3rd picture as a B-Frame */
frame = allocate_av1_frame (enc, width, height, FALSE);
frame->pic_num = frame_num;
frame->pic_order_cnt = 2;
encode_frame (enc, frame, STD_VIDEO_AV1_FRAME_TYPE_INTER,
frame_num, list0, list0_num, list1, list1_num);
check_encoded_frame (frame, GST_AV1_INTER_FRAME);
frame_num++;
_av1_encode_frame_free (enc, frame);
_av1_encode_frame_free (enc, list0[0]);
_av1_encode_frame_free (enc, list1[0]);
fail_unless (gst_buffer_pool_set_active (buffer_pool, FALSE));
gst_object_unref (buffer_pool);
fail_unless (gst_buffer_pool_set_active (img_pool, FALSE));
gst_object_unref (img_pool);
beach:
tear_down_encoder (enc);
}
GST_END_TEST;
static Suite *
vkvideo_suite (void)
{
Suite *s = suite_create ("vkvideo");
TCase *tc_basic = tcase_create ("general");
gboolean have_instance;
suite_add_tcase (s, tc_basic);
tcase_add_checked_fixture (tc_basic, setup, teardown);
/* FIXME: CI doesn't have a software vulkan renderer (and none exists currently) */
instance = gst_vulkan_instance_new ();
have_instance = gst_vulkan_instance_open (instance, NULL);
gst_object_unref (instance);
if (have_instance) {
tcase_add_test (tc_basic, test_encoder_av1_key);
tcase_add_test (tc_basic, test_encoder_av1_inter);
tcase_add_test (tc_basic, test_encoder_av1_inter_bi);
}
return s;
}
GST_CHECK_MAIN (vkvideo);

View File

@ -122,6 +122,7 @@ base_tests = [
[['libs/vkvideodecode.c'], not gstvulkan_dep.found() or vulkan_conf.get('GST_VULKAN_HAVE_VIDEO_EXTENSIONS') != 1, [gstvulkan_dep]],
[['libs/vkvideoencodeh264.c'], not gstvulkan_dep.found() or vulkan_conf.get('GST_VULKAN_HAVE_VIDEO_EXTENSIONS') != 1, [gstvulkan_dep, gstcodecparsers_dep]],
[['libs/vkvideoencodeh265.c'], not gstvulkan_dep.found() or vulkan_conf.get('GST_VULKAN_HAVE_VIDEO_EXTENSIONS') != 1, [gstvulkan_dep, gstcodecparsers_dep]],
[['libs/vkvideoencodeav1.c'], not gstvulkan_dep.found() or vulkan_conf.get('GST_VULKAN_HAVE_VIDEO_EXTENSIONS') != 1, [gstvulkan_dep, gstcodecparsers_dep]],
[['libs/d3d11device.cpp'], not gstd3d11_dep.found(), [gstd3d11_dep]],
[['libs/d3d11memory.c'], not gstd3d11_dep.found(), [gstd3d11_dep]],
[['libs/cudamemory.c'], not gstcuda_dep.found(), [gstcuda_dep, gstcuda_stub_dep]],