GStreamer基础教程之媒体信息采集

本文介绍了如何利用GStreamer的GstDiscoverer工具来同步或异步地获取URI的媒体信息,包括编码解码器、流信息、元数据和播放可行性。通过示例代码展示了如何解析媒体文件的详细信息,如持续时间、标签、流类型和编码器版本等。此外,还展示了在GStreamer中如何处理发现过程的错误和缺失插件的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目标

有时您可能想要快速找出文件(或 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,来输出检索到的信息。

这是 gst-discoverer-1.0 工具所做的简化版本(基础教程 10:GStreamer 工具),这是一个仅显示数据但不执行任何播放的应用程序。


#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) {
   // 我们来到这里是因为 Discoverer 已经完成了对一个 URI 的处理,并为我们提供了一个包含所有信息的 GstDiscovererInfo 结构。
    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)));
    //从GstDiscovererInfo*获取GstTagList*,并打印
    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");

    //从GstDiscovererInfo* 获取GstDiscovererStreamInfo 流信息。
    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 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); //gst_discoverer_new()创建一个新的 Discoverer 对象。第一个参数是每个文件的超时时间,以纳秒为单位(GST_SECOND为简单起见,使用 宏)。
    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);   //gst_discoverer_start()启动发现过程,但我们还没有提供任何要发现的 URI。接下来是这样做的:

    /* Add a request to process asynchronously the URI passed through the command line */
    if (!gst_discoverer_discover_uri_async (data.discoverer, uri)) { //gst_discoverer_discover_uri_async()将提供的 URI 排入队列以供发现。可以使用此函数将多个 URI 加入队列。当它们每个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);  //一旦我们完成了discoverer的过程,我们就会用gst_discoverer_stop()停止discoverer,然后执行g_object_unref()

    /* Free resources */
    g_object_unref (data.discoverer);
    g_main_loop_unref (data.loop);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值