GStreamer基础教程12——流
来源:互联网 发布:linux java 打包war 编辑:程序博客网 时间:2024/05/22 05:32
目标
直接播放Internet上的文件而不在本地保存就被称为流播放。我们在前面教程里已经这样做过了,使用了http://的URL。本教程展示的是在播放流的时候需要记住的几个点,特别是:
如何设置缓冲
如何从打断中恢复(因为失去了时钟)
介绍
当在播放流的时候,一旦从网络上取到媒体数据块就会进行解码和放入显示队列。这意味着如果网络来的数据延迟了,那么显示队列就可能没有数据,播放就会停下来。
解决这个问题的办法是建立缓冲,这就是说,在开始播放前允许队列里已经存储了一些数据。这样的话,播放虽然晚了一点开始,但如果网络有什么延时,那么还有一定的缓冲数据可以播放。
这个方案已经在GStreamer里面实现了,但前面的教程中没有涉及到这个方面。有些element,像在playbin2里面用到的queue2和multiqueue,都可以建立自己的缓冲然后根据缓冲的等级发送消息到总线上。一个应用如果希望能更好的适应各种网络环境,那么就该关注这些消息,当缓冲等级低到一定程度时就要暂停播放。
为了在多个sink中同步,我们使用了一个全局的时钟。这个时钟是GStreamer在所有的可以提供时钟的element中选出来的。在某些情况下,例如,一个RTP资源切换流或者更换输出设备,那么时钟就可能丢失。这时就需要重新建立一个时钟,这个过程在本教程会解释一下。
当时钟丢失的时候,应用会从总线上得到一个消息。要建立一个新的时钟,应用仅仅把pipeline设置到PAUSED状态然后重新置成PLAYING即可。
一个适应网络的例子
#include <gst/gst.h>#include <string.h> typedef struct _CustomData { gboolean is_live; GstElement *pipeline; GMainLoop *loop;} CustomData; static void cb_message (GstBus *bus, GstMessage *msg, CustomData *data) { switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_ERROR: { GError *err; gchar *debug; gst_message_parse_error (msg, &err, &debug); g_print ("Error: %s\n", err->message); g_error_free (err); g_free (debug); gst_element_set_state (data->pipeline, GST_STATE_READY); g_main_loop_quit (data->loop); break; } case GST_MESSAGE_EOS: /* end-of-stream */ gst_element_set_state (data->pipeline, GST_STATE_READY); g_main_loop_quit (data->loop); break; case GST_MESSAGE_BUFFERING: { gint percent = 0; /* If the stream is live, we do not care about buffering. */ if (data->is_live) break; gst_message_parse_buffering (msg, &percent); g_print ("Buffering (%3d%%)\r", percent); /* Wait until buffering is complete before start/resume playing */ if (percent < 100) gst_element_set_state (data->pipeline, GST_STATE_PAUSED); else gst_element_set_state (data->pipeline, GST_STATE_PLAYING); break; } case GST_MESSAGE_CLOCK_LOST: /* Get a new clock */ gst_element_set_state (data->pipeline, GST_STATE_PAUSED); gst_element_set_state (data->pipeline, GST_STATE_PLAYING); break; default: /* Unhandled message */ break; }} int main(int argc, char *argv[]) { GstElement *pipeline; GstBus *bus; GstStateChangeReturn ret; GMainLoop *main_loop; CustomData data; /* Initialize GStreamer */ gst_init (&argc, &argv); /* Initialize our data structure */ memset (&data, 0, sizeof (data)); /* Build the pipeline */ pipeline = gst_parse_launch ("playbin2 uri=http://docs.gstreamer.com/media/sintel_trailer-480p.webm", NULL); bus = gst_element_get_bus (pipeline); /* Start playing */ ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); if (ret == GST_STATE_CHANGE_FAILURE) { g_printerr ("Unable to set the pipeline to the playing state.\n"); gst_object_unref (pipeline); return -1; } else if (ret == GST_STATE_CHANGE_NO_PREROLL) { data.is_live = TRUE; } main_loop = g_main_loop_new (NULL, FALSE); data.loop = main_loop; data.pipeline = pipeline; gst_bus_add_signal_watch (bus); g_signal_connect (bus, "message", G_CALLBACK (cb_message), &data); g_main_loop_run (main_loop); /* Free resources */ g_main_loop_unref (main_loop); gst_object_unref (bus); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline); return 0;}
工作流程
这个例子中唯一特殊的是对特定消息的相互作用, 因此,初始化代码非常简单清晰。唯一新加的一点就是对实时流的检测:
/* Start playing */ ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); if (ret == GST_STATE_CHANGE_FAILURE) { g_printerr ("Unable to set the pipeline to the playing state.\n"); gst_object_unref (pipeline); return -1; } else if (ret == GST_STATE_CHANGE_NO_PREROLL) { data.is_live = TRUE; }
实时流是不能暂停的,所以在PAUSED状态的行为和PLAYING状态是一样的。在设置实时流到PAUSED成功后,会返回GST_STATE_CHANGE_NO_PREROLL,而不是平常的GST_STATE_CHANGE_SUCCESS。因为状态的变化是渐变的(从NULL到READY,从PAUSED到PLAYING),所以我们把pipeline设置到PLAYING状态,也会收到NO_PROROLL这个返回值。
我们关注实时流是因为我们希望可以关闭缓冲,所以我们一直在关注gst_element_set_state()的返回值并根据这个值来设置is_live变量。
现在我们看一下消息解析的回调函数里的关键部分:
case GST_MESSAGE_BUFFERING: { gint percent = 0; /* If the stream is live, we do not care about buffering. */ if (data->is_live) break; gst_message_parse_buffering (msg, &percent); g_print ("Buffering (%3d%%)\r", percent); /* Wait until buffering is complete before start/resume playing */ if (percent < 100) gst_element_set_state (data->pipeline, GST_STATE_PAUSED); else gst_element_set_state (data->pipeline, GST_STATE_PLAYING); break; }首先,如果是一个实时源,就不用关心这个缓冲消息。
我们使用gst_message_parse_buffering()来解析缓冲消息,从而获得缓冲等级。
其次,我们在缓冲等级小于100%时把pipeline设置成PAUSED状态,并把消息在调试区域打印出来;反之,我们就把pipeline设置成PLAYING状态。
在启动的时候,我们会看见在播放之前缓冲等级上升到100%,这就是我们希望达到的。如果在后面,网络变慢了或者失去响应,我们的缓冲也耗光了,我们会收到新的缓冲消息告诉我们缓冲等级已经低于100%,我们就把pipeline设置成PAUSED知道重新获得足够的数据。
case GST_MESSAGE_CLOCK_LOST: /* Get a new clock */ gst_element_set_state (data->pipeline, GST_STATE_PAUSED); gst_element_set_state (data->pipeline, GST_STATE_PLAYING); break;对于丢失时钟这个网络问题,我们简单地把pipeline设置成PAUSED状态然后在切换到PLAYING,这样一个新的时钟会被选择上,等待收到新的媒体数据。
- 【GStreamer开发】GStreamer基础教程12——流
- GStreamer基础教程12——流
- GStreamer基础教程12——流
- GStreamer基础教程02——GStreamer概念
- GStreamer基础教程10——GStreamer工具
- GStreamer基础教程02——GStreamer概念
- GStreamer基础教程02——GStreamer概念
- GStreamer基础教程10——GStreamer工具
- GStreamer基础教程02——GStreamer概念
- GStreamer基础教程10——GStreamer工具
- GStreamer基础教程02——GStreamer概念
- GStreamer基础教程10——GStreamer工具
- GStreamer基础教程02——GStreamer概念
- 【GStreamer开发】GStreamer基础教程02——GStreamer概念
- 【GStreamer开发】GStreamer基础教程10——GStreamer工具
- 【GStreamer开发】GStreamer基础教程01——Hello World
- 【GStreamer开发】GStreamer基础教程03——动态pipeline
- 【GStreamer开发】GStreamer基础教程04——时间管理
- [LeetCode] Word Ladder II
- js 浏览器自适应例子
- 年会上用到的抽奖
- HttpClient 教程 (三)
- 私人订制你们看了没没啥意思
- GStreamer基础教程12——流
- ExtJs读取服务器端数据 spring+springJDBC+struts2+extjs技术
- HttpClient 教程 (四)
- mysql在centos下如何修改字符编码
- 最快速的Android开发环境搭建ADT-Bundle及完成第一个程序Hello World~
- 黑马程序员-java基础第5、6天-面向对象(类的组成部分分析)
- 电脑蓝屏代码大全
- 邦盛 面试
- Poi excel