diff options
author | Ludvig Rappe <ludvigr@axis.com> | 2021-08-25 17:03:49 +0200 |
---|---|---|
committer | GStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org> | 2021-08-30 08:49:33 +0000 |
commit | 75c44583eeb0013b82d3bb8d4705e7468abba489 (patch) | |
tree | 0cf1d063d64669b794fd78a7b4484e45677b1a0b | |
parent | 4a1d8eac31408a5e6e01183a6217899538ac0628 (diff) | |
download | gstreamer-plugins-base-75c44583eeb0013b82d3bb8d4705e7468abba489.tar.gz |
pbutils: Add function to convert caps to MIME codec
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/1265>
-rw-r--r-- | gst-libs/gst/pbutils/codec-utils.c | 175 | ||||
-rw-r--r-- | gst-libs/gst/pbutils/codec-utils.h | 5 | ||||
-rw-r--r-- | tests/check/libs/pbutils.c | 119 |
3 files changed, 299 insertions, 0 deletions
diff --git a/gst-libs/gst/pbutils/codec-utils.c b/gst-libs/gst/pbutils/codec-utils.c index 7f585d1d1..5d0f5655c 100644 --- a/gst-libs/gst/pbutils/codec-utils.c +++ b/gst-libs/gst/pbutils/codec-utils.c @@ -2257,3 +2257,178 @@ done: return ret; } + +static gboolean +h264_caps_structure_get_profile_flags_level (GstStructure * caps_st, + guint8 * profile, guint8 * flags, guint8 * level) +{ + const GValue *codec_data_value = NULL; + GstBuffer *codec_data = NULL; + GstMapInfo map; + gboolean ret = FALSE; + + codec_data_value = gst_structure_get_value (caps_st, "codec_data"); + if (!codec_data_value) { + GST_DEBUG + ("video/x-h264 pad did not have codec_data set, cannot parse profile, flags and level"); + return FALSE; + } else { + guint8 *data = NULL; + gsize size; + + 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; + + if (!gst_codec_utils_h264_get_profile_flags_level (data, (guint) size, + profile, flags, level)) { + GST_WARNING + ("Failed to parse profile, flags and level from h264 codec data"); + goto done; + } + } + + ret = TRUE; + +done: + gst_buffer_unmap (codec_data, &map); + + return ret; +} + +static gboolean +aac_caps_structure_get_audio_object_type (GstStructure * caps_st, + guint8 * audio_object_type) +{ + gboolean ret = FALSE; + const GValue *codec_data_value = NULL; + GstBuffer *codec_data = NULL; + GstMapInfo map; + guint8 *data = NULL; + gsize size; + GstBitReader br; + + codec_data_value = gst_structure_get_value (caps_st, "codec_data"); + if (!codec_data_value) { + GST_DEBUG + ("audio/mpeg pad did not have codec_data set, cannot parse audio object type"); + 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; + + if (size < 2) { + GST_WARNING ("aac codec data is too small"); + goto done; + } + + gst_bit_reader_init (&br, data, size); + ret = gst_codec_utils_aac_get_audio_object_type (&br, audio_object_type); + +done: + gst_buffer_unmap (codec_data, &map); + + return ret; +} + +/** + * gst_codec_utils_caps_get_mime_codec: + * @caps: A #GstCaps to convert to mime codec + * + * Converts @caps to a RFC 6381 compatible codec string if possible. + * + * Useful for providing the 'codecs' field inside the 'Content-Type' HTTP + * header for containerized formats, such as mp4 or matroska. + * + * Returns: (transfer full): a RFC 6381 compatible codec string or %NULL + * + * Since: 1.20 + */ +gchar * +gst_codec_utils_caps_get_mime_codec (GstCaps * caps) +{ + gchar *mime_codec = NULL; + GstStructure *caps_st = NULL; + const gchar *media_type = NULL; + + g_return_val_if_fail (caps != NULL, NULL); + g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); + + caps_st = gst_caps_get_structure (caps, 0); + if (caps_st == NULL) { + GST_WARNING ("Failed to get structure from caps"); + goto done; + } + + media_type = gst_structure_get_name (caps_st); + + if (g_strcmp0 (media_type, "video/x-h264") == 0) { + /* avc1.AABBCC + * AA = profile + * BB = constraint set flags + * CC = level + */ + guint8 profile = 0; + guint8 flags = 0; + guint8 level = 0; + + if (!h264_caps_structure_get_profile_flags_level (caps_st, &profile, &flags, + &level)) { + GST_DEBUG + ("h264 caps did not contain 'codec_data', cannot determine detailed codecs info"); + mime_codec = g_strdup ("avc1"); + } else { + mime_codec = g_strdup_printf ("avc1.%02X%02X%02X", profile, flags, level); + } + } else if (g_strcmp0 (media_type, "video/x-h265") == 0) { + /* TODO: this simple "hev1" is not complete and should contain more info + * similar to how avc1 does. + * 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"); + } else if (g_strcmp0 (media_type, "video/x-av1") == 0) { + /* TODO: Some browsers won't play the video unless more codec information is + * available in the mime codec for av1. This is documented in + * https://aomediacodec.github.io/av1-isobmff/#codecsparam */ + mime_codec = g_strdup ("av01"); + } else if (g_strcmp0 (media_type, "video/x-vp8") == 0) { + /* TODO: most browsers won't play the video unless more codec information is + * available in the mime codec for vp8. */ + mime_codec = g_strdup ("vp08"); + } else if (g_strcmp0 (media_type, "video/x-vp9") == 0) { + /* TODO: most browsers won't play the video unless more codec information is + * available in the mime codec for vp9. This is documented in + * https://www.webmproject.org/vp9/mp4/ */ + mime_codec = g_strdup ("vp09"); + } else if (g_strcmp0 (media_type, "audio/mpeg") == 0) { + guint8 audio_object_type = 0; + if (aac_caps_structure_get_audio_object_type (caps_st, &audio_object_type)) { + mime_codec = g_strdup_printf ("mp4a.40.%u", audio_object_type); + } else { + mime_codec = g_strdup ("mp4a.40"); + } + } else if (g_strcmp0 (media_type, "audio/x-opus") == 0) { + mime_codec = g_strdup ("opus"); + } else if (g_strcmp0 (media_type, "audio/x-mulaw") == 0) { + mime_codec = g_strdup ("ulaw"); + } else if (g_strcmp0 (media_type, "audio/x-adpcm") == 0) { + if (g_strcmp0 (gst_structure_get_string (caps_st, "layout"), "g726") == 0) { + mime_codec = g_strdup ("g726"); + } + } else if (g_strcmp0 (media_type, "audio/x-raw") == 0) { + mime_codec = g_strdup ("raw"); + } + +done: + return mime_codec; +} diff --git a/gst-libs/gst/pbutils/codec-utils.h b/gst-libs/gst/pbutils/codec-utils.h index 36bf0d619..11ef18eb4 100644 --- a/gst-libs/gst/pbutils/codec-utils.h +++ b/gst-libs/gst/pbutils/codec-utils.h @@ -152,6 +152,11 @@ gboolean gst_codec_utils_opus_parse_header (GstBuffer * header, guint16 * pre_skip, gint16 * output_gain); +/* General */ +GST_PBUTILS_API +gchar * gst_codec_utils_caps_get_mime_codec (GstCaps * caps); + + G_END_DECLS #endif /* __GST_PB_UTILS_CODEC_UTILS_H__ */ diff --git a/tests/check/libs/pbutils.c b/tests/check/libs/pbutils.c index 846a26329..e43c5b679 100644 --- a/tests/check/libs/pbutils.c +++ b/tests/check/libs/pbutils.c @@ -1343,6 +1343,124 @@ GST_START_TEST (test_pb_utils_h265_profiles) GST_END_TEST; +GST_START_TEST (test_pb_utils_caps_get_mime_codec) +{ + GstCaps *caps = NULL; + gchar *mime_codec = NULL; + GstBuffer *buffer = NULL; + guint8 *codec_data = NULL; + gsize codec_data_len; + + /* h264 without codec data */ + 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"); + g_free (mime_codec); + gst_caps_unref (caps); + + /* h264 with codec data */ + codec_data_len = sizeof (guint8) * 7; + codec_data = g_malloc0 (codec_data_len); + codec_data[0] = 0x01; + codec_data[1] = 0x64; + codec_data[2] = 0x00; + codec_data[3] = 0x32; + /* seven bytes is the minumum for a valid h264 codec_data, but in + * gst_codec_utils_h264_get_profile_flags_level we only parse the first four + * bytes */ + buffer = gst_buffer_new_wrapped (codec_data, codec_data_len); + caps = + gst_caps_new_simple ("video/x-h264", "codec_data", GST_TYPE_BUFFER, + buffer, NULL); + mime_codec = gst_codec_utils_caps_get_mime_codec (caps); + fail_unless_equals_string (mime_codec, "avc1.640032"); + g_free (mime_codec); + gst_caps_unref (caps); + gst_buffer_unref (buffer); + + /* h265 */ + caps = gst_caps_new_empty_simple ("video/x-h265"); + mime_codec = gst_codec_utils_caps_get_mime_codec (caps); + fail_unless_equals_string (mime_codec, "hev1"); + g_free (mime_codec); + gst_caps_unref (caps); + + /* av1 */ + 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"); + g_free (mime_codec); + gst_caps_unref (caps); + + /* vp8 */ + 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"); + g_free (mime_codec); + gst_caps_unref (caps); + + /* vp9 */ + 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"); + g_free (mime_codec); + gst_caps_unref (caps); + + /* aac without codec data */ + caps = gst_caps_new_empty_simple ("audio/mpeg"); + mime_codec = gst_codec_utils_caps_get_mime_codec (caps); + fail_unless_equals_string (mime_codec, "mp4a.40"); + g_free (mime_codec); + gst_caps_unref (caps); + + /* aac with codec data */ + codec_data_len = sizeof (guint8) * 2; + codec_data = g_malloc0 (codec_data_len); + codec_data[0] = 0x11; + codec_data[1] = 0x88; + buffer = gst_buffer_new_wrapped (codec_data, codec_data_len); + caps = + gst_caps_new_simple ("audio/mpeg", "codec_data", GST_TYPE_BUFFER, buffer, + NULL); + mime_codec = gst_codec_utils_caps_get_mime_codec (caps); + fail_unless_equals_string (mime_codec, "mp4a.40.2"); + g_free (mime_codec); + gst_caps_unref (caps); + gst_buffer_unref (buffer); + + /* opus */ + 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"); + g_free (mime_codec); + gst_caps_unref (caps); + + /* mulaw */ + 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"); + g_free (mime_codec); + gst_caps_unref (caps); + + /* g726 */ + caps = + gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING, "g726", + NULL); + mime_codec = gst_codec_utils_caps_get_mime_codec (caps); + fail_unless_equals_string (mime_codec, "g726"); + g_free (mime_codec); + gst_caps_unref (caps); + + /* raw */ + caps = gst_caps_new_empty_simple ("audio/x-raw"); + mime_codec = gst_codec_utils_caps_get_mime_codec (caps); + fail_unless_equals_string (mime_codec, "raw"); + g_free (mime_codec); + gst_caps_unref (caps); +} + +GST_END_TEST; + static Suite * libgstpbutils_suite (void) { @@ -1361,6 +1479,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); return s; } |