pbutils: Add function to parse RFC 6381 codecs field

This is the opposite of `gst_codec_utils_caps_get_mime_codec()`, which allows
elements to get the `GstCaps`

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1894>
This commit is contained in:
Edward Hervey 2022-03-08 09:46:33 +01:00 committed by GStreamer Marge Bot
parent 6fec258930
commit bce779e66d
3 changed files with 266 additions and 2 deletions

View file

@ -2556,3 +2556,243 @@ gst_codec_utils_caps_get_mime_codec (GstCaps * caps)
done: done:
return mime_codec; return mime_codec;
} }
static GstCaps *
gst_codec_utils_caps_from_mime_codec_single (const gchar * codec)
{
GstCaps *caps = NULL;
gchar **subcodec = NULL;
gchar *subcodec0;
guint32 codec_fourcc;
GST_DEBUG ("Analyzing codec '%s'", codec);
/* rfc 6381 3.3
*
* For the ISO Base Media File Format, and the QuickTime movie file
* format, the first element of a 'codecs' parameter value is a sample
* description entry four-character code as registered by the MP4
* Registration Authority [MP4RA].
*
* See Also : http://mp4ra.org/#/codecs
*/
if (strlen (codec) < 4) {
GST_WARNING ("Invalid codec (smaller than 4 characters) : '%s'", codec);
goto beach;
}
subcodec = g_strsplit (codec, ".", 0);
subcodec0 = subcodec[0];
if (subcodec0 == NULL)
goto beach;
/* Skip any leading spaces */
while (*subcodec0 == ' ')
subcodec0++;
if (strlen (subcodec0) < 4) {
GST_WARNING ("Invalid codec (smaller than 4 characters) : '%s'", subcodec0);
goto beach;
}
GST_LOG ("subcodec[0] '%s'", subcodec0);
codec_fourcc = GST_READ_UINT32_LE (subcodec0);
switch (codec_fourcc) {
case GST_MAKE_FOURCC ('a', 'v', 'c', '1'):
case GST_MAKE_FOURCC ('a', 'v', 'c', '2'):
case GST_MAKE_FOURCC ('a', 'v', 'c', '3'):
case GST_MAKE_FOURCC ('a', 'v', 'c', '4'):
{
guint8 sps[3];
guint64 spsint64;
/* ISO 14496-15 Annex E : Sub-parameters for the MIME type “codecs”
* parameter */
caps = gst_caps_new_empty_simple ("video/x-h264");
if (subcodec[1]) {
/* The second element is the hexadecimal representation of the following
* three bytes in the (subset) sequence parameter set Network
* Abstraction Layer (NAL) unit specified in [AVC]:
* * profile_idc
* * constraint_set flags
* * level_idc
* */
spsint64 = g_ascii_strtoull (subcodec[1], NULL, 16);
sps[0] = spsint64 >> 16;
sps[1] = (spsint64 >> 8) & 0xff;
sps[2] = spsint64 & 0xff;
gst_codec_utils_h264_caps_set_level_and_profile (caps,
(const guint8 *) &sps, 3);
}
}
break;
case GST_MAKE_FOURCC ('m', 'p', '4', 'a'):
{
guint64 oti;
if (!subcodec[1])
break;
oti = g_ascii_strtoull (subcodec[1], NULL, 16);
/* For mp4a, mp4v and mp4s, the second element is the hexadecimal
* representation of the MP4 Registration Authority
* ObjectTypeIndication */
switch (oti) {
case 0x40:
{
guint64 audio_oti;
const gchar *profile = NULL;
/* MPEG-4 Audio (ISO/IEC 14496-3 */
caps =
gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 4,
NULL);
if (!subcodec[2])
break;
/* If present, last element is the audio object type */
audio_oti = g_ascii_strtoull (subcodec[2], NULL, 16);
switch (audio_oti) {
case 1:
profile = "main";
break;
case 2:
profile = "lc";
break;
case 3:
profile = "ssr";
break;
case 4:
profile = "ltp";
break;
default:
GST_WARNING ("Unhandled MPEG-4 Audio Object Type: 0x%"
G_GUINT64_FORMAT "x", audio_oti);
break;
}
if (profile)
gst_caps_set_simple (caps, "profile", G_TYPE_STRING, profile, NULL);
break;
}
default:
GST_WARNING ("Unknown ObjectTypeIndication 0x%" G_GUINT64_FORMAT "x",
oti);
break;
}
}
break;
case GST_MAKE_FOURCC ('h', 'e', 'v', '1'):
case GST_MAKE_FOURCC ('h', 'v', 'c', '1'):
{
/* ISO 14496-15 Annex E : Sub-parameters for the MIME type “codecs”
* parameter */
caps = gst_caps_new_empty_simple ("video/x-h265");
/* FIXME : Extract information from the following component */
break;
}
/* Following are not defined in rfc 6831 but are registered MP4RA codecs */
case GST_MAKE_FOURCC ('a', 'c', '-', '3'):
/* ETSI TS 102 366 v1.4.1 - Digital Audio Compression (AC-3, Enhanced AC-3) Standard, Annex F */
caps = gst_caps_new_empty_simple ("audio/x-ac3");
break;
case GST_MAKE_FOURCC ('e', 'c', '+', '3'):
GST_FIXME
("Signalling of ATMOS ('ec+3') isn't defined yet. Falling back to EAC3 caps");
/* withdrawn, unused, do not use (was enhanced AC-3 audio with JOC) */
case GST_MAKE_FOURCC ('e', 'c', '-', '3'):
/* ETSI TS 102 366 v1.4.1 - Digital Audio Compression (AC-3, Enhanced AC-3) Standard, Annex F */
caps = gst_caps_new_empty_simple ("audio/x-eac3");
break;
case GST_MAKE_FOURCC ('s', 't', 'p', 'p'):
/* IMSC1-conformant TTM XML */
caps = gst_caps_new_empty_simple ("application/ttml+xml");
break;
case GST_MAKE_FOURCC ('w', 'v', 't', 't'):
/* WebVTT subtitles */
caps = gst_caps_new_empty_simple ("application/x-subtitle-vtt");
break;
case GST_MAKE_FOURCC ('v', 'p', '0', '8'):
/* VP8 */
caps = gst_caps_new_empty_simple ("video/x-vp8");
break;
case GST_MAKE_FOURCC ('v', 'p', '0', '9'):
/* VP9 */
caps = gst_caps_new_empty_simple ("video/x-vp9");
break;
case GST_MAKE_FOURCC ('a', 'v', '0', '1'):
/* AV1 */
caps = gst_caps_new_empty_simple ("video/x-av1");
break;
case GST_MAKE_FOURCC ('o', 'p', 'u', 's'):
/* Opus */
caps = gst_caps_new_empty_simple ("audio/x-opus");
break;
case GST_MAKE_FOURCC ('u', 'l', 'a', 'w'):
/* ulaw */
caps = gst_caps_new_empty_simple ("audio/x-mulaw");
break;
case GST_MAKE_FOURCC ('g', '7', '2', '6'):
/* ulaw */
caps =
gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING, "g726",
NULL);
break;
default:
GST_WARNING ("Unknown codec '%s' please file a bug", codec);
break;
}
beach:
if (subcodec != NULL)
g_strfreev (subcodec);
return caps;
}
/**
* gst_codec_utils_caps_from_mime_codec:
* @codecs_field: A mime codec string field
*
* Converts a RFC 6381 compatible codec string to #GstCaps. More than one codec
* string can be present (separated by `,`).
*
* Registered codecs can be found at http://mp4ra.org/#/codecs
*
* Returns: (transfer full) (nullable): The corresponding #GstCaps or %NULL
*
* Since: 1.22
*/
GstCaps *
gst_codec_utils_caps_from_mime_codec (const gchar * codecs_field)
{
gchar **codecs = NULL;
GstCaps *caps = NULL;
guint i;
g_return_val_if_fail (codecs_field != NULL, NULL);
GST_LOG ("codecs_field '%s'", codecs_field);
codecs = g_strsplit (codecs_field, ",", 0);
if (codecs == NULL) {
GST_WARNING ("Invalid 'codecs' field : '%s'", codecs_field);
goto beach;
}
for (i = 0; codecs[i]; i++) {
const gchar *codec = codecs[i];
if (caps == NULL)
caps = gst_codec_utils_caps_from_mime_codec_single (codec);
else
gst_caps_append (caps,
gst_codec_utils_caps_from_mime_codec_single (codec));
}
beach:
g_strfreev (codecs);
GST_LOG ("caps %" GST_PTR_FORMAT, caps);
return caps;
}

View file

@ -156,6 +156,8 @@ gboolean gst_codec_utils_opus_parse_header (GstBuffer * header,
GST_PBUTILS_API GST_PBUTILS_API
gchar * gst_codec_utils_caps_get_mime_codec (GstCaps * caps); gchar * gst_codec_utils_caps_get_mime_codec (GstCaps * caps);
GST_PBUTILS_API
GstCaps * gst_codec_utils_caps_from_mime_codec (const gchar *codecs_field);
G_END_DECLS G_END_DECLS

View file

@ -1359,9 +1359,10 @@ static const guint8 h265_sample_codec_data[] = {
0x44, 0x01, 0xc0, 0x2c, 0xbc, 0x14, 0xc9 0x44, 0x01, 0xc0, 0x2c, 0xbc, 0x14, 0xc9
}; };
GST_START_TEST (test_pb_utils_caps_get_mime_codec) GST_START_TEST (test_pb_utils_caps_mime_codec)
{ {
GstCaps *caps = NULL; GstCaps *caps = NULL;
GstCaps *caps2 = NULL;
gchar *mime_codec = NULL; gchar *mime_codec = NULL;
GstBuffer *buffer = NULL; GstBuffer *buffer = NULL;
guint8 *codec_data = NULL; guint8 *codec_data = NULL;
@ -1371,6 +1372,9 @@ GST_START_TEST (test_pb_utils_caps_get_mime_codec)
caps = gst_caps_new_empty_simple ("video/x-h264"); caps = gst_caps_new_empty_simple ("video/x-h264");
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, "avc1"); fail_unless_equals_string (mime_codec, "avc1");
caps2 = gst_codec_utils_caps_from_mime_codec (mime_codec);
fail_unless (gst_caps_is_equal_fixed (caps, caps2));
gst_caps_unref (caps2);
g_free (mime_codec); g_free (mime_codec);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -1412,6 +1416,9 @@ GST_START_TEST (test_pb_utils_caps_get_mime_codec)
caps = gst_caps_new_empty_simple ("video/x-av1"); caps = gst_caps_new_empty_simple ("video/x-av1");
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, "av01"); fail_unless_equals_string (mime_codec, "av01");
caps2 = gst_codec_utils_caps_from_mime_codec (mime_codec);
fail_unless (gst_caps_is_equal_fixed (caps, caps2));
gst_caps_unref (caps2);
g_free (mime_codec); g_free (mime_codec);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -1419,6 +1426,9 @@ GST_START_TEST (test_pb_utils_caps_get_mime_codec)
caps = gst_caps_new_empty_simple ("video/x-vp8"); caps = gst_caps_new_empty_simple ("video/x-vp8");
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, "vp08"); fail_unless_equals_string (mime_codec, "vp08");
caps2 = gst_codec_utils_caps_from_mime_codec (mime_codec);
fail_unless (gst_caps_is_equal_fixed (caps, caps2));
gst_caps_unref (caps2);
g_free (mime_codec); g_free (mime_codec);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -1426,6 +1436,9 @@ GST_START_TEST (test_pb_utils_caps_get_mime_codec)
caps = gst_caps_new_empty_simple ("video/x-vp9"); caps = gst_caps_new_empty_simple ("video/x-vp9");
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, "vp09"); fail_unless_equals_string (mime_codec, "vp09");
caps2 = gst_codec_utils_caps_from_mime_codec (mime_codec);
fail_unless (gst_caps_is_equal_fixed (caps, caps2));
gst_caps_unref (caps2);
g_free (mime_codec); g_free (mime_codec);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -1462,6 +1475,9 @@ GST_START_TEST (test_pb_utils_caps_get_mime_codec)
caps = gst_caps_new_empty_simple ("audio/x-opus"); caps = gst_caps_new_empty_simple ("audio/x-opus");
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, "opus"); fail_unless_equals_string (mime_codec, "opus");
caps2 = gst_codec_utils_caps_from_mime_codec (mime_codec);
fail_unless (gst_caps_is_equal_fixed (caps, caps2));
gst_caps_unref (caps2);
g_free (mime_codec); g_free (mime_codec);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -1469,6 +1485,9 @@ GST_START_TEST (test_pb_utils_caps_get_mime_codec)
caps = gst_caps_new_empty_simple ("audio/x-mulaw"); caps = gst_caps_new_empty_simple ("audio/x-mulaw");
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, "ulaw"); fail_unless_equals_string (mime_codec, "ulaw");
caps2 = gst_codec_utils_caps_from_mime_codec (mime_codec);
fail_unless (gst_caps_is_equal_fixed (caps, caps2));
gst_caps_unref (caps2);
g_free (mime_codec); g_free (mime_codec);
gst_caps_unref (caps); gst_caps_unref (caps);
@ -1478,6 +1497,9 @@ GST_START_TEST (test_pb_utils_caps_get_mime_codec)
NULL); 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, "g726"); fail_unless_equals_string (mime_codec, "g726");
caps2 = gst_codec_utils_caps_from_mime_codec (mime_codec);
fail_unless (gst_caps_is_equal_fixed (caps, caps2));
gst_caps_unref (caps2);
g_free (mime_codec); g_free (mime_codec);
gst_caps_unref (caps); gst_caps_unref (caps);
} }
@ -1504,7 +1526,7 @@ libgstpbutils_suite (void)
tcase_add_test (tc_chain, test_pb_utils_h264_profiles); tcase_add_test (tc_chain, test_pb_utils_h264_profiles);
tcase_add_test (tc_chain, test_pb_utils_h264_get_profile_flags_level); tcase_add_test (tc_chain, test_pb_utils_h264_get_profile_flags_level);
tcase_add_test (tc_chain, test_pb_utils_h265_profiles); tcase_add_test (tc_chain, test_pb_utils_h265_profiles);
tcase_add_test (tc_chain, test_pb_utils_caps_get_mime_codec); tcase_add_test (tc_chain, test_pb_utils_caps_mime_codec);
return s; return s;
} }