Gstreamer基础教程9: 媒体信息采集
2021/6/7 10:51:19
本文主要是介绍Gstreamer基础教程9: 媒体信息采集,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章目录
- 1.Goal
- 2.介绍
- 3.The GStreamer Discoverer
- 3.1 Compile
- 3.2 Code
- 4.解析
- 5.讨论
1.Goal
有时您可能想快速找出文件(或URI)包含哪种媒体,或者您是否能够播放所有媒体。 您可以构建一个管道,将其设置为运行,并查看总线消息,但是GStreamer具有一个实用程序可以为您完成此任务。 本教程显示:
- 如何恢复有关URI的信息
- 如何确定URI是否可播放
2.介绍
GstDiscoverer是在pbutils库(插件基础实用程序)中找到的实用程序对象,该对象接受URI或URI列表,并返回有关它们的信息。 它可以在同步或异步模式下工作。
在同步模式下,只有一个要调用的函数gst_discoverer_discover_uri会阻塞,直到信息准备就绪为止。 由于这种阻塞,通常对于基于GUI的应用程序来说不太有趣,并且使用了异步模式,如本教程中所述。
恢复的信息包括编解码器描述,流拓扑(流和子流的数量)和可用的元数据(如音频语言)。
例如,这是https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm的结果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S5cXmR95-1623032260818)(:/b1d136ffd4f24504813101ca78ac4a45)]
以下代码尝试发现通过命令行提供的URI,并输出检索到的信息(如果未提供URI,则使用默认值)。
这是gst-discoverer-1.0工具的简化版本(基础教程10:GStreamer工具),该工具仅显示数据,不执行任何播放。
3.The GStreamer Discoverer
3.1 Compile
gcc basic-tutorial-9.c -o basic-tutorial-9 `pkg-config --cflags --libs gstreamer-1.0 gstreamer-pbutils-1.0`
3.2 Code
#include <string.h> #include <gst/gst.h> #include <gst/pbutils/pbutils.h> /* Structure to contain all our information, so we can pass it around */ typedef struct _CustomData { GstDiscoverer *discoverer; GMainLoop *loop; } CustomData; /* Print a tag in a human-readable format (name: value) */ static void print_tag_foreach (const GstTagList *tags, const gchar *tag, gpointer user_data) { GValue val = { 0, }; gchar *str; gint depth = GPOINTER_TO_INT (user_data); gst_tag_list_copy_value (&val, tags, tag); if (G_VALUE_HOLDS_STRING (&val)) str = g_value_dup_string (&val); else str = gst_value_serialize (&val); g_print ("%*s%s: %s\n", 2 * depth, " ", gst_tag_get_nick (tag), str); g_free (str); g_value_unset (&val); } /* Print information regarding a stream */ static void print_stream_info (GstDiscovererStreamInfo *info, gint depth) { gchar *desc = NULL; GstCaps *caps; const GstTagList *tags; caps = gst_discoverer_stream_info_get_caps (info); if (caps) { if (gst_caps_is_fixed (caps)) desc = gst_pb_utils_get_codec_description (caps); else desc = gst_caps_to_string (caps); gst_caps_unref (caps); } g_print ("%*s%s: %s\n", 2 * depth, " ", gst_discoverer_stream_info_get_stream_type_nick (info), (desc ? desc : "")); if (desc) { g_free (desc); desc = NULL; } tags = gst_discoverer_stream_info_get_tags (info); if (tags) { g_print ("%*sTags:\n", 2 * (depth + 1), " "); gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (depth + 2)); } } /* Print information regarding a stream and its substreams, if any */ static void print_topology (GstDiscovererStreamInfo *info, gint depth) { GstDiscovererStreamInfo *next; if (!info) return; print_stream_info (info, depth); next = gst_discoverer_stream_info_get_next (info); if (next) { print_topology (next, depth + 1); gst_discoverer_stream_info_unref (next); } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) { GList *tmp, *streams; streams = gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO (info)); for (tmp = streams; tmp; tmp = tmp->next) { GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data; print_topology (tmpinf, depth + 1); } gst_discoverer_stream_info_list_free (streams); } } /* This function is called every time the discoverer has information regarding * one of the URIs we provided.*/ static void on_discovered_cb (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, CustomData *data) { GstDiscovererResult result; const gchar *uri; const GstTagList *tags; GstDiscovererStreamInfo *sinfo; uri = gst_discoverer_info_get_uri (info); result = gst_discoverer_info_get_result (info); switch (result) { case GST_DISCOVERER_URI_INVALID: g_print ("Invalid URI '%s'\n", uri); break; case GST_DISCOVERER_ERROR: g_print ("Discoverer error: %s\n", err->message); break; case GST_DISCOVERER_TIMEOUT: g_print ("Timeout\n"); break; case GST_DISCOVERER_BUSY: g_print ("Busy\n"); break; case GST_DISCOVERER_MISSING_PLUGINS:{ const GstStructure *s; gchar *str; s = gst_discoverer_info_get_misc (info); str = gst_structure_to_string (s); g_print ("Missing plugins: %s\n", str); g_free (str); break; } case GST_DISCOVERER_OK: g_print ("Discovered '%s'\n", uri); break; } if (result != GST_DISCOVERER_OK) { g_printerr ("This URI cannot be played\n"); return; } /* If we got no error, show the retrieved information */ g_print ("\nDuration: %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (gst_discoverer_info_get_duration (info))); tags = gst_discoverer_info_get_tags (info); if (tags) { g_print ("Tags:\n"); gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (1)); } g_print ("Seekable: %s\n", (gst_discoverer_info_get_seekable (info) ? "yes" : "no")); g_print ("\n"); sinfo = gst_discoverer_info_get_stream_info (info); if (!sinfo) return; g_print ("Stream information:\n"); print_topology (sinfo, 1); gst_discoverer_stream_info_unref (sinfo); g_print ("\n"); } /* This function is called when the discoverer has finished examining * all the URIs we provided.*/ static void on_finished_cb (GstDiscoverer *discoverer, CustomData *data) { g_print ("Finished discovering\n"); g_main_loop_quit (data->loop); } int main (int argc, char **argv) { CustomData data; GError *err = NULL; gchar *uri = "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm"; /* if a URI was provided, use it instead of the default one */ if (argc > 1) { uri = argv[1]; } /* Initialize cumstom data structure */ memset (&data, 0, sizeof (data)); /* Initialize GStreamer */ gst_init (&argc, &argv); g_print ("Discovering '%s'\n", uri); /* Instantiate the Discoverer */ data.discoverer = gst_discoverer_new (5 * GST_SECOND, &err); if (!data.discoverer) { g_print ("Error creating discoverer instance: %s\n", err->message); g_clear_error (&err); return -1; } /* Connect to the interesting signals */ g_signal_connect (data.discoverer, "discovered", G_CALLBACK (on_discovered_cb), &data); g_signal_connect (data.discoverer, "finished", G_CALLBACK (on_finished_cb), &data); /* Start the discoverer process (nothing to do yet) */ gst_discoverer_start (data.discoverer); /* Add a request to process asynchronously the URI passed through the command line */ if (!gst_discoverer_discover_uri_async (data.discoverer, uri)) { g_print ("Failed to start discovering URI '%s'\n", uri); g_object_unref (data.discoverer); return -1; } /* Create a GLib Main Loop and set it to run, so we can wait for the signals */ data.loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (data.loop); /* Stop the discoverer process */ gst_discoverer_stop (data.discoverer); /* Free resources */ g_object_unref (data.discoverer); g_main_loop_unref (data.loop); return 0; }
4.解析
这些是使用GstDiscoverer的主要步骤
/* Instantiate the Discoverer */ data.discoverer = gst_discoverer_new (5 * GST_SECOND, &err); if (!data.discoverer) { g_print ("Error creating discoverer instance: %s\n", err->message); g_clear_error (&err); return -1; }
gst_discoverer_new创建一个新的Discoverer对象。 第一个参数是每个文件的超时时间,以纳秒为单位(为简单起见,请使用GST_SECOND宏)。
/* Connect to the interesting signals */ g_signal_connect (data.discoverer, "discovered", G_CALLBACK (on_discovered_cb), &data); g_signal_connect (data.discoverer, "finished", G_CALLBACK (on_finished_cb), &data);
链接感兴趣的signal。
/* Start the discoverer process (nothing to do yet) */ gst_discoverer_start (data.discoverer);
gst_discoverer_start启动发现过程,但我们尚未提供任何要发现的URI。 接下来完成:
/* Add a request to process asynchronously the URI passed through the command line */ if (!gst_discoverer_discover_uri_async (data.discoverer, uri)) { g_print ("Failed to start discovering URI '%s'\n", uri); g_object_unref (data.discoverer); return -1; }
gst_discoverer_discover_uri_async将提供的URI放入队列中进行发现。可以使用此功能将多个URI排队。当它们的发现过程完成时,将触发已注册的回调函数。
/* Create a GLib Main Loop and set it to run, so we can wait for the signals */ data.loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (data.loop);
通常的GLib主循环被实例化并执行。当从on_finished_cb回调调用g_main_loop_quit时,我们将退出循环。
/* Stop the discoverer process */ gst_discoverer_stop (data.discoverer);
discoverer完成操作后,请使用gst_discoverer_stop停止它,并使用g_object_unref取消引用它。
现在让我们回顾一下我们已经注册的回调on_discovered_cb:
/* This function is called every time the discoverer has information regarding * one of the URIs we provided.*/ static void on_discovered_cb (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, CustomData *data) { GstDiscovererResult result; const gchar *uri; const GstTagList *tags; GstDiscovererStreamInfo *sinfo; uri = gst_discoverer_info_get_uri (info); result = gst_discoverer_info_get_result (info);
在Discoverer已完成对一个URI的处理时该函数被调用,并为我们提供了包含所有信息的GstDiscovererInfo结构。
第一步是使用gst_discoverer_info_get_uri检索此调用引用的特定URI(有时会运行多个发现进程,在本示例中不是这种情况),并使用gst_discoverer_info_get_result()检索发现结果。
switch (result) { case GST_DISCOVERER_URI_INVALID: g_print ("Invalid URI '%s'\n", uri); break; case GST_DISCOVERER_ERROR: g_print ("Discoverer error: %s\n", err->message); break; case GST_DISCOVERER_TIMEOUT: g_print ("Timeout\n"); break; case GST_DISCOVERER_BUSY: g_print ("Busy\n"); break; case GST_DISCOVERER_MISSING_PLUGINS:{ const GstStructure *s; gchar *str; s = gst_discoverer_info_get_misc (info); str = gst_structure_to_string (s); g_print ("Missing plugins: %s\n", str); g_free (str); break; } case GST_DISCOVERER_OK: g_print ("Discovered '%s'\n", uri); break; } if (result != GST_DISCOVERER_OK) { g_printerr ("This URI cannot be played\n"); return; }
如代码所示,GST_DISCOVERER_OK以外的任何结果都意味着存在某种问题,并且该URI无法播放。原因可能会有所不同,但是枚举值非常明确(GST_DISCOVERER_BUSY仅在同步模式下会发生,本示例中未使用)。
如果没有发生错误,则可以使用不同的gst_discoverer_info_get_*方法(例如,例如gst_discoverer_info_get_duration)从GstDiscovererInfo结构中检索信息。
由列表组成的信息位,例如tags和stream info,需要一些额外的解析:
tags = gst_discoverer_info_get_tags (info); if (tags) { g_print ("Tags:\n"); gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (1)); }
tags是附加到媒体的元数据(标签)。可以使用gst_tag_list_foreach检查它们,该函数将为找到的每个tag调用print_tag_foreach(例如,也可以手动遍历列表,或者可以使用gst_tag_list_get_string搜索特定的tag)。print_tag_foreach的代码几乎是不言自明的。
sinfo = gst_discoverer_info_get_stream_info (info); if (!sinfo) return; g_print ("Stream information:\n"); print_topology (sinfo, 1); gst_discoverer_stream_info_unref (sinfo);
gst_discoverer_info_get_stream_info返回一个GstDiscovererStreamInfo结构,该结构在print_topology函数中进行了解析,然后被gst_discoverer_stream_info_unref丢弃。
/* Print information regarding a stream and its substreams, if any */ static void print_topology (GstDiscovererStreamInfo *info, gint depth) { GstDiscovererStreamInfo *next; if (!info) return; print_stream_info (info, depth); next = gst_discoverer_stream_info_get_next (info); if (next) { print_topology (next, depth + 1); gst_discoverer_stream_info_unref (next); } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) { GList *tmp, *streams; streams = gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO (info)); for (tmp = streams; tmp; tmp = tmp->next) { GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data; print_topology (tmpinf, depth + 1); } gst_discoverer_stream_info_list_free (streams); } }
print_stream_info函数的代码也很不言自明:它也使用print_tag_foreach打印流的功能,然后打印相关的caps。
然后,print_topology寻找下一个要显示的元素。 如果gst_discoverer_stream_info_get_next返回一个非NULL流信息,则它指向我们的后代,并且应该显示该信息。 否则,如果我们是容器,则对通过gst_discoverer_container_info_get_streams获得的每个子代递归调用print_topology。 否则,我们是最终的流,不需要递归(公认的Discoverer API的这一部分有些晦涩)。
Ref
- gst_discoverer_new
GstDiscoverer *gst_discoverer_new(GstClockTime timout, GError **error))
创建具有timeout的GstDiscoverer。
timeout以纳秒为单位,范围为1s1h(GST_SECOND3600*GST_SECOND)
- gst_discoverer_start
- gst_discoverer_discover_uri_async
- gst_discoverer_info_get_uri
- gst_discoverer_info_get_result
- gst-discoverer_info_get_misc
- gst_structure_to_string
- gst_discoverer_info_get_*
- gst_tag_list_foreach
- gst_discoverer_stream_info_get_next
- gst_discoverer_container_info_get_streams
5.讨论
本教程显示:
- 如何使用GstDiscoverer恢复有关URI的信息
- 如何通过查看使用gst_discoverer_info_get_result获得的返回码来确定URI是否可播放
这篇关于Gstreamer基础教程9: 媒体信息采集的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26Mybatis官方生成器资料详解与应用教程
- 2024-11-26Mybatis一级缓存资料详解与实战教程
- 2024-11-26Mybatis一级缓存资料详解:新手快速入门
- 2024-11-26SpringBoot3+JDK17搭建后端资料详尽教程
- 2024-11-26Springboot单体架构搭建资料:新手入门教程
- 2024-11-26Springboot单体架构搭建资料详解与实战教程
- 2024-11-26Springboot框架资料:新手入门教程
- 2024-11-26Springboot企业级开发资料入门教程
- 2024-11-26SpringBoot企业级开发资料详解与实战教程
- 2024-11-26Springboot微服务资料:新手入门全攻略