Merge branch 'vulkan-encoder' into 'main'

Vulkan: add vulkan video encoder object and its associated unit tests for H.264 and H.265

See merge request gstreamer/gstreamer!6676
This commit is contained in:
Stéphane Cerveau 2024-05-03 20:06:25 +00:00
commit 4f23848095
21 changed files with 4147 additions and 128 deletions

View file

@ -2269,6 +2269,22 @@ second argument is @data.</doc>
</parameters>
</function>
</bitfield>
<record name="VulkanEncodeQueryResult" c:type="GstVulkanEncodeQueryResult" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkoperation.h">Encoder query result. Expected to be used in gst_vulkan_operation_get_query()</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkoperation.h"/>
<field name="offset" writable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkoperation.h">buffer offset</doc>
<type name="guint32" c:type="guint32"/>
</field>
<field name="data_size" writable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkoperation.h">data size</doc>
<type name="guint32" c:type="guint32"/>
</field>
<field name="status" writable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkoperation.h">status</doc>
<type name="guint32" c:type="guint32"/>
</field>
</record>
<enumeration name="VulkanError" version="1.18" glib:type-name="GstVulkanError" glib:get-type="gst_vulkan_error_get_type" c:type="GstVulkanError" glib:error-domain="gst-vulkan-error">
<member name="failed" value="0" c:identifier="GST_VULKAN_FAILED" glib:nick="failed">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkerror.h">undetermined error</doc>
@ -3515,6 +3531,24 @@ dest or DPB images.</doc>
</parameter>
</parameters>
</function>
<function name="config_set_encode_caps" c:identifier="gst_vulkan_image_buffer_pool_config_set_encode_caps" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkimagebufferpool.c">Encode @caps are used when the buffers are going to be used either as encoded
src or DPB images.</doc>
<source-position filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkimagebufferpool.h"/>
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
<parameters>
<parameter name="config" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkimagebufferpool.c">the #GstStructure with the pool's configuration.</doc>
<type name="Gst.Structure" c:type="GstStructure*"/>
</parameter>
<parameter name="caps" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkimagebufferpool.c">Upstream encode caps.</doc>
<type name="Gst.Caps" c:type="GstCaps*"/>
</parameter>
</parameters>
</function>
<field name="bufferpool">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkimagebufferpool.h">parent #GstBufferPool</doc>
<type name="Gst.BufferPool" c:type="GstBufferPool"/>

View file

@ -17,6 +17,7 @@ G_BEGIN_DECLS
#mesondefine GST_VULKAN_HAVE_WINDOW_WIN32
#mesondefine GST_VULKAN_HAVE_WINDOW_ANDROID
#mesondefine GST_VULKAN_HAVE_VIDEO_EXTENSIONS
#mesondefine GST_VULKAN_HAVE_VIDEO_ENCODERS
G_END_DECLS

View file

@ -191,10 +191,10 @@ gst_vulkan_device_constructed (GObject * object)
VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME,
VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME,
VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME,
#ifdef VK_ENABLE_BETA_EXTENSIONS
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME,
VK_EXT_VIDEO_ENCODE_H264_EXTENSION_NAME,
VK_EXT_VIDEO_ENCODE_H265_EXTENSION_NAME,
VK_KHR_VIDEO_ENCODE_H264_EXTENSION_NAME,
VK_KHR_VIDEO_ENCODE_H265_EXTENSION_NAME,
#endif
#endif
};
@ -375,7 +375,7 @@ gst_vulkan_device_choose_queues (GstVulkanDevice * device)
int graph_index, comp_index, tx_index;
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
int dec_index = -1;
#ifdef VK_ENABLE_BETA_EXTENSIONS
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
int enc_index = -1;
#endif
#endif
@ -401,7 +401,7 @@ gst_vulkan_device_choose_queues (GstVulkanDevice * device)
dec_index = _pick_queue_family (queue_family_props, n_queue_families,
VK_QUEUE_VIDEO_DECODE_BIT_KHR, family_scores);
array = _append_queue_create_info (array, dec_index, queue_family_props);
#ifdef VK_ENABLE_BETA_EXTENSIONS
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
enc_index = _pick_queue_family (queue_family_props, n_queue_families,
VK_QUEUE_VIDEO_ENCODE_BIT_KHR, family_scores);
array = _append_queue_create_info (array, enc_index, queue_family_props);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,193 @@
/*
* GStreamer
* Copyright (C) 2023 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <gst/vulkan/vulkan.h>
#define GST_TYPE_VULKAN_ENCODER (gst_vulkan_encoder_get_type())
#define GST_VULKAN_ENCODER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GST_TYPE_VULKAN_ENCODER, GstVulkanEncoder))
#define GST_VULKAN_ENCODER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GST_TYPE_VULKAN_ENCODER, GstVulkanEncoderClass))
#define GST_IS_VULKAN_ENCODER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GST_TYPE_VULKAN_ENCODER))
#define GST_IS_VULKAN_ENCODER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GST_TYPE_VULKAN_ENCODER))
#define GST_VULKAN_ENCODER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GST_TYPE_VULKAN_ENCODER, GstVulkanEncoderClass))
GST_VULKAN_API
GType gst_vulkan_encoder_get_type (void);
typedef struct _GstVulkanEncoder GstVulkanEncoder;
typedef struct _GstVulkanEncoderClass GstVulkanEncoderClass;
typedef union _GstVulkanEncoderParameters GstVulkanEncoderParameters;
typedef union _GstVulkanEncoderParametersOverrides GstVulkanEncoderParametersOverrides;
typedef union _GstVulkanEncoderParametersFeedback GstVulkanEncoderParametersFeedback;
typedef struct _GstVulkanEncodePicture GstVulkanEncodePicture;
/**
* GstVulkanEncodePicture:
* @is_ref: picture is reference
* @nb_refs: number of references
* @slotIndex: slot index
* @packed_headers: packed headers
* @pic_num: picture number
* @pic_order_cnt: order count
* @width: picture width
* @height: picture height
* @fps_n: fps numerator
* @fps_d: fps denominator
* @in_buffer: input buffer
* @out_buffer: output buffer
*
* It contains the whole state for encoding a single picture.
*
* Since: 1.24
*/
struct _GstVulkanEncodePicture
{
gboolean is_ref;
gint nb_refs;
gint slotIndex;
/* picture parameters */
GPtrArray *packed_headers;
gint pic_num;
gint pic_order_cnt;
gint width;
gint height;
gint fps_n;
gint fps_d;
GstBuffer *in_buffer;
GstBuffer *dpb_buffer;
GstBuffer *out_buffer;
/* Input frame */
GstVulkanImageView *img_view;
GstVulkanImageView *dpb_view;
VkVideoPictureResourceInfoKHR dpb;
void *codec_rc_info;
void *codec_pic_info;
void *codec_rc_layer_info;
void *codec_dpb_slot_info;
void *codec_quality_level;
};
/**
* GstVulkanEncoder:
* @parent: the parent #GstObject
* @queue: the #GstVulkanQueue to command buffers will be allocated from
*
* Since: 1.24
**/
struct _GstVulkanEncoder
{
GstObject parent;
GstVulkanQueue *queue;
guint codec;
/* <private> */
gpointer _reserved [GST_PADDING];
};
/**
* GstVulkanEncoderClass:
* @parent_class: the parent #GstObjectClass
*
* Since: 1.24
*/
struct _GstVulkanEncoderClass
{
GstObjectClass parent;
/* <private> */
gpointer _reserved [GST_PADDING];
};
union _GstVulkanEncoderParameters
{
/*< private >*/
VkVideoEncodeH264SessionParametersCreateInfoKHR h264;
VkVideoEncodeH265SessionParametersCreateInfoKHR h265;
};
union _GstVulkanEncoderParametersOverrides
{
/*< private >*/
VkVideoEncodeH264SessionParametersGetInfoKHR h264;
VkVideoEncodeH265SessionParametersGetInfoKHR h265;
};
union _GstVulkanEncoderParametersFeedback
{
VkVideoEncodeH264SessionParametersFeedbackInfoKHR h264;
VkVideoEncodeH265SessionParametersFeedbackInfoKHR h265;
};
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstVulkanEncoder, gst_object_unref)
GST_VULKAN_API
GstVulkanEncoder * gst_vulkan_encoder_create_from_queue (GstVulkanQueue * queue,
guint codec);
GST_VULKAN_API
gboolean gst_vulkan_encoder_start (GstVulkanEncoder * self,
GstVulkanVideoProfile * profile,
guint32 out_buffer_size,
GError ** error);
GST_VULKAN_API
gboolean gst_vulkan_encoder_stop (GstVulkanEncoder * self);
GST_VULKAN_API
gboolean gst_vulkan_encoder_update_video_session_parameters
(GstVulkanEncoder * self,
GstVulkanEncoderParameters *enc_params,
GError ** error);
GST_VULKAN_API
gboolean gst_vulkan_encoder_video_session_parameters_overrides
(GstVulkanEncoder * self,
GstVulkanEncoderParametersOverrides * params,
GstVulkanEncoderParametersFeedback * feedback,
gsize * data_size,
gpointer * data,
GError ** error);
GST_VULKAN_API
gboolean gst_vulkan_encoder_create_dpb_pool (GstVulkanEncoder * self,
GstCaps * caps);
GST_VULKAN_API
gboolean gst_vulkan_encoder_encode (GstVulkanEncoder * self,
GstVulkanEncodePicture * pic,
GstVulkanEncodePicture ** ref_pics);
GST_VULKAN_API
gboolean gst_vulkan_encoder_caps (GstVulkanEncoder * self,
GstVulkanVideoCapabilities * caps);
GST_VULKAN_API
GstCaps * gst_vulkan_encoder_profile_caps (GstVulkanEncoder * self);
GST_VULKAN_API
GstVulkanEncodePicture * gst_vulkan_encode_picture_new (GstVulkanEncoder * self,
GstBuffer * in_buffer,
gint width,
gint height,
gboolean is_ref,
gint nb_refs);
GST_VULKAN_API
void gst_vulkan_encode_picture_free (GstVulkanEncodePicture * pic);

View file

@ -48,8 +48,8 @@ struct _GstVulkanImageBufferPoolPrivate
VkFormat vk_fmts[GST_VIDEO_MAX_PLANES];
int n_imgs;
guint32 n_layers;
gboolean has_profile;
GstVulkanVideoProfile profile;
guint32 n_profiles;
GstVulkanVideoProfile profiles[2];
GstVulkanOperation *exec;
};
@ -109,11 +109,30 @@ gst_vulkan_image_buffer_pool_config_set_decode_caps (GstStructure * config,
gst_structure_set (config, "decode-caps", GST_TYPE_CAPS, caps, NULL);
}
/**
* gst_vulkan_image_buffer_pool_config_set_encode_caps:
* @config: the #GstStructure with the pool's configuration.
* @caps: Upstream encode caps.
*
* Encode @caps are used when the buffers are going to be used either as encoded
* src or DPB images.
*
* Since: 1.26
*/
void
gst_vulkan_image_buffer_pool_config_set_encode_caps (GstStructure * config,
GstCaps * caps)
{
g_return_if_fail (GST_IS_CAPS (caps));
gst_structure_set (config, "encode-caps", GST_TYPE_CAPS, caps, NULL);
}
static inline gboolean
gst_vulkan_image_buffer_pool_config_get_allocation_params (GstStructure *
config, VkImageUsageFlags * usage, VkMemoryPropertyFlags * mem_props,
VkImageLayout * initial_layout, guint64 * initial_access,
guint32 * n_layers, GstCaps ** decode_caps)
guint32 * n_layers, GstCaps ** decode_caps, GstCaps ** encode_caps)
{
if (!gst_structure_get_uint (config, "usage", usage)) {
*usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT
@ -136,6 +155,9 @@ gst_vulkan_image_buffer_pool_config_get_allocation_params (GstStructure *
if (decode_caps)
gst_structure_get (config, "decode-caps", GST_TYPE_CAPS, decode_caps, NULL);
if (encode_caps)
gst_structure_get (config, "encode-caps", GST_TYPE_CAPS, encode_caps, NULL);
return TRUE;
}
@ -149,7 +171,8 @@ gst_vulkan_image_buffer_pool_set_config (GstBufferPool * pool,
VkImageUsageFlags requested_usage, supported_usage;
VkImageCreateInfo image_info;
guint min_buffers, max_buffers;
GstCaps *caps = NULL, *decode_caps = NULL;
GstCaps *caps = NULL, *decode_caps = NULL, *encode_caps = NULL;
GstCapsFeatures *features;
gboolean found, no_multiplane, ret = TRUE;
guint i;
@ -176,27 +199,37 @@ gst_vulkan_image_buffer_pool_set_config (GstBufferPool * pool,
gst_vulkan_image_buffer_pool_config_get_allocation_params (config,
&priv->usage, &priv->mem_props, &priv->initial_layout,
&priv->initial_access, &priv->n_layers, &decode_caps);
&priv->initial_access, &priv->n_layers, &decode_caps, &encode_caps);
priv->has_profile = FALSE;
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
if (decode_caps && ((priv->usage
& (VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR
| VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR)) != 0)) {
priv->has_profile =
gst_vulkan_video_profile_from_caps (&priv->profile, decode_caps,
GST_VULKAN_VIDEO_OPERATION_DECODE);
}
{
guint n = 0;
if (decode_caps && ((priv->usage
& (VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR
| VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR)) != 0)) {
n++;
if (gst_vulkan_video_profile_from_caps (&priv->profiles[priv->n_profiles],
decode_caps, GST_VULKAN_VIDEO_OPERATION_DECODE))
priv->n_profiles++;
}
gst_clear_caps (&decode_caps);
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
if (encode_caps && ((priv->usage
& (VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR
| VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR)) != 0)) {
n++;
if (gst_vulkan_video_profile_from_caps (&priv->profiles[priv->n_profiles],
encode_caps, GST_VULKAN_VIDEO_OPERATION_ENCODE))
priv->n_profiles++;
}
gst_clear_caps (&encode_caps);
#endif
gst_clear_caps (&decode_caps);
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
if (((priv->usage & (VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR
| VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR)) != 0)
&& !priv->has_profile)
goto missing_profile;
if (priv->has_profile) {
if (priv->n_profiles != n)
goto missing_profile;
}
if (priv->n_profiles > 0) {
no_multiplane = FALSE;
/* HACK(victor): NVIDIA & RADV drivers don't report decoding features for
@ -249,10 +282,12 @@ gst_vulkan_image_buffer_pool_set_config (GstBufferPool * pool,
GstVulkanImageMemory *img_mem;
guint width, height;
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
VkVideoProfileInfoKHR profiles[] =
{ priv->profiles[0].profile, priv->profiles[1].profile };
VkVideoProfileListInfoKHR profile_list = {
.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR,
.profileCount = 1,
.pProfiles = &priv->profile.profile,
.profileCount = priv->n_profiles,
.pProfiles = profiles,
};
#endif
@ -269,7 +304,7 @@ gst_vulkan_image_buffer_pool_set_config (GstBufferPool * pool,
image_info.extent = (VkExtent3D) { width, height, 1 };
/* *INDENT-ON* */
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
if (priv->has_profile)
if (priv->n_profiles > 0)
image_info.pNext = &profile_list;
#endif
@ -456,10 +491,12 @@ gst_vulkan_image_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
GstMemory *mem;
guint width, height;
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
VkVideoProfileInfoKHR profiles[] =
{ priv->profiles[0].profile, priv->profiles[1].profile };
VkVideoProfileListInfoKHR profile_list = {
.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR,
.profileCount = 1,
.pProfiles = &priv->profile.profile,
.profileCount = priv->n_profiles,
.pProfiles = profiles,
};
#endif
@ -476,7 +513,7 @@ gst_vulkan_image_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
image_info.extent = (VkExtent3D) { width, height, 1 };
/* *INDENT-ON* */
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
if (priv->has_profile)
if (priv->n_profiles > 0)
image_info.pNext = &profile_list;
#endif

View file

@ -93,6 +93,11 @@ void gst_vulkan_image_buffer_pool_config_set_decode_caps
(GstStructure * config,
GstCaps * caps);
GST_VULKAN_API
void gst_vulkan_image_buffer_pool_config_set_encode_caps
(GstStructure * config,
GstCaps * caps);
G_END_DECLS
#endif /* __GST_VULKAN_IMAGE_BUFFER_POOL_H__ */

View file

@ -1249,7 +1249,14 @@ gst_vulkan_operation_enable_query (GstVulkanOperation * self,
if (priv->has_video)
stride = sizeof (guint32);
break;
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
case VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR:
if (priv->has_video)
stride = sizeof (GstVulkanEncodeQueryResult);
break;
#endif
#endif
default:
break;
}
@ -1291,7 +1298,11 @@ gst_vulkan_operation_get_query (GstVulkanOperation * self, gpointer * result,
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
if (priv->has_video
&& (priv->query_type == VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR)) {
&& (priv->query_type == VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
|| priv->query_type == VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR
#endif
)) {
flags |= VK_QUERY_RESULT_WITH_STATUS_BIT_KHR;
}
#endif

View file

@ -63,6 +63,23 @@ struct _GstVulkanOperationClass
};
/**
* GstVulkanEncodeQueryResult:
* @offset: buffer offset
* @data_size: data size
* @status: status
*
* Encoder query result. Expected to be used in gst_vulkan_operation_get_query()
*
* Since: 1.26
*/
struct _GstVulkanEncodeQueryResult
{
guint32 offset;
guint32 data_size;
guint32 status;
};
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstVulkanOperation, gst_object_unref)
GST_VULKAN_API

View file

@ -37,6 +37,16 @@ const VkExtensionProperties _vk_codec_extensions[] = {
.extensionName = VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_EXTENSION_NAME,
.specVersion = VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_SPEC_VERSION,
},
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
[GST_VK_VIDEO_EXTENSION_ENCODE_H264] = {
.extensionName = VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_EXTENSION_NAME,
.specVersion = VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_SPEC_VERSION,
},
[GST_VK_VIDEO_EXTENSION_ENCODE_H265] = {
.extensionName = VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_EXTENSION_NAME,
.specVersion = VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_SPEC_VERSION,
}
#endif
};
const VkComponentMapping _vk_identity_component_map = {
@ -66,6 +76,9 @@ gst_vulkan_video_get_vk_functions (GstVulkanInstance * instance,
} \
} G_STMT_END;
GST_VULKAN_VIDEO_FN_LIST (GET_PROC_ADDRESS_REQUIRED)
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
GST_VULKAN_VIDEO_ENCODING_FN_LIST (GET_PROC_ADDRESS_REQUIRED)
#endif
#undef GET_PROC_ADDRESS_REQUIRED
ret = TRUE;

View file

@ -43,6 +43,8 @@ struct _GstVulkanVideoSession
typedef enum {
GST_VK_VIDEO_EXTENSION_DECODE_H264,
GST_VK_VIDEO_EXTENSION_DECODE_H265,
GST_VK_VIDEO_EXTENSION_ENCODE_H264,
GST_VK_VIDEO_EXTENSION_ENCODE_H265,
} GST_VK_VIDEO_EXTENSIONS;
#define GST_VULKAN_VIDEO_FN_LIST(V) \
@ -61,14 +63,23 @@ typedef enum {
V(CmdEndVideoCoding) \
V(CmdDecodeVideo)
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
#define GST_VULKAN_VIDEO_ENCODING_FN_LIST(V) \
V(CmdEncodeVideo) \
V(GetEncodedVideoSessionParameters)
#endif
struct _GstVulkanVideoFunctions
{
#define DEFINE_FUNCTION(name) G_PASTE(G_PASTE(PFN_vk, name), KHR) name;
GST_VULKAN_VIDEO_FN_LIST (DEFINE_FUNCTION)
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
GST_VULKAN_VIDEO_ENCODING_FN_LIST (DEFINE_FUNCTION)
#endif
#undef DEFINE_FUNCTION
};
extern const VkExtensionProperties _vk_codec_extensions[2];
extern const VkExtensionProperties _vk_codec_extensions[4];
extern const VkComponentMapping _vk_identity_component_map;
gboolean gst_vulkan_video_get_vk_functions (GstVulkanInstance * instance,

View file

@ -36,6 +36,12 @@ static const struct {
VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_INFO_KHR },
{ GST_VULKAN_VIDEO_OPERATION_DECODE, VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR, "video/x-h265",
VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_INFO_KHR },
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
{ GST_VULKAN_VIDEO_OPERATION_ENCODE, VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR, "video/x-h264",
VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_INFO_KHR },
{ GST_VULKAN_VIDEO_OPERATION_ENCODE, VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR, "video/x-h265",
VK_STRUCTURE_TYPE_VIDEO_ENCODE_H265_PROFILE_INFO_KHR },
#endif
};
static const struct {
@ -146,6 +152,28 @@ gst_vulkan_video_profile_to_caps (const GstVulkanVideoProfile * profile)
}
}
break;
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
if (profile->codec.h264enc.sType == video_codecs_map[i].stype) {
int j;
for (j = 0; j < G_N_ELEMENTS (h264_profile_map); j++) {
if (profile->codec.h264enc.stdProfileIdc
== h264_profile_map[j].vk_profile)
profile_str = h264_profile_map[j].profile_str;
}
}
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
if (profile->codec.h265enc.sType == video_codecs_map[i].stype) {
int j;
for (j = 0; j < G_N_ELEMENTS (h265_profile_map); j++) {
if (profile->codec.h265enc.stdProfileIdc
== h265_profile_map[j].vk_profile)
profile_str = h265_profile_map[j].profile_str;
}
}
break;
#endif
default:
break;
}
@ -282,6 +310,44 @@ gst_vulkan_video_profile_from_caps (GstVulkanVideoProfile * profile,
}
break;
}
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:{
int j;
profile->codec.h264enc.sType = video_codecs_map[i].stype;
profile->codec.h264enc.stdProfileIdc =
STD_VIDEO_H264_PROFILE_IDC_INVALID;
profile->profile.pNext = &profile->codec;
profile_str = gst_structure_get_string (structure, "profile");
for (j = 0; profile_str && j < G_N_ELEMENTS (h264_profile_map); j++) {
if (g_strcmp0 (profile_str, h264_profile_map[j].profile_str) == 0) {
profile->codec.h264enc.stdProfileIdc =
h264_profile_map[j].vk_profile;
break;
}
}
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:{
int j;
profile->codec.h265enc.sType = video_codecs_map[i].stype;
profile->codec.h265enc.stdProfileIdc =
STD_VIDEO_H265_PROFILE_IDC_INVALID;
profile->profile.pNext = &profile->codec;
profile_str = gst_structure_get_string (structure, "profile");
for (j = 0; profile_str && j < G_N_ELEMENTS (h265_profile_map); j++) {
if (g_strcmp0 (profile_str, h265_profile_map[j].profile_str) == 0) {
profile->codec.h265enc.stdProfileIdc =
h265_profile_map[j].vk_profile;
break;
}
}
break;
}
#endif
default:
profile->usage.decode.pNext = NULL;
break;

View file

@ -39,12 +39,19 @@ struct _GstVulkanVideoProfile
VkVideoProfileInfoKHR profile;
union {
VkVideoDecodeUsageInfoKHR decode;
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
VkVideoEncodeUsageInfoKHR encode;
#endif
} usage;
union {
VkBaseInStructure base;
VkVideoDecodeH264ProfileInfoKHR h264dec;
VkVideoDecodeH265ProfileInfoKHR h265dec;
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
VkVideoEncodeH264ProfileInfoKHR h264enc;
VkVideoEncodeH265ProfileInfoKHR h265enc;
#endif
} codec;
#endif
gpointer _reserved[GST_PADDING];
@ -65,6 +72,10 @@ struct _GstVulkanVideoCapabilities
VkBaseInStructure base;
VkVideoDecodeH264CapabilitiesKHR h264dec;
VkVideoDecodeH265CapabilitiesKHR h265dec;
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
VkVideoEncodeH264CapabilitiesKHR h264enc;
VkVideoEncodeH265CapabilitiesKHR h265enc;
#endif
} codec;
#endif
gpointer _reserved[GST_PADDING];

View file

@ -100,6 +100,7 @@ vulkan_conf_options = [
'GST_VULKAN_HAVE_WINDOW_WIN32',
'GST_VULKAN_HAVE_WINDOW_ANDROID',
'GST_VULKAN_HAVE_VIDEO_EXTENSIONS',
'GST_VULKAN_HAVE_VIDEO_ENCODERS',
]
foreach option : vulkan_conf_options
@ -337,6 +338,29 @@ static StdVideoH265PictureParameterSet h265_pps;
'gstvkvideo-private.c',
'gstvkdecoder-private.c',
)
encoder_test = '''
#include <vulkan/vulkan.h>
#if !(defined(VERSION_1_4) || (defined(VK_VERSION_1_3) && VK_HEADER_VERSION >= 275))
#error "Need at least Vulkan 1.3.275"
#endif
static VkVideoEncodeH264ProfileInfoKHR h264_profile;
static VkVideoEncodeH265ProfileInfoKHR h265_profile;
'''
if vulkan_inc_dir != ''
have_vk_encoder = cc.compiles(encoder_test, dependencies : vulkan_dep, args: '-I' + vulkan_inc_dir)
else
have_vk_encoder = cc.compiles(encoder_test, dependencies : vulkan_dep)
endif
if have_vk_encoder
vulkan_conf.set('GST_VULKAN_HAVE_VIDEO_ENCODERS', 1)
vulkan_sources += files(
'gstvkencoder-private.c',
)
endif
elif get_option('vulkan-video').enabled()
error('Vulkan Video extensions headers not found')
endif

View file

@ -118,6 +118,7 @@ typedef struct _GstVulkanVideoCapabilities GstVulkanVideoCapabilities;
typedef struct _GstVulkanOperation GstVulkanOperation;
typedef struct _GstVulkanOperationClass GstVulkanOperationClass;
typedef struct _GstVulkanOperationPrivate GstVulkanOperationPrivate;
typedef struct _GstVulkanEncodeQueryResult GstVulkanEncodeQueryResult;
G_END_DECLS

View file

@ -18,7 +18,7 @@
* Boston, MA 02111-1307, USA.
*/
/* H264 DECODER: 1 frame 320x240 blue box */
/* 1 frame 320x240 blue box */
static StdVideoH264HrdParameters h264_std_hrd = {
.cpb_cnt_minus1 = 0,
.bit_rate_scale = 4,
@ -84,7 +84,7 @@ static StdVideoH264SequenceParameterSet h264_std_sps = {
.vui_parameters_present_flag = 1,
},
.profile_idc = STD_VIDEO_H264_PROFILE_IDC_MAIN,
.level_idc = STD_VIDEO_H264_LEVEL_IDC_2_1,
.level_idc = STD_VIDEO_H264_LEVEL_IDC_4_1,
.chroma_format_idc = STD_VIDEO_H264_CHROMA_FORMAT_IDC_420,
.seq_parameter_set_id = 0,
.bit_depth_luma_minus8 = 0,
@ -137,94 +137,3 @@ static const uint8_t h264_slice[] = {
0x93, 0xa2, 0x39, 0xa9, 0x99, 0x1e, 0xc5, 0x01, 0x4a, 0x00, 0x0c, 0x03, 0x0d,
0x75, 0x45, 0x2a, 0xe3, 0x3d, 0x7f, 0x10, 0x03, 0x82
};
/* H265 DECODER: 1 frame 320x240 blue box */
static StdVideoH265HrdParameters h265_std_hrd = { 0, };
static StdVideoH265ProfileTierLevel h265_std_ptl = {
.flags = {
.general_progressive_source_flag = 1,
.general_frame_only_constraint_flag = 1,
},
.general_profile_idc = STD_VIDEO_H265_PROFILE_IDC_MAIN,
.general_level_idc = STD_VIDEO_H265_LEVEL_IDC_6_0,
};
static StdVideoH265DecPicBufMgr h265_std_pbm = {
.max_latency_increase_plus1 = {5, 0,},
.max_dec_pic_buffering_minus1 = {4, 0,},
.max_num_reorder_pics = {2, 0,},
};
static StdVideoH265VideoParameterSet h265_std_vps = {
.flags = {
.vps_temporal_id_nesting_flag = 1,
.vps_sub_layer_ordering_info_present_flag = 1,
},
.vps_video_parameter_set_id = 0,
.pDecPicBufMgr = &h265_std_pbm,
.pHrdParameters = &h265_std_hrd,
.pProfileTierLevel = &h265_std_ptl,
};
static StdVideoH265SequenceParameterSetVui h265_std_sps_vui = {
.flags = {
.video_signal_type_present_flag = 1,
.vui_timing_info_present_flag = 1,
},
.aspect_ratio_idc = STD_VIDEO_H265_ASPECT_RATIO_IDC_UNSPECIFIED,
.video_format = 5,
.colour_primaries = 2,
.transfer_characteristics = 2,
.matrix_coeffs = 2,
.vui_num_units_in_tick = 1,
.vui_time_scale = 25,
.pHrdParameters = &h265_std_hrd,
};
static StdVideoH265SequenceParameterSet h265_std_sps = {
.flags = {
.sps_temporal_id_nesting_flag = 1,
.sps_sub_layer_ordering_info_present_flag = 1,
.sample_adaptive_offset_enabled_flag = 1,
.sps_temporal_mvp_enabled_flag = 1,
.strong_intra_smoothing_enabled_flag = 1,
.vui_parameters_present_flag = 1,
.sps_extension_present_flag = 1,
},
.chroma_format_idc = STD_VIDEO_H265_CHROMA_FORMAT_IDC_420,
.pic_width_in_luma_samples = 320,
.pic_height_in_luma_samples = 240,
.sps_video_parameter_set_id = 0,
.sps_seq_parameter_set_id = 0,
.log2_max_pic_order_cnt_lsb_minus4 = 4,
.log2_diff_max_min_luma_coding_block_size = 3,
.log2_diff_max_min_luma_transform_block_size = 3,
.pProfileTierLevel = &h265_std_ptl,
.pDecPicBufMgr = &h265_std_pbm,
.pSequenceParameterSetVui = &h265_std_sps_vui,
};
static StdVideoH265PictureParameterSet h265_std_pps = {
.flags = {
.sign_data_hiding_enabled_flag = 1,
.cu_qp_delta_enabled_flag = 1,
.weighted_pred_flag = 1,
.entropy_coding_sync_enabled_flag = 1,
.uniform_spacing_flag = 1,
.loop_filter_across_tiles_enabled_flag = 1,
.pps_loop_filter_across_slices_enabled_flag = 1,
},
.pps_pic_parameter_set_id = 0,
.pps_seq_parameter_set_id = 0,
.sps_video_parameter_set_id = 0,
.diff_cu_qp_delta_depth = 1,
};
static const uint8_t h265_slice[] = {
0x28, 0x01, 0xaf, 0x1d, 0x21, 0x6a, 0x83, 0x40, 0xf7, 0xcf, 0x80, 0xff, 0xf8,
0x90, 0xfa, 0x3b, 0x77, 0x87, 0x96, 0x96, 0xba, 0xfa, 0xcd, 0x61, 0xb5, 0xe3,
0xc1, 0x02, 0x2d, 0xe0, 0xa8, 0x17, 0x96, 0x03, 0x4c, 0x4e, 0x1a, 0x9e, 0xd0,
0x93, 0x0b, 0x93, 0x40, 0x00, 0x05, 0xec, 0x87, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x56, 0x40
};

View file

@ -0,0 +1,110 @@
/* GStreamer
*
* Copyright (C) 2024 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.
*/
/* H.265: 1 frame 320x240 blue box */
StdVideoH265HrdParameters h265_std_hrd = { 0, };
static StdVideoH265ProfileTierLevel h265_std_ptl = {
.flags = {
.general_progressive_source_flag = 1,
.general_frame_only_constraint_flag = 1,
},
.general_profile_idc = STD_VIDEO_H265_PROFILE_IDC_MAIN,
.general_level_idc = STD_VIDEO_H265_LEVEL_IDC_6_0,
};
static StdVideoH265DecPicBufMgr h265_std_pbm = {
.max_latency_increase_plus1 = {5, 0,},
.max_dec_pic_buffering_minus1 = {4, 0,},
.max_num_reorder_pics = {2, 0,},
};
static StdVideoH265VideoParameterSet h265_std_vps = {
.flags = {
.vps_temporal_id_nesting_flag = 1,
.vps_sub_layer_ordering_info_present_flag = 1,
},
.vps_video_parameter_set_id = 0,
.pDecPicBufMgr = &h265_std_pbm,
.pHrdParameters = &h265_std_hrd,
.pProfileTierLevel = &h265_std_ptl,
};
static StdVideoH265SequenceParameterSetVui h265_std_sps_vui = {
.flags = {
.video_signal_type_present_flag = 1,
.vui_timing_info_present_flag = 1,
},
.aspect_ratio_idc = STD_VIDEO_H265_ASPECT_RATIO_IDC_UNSPECIFIED,
.video_format = 5,
.colour_primaries = 2,
.transfer_characteristics = 2,
.matrix_coeffs = 2,
.vui_num_units_in_tick = 1,
.vui_time_scale = 25,
.pHrdParameters = &h265_std_hrd,
};
static StdVideoH265SequenceParameterSet h265_std_sps = {
.flags = {
.sps_temporal_id_nesting_flag = 1,
.sps_sub_layer_ordering_info_present_flag = 1,
.sample_adaptive_offset_enabled_flag = 1,
.sps_temporal_mvp_enabled_flag = 1,
.strong_intra_smoothing_enabled_flag = 1,
.vui_parameters_present_flag = 1,
.sps_extension_present_flag = 1,
},
.chroma_format_idc = STD_VIDEO_H265_CHROMA_FORMAT_IDC_420,
.pic_width_in_luma_samples = 320,
.pic_height_in_luma_samples = 240,
.sps_video_parameter_set_id = 0,
.sps_seq_parameter_set_id = 0,
.log2_max_pic_order_cnt_lsb_minus4 = 4,
.log2_diff_max_min_luma_coding_block_size = 3,
.log2_diff_max_min_luma_transform_block_size = 3,
.pProfileTierLevel = &h265_std_ptl,
.pDecPicBufMgr = &h265_std_pbm,
.pSequenceParameterSetVui = &h265_std_sps_vui,
};
static StdVideoH265PictureParameterSet h265_std_pps = {
.flags = {
.sign_data_hiding_enabled_flag = 1,
.cu_qp_delta_enabled_flag = 1,
.weighted_pred_flag = 1,
.entropy_coding_sync_enabled_flag = 1,
.uniform_spacing_flag = 1,
.loop_filter_across_tiles_enabled_flag = 1,
.pps_loop_filter_across_slices_enabled_flag = 1,
},
.pps_pic_parameter_set_id = 0,
.pps_seq_parameter_set_id = 0,
.sps_video_parameter_set_id = 0,
.diff_cu_qp_delta_depth = 1,
};
static const uint8_t h265_slice[] = {
0x28, 0x01, 0xaf, 0x1d, 0x21, 0x6a, 0x83, 0x40, 0xf7, 0xcf, 0x80, 0xff, 0xf8,
0x90, 0xfa, 0x3b, 0x77, 0x87, 0x96, 0x96, 0xba, 0xfa, 0xcd, 0x61, 0xb5, 0xe3,
0xc1, 0x02, 0x2d, 0xe0, 0xa8, 0x17, 0x96, 0x03, 0x4c, 0x4e, 0x1a, 0x9e, 0xd0,
0x93, 0x0b, 0x93, 0x40, 0x00, 0x05, 0xec, 0x87, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x56, 0x40
};

View file

@ -275,7 +275,7 @@ download_and_check_output_buffer (GstVulkanDecoder * dec, VkFormat vk_format,
gst_object_unref (out_pool);
}
#include "vkcodecparams.c"
#include "vkcodecparams_h264.c"
static VkVideoDecodeH264SessionParametersAddInfoKHR h264_params = {
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_KHR,
@ -441,6 +441,8 @@ GST_START_TEST (test_h264_decoder)
GST_END_TEST;
#include "vkcodecparams_h265.c"
static VkVideoDecodeH265SessionParametersAddInfoKHR h265_params = {
.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_KHR,
.stdVPSCount = 1,

View file

@ -0,0 +1,981 @@
/* GStreamer
*
* Copyright (C) 2024 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/gst.h>
#include <gst/check/gstcheck.h>
#include <gst/vulkan/vulkan.h>
#include <gst/codecparsers/gsth264parser.h>
#include <gst/vulkan/gstvkencoder-private.h>
// Include h264 std session params
#include "vkcodecparams_h264.c"
static GstVulkanInstance *instance;
static GstVulkanQueue *encode_queue = NULL;
static GstVulkanQueue *gfx_queue = NULL;
static GstBufferPool *img_pool;
static GstBufferPool *buffer_pool;
static GstVulkanOperation *exec = NULL;
static GstVideoInfo in_info;
static GstVideoInfo out_info;
typedef struct
{
GstVulkanEncodePicture *picture;
VkVideoEncodeH264NaluSliceInfoKHR slice_info;
VkVideoEncodeH264PictureInfoKHR enc_pic_info;
VkVideoEncodeH264DpbSlotInfoKHR dpb_slot_info;
VkVideoEncodeH264RateControlInfoKHR rc_info;
VkVideoEncodeH264RateControlLayerInfoKHR rc_layer_info;
VkVideoEncodeH264QualityLevelPropertiesKHR quality_level;
StdVideoEncodeH264SliceHeader slice_hdr;
StdVideoEncodeH264PictureInfo pic_info;
StdVideoEncodeH264ReferenceInfo ref_info;
StdVideoEncodeH264ReferenceListsInfo ref_list_info;
} GstVulkanH264EncodeFrame;
static GstVulkanH264EncodeFrame *
_h264_encode_frame_new (GstVulkanEncodePicture * picture)
{
GstVulkanH264EncodeFrame *frame;
g_return_val_if_fail (picture, NULL);
frame = g_new (GstVulkanH264EncodeFrame, 1);
frame->picture = picture;
return frame;
}
static void
_h264_encode_frame_free (gpointer pframe)
{
GstVulkanH264EncodeFrame *frame = pframe;
g_clear_pointer (&frame->picture, gst_vulkan_encode_picture_free);
g_free (frame);
}
static void
setup (void)
{
instance = gst_vulkan_instance_new ();
fail_unless (gst_vulkan_instance_open (instance, NULL));
}
static void
teardown (void)
{
gst_clear_object (&encode_queue);
gst_clear_object (&gfx_queue);
gst_object_unref (instance);
}
#define H264_MB_SIZE_ALIGNMENT 16
/* initialize the vulkan image buffer pool */
static GstBufferPool *
allocate_image_buffer_pool (GstVulkanEncoder * enc, uint32_t width,
uint32_t height)
{
GstVideoFormat format = GST_VIDEO_FORMAT_NV12;
GstCaps *profile_caps, *caps = gst_caps_new_simple ("video/x-raw", "format",
G_TYPE_STRING, gst_video_format_to_string (format), "width", G_TYPE_INT,
width, "height", G_TYPE_INT, height, NULL);
GstBufferPool *pool = gst_vulkan_image_buffer_pool_new (encode_queue->device);
GstStructure *config = gst_buffer_pool_get_config (pool);
gsize frame_size = width * height * 2; //NV12
gst_caps_set_features_simple (caps,
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE, NULL));
fail_unless (gst_vulkan_encoder_create_dpb_pool (enc, caps));
gst_video_info_from_caps (&out_info, caps);
gst_buffer_pool_config_set_params (config, caps, frame_size, 1, 0);
gst_vulkan_image_buffer_pool_config_set_allocation_params (config,
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR,
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
profile_caps = gst_vulkan_encoder_profile_caps (enc);
gst_vulkan_image_buffer_pool_config_set_encode_caps (config, profile_caps);
gst_caps_unref (caps);
gst_caps_unref (profile_caps);
fail_unless (gst_buffer_pool_set_config (pool, config));
fail_unless (gst_buffer_pool_set_active (pool, TRUE));
return pool;
}
static GstBufferPool *
allocate_buffer_pool (GstVulkanEncoder * enc, uint32_t width, uint32_t height)
{
GstVideoFormat format = GST_VIDEO_FORMAT_NV12;
GstCaps *profile_caps, *caps = gst_caps_new_simple ("video/x-raw", "format",
G_TYPE_STRING, gst_video_format_to_string (format), "width", G_TYPE_INT,
width, "height", G_TYPE_INT, height, NULL);
gsize frame_size = width * height * 2; //NV12
GstBufferPool *pool = gst_vulkan_buffer_pool_new (encode_queue->device);
GstStructure *config = gst_buffer_pool_get_config (pool);
gst_caps_set_features_simple (caps,
gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_VULKAN_BUFFER, NULL));
gst_video_info_from_caps (&in_info, caps);
gst_buffer_pool_config_set_params (config, caps, frame_size, 1, 0);
profile_caps = gst_vulkan_encoder_profile_caps (enc);
gst_vulkan_image_buffer_pool_config_set_encode_caps (config, profile_caps);
gst_caps_unref (caps);
gst_caps_unref (profile_caps);
gst_vulkan_image_buffer_pool_config_set_allocation_params (config,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
VK_IMAGE_LAYOUT_VIDEO_ENCODE_SRC_KHR, VK_ACCESS_TRANSFER_WRITE_BIT);
fail_unless (gst_buffer_pool_set_config (pool, config));
fail_unless (gst_buffer_pool_set_active (pool, TRUE));
return pool;
}
static GstBuffer *
generate_input_buffer (GstBufferPool * pool, int width, int height)
{
int i;
GstBuffer *buffer;
GstMapInfo info;
GstMemory *mem;
if ((gst_buffer_pool_acquire_buffer (pool, &buffer, NULL))
!= GST_FLOW_OK)
goto out;
// PLANE Y COLOR BLUE
mem = gst_buffer_peek_memory (buffer, 0);
gst_memory_map (mem, &info, GST_MAP_WRITE);
for (i = 0; i < width * height; i++)
info.data[i] = 0x29;
gst_memory_unmap (mem, &info);
// PLANE UV
mem = gst_buffer_peek_memory (buffer, 1);
gst_memory_map (mem, &info, GST_MAP_WRITE);
for (i = 0; i < width * height / 2; i++) {
info.data[i] = 0xf0;
info.data[i++] = 0x6e;
}
gst_memory_unmap (mem, &info);
out:
return buffer;
}
/* upload the raw input buffer pool into a vulkan image buffer */
static GstFlowReturn
upload_buffer_to_image (GstBufferPool * pool, GstBuffer * inbuf,
GstBuffer ** outbuf)
{
GstFlowReturn ret = GST_FLOW_OK;
GError *error = NULL;
GstVulkanCommandBuffer *cmd_buf;
guint i, n_mems, n_planes;
GArray *barriers = NULL;
VkImageLayout dst_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
if ((ret = gst_buffer_pool_acquire_buffer (pool, outbuf, NULL))
!= GST_FLOW_OK)
goto out;
if (!exec) {
GstVulkanCommandPool *cmd_pool =
gst_vulkan_queue_create_command_pool (gfx_queue, &error);
if (!cmd_pool)
goto error;
exec = gst_vulkan_operation_new (cmd_pool);
gst_object_unref (cmd_pool);
}
if (!gst_vulkan_operation_add_dependency_frame (exec, *outbuf,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT))
goto error;
if (!gst_vulkan_operation_begin (exec, &error))
goto error;
cmd_buf = exec->cmd_buf;
if (!gst_vulkan_operation_add_frame_barrier (exec, *outbuf,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, NULL))
goto unlock_error;
barriers = gst_vulkan_operation_retrieve_image_barriers (exec);
if (barriers->len == 0) {
ret = GST_FLOW_ERROR;
goto unlock_error;
}
VkDependencyInfoKHR dependency_info = {
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO_KHR,
.pImageMemoryBarriers = (gpointer) barriers->data,
.imageMemoryBarrierCount = barriers->len,
};
gst_vulkan_operation_pipeline_barrier2 (exec, &dependency_info);
dst_layout = g_array_index (barriers, VkImageMemoryBarrier2KHR, 0).newLayout;
g_clear_pointer (&barriers, g_array_unref);
n_mems = gst_buffer_n_memory (*outbuf);
n_planes = GST_VIDEO_INFO_N_PLANES (&out_info);
for (i = 0; i < n_planes; i++) {
VkBufferImageCopy region;
GstMemory *in_mem, *out_mem;
GstVulkanBufferMemory *buf_mem;
GstVulkanImageMemory *img_mem;
const VkImageAspectFlags aspects[] = { VK_IMAGE_ASPECT_PLANE_0_BIT,
VK_IMAGE_ASPECT_PLANE_1_BIT, VK_IMAGE_ASPECT_PLANE_2_BIT,
};
VkImageAspectFlags plane_aspect;
guint idx;
in_mem = gst_buffer_peek_memory (inbuf, i);
buf_mem = (GstVulkanBufferMemory *) in_mem;
if (n_planes == n_mems)
plane_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
else
plane_aspect = aspects[i];
/* *INDENT-OFF* */
region = (VkBufferImageCopy) {
.bufferOffset = 0,
.bufferRowLength = GST_VIDEO_INFO_COMP_WIDTH (&in_info, i),
.bufferImageHeight = GST_VIDEO_INFO_COMP_HEIGHT (&in_info, i),
.imageSubresource = {
.aspectMask = plane_aspect,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = { .x = 0, .y = 0, .z = 0, },
.imageExtent = {
.width = GST_VIDEO_INFO_COMP_WIDTH (&out_info, i),
.height = GST_VIDEO_INFO_COMP_HEIGHT (&out_info, i),
.depth = 1,
}
};
idx = MIN (i, n_mems - 1);
out_mem = gst_buffer_peek_memory (*outbuf, idx);
if (!gst_is_vulkan_image_memory (out_mem)) {
GST_WARNING ("Output is not a GstVulkanImageMemory");
goto unlock_error;
}
img_mem = (GstVulkanImageMemory *) out_mem;
gst_vulkan_command_buffer_lock (cmd_buf);
vkCmdCopyBufferToImage (cmd_buf->cmd, buf_mem->buffer, img_mem->image,
dst_layout, 1, &region);
gst_vulkan_command_buffer_unlock (cmd_buf);
}
if (!gst_vulkan_operation_end (exec, &error))
goto error;
/*Hazard WRITE_AFTER_WRITE*/
gst_vulkan_operation_wait (exec);
ret = GST_FLOW_OK;
out:
return ret;
unlock_error:
gst_vulkan_operation_reset (exec);
error:
if (error) {
GST_WARNING ("Error: %s", error->message);
g_clear_error (&error);
}
gst_clear_buffer (outbuf);
ret = GST_FLOW_ERROR;
goto out;
}
static GstVulkanH264EncodeFrame *
allocate_frame (GstVulkanEncoder * enc, int width,
int height, gboolean is_ref, gint nb_refs)
{
GstVulkanH264EncodeFrame *frame;
GstBuffer *in_buffer, *img_buffer;
in_buffer = generate_input_buffer (buffer_pool, width, height);
upload_buffer_to_image(img_pool, in_buffer, &img_buffer);
frame = _h264_encode_frame_new (gst_vulkan_encode_picture_new (enc, img_buffer, width, height, is_ref,
nb_refs));
fail_unless (frame);
fail_unless (frame->picture);
gst_buffer_unref (in_buffer);
gst_buffer_unref (img_buffer);
return frame;
}
#define PICTURE_TYPE(slice_type, is_ref) \
(slice_type == STD_VIDEO_H264_SLICE_TYPE_I && is_ref) ? \
STD_VIDEO_H264_PICTURE_TYPE_IDR : (StdVideoH264PictureType) slice_type
static void
encode_frame (GstVulkanEncoder * enc, GstVulkanH264EncodeFrame * frame,
StdVideoH264SliceType slice_type, guint frame_num,
GstVulkanH264EncodeFrame ** list0, gint list0_num,
GstVulkanH264EncodeFrame ** list1, gint list1_num, gint sps_id, gint pps_id)
{
GstVulkanVideoCapabilities enc_caps;
int i, ref_pics_num = 0;
GstVulkanEncodePicture *ref_pics[16] = { NULL, };
guint qp_i = 26;
guint qp_p = 26;
guint qp_b = 26;
GstVulkanEncodePicture *picture = frame->picture;
GST_DEBUG ("Encoding frame num:%d", frame_num);
fail_unless (gst_vulkan_encoder_caps (enc, &enc_caps));
frame->slice_hdr = (StdVideoEncodeH264SliceHeader) {
/* *INDENT-OFF* */
.flags = (StdVideoEncodeH264SliceHeaderFlags) {
.direct_spatial_mv_pred_flag = 0,
.num_ref_idx_active_override_flag = (slice_type != STD_VIDEO_H264_SLICE_TYPE_I && (list0_num > 0 || list1_num > 0)),
},
.first_mb_in_slice = 0,
.slice_type = slice_type,
.cabac_init_idc = STD_VIDEO_H264_CABAC_INIT_IDC_0,
.disable_deblocking_filter_idc = STD_VIDEO_H264_DISABLE_DEBLOCKING_FILTER_IDC_DISABLED,
.slice_alpha_c0_offset_div2 = 0,
.slice_beta_offset_div2 = 0,
.pWeightTable = NULL,
/* *INDENT-ON* */
};
frame->pic_info = (StdVideoEncodeH264PictureInfo) {
/* *INDENT-OFF* */
.flags = (StdVideoEncodeH264PictureInfoFlags) {
.IdrPicFlag = (slice_type == STD_VIDEO_H264_SLICE_TYPE_I && picture->is_ref),
.is_reference = picture->is_ref, /* TODO: Check why it creates a deadlock in query result when TRUE */
.no_output_of_prior_pics_flag = 0,
.long_term_reference_flag = 0,
.adaptive_ref_pic_marking_mode_flag = 0,
},
.seq_parameter_set_id = sps_id,
.pic_parameter_set_id = pps_id,
.primary_pic_type = PICTURE_TYPE (slice_type, picture->is_ref),
.frame_num = frame_num,
.PicOrderCnt = picture->pic_order_cnt,
/* *INDENT-ON* */
};
if (picture->nb_refs) {
/* *INDENT-OFF* */
frame->ref_list_info = (StdVideoEncodeH264ReferenceListsInfo) {
.flags = {
.ref_pic_list_modification_flag_l0 = 0,
.ref_pic_list_modification_flag_l1 = 0,
},
.num_ref_idx_l0_active_minus1 = 0,
.num_ref_idx_l1_active_minus1 = 0,
.RefPicList0 = {0, },
.RefPicList1 = {0, },
.refList0ModOpCount = 0,
.refList1ModOpCount = 0,
.refPicMarkingOpCount = 0,
.reserved1 = {0, },
.pRefList0ModOperations = NULL,
.pRefList1ModOperations = NULL,
.pRefPicMarkingOperations = NULL,
};
frame->pic_info.pRefLists = &frame->ref_list_info;
/* *INDENT-ON* */
}
memset (frame->ref_list_info.RefPicList0, STD_VIDEO_H264_NO_REFERENCE_PICTURE,
STD_VIDEO_H264_MAX_NUM_LIST_REF);
memset (frame->ref_list_info.RefPicList1, STD_VIDEO_H264_NO_REFERENCE_PICTURE,
STD_VIDEO_H264_MAX_NUM_LIST_REF);
frame->slice_info = (VkVideoEncodeH264NaluSliceInfoKHR) {
/* *INDENT-OFF* */
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_NALU_SLICE_INFO_KHR,
.pNext = NULL,
.pStdSliceHeader = &frame->slice_hdr,
/* *INDENT-ON* */
};
frame->rc_layer_info = (VkVideoEncodeH264RateControlLayerInfoKHR) {
/* *INDENT-OFF* */
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_LAYER_INFO_KHR,
.pNext = NULL,
.useMinQp = TRUE,
.minQp = { qp_i, qp_p, qp_b },
.useMaxQp = TRUE,
.maxQp = { qp_i, qp_p, qp_b },
.useMaxFrameSize = 0,
.maxFrameSize = (VkVideoEncodeH264FrameSizeKHR) {0, 0, 0},
/* *INDENT-ON* */
};
frame->rc_info = (VkVideoEncodeH264RateControlInfoKHR) {
/* *INDENT-OFF* */
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_RATE_CONTROL_INFO_KHR,
.pNext = &frame->rc_layer_info,
.gopFrameCount = 0,
.idrPeriod = 0,
.consecutiveBFrameCount = 0,
.temporalLayerCount = 1,
/* *INDENT-ON* */
};
frame->quality_level = (VkVideoEncodeH264QualityLevelPropertiesKHR) {
/* *INDENT-OFF* */
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_QUALITY_LEVEL_PROPERTIES_KHR,
.pNext = NULL,
.preferredRateControlFlags = VK_VIDEO_ENCODE_H264_RATE_CONTROL_REGULAR_GOP_BIT_KHR,
.preferredGopFrameCount = 0,
.preferredIdrPeriod = 0,
.preferredConsecutiveBFrameCount = 0,
.preferredConstantQp = { qp_i, qp_p, qp_b },
.preferredMaxL0ReferenceCount = 0,
.preferredMaxL1ReferenceCount = 0,
.preferredStdEntropyCodingModeFlag = 0,
/* *INDENT-ON* */
};
frame->enc_pic_info = (VkVideoEncodeH264PictureInfoKHR) {
/* *INDENT-OFF* */
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PICTURE_INFO_KHR,
.pNext = NULL,
.naluSliceEntryCount = 1,
.pNaluSliceEntries = &frame->slice_info,
.pStdPictureInfo = &frame->pic_info,
.generatePrefixNalu = (enc_caps.codec.h264enc.flags & VK_VIDEO_ENCODE_H264_CAPABILITY_GENERATE_PREFIX_NALU_BIT_KHR),
/* *INDENT-ON* */
};
frame->ref_info = (StdVideoEncodeH264ReferenceInfo) {
/* *INDENT-OFF* */
.flags = {
.used_for_long_term_reference = 0,
},
.primary_pic_type = PICTURE_TYPE (slice_type, picture->is_ref),
.FrameNum = frame_num,
.PicOrderCnt = picture->pic_order_cnt,
.long_term_pic_num = 0,
.long_term_frame_idx = 0,
.temporal_id = 0,
/* *INDENT-ON* */
};
frame->dpb_slot_info = (VkVideoEncodeH264DpbSlotInfoKHR) {
/* *INDENT-OFF* */
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_DPB_SLOT_INFO_KHR,
.pNext = NULL,
.pStdReferenceInfo = &frame->ref_info,
/* *INDENT-ON* */
};
picture->codec_pic_info = &frame->enc_pic_info;
picture->codec_rc_layer_info = &frame->rc_layer_info;
picture->codec_quality_level = &frame->quality_level;
picture->codec_rc_info = &frame->rc_info;
picture->codec_dpb_slot_info = &frame->dpb_slot_info;
for (i = 0; i < list0_num; i++) {
ref_pics[i] = list0[i]->picture;
frame->ref_list_info.RefPicList0[0] = list0[i]->picture->slotIndex;
ref_pics_num++;
}
for (i = 0; i < list1_num; i++) {
ref_pics[i + list0_num] = list1[i]->picture;
frame->ref_list_info.RefPicList1[i] = list1[i]->picture->slotIndex;
ref_pics_num++;
}
picture->nb_refs = ref_pics_num;
fail_unless (gst_vulkan_encoder_encode (enc, picture, ref_pics));
}
static void
check_h264_nalu (guint8 * bitstream, gsize size, GstH264NalUnitType nal_type)
{
GstH264ParserResult res;
GstH264NalUnit nalu;
GstH264NalParser *const parser = gst_h264_nal_parser_new ();
res = gst_h264_parser_identify_nalu (parser, bitstream, 0, size, &nalu);
assert_equals_int (res, GST_H264_PARSER_NO_NAL_END);
assert_equals_int (nalu.type, nal_type);
switch (nal_type) {
case GST_H264_NAL_SPS:
{
GstH264SPS sps;
res = gst_h264_parser_parse_sps (parser, &nalu, &sps);
assert_equals_int (res, GST_H264_PARSER_OK);
break;
}
case GST_H264_NAL_PPS:
{
GstH264PPS pps;
res = gst_h264_parser_parse_pps (parser, &nalu, &pps);
assert_equals_int (res, GST_H264_PARSER_BROKEN_LINK);
break;
}
default:
res = gst_h264_parser_parse_nal (parser, &nalu);
assert_equals_int (res, GST_H264_PARSER_OK);
break;
}
gst_h264_nal_parser_free (parser);
}
static void
check_h264_session_params (GstVulkanEncoder * enc, gint sps_id, gint pps_id)
{
GError *err = NULL;
GstVulkanEncoderParametersFeedback feedback = { 0, };
guint8 *bitstream = NULL;
gsize bitstream_size = 0;
GstVulkanEncoderParametersOverrides override_params = {
.h264 = {
.sType =
VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_GET_INFO_KHR,
.writeStdSPS = VK_TRUE,
.writeStdPPS = VK_FALSE,
.stdSPSId = 0,
.stdPPSId = 0,
}
};
fail_unless (gst_vulkan_encoder_video_session_parameters_overrides (enc,
&override_params, &feedback, &bitstream_size,
(gpointer *) & bitstream, &err));
check_h264_nalu (bitstream, bitstream_size, GST_H264_NAL_SPS);
override_params = (GstVulkanEncoderParametersOverrides) {
.h264 = {
.sType =
VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_GET_INFO_KHR,
.writeStdSPS = VK_FALSE,
.writeStdPPS = VK_TRUE,
.stdSPSId = 0,
.stdPPSId = 0,
}
};
g_free (bitstream);
fail_unless (gst_vulkan_encoder_video_session_parameters_overrides (enc,
&override_params, &feedback, &bitstream_size,
(gpointer *) & bitstream, &err));
check_h264_nalu (bitstream, bitstream_size, GST_H264_NAL_PPS);
g_free (bitstream);
}
static GstVulkanEncoder *
setup_h264_encoder (guint32 width, gint32 height, gint sps_id, gint pps_id)
{
GstVulkanEncoder *enc = NULL;
int i;
GError *err = NULL;
uint32_t mbAlignedWidth, mbAlignedHeight;
GstVulkanVideoProfile profile;
StdVideoH264ProfileIdc profile_idc = STD_VIDEO_H264_PROFILE_IDC_HIGH;
GstVulkanEncoderParameters enc_params;
VkVideoEncodeH264SessionParametersAddInfoKHR params_add;
profile = (GstVulkanVideoProfile) {
/* *INDENT-OFF* */
.profile = {
.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR,
.pNext = &profile.usage.encode,
.videoCodecOperation = VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR,
.chromaSubsampling = VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR,
.chromaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR,
.lumaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR,
},
.usage.encode = {
.pNext = &profile.codec,
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_USAGE_INFO_KHR,
.videoUsageHints = VK_VIDEO_ENCODE_USAGE_DEFAULT_KHR,
.videoContentHints = VK_VIDEO_ENCODE_CONTENT_DEFAULT_KHR,
.tuningMode = VK_VIDEO_ENCODE_TUNING_MODE_DEFAULT_KHR,
},
.codec.h264enc = {
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_PROFILE_INFO_KHR,
.stdProfileIdc = profile_idc,
}
/* *INDENT-ON* */
};
for (i = 0; i < instance->n_physical_devices; i++) {
GstVulkanDevice *device = gst_vulkan_device_new_with_index (instance, i);
encode_queue =
gst_vulkan_device_select_queue (device, VK_QUEUE_VIDEO_ENCODE_BIT_KHR);
gfx_queue = gst_vulkan_device_select_queue (device, VK_QUEUE_GRAPHICS_BIT);
gst_object_unref (device);
if (encode_queue && gfx_queue)
break;
}
if (!encode_queue) {
GST_WARNING ("Unable to find encoding queue");
return NULL;
}
if (!gfx_queue) {
GST_WARNING ("Unable to find graphics queue");
return NULL;
}
enc = gst_vulkan_encoder_create_from_queue (encode_queue,
VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR);
if (!enc) {
GST_WARNING ("Unable to create a vulkan encoder, queue=%p", encode_queue);
return NULL;
}
fail_unless (gst_vulkan_encoder_start (enc, &profile, width * height * 3,
&err));
mbAlignedWidth = GST_ROUND_UP_16 (width);
mbAlignedHeight = GST_ROUND_UP_16 (height);
h264_std_sps.profile_idc = profile_idc;
h264_std_sps.seq_parameter_set_id = sps_id;
h264_std_sps.pic_width_in_mbs_minus1 =
mbAlignedWidth / H264_MB_SIZE_ALIGNMENT - 1;
h264_std_sps.pic_height_in_map_units_minus1 =
mbAlignedHeight / H264_MB_SIZE_ALIGNMENT - 1;
h264_std_sps.frame_crop_right_offset = mbAlignedWidth - width;
h264_std_sps.frame_crop_bottom_offset = mbAlignedHeight - height;
params_add = (VkVideoEncodeH264SessionParametersAddInfoKHR) {
/* *INDENT-OFF* */
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_ADD_INFO_KHR,
.pStdSPSs = &h264_std_sps,
.stdSPSCount = 1,
.pStdPPSs = &h264_std_pps,
.stdPPSCount = 1,
/* *INDENT-ON* */
};
enc_params.h264 = (VkVideoEncodeH264SessionParametersCreateInfoKHR) {
/* *INDENT-OFF* */
.sType = VK_STRUCTURE_TYPE_VIDEO_ENCODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR,
.maxStdSPSCount = 1,
.maxStdPPSCount = 1,
.pParametersAddInfo = &params_add
/* *INDENT-ON* */
};
fail_unless (gst_vulkan_encoder_update_video_session_parameters (enc,
&enc_params, &err));
/* retrieve the SPS/PPS from the device */
check_h264_session_params (enc, sps_id, pps_id);
return enc;
}
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 (&encode_queue);
gst_clear_object (&gfx_queue);
}
static void
check_encoded_frame (GstVulkanH264EncodeFrame * frame,
GstH264NalUnitType nal_type)
{
GstMapInfo info;
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);
check_h264_nalu (info.data, info.size, nal_type);
gst_buffer_unmap (frame->picture->out_buffer, &info);
}
/* Greater than the maxDpbSlots == 16*/
#define N_BUFFERS 17
#define FRAME_WIDTH 320
#define FRAME_HEIGHT 240
GST_START_TEST (test_encoder_h264_i)
{
GstVulkanEncoder *enc;
uint32_t width = FRAME_WIDTH;
uint32_t height = FRAME_HEIGHT;
gint sps_id = 0;
gint pps_id = 0;
GstVulkanH264EncodeFrame *frame;
int frame_num = 0;
int i;
/* Create and setup a H.264 encoder with its initial session parameters */
enc = setup_h264_encoder (width, height, sps_id, pps_id);
if (!enc) {
GST_WARNING ("Unable to initialize H264 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_frame (enc, width, height, TRUE, 0);
encode_frame (enc, frame, STD_VIDEO_H264_SLICE_TYPE_I,
frame_num, NULL, 0, NULL, 0, sps_id, pps_id);
check_encoded_frame (frame, GST_H264_NAL_SLICE_IDR);
frame_num++;
_h264_encode_frame_free (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_h264_i_p)
{
GstVulkanEncoder *enc;
uint32_t width = FRAME_WIDTH;
uint32_t height = FRAME_HEIGHT;
gint sps_id = 0;
gint pps_id = 0;
GstVulkanH264EncodeFrame *frame;
GstVulkanH264EncodeFrame *list0[16] = { NULL, };
gint list0_num = 1;
int frame_num = 0;
int i = 0;
enc = setup_h264_encoder (width, height, sps_id, pps_id);
if (!enc) {
GST_WARNING ("Unable to initialize H264 encoder");
return;
}
buffer_pool = allocate_buffer_pool (enc, width, height);
img_pool = allocate_image_buffer_pool (enc, width, height);
/* Encode first picture as an IDR-Frame */
frame = allocate_frame (enc, width, height, TRUE, 0);
encode_frame (enc, frame, STD_VIDEO_H264_SLICE_TYPE_I,
frame_num, NULL, 0, NULL, 0, sps_id, pps_id);
check_encoded_frame (frame, GST_H264_NAL_SLICE_IDR);
list0[0] = frame;
frame_num++;
/* Encode following pictures as P-Frames */
for (i = 1; i < N_BUFFERS; i++) {
frame = allocate_frame (enc, width, height, TRUE, list0_num);
frame->picture->pic_num = frame_num;
frame->picture->pic_order_cnt = frame_num;
encode_frame (enc, frame, STD_VIDEO_H264_SLICE_TYPE_P,
frame_num, list0, list0_num, NULL, 0, sps_id, pps_id);
check_encoded_frame (frame, GST_H264_NAL_SLICE);
_h264_encode_frame_free (list0[0]);
list0[0] = frame;
frame_num++;
}
_h264_encode_frame_free (list0[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);
tear_down_encoder (enc);
}
GST_END_TEST;
GST_START_TEST (test_encoder_h264_i_p_b)
{
GstVulkanEncoder *enc;
uint32_t width = FRAME_WIDTH;
uint32_t height = FRAME_HEIGHT;
gint sps_id = 0;
gint pps_id = 0;
GstVulkanH264EncodeFrame *frame;
GstVulkanH264EncodeFrame *list0[16] = { NULL, };
GstVulkanH264EncodeFrame *list1[16] = { NULL, };
gint list0_num = 0;
gint list1_num = 0;
int frame_num = 0;
GstVulkanVideoCapabilities enc_caps;
enc = setup_h264_encoder (width, height, sps_id, pps_id);
if (!enc) {
GST_WARNING ("Unable to initialize H264 encoder");
return;
}
fail_unless (gst_vulkan_encoder_caps (enc, &enc_caps));
if (!enc_caps.codec.h264enc.maxL1ReferenceCount) {
GST_WARNING ("Driver does not support B 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_frame (enc, width, height, TRUE, 0);
fail_unless (frame->picture != NULL);
encode_frame (enc, frame, STD_VIDEO_H264_SLICE_TYPE_I,
frame_num, NULL, 0, NULL, 0, sps_id, pps_id);
check_encoded_frame (frame, GST_H264_NAL_SLICE_IDR);
list0[0] = frame;
list0_num++;
frame_num++;
/* Encode 4th picture as a P-Frame */
frame = allocate_frame (enc, width, height, TRUE, list0_num);
frame->picture->pic_num = 3;
frame->picture->pic_order_cnt = frame->picture->pic_num * 2;
encode_frame (enc, frame, STD_VIDEO_H264_SLICE_TYPE_P,
frame_num, list0, list0_num, list1, list1_num, sps_id, pps_id);
check_encoded_frame (frame, GST_H264_NAL_SLICE);
list1[0] = frame;
list1_num++;
frame_num++;
/* Encode second picture as a B-Frame */
frame = allocate_frame (enc, width, height, FALSE, list0_num + list1_num);
frame->picture->pic_num = 1;
frame->picture->pic_order_cnt = frame->picture->pic_num * 2;
encode_frame (enc, frame, STD_VIDEO_H264_SLICE_TYPE_B,
frame_num, list0, list0_num, list1, list1_num, sps_id, pps_id);
check_encoded_frame (frame, GST_H264_NAL_SLICE);
frame_num++;
_h264_encode_frame_free (frame);
/* Encode third picture as a B-Frame */
frame = allocate_frame (enc, width, height, FALSE, list0_num + list1_num);
frame->picture->pic_num = 2;
frame->picture->pic_order_cnt = frame->picture->pic_num * 2;
encode_frame (enc, frame, STD_VIDEO_H264_SLICE_TYPE_B,
frame_num, list0, list0_num, list1, list1_num, sps_id, pps_id);
check_encoded_frame (frame, GST_H264_NAL_SLICE);
frame_num++;
_h264_encode_frame_free (frame);
_h264_encode_frame_free (list0[0]);
_h264_encode_frame_free (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) {
#if GST_VULKAN_HAVE_VIDEO_ENCODERS
tcase_add_test (tc_basic, test_encoder_h264_i);
tcase_add_test (tc_basic, test_encoder_h264_i_p);
tcase_add_test (tc_basic, test_encoder_h264_i_p_b);
#endif
}
return s;
}
GST_CHECK_MAIN (vkvideo);

File diff suppressed because it is too large Load diff

View file

@ -109,6 +109,8 @@ base_tests = [
[['libs/vkinstance.c'], not gstvulkan_dep.found(), [gstvulkan_dep]],
[['libs/vkimagebufferpool.c'], not gstvulkan_dep.found(), [gstvulkan_dep]],
[['libs/vkvideodecode.c'], not gstvulkan_dep.found() or vulkan_conf.get('GST_VULKAN_HAVE_VIDEO_EXTENSIONS') != 1, [gstvulkan_dep]],
[['libs/vkvideoh264encode.c'], not gstvulkan_dep.found() or vulkan_conf.get('GST_VULKAN_HAVE_VIDEO_ENCODERS') != 1, [gstvulkan_dep, gstcodecparsers_dep]],
[['libs/vkvideoh265encode.c'], not gstvulkan_dep.found() or vulkan_conf.get('GST_VULKAN_HAVE_VIDEO_ENCODERS') != 1, [gstvulkan_dep, gstcodecparsers_dep]],
[['libs/d3d11device.cpp'], not gstd3d11_dep.found(), [gstd3d11_dep, gstvideo_dep]],
[['libs/d3d11memory.c'], not gstd3d11_dep.found(), [gstd3d11_dep]],
[['libs/cudamemory.c'], not gstcuda_dep.found(), [gstcuda_dep, gstcuda_stub_dep]],