diff --git a/subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c b/subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c index 78bdb7a22d..797fc88096 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c +++ b/subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c @@ -2556,3 +2556,243 @@ gst_codec_utils_caps_get_mime_codec (GstCaps * caps) done: 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; +} diff --git a/subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h b/subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h index 1aa72467c1..43398c4eaa 100644 --- a/subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h +++ b/subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h @@ -156,6 +156,8 @@ gboolean gst_codec_utils_opus_parse_header (GstBuffer * header, GST_PBUTILS_API 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 diff --git a/subprojects/gst-plugins-base/tests/check/libs/pbutils.c b/subprojects/gst-plugins-base/tests/check/libs/pbutils.c index a92229972a..801ab96e28 100644 --- a/subprojects/gst-plugins-base/tests/check/libs/pbutils.c +++ b/subprojects/gst-plugins-base/tests/check/libs/pbutils.c @@ -1359,9 +1359,10 @@ static const guint8 h265_sample_codec_data[] = { 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 *caps2 = NULL; gchar *mime_codec = NULL; GstBuffer *buffer = 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"); mime_codec = gst_codec_utils_caps_get_mime_codec (caps); 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); 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"); mime_codec = gst_codec_utils_caps_get_mime_codec (caps); 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); 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"); mime_codec = gst_codec_utils_caps_get_mime_codec (caps); 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); 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"); mime_codec = gst_codec_utils_caps_get_mime_codec (caps); 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); 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"); mime_codec = gst_codec_utils_caps_get_mime_codec (caps); 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); 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"); mime_codec = gst_codec_utils_caps_get_mime_codec (caps); 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); gst_caps_unref (caps); @@ -1478,6 +1497,9 @@ GST_START_TEST (test_pb_utils_caps_get_mime_codec) NULL); mime_codec = gst_codec_utils_caps_get_mime_codec (caps); 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); 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_get_profile_flags_level); 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; }