tests: add vulkan AV1 encode test
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8841>
This commit is contained in:
parent
167e41c343
commit
34a009d85c
830
subprojects/gst-plugins-bad/tests/check/libs/vkvideoencodeav1.c
Normal file
830
subprojects/gst-plugins-bad/tests/check/libs/vkvideoencodeav1.c
Normal 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);
|
@ -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]],
|
||||
|
Loading…
x
Reference in New Issue
Block a user