• GStreamer 进阶


    在前面的章节中,我们学会了怎么使用GStreamer编写一个应用程序,这节开始我们对GStreamer更高级的特性进行探索

    一些章节主要用于解释GStreamer内部的工作原理,比如涉及调度、自动插入和同步的部分,这是实际应用程序开发通常不需要的知识。其他章节会讨论更高级的管道-应用程序交互方法,这些方法对于某些应用程序非常有用。这些章节包括关于元数据、查询和事件、接口、动态参数和管道数据操作。

    进度跟踪和调整

    之前我们已经学习了,使用GStreamer创建一个多媒体处理的pipeline,不过在多媒体应用中,我们通常需要查询媒体文件的总时间、当前播放位置,以及跳转到指定的时间点。GStreamer提供了相应的接口来实现此功能,在本文中,我们将通过示例了解如何查询时间信息,以及如何进行跳转到指定位置。

    GStreamer查询机制

    GStreamer提供了GstQuery的查询机制,用于查询Element或Pad的相应信息。例如:查询当前的播放速率,产生的延迟,是否支持跳转等。可查看GstQuery文档了解所支持的类型。

    要查询所需的信息,首先需要构造一个查询的类型,然后使用Element或Pad的查询接口获取数据,最终再解析相应结果。 下面的例子介绍了如何使用GstQuery查询Pipeline的总时间:

    1. GstQuery *query = gst_query_new_duration (GST_FORMAT_TIME);
    2. gboolean res = gst_element_query (pipeline, query);
    3. if (res) {
    4. gint64 duration;
    5. gst_query_parse_duration (query, NULL, &duration);
    6. g_print ("duration = %"GST_TIME_FORMAT, GST_TIME_ARGS (duration));
    7. } else {
    8. g_print ("duration query failed...");
    9. }
    10. gst_query_unref (query);

    Metadata

    在常见的媒体文件中,通常包含一些数据(例如:歌手,专辑,编码类型等),用于描述媒体文件。通常称这些数据为元数据(Metadata:data that provides information about other data)。我们可以通过这些元数据对媒体进行归类,同时可以在播放的过程中通过界面显示。本文将介绍GStreamer是如何快速获取元数据。

    GStream将元数据分为了两类:

    • 流信息(Stream-info):用于描述流的属性。例如:编码类型,分辨率,采样率等。

            Stream-info可以通过Pipeline中所有的GstCap获取,使用方式在媒体类型与Pad中有描述,本文将不再复述。

    • 流标签(Stream-tag):用于描述非技术性的信息。例如:作者,标题,专辑等。

            Stream-tag可以通过GstBus,监听GST_MESSAGE_TAG消息,从消息中提取相应信息。
    需要注意的是,Gstreamer可能触发多次GST_MESSAGE_TAG消息,应用程序可以通过gst_tag_list_merge ()合并多个标签,再在适当的时间显示,当切换媒体文件时,需要清空缓存。
    使用此函数时,需要采用GST_TAG_MERGE_PREPEND,这样后续更新的元数据会有更高的优先级。

    Metadata reading

    可以很容易从GstPad中获取媒体信息,不过这需要访问所有的pads信息,这种方法已经在Using capabilities for metadata.中讲过,所以这里不再重复。

    可以通过GStreamer的总线读取Tag,可以通过监听GST_MESSAGE_TAG消息,然后处理他们,需要注意的是,GST_MESSAGE_TAG可能会发射多次,最好用gst_tag_list_merge ()处理一下。

    下面的demo程序展示了如何获取tags

    1. /* compile with:
    2. * gcc -o tags tags.c `pkg-config --cflags --libs gstreamer-1.0` */
    3. #include <gst/gst.h>
    4. /*
    5. 此函数用于输出一个标签的值。GStreamer会将多个标签都放在同一个GstTagList中。每一个标签可以包含多个值,所以首先通过gst_tag_list_get_tag_size ()接口及标签名(tag)获取其值的数量,然后再获取相应的值。
    6.   本例使用GValue来进行通用的处理,所以需要先判断数据的类型,再通过GValue接口获取。实际处理标签时,可以根据规范(例如ID3Tag)得到标签值的类型,直接通过GstTagList接口获取,例如:当标签名为title时,我们可以直接使用gst_tag_list_get_string()取得title的字符串,不需要再通过GValue转换,
    7. */
    8. static void
    9. print_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data)
    10. {
    11. int i, num;
    12. num = gst_tag_list_get_tag_size (list, tag);
    13. for (i = 0; i < num; ++i) {
    14. const GValue *val;
    15. /* Note: when looking for specific tags, use the gst_tag_list_get_xyz() API,
    16. * we only use the GValue approach here because it is more generic */
    17. val = gst_tag_list_get_value_index (list, tag, i);
    18. if (G_VALUE_HOLDS_STRING (val)) {
    19. g_print ("\t%20s : %s\n", tag, g_value_get_string (val));
    20. } else if (G_VALUE_HOLDS_UINT (val)) {
    21. g_print ("\t%20s : %u\n", tag, g_value_get_uint (val));
    22. } else if (G_VALUE_HOLDS_DOUBLE (val)) {
    23. g_print ("\t%20s : %g\n", tag, g_value_get_double (val));
    24. } else if (G_VALUE_HOLDS_BOOLEAN (val)) {
    25. g_print ("\t%20s : %s\n", tag,
    26. (g_value_get_boolean (val)) ? "true" : "false");
    27. } else if (GST_VALUE_HOLDS_BUFFER (val)) {
    28. GstBuffer *buf = gst_value_get_buffer (val);
    29. guint buffer_size = gst_buffer_get_size (buf);
    30. g_print ("\t%20s : buffer of size %u\n", tag, buffer_size);
    31. } else if (GST_VALUE_HOLDS_DATE_TIME (val)) {
    32. GstDateTime *dt = g_value_get_boxed (val);
    33. gchar *dt_str = gst_date_time_to_iso8601_string (dt);
    34. g_print ("\t%20s : %s\n", tag, dt_str);
    35. g_free (dt_str);
    36. } else {
    37. g_print ("\t%20s : tag of type '%s'\n", tag, G_VALUE_TYPE_NAME (val));
    38. }
    39. }
    40. }
    41. /*由于我们只需要提取相应的媒体信息,不需要关心具体的数据,所以这里使用了fakesink,fakesink会直接丢弃掉所有收到的数据。同时在此处监听了"pad-added"的信号,用于动态连接Pipeline,这种处理方式已在动态连接Pipeline中进行了详细的介绍。*/
    42. static void
    43. on_new_pad (GstElement * dec, GstPad * pad, GstElement * fakesink)
    44. {
    45. GstPad *sinkpad;
    46. sinkpad = gst_element_get_static_pad (fakesink, "sink");
    47. if (!gst_pad_is_linked (sinkpad)) {
    48. if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
    49. g_error ("Failed to link pads!");
    50. }
    51. gst_object_unref (sinkpad);
    52. }
    53. int
    54. main (int argc, char ** argv)
    55. {
    56. GstElement *pipe, *dec, *sink;
    57. GstMessage *msg;
    58. gchar *uri;
    59. gst_init (&argc, &argv);
    60. if (argc < 2)
    61. g_error ("Usage: %s FILE or URI", argv[0]);
    62. if (gst_uri_is_valid (argv[1])) {
    63. uri = g_strdup (argv[1]);
    64. } else {
    65. uri = gst_filename_to_uri (argv[1], NULL);
    66. }
    67. pipe = gst_pipeline_new ("pipeline");
    68. dec = gst_element_factory_make ("uridecodebin", NULL);
    69. g_object_set (dec, "uri", uri, NULL);
    70. gst_bin_add (GST_BIN (pipe), dec);
    71. sink = gst_element_factory_make ("fakesink", NULL);
    72. gst_bin_add (GST_BIN (pipe), sink);
    73. g_signal_connect (dec, "pad-added", G_CALLBACK (on_new_pad), sink);
    74. gst_element_set_state (pipe, GST_STATE_PAUSED);
    75. while (TRUE) {
    76. GstTagList *tags = NULL;
    77. msg = gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipe),
    78. GST_CLOCK_TIME_NONE,
    79. GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_TAG | GST_MESSAGE_ERROR);
    80. if (GST_MESSAGE_TYPE (msg) != GST_MESSAGE_TAG) /* error or async_done */
    81. break;
    82. gst_message_parse_tag (msg, &tags);
    83. g_print ("Got tags from element %s:\n", GST_OBJECT_NAME (msg->src));
    84. gst_tag_list_foreach (tags, print_one_tag, NULL);
    85. g_print ("\n");
    86. gst_tag_list_unref (tags);
    87. gst_message_unref (msg);
    88. }
    89. if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
    90. GError *err = NULL;
    91. gst_message_parse_error (msg, &err, NULL);
    92. g_printerr ("Got error: %s\n", err->message);
    93. g_error_free (err);
    94. }
    95. gst_message_unref (msg);
    96. gst_element_set_state (pipe, GST_STATE_NULL);
    97. gst_object_unref (pipe);
    98. g_free (uri);
    99. return 0;
    100. }

    Tag writing

    Tag的写入主要使用GstTagSetter

     接口,需要你的管道中的元素支持tag设置,可以通过gst_bin_iterate_all_by_interface (pipeline, GST_TYPE_TAG_SETTER).函数查看是否支持写入,

     GStreamer的标记支持中有一个很好的额外特性,就是标记被保存在管道中。 将标记包含到另一种支持标记的媒体类型中,然后将标记作为数据流的一部分处理,并将其合并到新编写的媒体文件中。

    GStreamer的时钟和同步

  • 相关阅读:
    MYSQL快速从另外一张表中更新数据
    android studio platform使用体验分享(as无法跳转c/c++等native源码的福音,强烈推荐)
    MySQL XA事务文档翻译
    java 企业工程管理系统软件源码 自主研发 工程行业适用
    利用对话式人工智能简化运营:案例研究
    【免费赠送源码】Springboot旅游信息采集管理与分享系统n2qez计算机毕业设计-课程设计-期末作业-毕设程序代做
    DzzOffice集成功能最丰富的开源PHP+MySQL办公系统套件
    lodash学习
    阿里巴巴内部纯享的这份SpringBoot+VUE全栈开发实战手册,绝了
    遥测终端赋能水库泄洪监测预警,筑牢度汛安全防线!
  • 原文地址:https://blog.csdn.net/hn_zhangkun/article/details/125528924