GStreamer Tutorial 中文翻译:Basic tutorial 9: Media information gathering

GStreamer Tutorial 9中文翻译



前言

由于工作原因,用的GStreamer的图像解码库,所以记录GStreamer Tutorial 的中文翻译和个人的理解以便学习。若有不足请多指教。侵删。


Basic tutorial 9: Media information gathering

目的

有时,您可能希望快速查明文件(或URI)包含何种类型的媒体,或者是否能够播放这些媒体。您可以构建管道,将其设置为运行,并监视总线消息,但是GStreamer有一个实用程序可以为您做到这一点。本教程展示了:

  • 如何恢复有关URI的信息;
  • 如何确定一个URI是否可播放。

简介

GstDiscoverer是在pbutils库(插件基础实用程序)中找到的实用程序对象,它接受URI或URI列表,并返回关于它们的信息。它可以在同步或异步模式下工作。
在同步模式下,只需要调用一个函数gst_discoverer_discover_uri(),它会阻塞直到信息准备好。由于这种阻塞,基于gui的应用程序通常不太使用,因此会使用异步模式,如本教程所述。
恢复的信息包括编解码器描述、流拓扑(流和子流的数量)和可用的元数据(如音频语言)。
例如,这是发现https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm的结果

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

下面的代码尝试发现通过命令行提供的URI,并输出检索到的信息(如果没有提供URI,则使用默认的URI)。
这是gst-discoverer-1.0工具所做事情的简化版本(基础教程10:GStreamer工具),它是一个只显示数据,但不执行任何回放的应用程序.

The GStreamer Discoverer

将代码复制到basic-tutorial-9.c中,或在你的GStreamer路径中找到它。
basic-tutorial-9.c

gcc basic-tutorial-9.c -o basic-tutorial-9 `pkg-config --cflags --libs gstreamer-1.0 gstreamer-pbutils-1.0`
#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 custom 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的主要步骤:

/* Create the elements */
/* 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);

像往常一样,连接感兴趣的信号。我们将在代码片段中讨论它们的回调。

/* Start the discoverer process (nothing to do yet) */
gst_discoverer_start (data.discoverer);

gst_discoverer_start()启动Discoverer进程,但是我们还没有提供任何用于发现的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()取消它。
现在让我们回顾一下我们已经注册的回调函数:

/* 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 = 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(还可以手动遍历列表,或者使用gst_tag_list_get_string()搜索特定的标记)。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()返回一个在print_topology函数中解析的GstDiscovererStreamInfo结构,然后使用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()返回一个非空的流信息,它引用我们的后代,应该显示出来。否则,如果我们是一个容器,则递归地调用print_topology对通过gst_discoverer_container_info_get_streams()获得的每个子容器。否则,我们就是一个最终的流,不需要递归(Discoverer API的这一部分确实有点晦涩)。

结论

这个例程展示了:

  • 如何使用GstDiscoverer恢复URI信息;
  • 如何通过查看通过gst_discoverer_info_get_result()获得的返回代码来确定URI是否可以播放。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值