Gstreamer基础教程10 - Streaming
摘要
我们把直接从网络播放一个媒体文件的方式称为在线播放(Online Streaming),我们已经在以往的例子中体验了GStreamer的在线播放功能,当我们指定播放URI为 http:// 时,GStreamer内部会自动通过网络获取媒体数据。在今天的示例中,我们将进一步了解如何处理由网络问题导致的视频缓冲及时钟丢失的问题。
在线播放
在我们进行在线播放时,我们会将收到的媒体数据立即进行解码并送入显示队列显示。当网络不理想时,我们通常不能及时的接收数据,显示队列中的数据会被耗尽而不能得到及时的补充,这会导致播放出现卡顿。
一种通用的处理方式是创建一个缓冲队列,在队列的数据量达到一定阀值时才进行播放,这样会导致起播时间会有一定的延迟,但会使后续的播放更加流畅,避免了因部分数据无法及时到达造成的停顿。
GStreamer框架已经实现了缓冲队列,但在以往的示例中我们并没有使用其相关的功能。某些Element(例如playbin中使用的queue2及multiqueue)可以创建缓冲队列,并在超过/低于指定的数据阀值时产生相应的信号。应用程序可以监听此类信号,在数据不足时(buffer值小于100%)主动暂停播放,在数据充足时恢复播放。
为了达到多个Sink的同步(例如音视频同步),我们需要使用一个全局的参考时钟,GStreamer会在播放时自动选取一个时钟。在某些网络在线播放的情况下原有时钟会失效,我们需要重新选取一个参考时钟。例如,RTP Source切换流或者改变输出设备。
在参考时钟丢失时,GStreamer框架会产生相应的事件,应用层需要对其作出响应,由于GStreamer在进入PLAYING状态时会自动选取参考时钟,所以我们只需在收到时钟丢失事件时将Pipeline的状态切换到PUASED,再切换到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 = ; /* 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 < )
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, , sizeof (data)); /* Build the pipeline */
pipeline = gst_parse_launch ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/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 -;
} 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 ;
}
将代码保存为basic-tutorial-10.c,执行下列命令编译可得到运行程序。
gcc basic-tutorial-.c -o basic-tutorial- `pkg-config --cflags --libs gstreamer-1.0`
由于通过网络获取数据,视频显示窗口可能会有短暂的等待时间,在终端的buffering达到100%时才会开始播放。
源码分析
GStreamer 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 -;
} else if (ret == GST_STATE_CHANGE_NO_PREROLL) {
data.is_live = TRUE;
}
对于实时的媒体流,我们无法将其设置为PAUSED状态,所以在通过gst_element_set_state 将Pipeline设置成PAUSED状态时,我们会收到GST_STATE_CHANGE_NO_PREROLL。正常情况会返回GST_STATE_CHANGE_SUCCESS 。由于GStreamer的状态会依次从NULL, READY, PAUSED转换为PLAYING,所以我们将状态设置为PLAYING时,也会收到NO_PREROLL返回值。
这里设置is_live标识是因为我们不对其进行缓冲处理。
case GST_MESSAGE_BUFFERING: {
gint percent = ;
/* 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 < )
gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
else
gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
break;
}
在非实时流的情况下,如果缓存队列的数据不足,我们会收到GST_MESSAGE_BUFFERING事件,收到此事件时,我们可以通过gst_message_parse_buffering()得到缓冲进度,如果进度小于100%我们就暂停播放,在缓冲完成后我们再恢复播放。如果使用playbin,我们可以直接通过buffer-size或buffer-duration属性去修改缓冲区大小。
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;
针对于时钟丢失的这种情况,我们只需在收到GST_MESSAGE_CLOCK_LOST事件时,改变Pipline的状态,由GStreamer自动选取参考时钟即可。
总结
通过本文,我们了解了如何应对两种简单的网络播放问题:
- 通过缓冲消息来控制播放状态。
- 在时钟丢失时重新选择时钟。
通过使用缓冲队列,可以使得网络播放更加流畅。
引用
https://gstreamer.freedesktop.org/documentation/tutorials/basic/streaming.html?gi-language=c
Gstreamer基础教程10 - Streaming的更多相关文章
- 【GStreamer开发】GStreamer基础教程10——GStreamer工具
目标 GStreamer提供了一系列方便使用的工具.这篇教程里不牵涉任何代码,但还是会讲一些有用的内容: 如何在命令行下建立一个pipeline--完全不使用C 如何找出一个element的Capab ...
- 【GStreamer开发】GStreamer基础教程14——常用的element
目标 本教程给出了一系列开发中常用的element.它们包括大杂烩般的eleemnt(比如playbin2)以及一些调试时很有用的element. 简单来说,下面用gst-launch这个工具给出一个 ...
- GStreamer基础教程02 - 基本概念
摘要 在 Gstreamer基础教程01 - Hello World中,我们介绍了如何快速的通过一个字符串创建一个简单的pipeline.为了能够更好的控制pipline中的element,我们需要单 ...
- GStreamer基础教程09 - Appsrc及Appsink
摘要 在我们前面的文章中,我们的Pipline都是使用GStreamer自带的插件去产生/消费数据.在实际的情况中,我们的数据源可能没有相应的gstreamer插件,但我们又需要将数据发送到GStre ...
- GStreamer基础教程12 - 常用命令工具
摘要 GStreamer提供了不同的命令行工具用于快速的查看信息以及验证Pipeline的是否能够正确运行,在平时的开发过程中,我们也优先使用GStreamer的命令行工具验证,再将Pipeline集 ...
- 【GStreamer开发】GStreamer基础教程13——播放速度
目标 快进,倒放和慢放是trick模式的共同技巧,它们有一个共同点就是它们都修改了播放的速度.本教程会展示如何来获得这些效果和如何进行逐帧的跳跃.主要内容是: 如何来变换播放的速度,变快或者变慢,前进 ...
- 【GStreamer开发】GStreamer基础教程08——pipeline的快捷访问
目标 GStreamer建立的pipeline不需要完全关闭.有多种方法可以让数据在任何时候送到pipeline中或者从pipeline中取出.本教程会展示: 如何把外部数据送到pipeline中 如 ...
- 【GStreamer开发】GStreamer基础教程07——多线程和Pad的有效性
目标 GStreamer会自动处理多线程这部分,但在有些情况下,你需要手动对线程做解耦.本教程会教你怎样才能做到这一点,另外也展示了Pad的有效性.主要内容包括: 如何针对部分的pipeline建立一 ...
- 【GStreamer开发】GStreamer基础教程05——集成GUI工具
目标 本教程展示了如何在GStreamer集成一个GUI(比如:GTK+).最基本的原则是GStreamer处理多媒体的播放而GUI处理和用户的交互. 在这个教程里面,我们可以学到: 如何告诉GStr ...
随机推荐
- [Advanced Python] 14 - "Generator": calculating prime
高性能编程 几个核心问题 • 生成器是怎样节约内存的?• 使用生成器的最佳时机是什么?• 我如何使用 itertools 来创建复杂的生成器工作流?• 延迟估值何时有益,何时无益? From: htt ...
- 品Spring:实现bean定义时采用的“先进生产力”
前景回顾 当我们把写好的业务代码交给Spring之后,Spring都会做些什么呢? 仔细想象一下,再稍微抽象一下,Spring所做的几乎全部都是: “bean的实例化,bean的依赖装配,bean的初 ...
- 88.CSS---Grid 网格布局教程
grid 兼容性查看请点此处 最新Grid兼容 grid 布局就是给父元素(容器)添加display:grid,然后使子元素(项目)改变布局, 1 2 3 4 5 6 7 8 9 上面九个正方形的代码 ...
- Android类似日历的翻转控件
最近写了个翻转面板的控件拿出来与大家分享一下,类似日历的那种,写的比较简单有需要的可以直接拿去用.直接上效果图吧,代码我放在百度云了,有问题的话直接回复就好呢,大家一起交流下. http://pan. ...
- 如何制作一个Nginx镜像
1,从这里你将学到编写Dockerfile的4个重要指令RUN,EXPOSE,ADD,ENTRYPOINT2,在Dockerfile中编写拷贝文件至容器的方法3, 安装一个nginx server,并 ...
- 只要听说过电脑的人都能看懂的网上pdf全书获取项目
作者:周奇 最近我要获取<概统>的教材自学防挂科(线代已死),于是我看到 htt链ps:/链/max链.book接118接.com接/html/2018/0407/160495927.sh ...
- C# 服务里面调用Python.exe 来执行python文件
问题描述:在WCF服务里面通过调用python.exe来执行py文件,像下面这样py文件路径+参数,用空格隔开.会出现调用结果为空的现象 System.Diagnostics.ProcessStart ...
- cat命令显示文件指定行
cat filename | tail -n 100 显示文件最后100行 cat filename | head -n 100 显示文件前面100行 cat filename | tail -n + ...
- apache中通过mod_rewrite实现伪静态页面的方法
rewrite规则学习 我们新建一个.htaccess文件之后,就在里面写入以下内容: RewriteEngine on #rewriteengine为重写引擎开关on为开启off为关闭 Rewrit ...
- Win10下80端口被System占用导致Apache无法启动
Windows10下80端口被PID为4的System占用导致Apache无法启动的分析与解决方案 方法/步骤 最近更新了Windows10,总体上来说效果还是蛮不错的,然而今天在开启Apac ...