您的位置:首页 > 其它

GStreamer基础教程09——收集媒体信息

2017-01-28 13:56 656 查看

原文:https://gstreamer.freedesktop.org/documentation/tutorials/basic/index.html

译文原文:https://www.geek-share.com/detail/2606111483.html

原码:git clone git://anongit.freedesktop.org/gstreamer/gst-docs

编译方式:gcc basic-tutorial-9.c -o basic-tutorial-9 `pkg-config --cflags --libs gstreamer-1.0 gstreamer-pbutils-1.0`


Need help?

If you need help to compile this code, refer to the Building the tutorials section for your platform: Linux, Mac OS X or Windows, or use this specific command on Linux:

gcc basic-tutorial-9.c -o basic-tutorial-9 `pkg-config --cflags --libs gstreamer-1.0 gstreamer-pbutils-1.0`

If you need help to run this code, refer to the Running the tutorials section for your platform: Linux, Mac OS X or Windows.

This tutorial opens the URI passed as the first parameter in the command line (or a default URI if none is provided) and outputs information about it on the screen. If the media is located on the Internet, the application might take a bit to react depending on your connection speed.

Required libraries: 

gstreamer-pbutils-1.0
 
gstreamer-1.0


目标

      有时你需要快速的了解一个文件(或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);
1aa70

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方法。当然,如果我们是一个叶子流了,就不在需要继续递归下去了。


Conclusion


This tutorial has shown:

  • How to recover information regarding a URI using the GstDiscoverer
  • How to find out if a URI is playable by looking at the return code obtained with gst_discoverer_info_get_result().
It has been a pleasure having you here, and see you soon!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: