GStreamer基础教程09——收集媒体信息
2014-03-18 10:58
513 查看
目标
有时你需要快速的了解一个文件(或URI)包含的媒体格式或者看看是否支持这种格式。当然你可以创建一个pipeline,设置运行,观察总线上的消息,但GStreamer提供了一个工具可以帮你做这些。本教程主要讲述:
如何获得一个URI上的信息
如何确定一个URI是可以播放的
介绍
GstDiscover是一个在pbutils库提供的工具,接受输入URI或者URI列表,返回它们的信息。这个工具可以工作在同步或者异步模式下。
在同步模式下,只有一个API可以用,就是gst_discoverer_discover_uri(),这个API会阻塞线程直到得到需要的信息。因为阻塞会带来GUI上很糟糕的体验,所以基于UI的应用主要使用异步模式,本教程也是基于异步模式的。
检测到的信息会包括codec的描述,流的拓扑结构和可以获得的元数据。
这是gst-discoverer工具使用的简单版本,这个应用仅仅显示数据,甚至不提供播放功能。
GStreamer Discoverer
工作流程
下面是使用GstDiscoverer的主要步骤。
像平常一样对必要的的信号注册回调,我们在他们的回调里面继续讨论。
GLib已经在运行主循环了,我们在on_finished_cb回调中调用g_main_loop_quit()来退出主循环。
现在让我们来看一下我们注册的回调函数:
当我们运行到这些代码时,discoverer已经完成了一个URI的检查了,用GstDiscovererInfo这个数据结构把内容传给我们。
这里第一步是用gst_discoverer_info_get_uri()方法去获得调用这个回调的URI(因为可能存在多个URI),然后通过gst_discoverer_info_get_result()方法来获得数据。
如果没有发生错误,那么可以用gst_discoverer_info_get_*系列方法从GstDiscovererInfo这个结构里面获得数据了。比如,用gst_discoverer_info_get_duration()方法可以获得播放总长度。
信息的位是由列表组成的,就像标签和流信息,需要再解析一下:
print_topology方法会寻找下一个流来显示。如果get_discoverer_stream_info_get_next()返回一个非空流信息的话,它需要被显示。另一方面,如果我们是一个容器,我们每一个用get_discoverer_container_info_get_streams()方法获得的子流都需要递归的调用print_topology方法。当然,如果我们是一个叶子流了,就不在需要继续递归下去了。
有时你需要快速的了解一个文件(或URI)包含的媒体格式或者看看是否支持这种格式。当然你可以创建一个pipeline,设置运行,观察总线上的消息,但GStreamer提供了一个工具可以帮你做这些。本教程主要讲述:
如何获得一个URI上的信息
如何确定一个URI是可以播放的
介绍
GstDiscover是一个在pbutils库提供的工具,接受输入URI或者URI列表,返回它们的信息。这个工具可以工作在同步或者异步模式下。
在同步模式下,只有一个API可以用,就是gst_discoverer_discover_uri(),这个API会阻塞线程直到得到需要的信息。因为阻塞会带来GUI上很糟糕的体验,所以基于UI的应用主要使用异步模式,本教程也是基于异步模式的。
检测到的信息会包括codec的描述,流的拓扑结构和可以获得的元数据。
As an example, this is the result of discovering http://docs.gstreamer.com/media/sintel_trailer-480p.webm (Click to expand) Duration: 0:00:52.250000000 Tags: video codec: On2 VP8 language code: en container format: Matroska application name: ffmpeg2theora-0.24 encoder: Xiph.Org libVorbis I 20090709 encoder version: 0 audio codec: Vorbis nominal bitrate: 80000 bitrate: 80000 Seekable: yes Stream information: container: WebM audio: Vorbis Tags: language code: en container format: Matroska audio codec: Vorbis application name: ffmpeg2theora-0.24 encoder: Xiph.Org libVorbis I 20090709 encoder version: 0 nominal bitrate: 80000 bitrate: 80000 video: VP8 Tags: video codec: VP8 video container format: Matroska下面的代码试着通过命令行的方式来运行discover工具来解析URI的内容,输出获得的信息。
这是gst-discoverer工具使用的简单版本,这个应用仅仅显示数据,甚至不提供播放功能。
GStreamer Discoverer
#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 = "http://docs.gstreamer.com/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; }
工作流程
下面是使用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_discover_new()会创建一个Discoverer对象,第一个参数是超时时间(使用纳秒做单位)。
/* 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);gst_discover_start()启动检查进程,但我们还没有提供URI,下面就是提供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入队。在检查过程把所有的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);一旦我们检查结束,我们会调用gst_discoverer_stop()来停止,并且调用g_object_unref()来释放资源。
现在让我们来看一下我们注册的回调函数:
/* 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(因为可能存在多个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_*系列方法从GstDiscovererInfo这个结构里面获得数据了。比如,用gst_discoverer_info_get_duration()方法可以获得播放总长度。
信息的位是由列表组成的,就像标签和流信息,需要再解析一下:
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)); }标签是附着在媒体上得元数据。它们可以用gst_tag_list_foreach()方法来检查,每检查到一个标签就调用一次print_tag_foreach()。至于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方法来打印流的capabilities和相关联的tags。
print_topology方法会寻找下一个流来显示。如果get_discoverer_stream_info_get_next()返回一个非空流信息的话,它需要被显示。另一方面,如果我们是一个容器,我们每一个用get_discoverer_container_info_get_streams()方法获得的子流都需要递归的调用print_topology方法。当然,如果我们是一个叶子流了,就不在需要继续递归下去了。
相关文章推荐
- 【GStreamer开发】GStreamer基础教程09——收集媒体信息
- GStreamer基础教程09——收集媒体信息
- GStreamer基础教程04——时间管理
- CSS基础教程 -- 媒体查询屏幕适配
- 【GStreamer开发】GStreamer基础教程07——多线程和Pad的有效性
- [转]Ultra Fractal教程系列17——学习基础技巧09——同步颜色并保存图案
- Hibernate搭建教程以及基础信息介绍
- 【GStreamer开发】GStreamer基础教程08——pipeline的快捷访问
- 用percona-toolkit为MySQL收集系统和性能信息的教程
- 收集Oracle数据库中的SQL基线信息(一)基础信息收集
- GStreamer基础教程06——媒体格式和pad的Capabilities
- GStreamer基础教程01——Hello World
- 被动信息收集1——DNS基础 + DNS解析工具 NSLOOKUP使用
- 黑客教程三:扫描和信息收集
- GStreamer基础教程05——集成GUI工具
- GStreamer基础教程10——GStreamer工具
- Android基础教程(四)之------获取手机联系人信息
- GStreamer基础教程
- 1.10 基础知识——GP3.1 制度化 & GP3.2 收集改进信息
- 网络犯罪分子通过社交媒体收集企业敏感信息