pbutils: add profile-tier-level functions for VVC/H.266

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5088>
This commit is contained in:
Carlos Bentzen 2025-02-07 12:33:27 +01:00 committed by GStreamer Marge Bot
parent bf5f642841
commit 7faa031e0e
4 changed files with 485 additions and 0 deletions

View File

@ -3517,6 +3517,110 @@ is expected to have the same format as for gst_codec_utils_h264_get_profile().</
</parameter>
</parameters>
</function>
<function name="codec_utils_h266_caps_set_level_tier_and_profile" c:identifier="gst_codec_utils_h266_caps_set_level_tier_and_profile" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Sets the level, tier and profile in @caps if it can be determined from
@decoder_configuration. See gst_codec_utils_h266_get_level(),
gst_codec_utils_h266_get_tier() and gst_codec_utils_h266_get_profile()
for more details on the parameters.</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h"/>
<return-value transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">%TRUE if the level, tier, profile could be set, %FALSE otherwise.</doc>
<type name="gboolean" c:type="gboolean"/>
</return-value>
<parameters>
<parameter name="caps" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">the #GstCaps to which the level, tier and profile are to be added</doc>
<type name="Gst.Caps" c:type="GstCaps*"/>
</parameter>
<parameter name="decoder_configuration" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Pointer to the VvcDecoderConfigurationRecord struct as defined in ISO/IEC 14496-15</doc>
<array length="2" zero-terminated="0" c:type="const guint8*">
<type name="guint8" c:type="guint8"/>
</array>
</parameter>
<parameter name="len" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Length of the data available in @decoder_configuration.</doc>
<type name="guint" c:type="guint"/>
</parameter>
</parameters>
</function>
<function name="codec_utils_h266_get_level" c:identifier="gst_codec_utils_h266_get_level" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Converts the level indication (general_level_idc) in the stream's
ptl_record structure into a string.</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h"/>
<return-value transfer-ownership="none" nullable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">The level as a const string, or %NULL if there is an error.</doc>
<type name="utf8" c:type="const gchar*"/>
</return-value>
<parameters>
<parameter name="ptl_record" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Pointer to the VvcPTLRecord structure as defined in ISO/IEC 14496-15.</doc>
<array length="1" zero-terminated="0" c:type="const guint8*">
<type name="guint8" c:type="guint8"/>
</array>
</parameter>
<parameter name="len" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Length of the data available in @ptl_record.</doc>
<type name="guint" c:type="guint"/>
</parameter>
</parameters>
</function>
<function name="codec_utils_h266_get_level_idc" c:identifier="gst_codec_utils_h266_get_level_idc" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Transform a level string from the caps into the level_idc</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h"/>
<return-value transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">the level_idc or 0 if the level is unknown</doc>
<type name="guint8" c:type="guint8"/>
</return-value>
<parameters>
<parameter name="level" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">A level string from caps</doc>
<type name="utf8" c:type="const gchar*"/>
</parameter>
</parameters>
</function>
<function name="codec_utils_h266_get_profile" c:identifier="gst_codec_utils_h266_get_profile" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Converts the profile indication (general_profile_idc) in the stream's
ptl_record structure into a string.</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h"/>
<return-value transfer-ownership="none" nullable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">The profile as a const string, or %NULL if there is an error.</doc>
<type name="utf8" c:type="const gchar*"/>
</return-value>
<parameters>
<parameter name="ptl_record" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Pointer to the VvcPTLRecord structure as defined in ISO/IEC 14496-15.</doc>
<array length="1" zero-terminated="0" c:type="const guint8*">
<type name="guint8" c:type="guint8"/>
</array>
</parameter>
<parameter name="len" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Length of the data available in @ptl_record</doc>
<type name="guint" c:type="guint"/>
</parameter>
</parameters>
</function>
<function name="codec_utils_h266_get_tier" c:identifier="gst_codec_utils_h266_get_tier" version="1.26">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Converts the tier indication (general_tier_flag) in the stream's
ptl_record structure into a string.</doc>
<source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h"/>
<return-value transfer-ownership="none" nullable="1">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">The tier as a const string, or %NULL if there is an error.</doc>
<type name="utf8" c:type="const gchar*"/>
</return-value>
<parameters>
<parameter name="ptl_record" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Pointer to the VvcPTLRecord structure as defined in ISO/IEC 14496-15.</doc>
<array length="1" zero-terminated="0" c:type="const guint8*">
<type name="guint8" c:type="guint8"/>
</array>
</parameter>
<parameter name="len" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Length of the data available in @ptl_record.</doc>
<type name="guint" c:type="guint"/>
</parameter>
</parameters>
</function>
<function name="codec_utils_mpeg4video_caps_set_level_and_profile" c:identifier="gst_codec_utils_mpeg4video_caps_set_level_and_profile">
<doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Sets the level and profile in @caps if it can be determined from
@vis_obj_seq. See gst_codec_utils_mpeg4video_get_level() and

View File

@ -1544,6 +1544,304 @@ gst_codec_utils_h265_caps_set_level_tier_and_profile (GstCaps * caps,
return (level != NULL && tier != NULL && profile != NULL);
}
/**
* gst_codec_utils_h266_get_profile:
* @ptl_record: (array length=len): Pointer to the VvcPTLRecord structure as defined in ISO/IEC 14496-15.
* @len: Length of the data available in @ptl_record
*
* Converts the profile indication (general_profile_idc) in the stream's
* ptl_record structure into a string.
*
* Returns: (nullable): The profile as a const string, or %NULL if there is an error.
*
* Since: 1.26
*/
const gchar *
gst_codec_utils_h266_get_profile (const guint8 * ptl_record, guint len)
{
gint profile_idc;
g_return_val_if_fail (ptl_record != NULL, NULL);
if (len < 2)
return NULL;
GST_MEMDUMP ("VvcPTLRecord", ptl_record, len);
profile_idc = (ptl_record[1] & 0xFE) >> 1;
if (!profile_idc)
return NULL;
switch (profile_idc) {
case 1:
return "main-10";
break;
case 2:
return "main-12";
break;
case 10:
return "main-12-intra";
break;
case 17:
return "multilayer-main-10";
break;
case 33:
return "main-444-10";
break;
case 34:
return "main-444-12";
break;
case 35:
return "main-444-16";
break;
case 42:
return "main-444-12-intra";
break;
case 43:
return "main-444-16-intra";
break;
case 49:
return "multilayer-main-444-10";
break;
case 65:
return "main-10-still-picture";
break;
case 66:
return "main-12-still-picture";
break;
case 97:
return "main-444-10-still-picture";
break;
case 98:
return "main-444-12-still-picture";
break;
case 99:
return "main-444-16-still-picture";
break;
default:
return NULL;
}
}
/**
* gst_codec_utils_h266_get_tier:
* @ptl_record: (array length=len): Pointer to the VvcPTLRecord structure as defined in ISO/IEC 14496-15.
* @len: Length of the data available in @ptl_record.
*
* Converts the tier indication (general_tier_flag) in the stream's
* ptl_record structure into a string.
*
* Returns: (nullable): The tier as a const string, or %NULL if there is an error.
*
* Since: 1.26
*/
const gchar *
gst_codec_utils_h266_get_tier (const guint8 * ptl_record, guint len)
{
const gchar *tier = NULL;
gint tier_flag = 0;
g_return_val_if_fail (ptl_record != NULL, NULL);
if (len < 2)
return NULL;
GST_MEMDUMP ("VvcPTLRecord", ptl_record, len);
tier_flag = ptl_record[1] & 0x01;
if (tier_flag)
tier = "high";
else
tier = "main";
return tier;
}
/**
* gst_codec_utils_h266_get_level:
* @ptl_record: (array length=len): Pointer to the VvcPTLRecord structure as defined in ISO/IEC 14496-15.
* @len: Length of the data available in @ptl_record.
*
* Converts the level indication (general_level_idc) in the stream's
* ptl_record structure into a string.
*
* Returns: (nullable): The level as a const string, or %NULL if there is an error.
*
* Since: 1.26
*/
const gchar *
gst_codec_utils_h266_get_level (const guint8 * ptl_record, guint len)
{
guint8 level_idc;
g_return_val_if_fail (ptl_record != NULL, NULL);
if (len < 3)
return NULL;
GST_MEMDUMP ("VvcPTLRecord", ptl_record, len);
level_idc = ptl_record[2];
if (!level_idc)
return NULL;
switch (level_idc) {
case 16:
return "1";
break;
case 32:
return "2";
break;
case 35:
return "2.1";
break;
case 48:
return "3";
break;
case 51:
return "3.1";
break;
case 64:
return "4";
break;
case 67:
return "4.1";
break;
case 80:
return "5";
break;
case 83:
return "5.1";
break;
case 86:
return "5.2";
break;
case 96:
return "6";
break;
case 99:
return "6.1";
break;
case 102:
return "6.2";
break;
case 105:
return "6.3";
break;
default:
return NULL;
}
}
/**
* gst_codec_utils_h266_get_level_idc:
* @level: A level string from caps
*
* Transform a level string from the caps into the level_idc
*
* Returns: the level_idc or 0 if the level is unknown
*
* Since: 1.26
*/
guint8
gst_codec_utils_h266_get_level_idc (const gchar * level)
{
g_return_val_if_fail (level != NULL, 0);
if (!strcmp (level, "1"))
return 16;
else if (!strcmp (level, "2"))
return 32;
else if (!strcmp (level, "2.1"))
return 35;
else if (!strcmp (level, "3"))
return 48;
else if (!strcmp (level, "3.1"))
return 51;
else if (!strcmp (level, "4"))
return 64;
else if (!strcmp (level, "4.1"))
return 67;
else if (!strcmp (level, "5"))
return 80;
else if (!strcmp (level, "5.1"))
return 83;
else if (!strcmp (level, "5.2"))
return 86;
else if (!strcmp (level, "6"))
return 96;
else if (!strcmp (level, "6.1"))
return 99;
else if (!strcmp (level, "6.2"))
return 102;
else if (!strcmp (level, "6.3"))
return 105;
GST_WARNING ("Invalid level %s", level);
return 0;
}
/**
* gst_codec_utils_h266_caps_set_level_tier_and_profile:
* @caps: the #GstCaps to which the level, tier and profile are to be added
* @decoder_configuration: (array length=len): Pointer to the VvcDecoderConfigurationRecord struct as defined in ISO/IEC 14496-15
* @len: Length of the data available in @decoder_configuration.
*
* Sets the level, tier and profile in @caps if it can be determined from
* @decoder_configuration. See gst_codec_utils_h266_get_level(),
* gst_codec_utils_h266_get_tier() and gst_codec_utils_h266_get_profile()
* for more details on the parameters.
*
* Returns: %TRUE if the level, tier, profile could be set, %FALSE otherwise.
*
* Since: 1.26
*/
gboolean
gst_codec_utils_h266_caps_set_level_tier_and_profile (GstCaps * caps,
const guint8 * decoder_configuration, guint len)
{
const gchar *level, *tier, *profile;
gboolean ptl_present_flag;
const guint8 *ptl_record;
g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), FALSE);
g_return_val_if_fail (GST_SIMPLE_CAPS_HAS_NAME (caps, "video/x-h266"), FALSE);
g_return_val_if_fail (decoder_configuration != NULL, FALSE);
if (len < 5)
return FALSE;
ptl_present_flag = decoder_configuration[0] & 0x01;
if (!ptl_present_flag)
return FALSE;
ptl_record = decoder_configuration + 4;
len -= 4;
level = gst_codec_utils_h266_get_level (ptl_record, len);
if (level != NULL)
gst_caps_set_simple (caps, "level", G_TYPE_STRING, level, NULL);
tier = gst_codec_utils_h266_get_tier (ptl_record, len);
if (tier != NULL)
gst_caps_set_simple (caps, "tier", G_TYPE_STRING, tier, NULL);
profile = gst_codec_utils_h266_get_profile (ptl_record, len);
if (profile != NULL)
gst_caps_set_simple (caps, "profile", G_TYPE_STRING, profile, NULL);
GST_LOG ("profile : %s", (profile) ? profile : "---");
GST_LOG ("tier : %s", (tier) ? tier : "---");
GST_LOG ("level : %s", (level) ? level : "---");
return (level != NULL && tier != NULL && profile != NULL);
}
/**
* gst_codec_utils_av1_get_seq_level_idx:
* @level: A level string from caps

View File

@ -97,6 +97,28 @@ gboolean gst_codec_utils_h265_caps_set_level_tier_and_profile (GstCaps
const guint8 * profile_tier_level,
guint len);
/* H.266 */
GST_PBUTILS_API
const gchar * gst_codec_utils_h266_get_profile (const guint8 * ptl_record,
guint len);
GST_PBUTILS_API
const gchar * gst_codec_utils_h266_get_tier (const guint8 * ptl_record,
guint len);
GST_PBUTILS_API
const gchar * gst_codec_utils_h266_get_level (const guint8 * ptl_record,
guint len);
GST_PBUTILS_API
guint8 gst_codec_utils_h266_get_level_idc (const gchar * level);
GST_PBUTILS_API
gboolean gst_codec_utils_h266_caps_set_level_tier_and_profile (GstCaps * caps,
const guint8 * decoder_configuration,
guint len);
/* AV1 */
GST_PBUTILS_API

View File

@ -1118,6 +1118,65 @@ GST_START_TEST (test_pb_utils_h264_get_profile_flags_level)
GST_END_TEST;
GST_START_TEST (test_pb_utils_h266_caps_set_level_tier_and_profile)
{
GstCaps *caps;
GstStructure *s;
const guint8 good_config[] =
{ 0xff, 0x00, 0x61, 0x1f, 0x01, 0x02, 0x50, 0x80 };
const guint8 short_config[] = { 0xff, 0x00, 0x61, 0x1f };
const guint8 no_ptl_config[] = { 0xfe, 0xba, 0xbe, 0xef };
const guint8 short_config_no_level[] = { 0xff, 0x00, 0x61, 0x1f, 0x01, 0x02 };
/* Happy path */
caps = gst_caps_new_empty_simple ("video/x-h266");
fail_unless (gst_codec_utils_h266_caps_set_level_tier_and_profile (caps,
good_config, sizeof (good_config)));
s = gst_caps_get_structure (caps, 0);
fail_unless_equals_string (gst_structure_get_string (s, "profile"),
"main-10");
fail_unless_equals_string (gst_structure_get_string (s, "level"), "5");
fail_unless_equals_string (gst_structure_get_string (s, "tier"), "main");
gst_caps_unref (caps);
/* Too short to have profile-tier-level */
caps = gst_caps_new_empty_simple ("video/x-h266");
fail_unless (!gst_codec_utils_h266_caps_set_level_tier_and_profile (caps,
short_config, sizeof (short_config)));
s = gst_caps_get_structure (caps, 0);
fail_unless_equals_string (gst_structure_get_string (s, "profile"), NULL);
fail_unless_equals_string (gst_structure_get_string (s, "level"), NULL);
fail_unless_equals_string (gst_structure_get_string (s, "tier"), NULL);
gst_caps_unref (caps);
/* ptl_present_flag = FALSE */
caps = gst_caps_new_empty_simple ("video/x-h266");
fail_unless (!gst_codec_utils_h266_caps_set_level_tier_and_profile (caps,
no_ptl_config, sizeof (no_ptl_config)));
s = gst_caps_get_structure (caps, 0);
fail_unless_equals_string (gst_structure_get_string (s, "profile"), NULL);
fail_unless_equals_string (gst_structure_get_string (s, "level"), NULL);
fail_unless_equals_string (gst_structure_get_string (s, "tier"), NULL);
gst_caps_unref (caps);
/* Too short to parse level */
caps = gst_caps_new_empty_simple ("video/x-h266");
fail_unless (!gst_codec_utils_h266_caps_set_level_tier_and_profile (caps,
short_config_no_level, sizeof (short_config_no_level)));
s = gst_caps_get_structure (caps, 0);
fail_unless_equals_string (gst_structure_get_string (s, "profile"),
"main-10");
fail_unless_equals_string (gst_structure_get_string (s, "level"), NULL);
fail_unless_equals_string (gst_structure_get_string (s, "tier"), "main");
gst_caps_unref (caps);
}
GST_END_TEST;
#define PROFILE_TIER_LEVEL_LEN 11
static void
@ -1712,6 +1771,8 @@ libgstpbutils_suite (void)
tcase_add_test (tc_chain, test_pb_utils_h265_profiles);
tcase_add_test (tc_chain, test_pb_utils_caps_mime_codec);
tcase_add_test (tc_chain, test_pb_utils_caps_from_mime_codec);
tcase_add_test (tc_chain, test_pb_utils_h266_caps_set_level_tier_and_profile);
return s;
}