mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-12 17:25:36 +00:00
pbutils: H.265 support for gst_codec_utils_caps_get_mime_codec()
The codec_data caps payload is parsed and a MIME codec string is generated according to the ISO/IEC 14496-15 specification. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1505>
This commit is contained in:
parent
1c8f7c32aa
commit
074c5d85cb
2 changed files with 132 additions and 9 deletions
|
@ -2343,6 +2343,109 @@ done:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
hevc_caps_get_mime_codec (GstCaps * caps, gchar ** mime_codec)
|
||||||
|
{
|
||||||
|
GstStructure *caps_st = NULL;
|
||||||
|
const GValue *codec_data_value = NULL;
|
||||||
|
GstBuffer *codec_data = NULL;
|
||||||
|
GstMapInfo map;
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
const gchar *stream_format;
|
||||||
|
guint8 *data = NULL;
|
||||||
|
gsize size;
|
||||||
|
guint16 profile_space;
|
||||||
|
guint8 tier_flag;
|
||||||
|
guint16 profile_idc;
|
||||||
|
guint32 compat_flags;
|
||||||
|
guchar constraint_indicator_flags[6];
|
||||||
|
guint8 level_idc;
|
||||||
|
guint32 compat_flag_parameter = 0;
|
||||||
|
GString *codec_string;
|
||||||
|
const guint8 *profile_tier_level;
|
||||||
|
unsigned last_flag_index;
|
||||||
|
|
||||||
|
caps_st = gst_caps_get_structure (caps, 0);
|
||||||
|
codec_data_value = gst_structure_get_value (caps_st, "codec_data");
|
||||||
|
stream_format = gst_structure_get_string (caps_st, "stream-format");
|
||||||
|
if (!codec_data_value) {
|
||||||
|
GST_DEBUG ("video/x-h265 caps did not have codec_data set, cannot parse");
|
||||||
|
return FALSE;
|
||||||
|
} else if (!stream_format) {
|
||||||
|
GST_DEBUG
|
||||||
|
("video/x-h265 caps did not have stream-format set, cannot parse");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
codec_data = gst_value_get_buffer (codec_data_value);
|
||||||
|
if (!gst_buffer_map (codec_data, &map, GST_MAP_READ)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
data = map.data;
|
||||||
|
size = map.size;
|
||||||
|
|
||||||
|
/* HEVCDecoderConfigurationRecord is at a minimum 23 bytes long */
|
||||||
|
if (size < 23) {
|
||||||
|
GST_DEBUG ("Incomplete HEVCDecoderConfigurationRecord");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_str_equal (stream_format, "hev1")
|
||||||
|
&& !g_str_equal (stream_format, "hvc1")) {
|
||||||
|
GST_DEBUG ("Unknown stream-format %s", stream_format);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
profile_tier_level = data + 1;
|
||||||
|
profile_space = (profile_tier_level[0] & 0x11) >> 6;
|
||||||
|
tier_flag = (profile_tier_level[0] & 0x001) >> 5;
|
||||||
|
profile_idc = (profile_tier_level[0] & 0x1f);
|
||||||
|
|
||||||
|
compat_flags = GST_READ_UINT32_BE (data + 2);
|
||||||
|
for (unsigned i = 0; i < 6; ++i)
|
||||||
|
constraint_indicator_flags[i] = GST_READ_UINT8 (data + 6 + i);
|
||||||
|
|
||||||
|
level_idc = data[12];
|
||||||
|
|
||||||
|
/* The 32 bits of the compat_flags, but in reverse bit order */
|
||||||
|
compat_flags =
|
||||||
|
((compat_flags & 0xaaaaaaaa) >> 1) | ((compat_flags & 0x55555555) << 1);
|
||||||
|
compat_flags =
|
||||||
|
((compat_flags & 0xcccccccc) >> 2) | ((compat_flags & 0x33333333) << 2);
|
||||||
|
compat_flags =
|
||||||
|
((compat_flags & 0xf0f0f0f0) >> 4) | ((compat_flags & 0x0f0f0f0f) << 4);
|
||||||
|
compat_flags =
|
||||||
|
((compat_flags & 0xff00ff00) >> 8) | ((compat_flags & 0x00ff00ff) << 8);
|
||||||
|
compat_flag_parameter = (compat_flags >> 16) | (compat_flags << 16);
|
||||||
|
|
||||||
|
codec_string = g_string_new (stream_format);
|
||||||
|
codec_string = g_string_append_c (codec_string, '.');
|
||||||
|
if (profile_space)
|
||||||
|
codec_string = g_string_append_c (codec_string, 'A' + profile_space - 1);
|
||||||
|
g_string_append_printf (codec_string, "%" G_GUINT16_FORMAT ".%X.%c%d",
|
||||||
|
profile_idc, compat_flag_parameter, tier_flag ? 'H' : 'L', level_idc);
|
||||||
|
|
||||||
|
/* Each of the 6 bytes of the constraint flags, starting from the byte containing the
|
||||||
|
* progressive_source_flag, each encoded as a hexadecimal number, and the encoding
|
||||||
|
* of each byte separated by a period; trailing bytes that are zero may be omitted.
|
||||||
|
*/
|
||||||
|
last_flag_index = 5;
|
||||||
|
while ((int) (constraint_indicator_flags[last_flag_index]) == 0)
|
||||||
|
--last_flag_index;
|
||||||
|
for (unsigned i = 0; i <= last_flag_index; ++i) {
|
||||||
|
g_string_append_printf (codec_string, ".%02X",
|
||||||
|
constraint_indicator_flags[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
*mime_codec = g_string_free (codec_string, FALSE);
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
|
||||||
|
done:
|
||||||
|
gst_buffer_unmap (codec_data, &map);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_codec_utils_caps_get_mime_codec:
|
* gst_codec_utils_caps_get_mime_codec:
|
||||||
* @caps: A #GstCaps to convert to mime codec
|
* @caps: A #GstCaps to convert to mime codec
|
||||||
|
@ -2395,13 +2498,10 @@ gst_codec_utils_caps_get_mime_codec (GstCaps * caps)
|
||||||
mime_codec = g_strdup_printf ("avc1.%02X%02X%02X", profile, flags, level);
|
mime_codec = g_strdup_printf ("avc1.%02X%02X%02X", profile, flags, level);
|
||||||
}
|
}
|
||||||
} else if (g_strcmp0 (media_type, "video/x-h265") == 0) {
|
} else if (g_strcmp0 (media_type, "video/x-h265") == 0) {
|
||||||
/* TODO: this simple "hev1" is not complete and should contain more info
|
if (!hevc_caps_get_mime_codec (caps, &mime_codec)) {
|
||||||
* similar to how avc1 does.
|
GST_DEBUG ("h265 caps parsing failed");
|
||||||
* However, as of writing there are no browsers that support h265 and the
|
|
||||||
* format of how to describe h265 codec info is badly documented.
|
|
||||||
* Examples exist online, but no public documentation seem to exist,
|
|
||||||
* however the paywalled ISO/IEC 14496-15 has it. */
|
|
||||||
mime_codec = g_strdup ("hev1");
|
mime_codec = g_strdup ("hev1");
|
||||||
|
}
|
||||||
} else if (g_strcmp0 (media_type, "video/x-av1") == 0) {
|
} else if (g_strcmp0 (media_type, "video/x-av1") == 0) {
|
||||||
/* TODO: Some browsers won't play the video unless more codec information is
|
/* TODO: Some browsers won't play the video unless more codec information is
|
||||||
* available in the mime codec for av1. This is documented in
|
* available in the mime codec for av1. This is documented in
|
||||||
|
|
|
@ -1343,6 +1343,22 @@ GST_START_TEST (test_pb_utils_h265_profiles)
|
||||||
|
|
||||||
GST_END_TEST;
|
GST_END_TEST;
|
||||||
|
|
||||||
|
static const guint8 h265_sample_codec_data[] = {
|
||||||
|
0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d,
|
||||||
|
0xf0, 0x00, 0xfc,
|
||||||
|
0xfd, 0xf8, 0xf8, 0x00, 0x00, 0x0f, 0x03, 0x20, 0x00, 0x01, 0x00, 0x18, 0x40,
|
||||||
|
0x01, 0x0c, 0x01,
|
||||||
|
0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0xb0, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0x03, 0x00,
|
||||||
|
0x5d, 0x15, 0xc0, 0x90, 0x21, 0x00, 0x01, 0x00, 0x22, 0x42, 0x01, 0x01, 0x01,
|
||||||
|
0x60, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x5d, 0xa0, 0x0a,
|
||||||
|
0x08, 0x0f, 0x16,
|
||||||
|
0x20, 0x57, 0xb9, 0x16, 0x55, 0x35, 0x01, 0x01, 0x01, 0x00, 0x80, 0x22, 0x00,
|
||||||
|
0x01, 0x00, 0x07,
|
||||||
|
0x44, 0x01, 0xc0, 0x2c, 0xbc, 0x14, 0xc9
|
||||||
|
};
|
||||||
|
|
||||||
GST_START_TEST (test_pb_utils_caps_get_mime_codec)
|
GST_START_TEST (test_pb_utils_caps_get_mime_codec)
|
||||||
{
|
{
|
||||||
GstCaps *caps = NULL;
|
GstCaps *caps = NULL;
|
||||||
|
@ -1379,11 +1395,18 @@ GST_START_TEST (test_pb_utils_caps_get_mime_codec)
|
||||||
gst_buffer_unref (buffer);
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
/* h265 */
|
/* h265 */
|
||||||
caps = gst_caps_new_empty_simple ("video/x-h265");
|
buffer =
|
||||||
|
gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
|
||||||
|
(gpointer) h265_sample_codec_data, sizeof (h265_sample_codec_data), 0,
|
||||||
|
sizeof (h265_sample_codec_data), NULL, NULL);
|
||||||
|
caps =
|
||||||
|
gst_caps_new_simple ("video/x-h265", "stream-format", G_TYPE_STRING,
|
||||||
|
"hvc1", "codec_data", GST_TYPE_BUFFER, buffer, NULL);
|
||||||
mime_codec = gst_codec_utils_caps_get_mime_codec (caps);
|
mime_codec = gst_codec_utils_caps_get_mime_codec (caps);
|
||||||
fail_unless_equals_string (mime_codec, "hev1");
|
fail_unless_equals_string (mime_codec, "hvc1.1.6.L93.B0");
|
||||||
g_free (mime_codec);
|
g_free (mime_codec);
|
||||||
gst_caps_unref (caps);
|
gst_caps_unref (caps);
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
/* av1 */
|
/* av1 */
|
||||||
caps = gst_caps_new_empty_simple ("video/x-av1");
|
caps = gst_caps_new_empty_simple ("video/x-av1");
|
||||||
|
|
Loading…
Reference in a new issue