【GStreamer开发】GStreamer基础教程09——收集媒体信息
目标
有时你需要快速的了解一个文件(或URI)包含的媒体格式或者看看是否支持这种格式。当然你可以创建一个pipeline,设置运行,观察总线上的消息,但GStreamer提供了一个工具可以帮你做这些。本教程主要讲述:
如何获得一个URI上的信息
如何确定一个URI是可以播放的
介绍
GstDiscover是一个在pbutils库提供的工具,接受输入URI或者URI列表,返回它们的信息。这个工具可以工作在同步或者异步模式下。
在同步模式下,只有一个API可以用,就是gst_discoverer_discover_uri(),这个API会阻塞线程直到得到需要的信息。因为阻塞会带来GUI上很糟糕的体验,所以基于UI的应用主要使用异步模式,本教程也是基于异步模式的。
检测到的信息会包括codec的描述,流的拓扑结构和可以获得的元数据。
- <span style="font-size:14px;">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</span>
下面的代码试着通过命令行的方式来运行discover工具来解析URI的内容,输出获得的信息。
这是gst-discoverer工具使用的简单版本,这个应用仅仅显示数据,甚至不提供播放功能。
GStreamer Discoverer
- <span style="font-size:14px;">#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;
- }</span>
工作流程
下面是使用GstDiscoverer的主要步骤。
- <span style="font-size:14px;"> /* Instantiate the Discoverer */
- 5 * GST_SECOND, &err);
- if (!data.discoverer) {
- g_print ("Error creating discoverer instance: %s\n", err->message);
- g_clear_error (&err);
- ;
- }</span>
gst_discover_new()会创建一个Discoverer对象,第一个参数是超时时间(使用纳秒做单位)。
- <span style="font-size:14px;"> /* 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);</span>
像平常一样对必要的的信号注册回调,我们在他们的回调里面继续讨论。
- <span style="font-size:14px;"> /* Start the discoverer process (nothing to do yet) */
- gst_discoverer_start (data.discoverer);</span>
gst_discover_start()启动检查进程,但我们还没有提供URI,下面就是提供URI了:
- <span style="font-size:14px;"> /* 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);
- ;
- }</span>
gst_discoverer_discover_uri_async()会把需要检查的URI放入队列,这个函数支持把多个URI入队。在检查过程把所有的URI都检查过之后,会调用注册的回调函数。
- <span style="font-size:14px;"> /* 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);</span>
GLib已经在运行主循环了,我们在on_finished_cb回调中调用g_main_loop_quit()来退出主循环。
- <span style="font-size:14px;"> /* Stop the discoverer process */
- gst_discoverer_stop (data.discoverer);</span>
一旦我们检查结束,我们会调用gst_discoverer_stop()来停止,并且调用g_object_unref()来释放资源。
现在让我们来看一下我们注册的回调函数:
- <span style="font-size:14px;">/* 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);</span>
当我们运行到这些代码时,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()方法来检查,每检查到一个标签就调用一次print_tag_foreach()。至于print_tag_foreach()方法的代码比较浅显,可以直接读懂。
- sinfo = gst_discoverer_info_get_stream_info (info);
- if (!sinfo)
- return;
- g_print ("Stream information:\n");
- );
- 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) {
- );
- 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;
- );
- }
- 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基础教程06 - 获取媒体信息
摘要 在常见的媒体文件中,通常包含一些数据(例如:歌手,专辑,编码类型等),用于描述媒体文件.通常称这些数据为元数据(Metadata:data that provides information a ...
- Android程序开发0基础教程(一)
程序猿学英语就上视觉英语网 Android程序开发0基础教程(一) 平台简单介绍 令人激动的Google手机操作系统平台-Android在2007年11月13日正式公布了,这是一个开放源码的操 ...
- GStreamer基础教程09 - Appsrc及Appsink
摘要 在我们前面的文章中,我们的Pipline都是使用GStreamer自带的插件去产生/消费数据.在实际的情况中,我们的数据源可能没有相应的gstreamer插件,但我们又需要将数据发送到GStre ...
- iOS开发零基础教程之生成git所需的SSH keys
在我们github看到了一个不错的第三方库时,可能我们想把他git clone到本地,我们需要复制他的SSH URL,如下图: 复制完地址之后,我们需要打开终端,然后输入命令: git clone + ...
- GStreamer基础教程12 - 常用命令工具
摘要 GStreamer提供了不同的命令行工具用于快速的查看信息以及验证Pipeline的是否能够正确运行,在平时的开发过程中,我们也优先使用GStreamer的命令行工具验证,再将Pipeline集 ...
- Chrome扩展开发基础教程(附HelloWorld)
1 概述 Chrome扩展开发的基础教程,代码基于原生JS+H5,教程内容基于谷歌扩展开发官方文档. 2 环境 Chrome 88.0.4324.96 Chromium 87.0.4280.141 B ...
- 【GStreamer开发】GStreamer基础教程14——常用的element
目标 本教程给出了一系列开发中常用的element.它们包括大杂烩般的eleemnt(比如playbin2)以及一些调试时很有用的element. 简单来说,下面用gst-launch这个工具给出一个 ...
- 【GStreamer开发】GStreamer基础教程13——播放速度
目标 快进,倒放和慢放是trick模式的共同技巧,它们有一个共同点就是它们都修改了播放的速度.本教程会展示如何来获得这些效果和如何进行逐帧的跳跃.主要内容是: 如何来变换播放的速度,变快或者变慢,前进 ...
- 【GStreamer开发】GStreamer基础教程10——GStreamer工具
目标 GStreamer提供了一系列方便使用的工具.这篇教程里不牵涉任何代码,但还是会讲一些有用的内容: 如何在命令行下建立一个pipeline--完全不使用C 如何找出一个element的Capab ...
随机推荐
- 【angularJS】学习笔记
一.一个html中多个ng-app //对于ng-app初始化一个AngularJS程序属性的使用需要注意,在一个页面中AngularJS自动加载第一个ng-app,其他ng-app会忽略 //如果需 ...
- 关于size
关于size它确实可以帮人算内存 但是: 在不会用到整个数组(尤其是在状压的时候) 不要用它,它只能算你申请了多少内存,但算不了会用多少!!! and 有人能告诉我,交题前不好好看看交的哪份代码是什么 ...
- Subspace Subcode
子码(subcode)的概念来自信息编码,不太容易理解.通常是子域编码(subfield subcode),也可以扩展到子空间编码(subspace subcode). 子空间或者子域编码的一个基本想 ...
- beego-vue URL重定向(beego和vue前后端分离开发,beego承载vue前端分离页面部署)
具体过程就不说,是搞这个的自然会动,只把关键代码贴出来. beego和vue前后端分离开发,beego承载vue前端分离页面部署 // landv.cnblogs.com //没有授权转载我的内容,再 ...
- Java SpringBoot使用126邮箱发送html内容邮件,带附件
package mail.demo; import org.junit.Test; import org.junit.runner.RunWith; import org.springframewor ...
- JavaScript substr() 方法
定义和用法 substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符. 语法 stringObject.substr(start,length) 参数 描述 start 必需.要 ...
- Git的使用(3) —— 远程版本库的操作(GitHub)
1. 配置SSH (1) GitHub 登陆GitHub后,点击右上角头像,选择 Setting . 在左面栏目中选择"SSH and GPG keys". 打开生成的SSH公钥文 ...
- De1ctf - shell shell shell记录
虽然是N1CTF原题,但是自己没遇见过,还是做的题少,记录一下吧== 1.源码泄露,直接可以下到所有源码,然后代码审计到一处insert型注入: 这里直接带入insert里面,跟进去看看 insert ...
- 2018-2019-2 《网络对抗技术》Exp7 网络欺诈防范 20165326
网络欺诈防范 实践内容 本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法.具体实践有 简单应用SET工具建立冒名网站 ettercap DNS spoof 结合应用两种技术, ...
- sem_init重复调用引发sem_wait线程无法被唤醒
问题 一段老代码,两个线程,一个线程调用sem_wait等待信号量,另外一个线程在某失败分支会调用sem_init清信号量,结果导致sem_wait线程无法被唤醒: 分析 Linux manpage ...