gstreamer/subprojects/gst-plugins-bad/gst-libs/gst/vulkan/gstvkvideoutils.c

480 lines
16 KiB
C

/*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstvkvideoutils.h"
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
/* *INDENT-OFF* */
static const struct {
GstVulkanVideoOperation video_operation;
VkVideoCodecOperationFlagBitsKHR codec;
const char *mime;
VkStructureType stype;
} video_codecs_map[] = {
{ GST_VULKAN_VIDEO_OPERATION_DECODE, VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR, "video/x-h264",
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 {
VkVideoChromaSubsamplingFlagBitsKHR chroma;
const char *chroma_str;
} video_chroma_map[] = {
{ VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, "4:2:0" },
{ VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR, "4:2:2" },
{ VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, "4:4:4" },
};
static const struct {
VkVideoComponentBitDepthFlagBitsKHR bitdepth;
int bit_depth;
} bit_depth_map[] = {
{VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR, 8},
{VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR, 10},
{VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR, 12},
};
static const struct {
StdVideoH264ProfileIdc vk_profile;
const char *profile_str;
} h264_profile_map[] = {
{ STD_VIDEO_H264_PROFILE_IDC_BASELINE, "baseline" },
{ STD_VIDEO_H264_PROFILE_IDC_MAIN, "main" },
{ STD_VIDEO_H264_PROFILE_IDC_HIGH, "high" },
{ STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE, "high-4:4:4" },
};
static const struct {
VkVideoDecodeH264PictureLayoutFlagBitsKHR layout;
const char *layout_str;
} h264_layout_map[] = {
{ VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_KHR, "progressive" },
{ VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_INTERLEAVED_LINES_BIT_KHR,
"interleaved" },
{ VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_SEPARATE_PLANES_BIT_KHR,
"fields" },
};
static const struct {
StdVideoH265ProfileIdc vk_profile;
const char *profile_str;
} h265_profile_map[] = {
{ STD_VIDEO_H265_PROFILE_IDC_MAIN, "main" },
{ STD_VIDEO_H265_PROFILE_IDC_MAIN_10, "main-10" },
{ STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE, "main-still-picture" },
{ STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS,
"format-range-extensions" },
{ STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS, "scc-extensions" },
};
/* *INDENT-ON* */
#endif
/**
* gst_vulkan_video_profile_to_caps: (skip)
* @profile: #GstVulkanVideoProfile to convert into a #GstCaps
*
* Returns: (transfer full): a #GstCaps from @profile
*
* Since: 1.24
*/
GstCaps *
gst_vulkan_video_profile_to_caps (const GstVulkanVideoProfile * profile)
{
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
const char *mime, *chroma_sub, *profile_str = NULL, *layout = NULL;
int i, luma, chroma;
GstCaps *caps;
g_return_val_if_fail (profile
&& profile->profile.sType == VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR,
NULL);
for (i = 0; i < G_N_ELEMENTS (video_codecs_map); i++) {
if (profile->profile.videoCodecOperation == video_codecs_map[i].codec) {
mime = video_codecs_map[i].mime;
switch (profile->profile.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
if (profile->codec.h264dec.sType == video_codecs_map[i].stype) {
int j;
for (j = 0; j < G_N_ELEMENTS (h264_profile_map); j++) {
if (profile->codec.h264dec.stdProfileIdc
== h264_profile_map[j].vk_profile) {
profile_str = h264_profile_map[j].profile_str;
break;
}
}
for (j = 0; j < G_N_ELEMENTS (h264_layout_map); j++) {
if (profile->codec.h264dec.pictureLayout
== h264_layout_map[j].layout) {
layout = h264_layout_map[j].layout_str;
break;
}
}
}
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
if (profile->codec.h265dec.sType == video_codecs_map[i].stype) {
int j;
for (j = 0; j < G_N_ELEMENTS (h265_profile_map); j++) {
if (profile->codec.h265dec.stdProfileIdc
== h265_profile_map[j].vk_profile)
profile_str = h265_profile_map[j].profile_str;
}
}
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;
}
break;
}
}
if (i == G_N_ELEMENTS (video_codecs_map))
return NULL;
for (i = 0; i < G_N_ELEMENTS (video_chroma_map); i++) {
if (profile->profile.chromaSubsampling == video_chroma_map[i].chroma) {
chroma_sub = video_chroma_map[i].chroma_str;
break;
}
}
if (i == G_N_ELEMENTS (video_chroma_map))
return NULL;
for (i = 0; i < G_N_ELEMENTS (bit_depth_map); i++) {
if (profile->profile.chromaBitDepth == bit_depth_map[i].bitdepth) {
chroma = bit_depth_map[i].bit_depth;
break;
}
}
if (i == G_N_ELEMENTS (bit_depth_map))
return NULL;
for (i = 0; i < G_N_ELEMENTS (bit_depth_map); i++) {
if (profile->profile.lumaBitDepth == bit_depth_map[i].bitdepth) {
luma = bit_depth_map[i].bit_depth;
break;
}
}
if (i == G_N_ELEMENTS (bit_depth_map))
return NULL;
caps = gst_caps_new_simple (mime, "chroma-format", G_TYPE_STRING, chroma_sub,
"bit-depth-luma", G_TYPE_UINT, luma, "bit-depth-chroma", G_TYPE_UINT,
chroma, NULL);
if (profile_str)
gst_caps_set_simple (caps, "profile", G_TYPE_STRING, profile_str, NULL);
if (layout)
gst_caps_set_simple (caps, "interlace-mode", G_TYPE_STRING, layout, NULL);
return caps;
#endif
return NULL;
}
/**
* gst_vulkan_video_profile_from_caps: (skip)
* @profile: (out): the output profile
* @caps: a #GstCaps to parse
* @video_operation: a supported video operation
*
* Returns: %TRUE if @caps was parsed correctly, otherwise %FALSE
*
* Since: 1.24
*/
gboolean
gst_vulkan_video_profile_from_caps (GstVulkanVideoProfile * profile,
GstCaps * caps, GstVulkanVideoOperation video_operation)
{
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
const GstStructure *structure;
const gchar *mime, *chroma_sub, *profile_str = NULL, *layout = NULL;
gint i, luma, chroma;
g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
g_return_val_if_fail (profile, FALSE);
g_return_val_if_fail (video_operation < GST_VULKAN_VIDEO_OPERATION_UNKNOWN,
FALSE);
structure = gst_caps_get_structure (caps, 0);
profile->usage.decode.sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_USAGE_INFO_KHR;
profile->usage.decode.videoUsageHints = VK_VIDEO_DECODE_USAGE_DEFAULT_KHR;
profile->profile.sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR;
profile->profile.pNext = &profile->usage;
mime = gst_structure_get_name (structure);
for (i = 0; i < G_N_ELEMENTS (video_codecs_map); i++) {
if ((video_codecs_map[i].video_operation == video_operation)
&& (g_strcmp0 (video_codecs_map[i].mime, mime) == 0)) {
profile->profile.videoCodecOperation = video_codecs_map[i].codec;
switch (profile->profile.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:{
int j;
profile->codec.h264dec.sType = video_codecs_map[i].stype;
profile->codec.h264dec.stdProfileIdc =
STD_VIDEO_H264_PROFILE_IDC_INVALID;
profile->codec.h264dec.pictureLayout =
VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_FLAG_BITS_MAX_ENUM_KHR;
profile->usage.decode.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.h264dec.stdProfileIdc =
h264_profile_map[j].vk_profile;
break;
}
}
layout = gst_structure_get_string (structure, "interlace-mode");
for (j = 0; layout && j < G_N_ELEMENTS (h264_layout_map); j++) {
if (g_strcmp0 (layout, h264_layout_map[j].layout_str) == 0) {
profile->codec.h264dec.pictureLayout = h264_layout_map[j].layout;
break;
}
}
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:{
int j;
profile->codec.h265dec.sType = video_codecs_map[i].stype;
profile->codec.h265dec.stdProfileIdc =
STD_VIDEO_H265_PROFILE_IDC_INVALID;
profile->usage.decode.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.h265dec.stdProfileIdc =
h265_profile_map[j].vk_profile;
break;
}
}
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;
}
break;
}
}
if (i == G_N_ELEMENTS (video_codecs_map))
return FALSE;
chroma_sub = gst_structure_get_string (structure, "chroma-format");
if (!chroma_sub)
return FALSE;
if (!gst_structure_get (structure, "bit-depth-luma", G_TYPE_UINT, &luma,
"bit-depth-chroma", G_TYPE_UINT, &chroma, NULL))
return FALSE;
for (i = 0; i < G_N_ELEMENTS (video_chroma_map); i++) {
if (g_strcmp0 (chroma_sub, video_chroma_map[i].chroma_str) == 0) {
profile->profile.chromaSubsampling = video_chroma_map[i].chroma;
break;
}
}
if (i == G_N_ELEMENTS (video_chroma_map))
return FALSE;
for (i = 0; i < G_N_ELEMENTS (bit_depth_map); i++) {
if (luma == bit_depth_map[i].bit_depth) {
profile->profile.lumaBitDepth = bit_depth_map[i].bitdepth;
break;
}
}
if (i == G_N_ELEMENTS (bit_depth_map))
return FALSE;
for (i = 0; i < G_N_ELEMENTS (bit_depth_map); i++) {
if (chroma == bit_depth_map[i].bit_depth) {
profile->profile.chromaBitDepth = bit_depth_map[i].bitdepth;
break;
}
}
if (i == G_N_ELEMENTS (bit_depth_map))
return FALSE;
#endif
return TRUE;
}
/**
* gst_vulkan_video_profile_is_valid: (skip)
* @profile: the output profile
* @codec: VkVideoCodecOperationFlagBitsKHR described by @profile
*
* Returns: %TRUE if @profile is correct and matches with @codec
*
* Since: 1.24
*/
gboolean
gst_vulkan_video_profile_is_valid (GstVulkanVideoProfile * profile, guint codec)
{
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
int i;
VkVideoCodecOperationFlagBitsKHR op = codec;
VkStructureType stype = VK_STRUCTURE_TYPE_MAX_ENUM;
if (op == VK_VIDEO_CODEC_OPERATION_NONE_KHR)
return FALSE;
if (profile->profile.videoCodecOperation != op)
return FALSE;
for (i = 0; i < G_N_ELEMENTS (video_codecs_map); i++) {
if (op == video_codecs_map[i].codec) {
stype = video_codecs_map[i].stype;
break;
}
}
if (stype == VK_STRUCTURE_TYPE_MAX_ENUM)
return FALSE;
if (profile->codec.base.sType != stype)
return FALSE;
return TRUE;
#endif
return FALSE;
}
/**
* gst_vulkan_video_profile_is_equal:
* @a: a #GstVulkanVideoProfile
* @b: another #GstVulkanVideoProfile
*
* Returns: whether @a and @b contains the same information.
*/
gboolean
gst_vulkan_video_profile_is_equal (const GstVulkanVideoProfile * a,
const GstVulkanVideoProfile * b)
{
#if GST_VULKAN_HAVE_VIDEO_EXTENSIONS
gboolean profile;
g_return_val_if_fail (a && b, FALSE);
profile = ((a->profile.videoCodecOperation == b->profile.videoCodecOperation)
&& (a->profile.chromaSubsampling == b->profile.chromaSubsampling)
&& (a->profile.chromaBitDepth == b->profile.chromaBitDepth)
&& (a->profile.lumaBitDepth == b->profile.lumaBitDepth)
&& (a->codec.base.sType == b->codec.base.sType));
if (!profile)
return FALSE;
switch (a->profile.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
return ((a->codec.h264dec.stdProfileIdc == b->codec.h264dec.stdProfileIdc)
&& a->codec.h264dec.pictureLayout == b->codec.h264dec.pictureLayout);
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
return (a->codec.h265dec.stdProfileIdc == b->codec.h265dec.stdProfileIdc);
default:
return FALSE;
}
g_assert_not_reached ();
#else
return FALSE;
#endif
}