【GStreamer开发】GStreamer基础教程12——流
目标
直接播放Internet上的文件而不在本地保存就被称为流播放。我们在前面教程里已经这样做过了,使用了http://的URL。本教程展示的是在播放流的时候需要记住的几个点,特别是:
如何设置缓冲
如何从打断中恢复(因为失去了时钟)
介绍
当在播放流的时候,一旦从网络上取到媒体数据块就会进行解码和放入显示队列。这意味着如果网络来的数据延迟了,那么显示队列就可能没有数据,播放就会停下来。
解决这个问题的办法是建立缓冲,这就是说,在开始播放前允许队列里已经存储了一些数据。这样的话,播放虽然晚了一点开始,但如果网络有什么延时,那么还有一定的缓冲数据可以播放。
这个方案已经在GStreamer里面实现了,但前面的教程中没有涉及到这个方面。有些element,像在playbin2里面用到的queue2和multiqueue,都可以建立自己的缓冲然后根据缓冲的等级发送消息到总线上。一个应用如果希望能更好的适应各种网络环境,那么就该关注这些消息,当缓冲等级低到一定程度时就要暂停播放。
为了在多个sink中同步,我们使用了一个全局的时钟。这个时钟是GStreamer在所有的可以提供时钟的element中选出来的。在某些情况下,例如,一个RTP资源切换流或者更换输出设备,那么时钟就可能丢失。这时就需要重新建立一个时钟,这个过程在本教程会解释一下。
当时钟丢失的时候,应用会从总线上得到一个消息。要建立一个新的时钟,应用仅仅把pipeline设置到PAUSED状态然后重新置成PLAYING即可。
一个适应网络的例子
- <span style="font-size:14px;">#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: {
- ;
- /* 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 */
- 00)
- 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, charchar *argv[]) {
- GstElement *pipeline;
- GstBus *bus;
- GstStateChangeReturn ret;
- GMainLoop *main_loop;
- CustomData data;
- /* Initialize GStreamer */
- gst_init (&argc, &argv);
- /* Initialize our data structure */
- , 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);
- ;
- } 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);
- ;
- }</span>
工作流程
这个例子中唯一特殊的是对特定消息的相互作用, 因此,初始化代码非常简单清晰。唯一新加的一点就是对实时流的检测:
- <span style="font-size:14px;"> /* 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);
- ;
- } else if (ret == GST_STATE_CHANGE_NO_PREROLL) {
- data.is_live = TRUE;
- }</span>
实时流是不能暂停的,所以在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: {
- ;
- /* 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 */
- 00)
- 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——流的更多相关文章
- Android程序开发0基础教程(一)
程序猿学英语就上视觉英语网 Android程序开发0基础教程(一) 平台简单介绍 令人激动的Google手机操作系统平台-Android在2007年11月13日正式公布了,这是一个开放源码的操 ...
- GStreamer基础教程12 - 常用命令工具
摘要 GStreamer提供了不同的命令行工具用于快速的查看信息以及验证Pipeline的是否能够正确运行,在平时的开发过程中,我们也优先使用GStreamer的命令行工具验证,再将Pipeline集 ...
- Java基础教程(12)--深入理解类
一.方法的返回值 当我们在程序中调用方法时,虚拟机将会跳转到对应的方法中去执行.当以下几种情况发生时,虚拟机将会回到调用方法的语句并继续向下执行: 执行完方法中所有的语句: 遇到return语句: ...
- Java基础教程——转换流
转换流 通常,Window默认的编码方式是GBK,Java项目一般建议设为UTF-8编码.这时候读取文件可能出现乱码.事实上实际应用中编码格式不匹配的场景非常多. 转换流可以指定编码方式,用于解决乱码 ...
- Ruby 基础教程 1-2
1.数组 创建 arrayname=[] arrayname=["1",12,"23"] 访问 arrayname[index] 更新 arrayname[in ...
- Java基础教程——打印流
打印流 打印流可以把原本输出到控制台的信息输出到文件中.PrintStream是字节打印流(还有个对应的字符打印流是PrintWriter,这里不涉及) System类中有个变量: public fi ...
- Java基础教程——缓冲流
缓冲流 "缓冲流"也叫"包装流",是对基本输入输出流的增强: 字节缓冲流: BufferedInputStream , BufferedOutputStream ...
- Java基础教程——字符流
字符流 字节流服务文本文件时,可能出现中文乱码.因为一个中文字符可能占用多个字节. 针对于非英语系的国家和地区,提供了一套方便读写方式--字符流. java.io.Reader java.io.Wri ...
- cocos基础教程(12)点击交互的三种处理
1.概述 游戏也好,程序也好,只有能与用户交互才有意义.手机上的交互大致可以分为两部分:点击和输入.其中点击更为重要,几乎是游戏中全部的交互.在Cocos2d-x 3.0中,更改了dispatch机制 ...
随机推荐
- [golang]Go内嵌静态资源go-bindata的安装及使用
使用 Go 开发应用的时候,有时会遇到需要读取静态资源的情况.比如开发 Web 应用,程序需要加载模板文件生成输出的 HTML.在程序部署的时候,除了发布应用可执行文件外,还需要发布依赖的静态资源文件 ...
- 编译器错误 CS0540
编译项目报错:包含类型不实现接口,CS0540 原因:试图在非派生自接口的类中实现接口成员. 解决方案: 删除接口成员的实现,或将接口添加到类的基类列表. 下面的两个示例生成 CS0540: 一. / ...
- [树链剖分]BZOJ3589动态树
题目描述 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0: 这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1: 小明希望你求出几条树枝上 ...
- php手记之05-tp5获取器与修改器
获取器 命名规范为: getFieldNameAttr 例如,我们需要对状态值进行转换,可以使用: <?php class User extends Model { public functio ...
- DNGuard HVM Unpacker(3.71 trial support and x64 fixed)
DNGuard HVM Unpacker(3.71 trial support and x64 fixed) Gr8 news. Finally got the x64 crash fixed. DN ...
- AAR文件简介
假如我们希望提供一个带有资源文件的第三方库给别人使用,总不能直接把源代码给别人,但是我们知道eclipse打包的时候不能包含res的资源文件,于是Android在发布Android studio的时候 ...
- MongoDB允许其它IP地址访问
网址:https://blog.csdn.net/sl1992/article/details/83964310 文章目录1.允许所有地址访问2.绑定内网IP3.绑定多个IP Linux服务器上安装M ...
- .net Core 中DateTime在Linux Docker中与Windows时间不一致
最近写了一个.net core项目,部署到CentOS并在docker上运行的时候,发现DateTime.Now获取的时间与Windows不一致(定时执行的任务,晚了8个小时),在Windows中可以 ...
- JVM 初始化阶段例子
创建如下Demo package com.example.jvm.classloader; class Parent{ static int a = 3; static { System.out.pr ...
- 五、postman-sandbox
一.在postman中运行一些JavaScript代码的地方 公共库(javascript) 环境变量与全局变量 动态变量 操作cookie 获取和查看请求及响应 读取数据文件 二.api文档 htt ...
