GStreamer —— 2.9、Windows下Qt加载GStreamer库后运行 - “教程9:媒体信息收集“(附:完整源码)
简介
上一个教程演示了多线程和 Pad 可用性。本教程介绍媒体信息收集。有时,您可能希望快速找出文件的媒体类型 (或 URI)包含媒体,或者您是否能够播放媒体。你 可以构建一个管道,将其设置为 Run,并监视总线消息,但 GStreamer 有一个实用程序可以为您执行此作。本教程 显示:
• 如何恢复有关 URI 的信息
• 如何确定 URI 是否可播放
GstDiscoverer是在库中找到的实用程序对象 (插件基础实用程序),它接受 URI 或 URI 列表,并返回 关于他们的信息。它可以在同步或异步模式下工作 模式。在同步模式下,只有一个函数可以调用,该函数会阻塞,直到信息为 准备。由于这种阻塞,它通常不太有趣 使用基于 GUI 的应用程序和异步模式,如前所述 在本教程中。gst_discoverer_discover_uri()恢复的信息包括编解码器描述、流拓扑 (流和子流的数量)和可用的元数据(如 audio language) 的 Interface。例如,这是结果发现 https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm
GStreamer相关运行库
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include/gstreamer-1.0/gst
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include/gstreamer-1.0
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/include/glib-2.0
INCLUDEPATH += D:/Software/GStreamer/1.0/mingw_x86_64/lib/glib-2.0/include
LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/gstreamer-1.0.lib
LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/glib-2.0.lib
LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/gobject-2.0.lib
LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/gstaudio-1.0.lib
LIBS += D:/Software/GStreamer/1.0/mingw_x86_64/lib/gstpbutils-1.0.lib
完整源码
#include <string.h>
#include <gst/gst.h>
#include <gst/pbutils/pbutils.h>
typedef struct _CustomData
{
GstDiscoverer *discoverer;
GMainLoop *loop;
} CustomData;
/* 以人类可读的格式打印标签(名称:值) */
static void print_tag_foreach (const GstTagList *tags, const gchar *tag, gpointer user_data)
{
GValue val = { 0, };
gst_tag_list_copy_value (&val, tags, tag); /* 将给定标签的内容复制到值 如果多个值关联,则将多个值合并为一个值 替换为标签。 使用后必须g_value_unset该值。 */
gchar *str;
if (G_VALUE_HOLDS_STRING (&val))
str = g_value_dup_string (&val);
else
str = gst_value_serialize (&val);
gint depth = GPOINTER_TO_INT (user_data);
g_print ("%*s%s: %s\n", 2 * depth, " ", gst_tag_get_nick (tag), str);
g_free (str);
g_value_unset (&val);
}
/* 打印有关流的信息 */
static void print_stream_info (GstDiscovererStreamInfo *info, gint depth)
{
gchar *desc = NULL;
GstCaps *caps = gst_discoverer_stream_info_get_caps (info);
if (caps)
{
/*
* 修复 GstCap 只描述一种格式,也就是说,它们恰好具有 一个结构,结构中的每个字段都描述一个固定类型。 非固定类型的示例包括 GST_TYPE_INT_RANGE 和 GST_TYPE_LIST。
如果上限固定,则为 TRUE
*/
if (gst_caps_is_fixed (caps))
{
/* 返回一个本地化的(尽可能可能的)字符串,描述 以大写字母指定的媒体格式,用于错误对话框或其他消息 以便用户看到。除非 caps 无效,否则不应返回 NULL。 */
desc = gst_pb_utils_get_codec_description (caps);
}
else
{
/* 将 Cap 转换为 String 表示形式。此字符串表示 可以通过 gst_caps_from_string 转换回 GstCap。 */
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;
}
const GstTagList *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));
}
}
/* 打印有关流及其子流的信息(如果有的话) */
static void print_topology (GstDiscovererStreamInfo *info, gint depth)
{
if (!info){ return; }
print_stream_info (info, depth);
GstDiscovererStreamInfo *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 *streams = gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO (info));
for (GList *tmp = streams; tmp; tmp = tmp->next)
{
GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data;
print_topology (tmpinf, depth + 1);
}
gst_discoverer_stream_info_list_free (streams);
}
}
/* 每当发现者有关于我们提供的URI之一的信息时,就会调用此函数。 */
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; }
/* 如果没有错误,显示检索到的信息 */
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");
}
/* 当发现者检查完我们提供的所有URI后,将调用此函数。.*/
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)
{
gchar *uri = (gchar *)("https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm");
if (argc > 1) { uri = argv[1]; }
CustomData data;
memset (&data, 0, sizeof (data));
/* 初始化GStreamer */
gst_init (&argc, &argv);
g_print ("Discovering '%s'\n", uri);
/* 实例化发现者 */
GError *err = NULL;
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;
}
/* 连接到有趣的信号 */
g_signal_connect (data.discoverer, "discovered", G_CALLBACK (on_discovered_cb), &data);
g_signal_connect (data.discoverer, "finished", G_CALLBACK (on_finished_cb), &data);
/* 启动发现者进程(目前还没什么可做的) */
gst_discoverer_start (data.discoverer);
/* 添加请求以异步处理通过命令行传递的URI */
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;
}
/* 创建一个GLib主循环并将其设置为运行,这样我们就可以等待信号了 */
data.loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (data.loop);
/* 停止发现者进程 */
gst_discoverer_stop (data.discoverer);
/* 释放资源 */
g_object_unref (data.discoverer);
g_main_loop_unref (data.loop);
return 0;
}
关注
笔者 - jxd