目的

在《GStreamer基础教程——流》里面我们展示了如何在较差的网络条件下使用缓冲这个机制来提升用户体验。本教程在《GStreamer基础教程——流》的基础上在扩展了一下,增加了把流的内容在本地存储。并且展示了:

如何开启既看式下载

如何知道下载的是什么

如何知道在哪里下载

如何限制下载数据的总量

介绍

当播放流的时候,从网络上获得的数据被锁住之后,会创建称为future-data的一个小的缓冲区。然而,在数据播放渲染之后就会被丢弃。这就意味着,如果用户想要倒回前面去看,相应地数据仍然需要再次下载。

像YouTube一样,播放流时播放器往往会裁剪,通常会把所有下载的数据都在本地保存,还会提供一个图形化的界面来显示已经下载了多少内容。

playbin2通过DOWNLOAD标志提供了一个比较类似的功能,它会把数据在本次临时保存起来用于在播放已经下载的部分时可以保持顺畅。

代码里面同时展示了如何使用缓冲查询,它可以让你知道哪部分的文件已经可用了。

一个适应网络并在本地存储数据的例子

[objc] view
plain
 copy

  1. <span style="font-size:14px;">#include <gst/gst.h>
  2. #include <string.h>
  3. #define GRAPH_LENGTH 80
  4. /* playbin2 flags */
  5. typedef enum {
  6. << 7) /* Enable progressive download (on selected formats) */
  7. } GstPlayFlags;
  8. typedef struct _CustomData {
  9. gboolean is_live;
  10. GstElement *pipeline;
  11. GMainLoop *loop;
  12. gint buffering_level;
  13. } CustomData;
  14. static void got_location (GstObject *gstobject, GstObject *prop_object, GParamSpec *prop, gpointer data) {
  15. gchar *location;
  16. g_object_get (G_OBJECT (prop_object), "temp-location", &location, NULL);
  17. g_print ("Temporary file: %s\n", location);
  18. /* Uncomment this line to keep the temporary file after the program exits */
  19. /* g_object_set (G_OBJECT (prop_object), "temp-remove", FALSE, NULL); */
  20. }
  21. static void cb_message (GstBus *bus, GstMessage *msg, CustomData *data) {
  22. switch (GST_MESSAGE_TYPE (msg)) {
  23. case GST_MESSAGE_ERROR: {
  24. GError *err;
  25. gchar *debug;
  26. gst_message_parse_error (msg, &err, &debug);
  27. g_print ("Error: %s\n", err->message);
  28. g_error_free (err);
  29. g_free (debug);
  30. gst_element_set_state (data->pipeline, GST_STATE_READY);
  31. g_main_loop_quit (data->loop);
  32. break;
  33. }
  34. case GST_MESSAGE_EOS:
  35. /* end-of-stream */
  36. gst_element_set_state (data->pipeline, GST_STATE_READY);
  37. g_main_loop_quit (data->loop);
  38. break;
  39. case GST_MESSAGE_BUFFERING:
  40. /* If the stream is live, we do not care about buffering. */
  41. if (data->is_live) break;
  42. gst_message_parse_buffering (msg, &data->buffering_level);
  43. /* Wait until buffering is complete before start/resume playing */
  44. 00)
  45. gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
  46. else
  47. gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
  48. break;
  49. case GST_MESSAGE_CLOCK_LOST:
  50. /* Get a new clock */
  51. gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
  52. gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
  53. break;
  54. default:
  55. /* Unhandled message */
  56. break;
  57. }
  58. }
  59. static gboolean refresh_ui (CustomData *data) {
  60. GstQuery *query;
  61. gboolean result;
  62. query = gst_query_new_buffering (GST_FORMAT_PERCENT);
  63. result = gst_element_query (data->pipeline, query);
  64. if (result) {
  65. gint n_ranges, range, i;
  66. gchar graph[GRAPH_LENGTH + 1];
  67. GstFormat format = GST_FORMAT_TIME;
  68. 4 position = 0, duration = 0;
  69. memset (graph, ' ', GRAPH_LENGTH);
  70. graph[GRAPH_LENGTH] = '\0';
  71. n_ranges = gst_query_get_n_buffering_ranges (query);
  72. ; range < n_ranges; range++) {
  73. 4 start, stop;
  74. gst_query_parse_nth_buffering_range (query, range, &start, &stop);
  75. 00;
  76. 00;
  77. for (i = (gint)start; i < stop; i++)
  78. graph [i] = '-';
  79. }
  80. if (gst_element_query_position (data->pipeline, &format, &position) &&
  81. GST_CLOCK_TIME_IS_VALID (position) &&
  82. gst_element_query_duration (data->pipeline, &format, &duration) &&
  83. GST_CLOCK_TIME_IS_VALID (duration)) {
  84. ));
  85. 00 ? 'X' : '>';
  86. }
  87. g_print ("[%s]", graph);
  88. 00) {
  89. g_print (" Buffering: %3d%%", data->buffering_level);
  90. } else {
  91. g_print ("                ");
  92. }
  93. g_print ("\r");
  94. }
  95. return TRUE;
  96. }
  97. int main(int argc, charchar *argv[]) {
  98. GstElement *pipeline;
  99. GstBus *bus;
  100. GstStateChangeReturn ret;
  101. GMainLoop *main_loop;
  102. CustomData data;
  103. guint flags;
  104. /* Initialize GStreamer */
  105. gst_init (&argc, &argv);
  106. /* Initialize our data structure */
  107. , sizeof (data));
  108. 00;
  109. /* Build the pipeline */
  110. pipeline = gst_parse_launch ("playbin2 uri=http://docs.gstreamer.com/media/sintel_trailer-480p.webm", NULL);
  111. bus = gst_element_get_bus (pipeline);
  112. /* Set the download flag */
  113. g_object_get (pipeline, "flags", &flags, NULL);
  114. flags |= GST_PLAY_FLAG_DOWNLOAD;
  115. g_object_set (pipeline, "flags", flags, NULL);
  116. /* Uncomment this line to limit the amount of downloaded data */
  117. /* g_object_set (pipeline, "ring-buffer-max-size", (guint64)4000000, NULL); */
  118. /* Start playing */
  119. ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
  120. if (ret == GST_STATE_CHANGE_FAILURE) {
  121. g_printerr ("Unable to set the pipeline to the playing state.\n");
  122. gst_object_unref (pipeline);
  123. ;
  124. } else if (ret == GST_STATE_CHANGE_NO_PREROLL) {
  125. data.is_live = TRUE;
  126. }
  127. main_loop = g_main_loop_new (NULL, FALSE);
  128. data.loop = main_loop;
  129. data.pipeline = pipeline;
  130. gst_bus_add_signal_watch (bus);
  131. g_signal_connect (bus, "message", G_CALLBACK (cb_message), &data);
  132. g_signal_connect (pipeline, "deep-notify::temp-location", G_CALLBACK (got_location), NULL);
  133. /* Register a function that GLib will call every second */
  134. , (GSourceFunc)refresh_ui, &data);
  135. g_main_loop_run (main_loop);
  136. /* Free resources */
  137. g_main_loop_unref (main_loop);
  138. gst_object_unref (bus);
  139. gst_element_set_state (pipeline, GST_STATE_NULL);
  140. gst_object_unref (pipeline);
  141. g_print ("\n");
  142. ;
  143. }</span>

工作流程

这份代码是基于《GStreamer基础教程——流》里面例子的,我们仅仅看一下不同的地方即可。

创建

[objc] view
plain
 copy

  1. <span style="font-size:14px;">  /* Set the download flag */
  2. g_object_get (pipeline, "flags", &flags, NULL);
  3. flags |= GST_PLAY_FLAG_DOWNLOAD;
  4. g_object_set (pipeline, "flags", flags, NULL);</span>

通过设置这个标志,playbin2通知它内部的queue存储所有下载的数据。

[objc] view
plain
 copy

  1. <span style="font-size:14px;">g_signal_connect (pipeline, "deep-notify::temp-location", G_CALLBACK (got_location), NULL);</span>

当它们的子element属性发生变化时,playbin2就会发出deep-notify信号。在这里我们希望知道temp-location属性是什么时候变化的,了解queue2会把下载的数据存在哪里。

[objc] view
plain
 copy

  1. <span style="font-size:14px;">static void got_location (GstObject *gstobject, GstObject *prop_object, GParamSpec *prop, gpointer data) {
  2. gchar *location;
  3. g_object_get (G_OBJECT (prop_object), "temp-location", &location, NULL);
  4. g_print ("Temporary file: %s\n", location);
  5. /* Uncomment this line to keep the temporary file after the program exits */
  6. /* g_object_set (G_OBJECT (prop_object), "temp-remove", FALSE, NULL); */
  7. }</span>

这个temp-location属性是从发出信号的element那里获得的并打印出来。

当pipeline状态从PAUSED切换到READY时,这个文件会被删除。正如注释里面写的那样,你可以通过设置queue2的temp-remove属性位FALSE来保留下载的数据。

UI

在main函数里我们启动了一个1s的定时器,这样可以每秒刷新一下UI界面。

[objc] view
plain
 copy

  1. /* Register a function that GLib will call every second */
  2. , (GSourceFunc)refresh_ui, &data);

refresh_ui方法会查询pipeline来了解当前下载的数据在文件的位置以及当前播放的位置。并且用一种动画的方式在屏幕上显示出来。

[objc] view
plain
 copy

  1. [---->-------                ]

这里的'-'代表的是下载的部分,'>'代表的是当前播放的位置(当暂停的时候变成'X'位置)。当你的网络速度足够快得时候你可能会看不到下载的动画,在开始的时候就下载结束了。

[objc] view
plain
 copy

  1. static gboolean refresh_ui (CustomData *data) {
  2. GstQuery *query;
  3. gboolean result;
  4. query = gst_query_new_buffering (GST_FORMAT_PERCENT);
  5. result = gst_element_query (data->pipeline, query);

我们在refresh_ui里面做的第一件事是就是用gst_query_new_buffering()创建一个GstQuery对象,并用gst_element_query()传给playbin2。在《GStreamer基础教程04——时间管理》里面我们展示了如何用明确的方法来查询位置/播放总时间等,如果要查询更复杂一些的内容(比如缓冲),那么我们会用更通用的gst_element_query()方法。

缓冲的查询可以基于不同的GstFormat,并非所有的element都可以响应所有格式的查询,所以需要检查在pipeline里支持哪些格式。如果gst_element_query()返回TRUE,那么查询是成功的。查询的结果用GstQuery封装起来,可以用下面的方法来解析:

[objc] view
plain
 copy

  1. n_ranges = gst_query_get_n_buffering_ranges (query);
  2. ; range < n_ranges; range++) {
  3. 4 start, stop;
  4. gst_query_parse_nth_buffering_range (query, range, &start, &stop);
  5. 00;
  6. 00;
  7. for (i = (gint)start; i < stop; i++)
  8. graph [i] = '-';
  9. }

数据并不需要保证被按照顺序从头开始下载,因为跳跃播放时会让下载从一个新的地方开始。因此,gst_query_get_n_buffering_ranges()返回下载块的数目或者范围,然后我们用gst_query_parse_nth_buffering_rang()方法来解析下载块的位置和大小。

我们在调用gst_query_new_buffering()的请求会决定返回数据的格式,在这个例子里面,返回值是比例。这些查询到得数据用来绘制UI的下载动画。

[objc] view
plain
 copy

  1. if (gst_element_query_position (data->pipeline, &format, &position) &&
  2. GST_CLOCK_TIME_IS_VALID (position) &&
  3. gst_element_query_duration (data->pipeline, &format, &duration) &&
  4. GST_CLOCK_TIME_IS_VALID (duration)) {
  5. ));
  6. 00 ? 'X' : '>';
  7. }

下一步就是当前位置的查询。它也支持比例的格式,所以代码和前面应该比较类似。不过这部分目前支持不是很好,所以我们使用了时间这个格式。

当前位置使用'>'或者'X'来表示,如果缓冲不到100%,cb_message会让pipeline处于PAUSE状态,那样我们就显示'X',如果已经满了100%,那么pipeline就在PLAYING状态,我们就显示'>'。

[objc] view
plain
 copy

  1. 00) {
  2. g_print (" Buffering: %3d%%", data->buffering_level);
  3. } else {
  4. g_print ("                ");
  5. }

最后,如果缓冲时钟小于100%,我们就把这个数据显示出来。

限制下载文件的大小

[objc] view
plain
 copy

  1. /* Uncomment this line to limit the amount of downloaded data */
  2. /* g_object_set (pipeline, "ring-buffer-max-size", (guint64)4000000, NULL); */

打开139行的注释,让我们看看这个是如何做到的。缩小临时文件的大小,这样播放过的区域就会被覆盖。

【GStreamer开发】GStreamer播放教程04——既看式流的更多相关文章

  1. IE开发人员工具教程

    写在前面 一直非常谷歌的控制台,因为我是做前端的,谷歌浏览器在我看来是解析JS最快的浏览器,所谓的熟能生巧,用熟悉了谷歌浏览器之后就特别喜欢用谷歌的控制台调试脚本.改变样式.查看HTML.查看资源加载 ...

  2. 【GStreamer开发】GStreamer基础教程13——播放速度

    目标 快进,倒放和慢放是trick模式的共同技巧,它们有一个共同点就是它们都修改了播放的速度.本教程会展示如何来获得这些效果和如何进行逐帧的跳跃.主要内容是: 如何来变换播放的速度,变快或者变慢,前进 ...

  3. 【GStreamer开发】GStreamer基础教程04——时间管理

    目标 本教程主要讲述一些和时间相关的内容.主要包括: 1. 如何问pipeline查询到流的总时间和当前播放的时间 2. 如何在流内部实现跳转功能 介绍 GstQuery是向一个element或者pa ...

  4. 【GStreamer开发】GStreamer播放教程01——playbin2的使用

    目标 我们前面已经使用过了playbin2这个element,它可以让我们做的很少而实现很多.本教程会展示当这个element的默认设置在一些特殊情形下不符合我们的需求是可以做的一些深度定制,我们会看 ...

  5. 【GStreamer开发】GStreamer基础教程05——集成GUI工具

    目标 本教程展示了如何在GStreamer集成一个GUI(比如:GTK+).最基本的原则是GStreamer处理多媒体的播放而GUI处理和用户的交互. 在这个教程里面,我们可以学到: 如何告诉GStr ...

  6. gstreamer应用开发(播放器)之旅

    GStreamer开发,主要分为两块:应用开发.插件开发. 插件开发人员,通常是编解码库的作者(做出了编解码库后,希望gstreamer能用起来这个库,因此增加这个适配层).芯片原厂人员(将自家的hw ...

  7. 【转】一步一步教你在Ubuntu12.04搭建gstreamer开发环境

    原文网址:http://blog.csdn.net/xsl1990/article/details/8333062 闲得蛋疼    无聊寂寞冷    随便写写弄弄 看到网上蛮多搭建gstreamer开 ...

  8. 安装gstreamer开发环境

    ubuntu中安装gstreamer开发环境: * 安装gstreamer基本库,工具,以及插件 sudo apt--dev gstreamer-tools gstreamer0.-tools gst ...

  9. C#开发Unity游戏教程循环遍历做出判断及Unity游戏示例

    C#开发Unity游戏教程循环遍历做出判断及Unity游戏示例 Unity中循环遍历每个数据,并做出判断 很多时候,游戏在玩家做出判断以后,游戏程序会遍历玩家身上大量的所需数据,然后做出判断,即首先判 ...

随机推荐

  1. cf 1051F 树+图

    $des$给定一张 $n$ 个点 $m$ 条边的带权无向联通图,$q$ 次询问,每次询问 $u_i$ 到 $v_i$ 的最短路长度.$n,q <= 10^5, m - n <= 20$ $ ...

  2. 2017.10.1 国庆清北 D1T2 两个逗比捉迷藏

    题目描述 你是能看到第二题的friends呢. ——laekov Hja和Yjq在玩捉迷藏.Yjq躲了起来,Hja要找他.在他们玩游戏的房间里,只有一堵不透明的墙和一个双面的镜子.Hja和Yjq可以看 ...

  3. 洛谷P3522 TEM-temperature

    题目 单调队列+阅读理解 简化题意. 找到一个最长的区间使得区间每个点的r要大于该点之前的点的l. 然后可以用单调队列维护单调递减的l.最后尺取法O(n)枚举所有区间并取最大值. 单调队列可以快速找某 ...

  4. UOJ310. 【UNR #2】黎明前的巧克力 [FWT]

    UOJ 思路 显然可以转化一下,变成统计异或起来等于0的集合个数,这样一个集合的贡献是\(2^{|S|}\). 考虑朴素的\(dp_{i,j}\)表示前\(i\)个数凑出了\(j\)的方案数,发现这其 ...

  5. SSM ehcache 配置 mapper 文件出错

    异常 十二月 26, 2017 1:44:49 下午 org.apache.tomcat.util.digester.SetPropertiesRule begin 警告: [SetPropertie ...

  6. phpstrom php出现404

    phpstrom搭配xampp用着很舒服,但是配置不小心容易出现404错误,解决并不难,几步就完成了 !第一步:依次选择Tools-Deployment-configuration进入后如果为空,点击 ...

  7. js中的那些遍历

    说到遍历,首先想到的是数组的遍历,方法不要太多,比如 for, forEach,map,filter,every,some等 下面来看下,用法 首先 定义一个数组: 1. for循环,需要知道数组的长 ...

  8. 无法将“Scaffold-DbContext”项识别为 cmdlet、函数、脚本文件或可运行程序的名称...

    原文链接:https://my.oschina.net/taadis/blog/889560 为什么80%的码农都做不了架构师?>>>     PM> Scaffold-DbC ...

  9. session设置存活时间的三种方式

    在web容器中设置(此处以tomcat为例)在tomcat-5.0.28\conf\web.xml中设置,以下是tomcat 5.0中的默认配置: [html] view plain copy < ...

  10. 如何提高工具开发和数据分析的效率?| jupyter | Rstudio server

    这部分是超级干货,也能直接体现一个开发分析者的能力. 主要分为两部分: 1. 面对新问题时,如何高效的分析和开发? 2. 面对相似的问题时,如何最快时间的利用之前的开发经验? 因为现在我主要用shel ...