摘要

  我们把直接从网络播放一个媒体文件的方式称为在线播放(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

作者:John.Leng
本文版权归作者所有,欢迎转载。商业转载请联系作者获得授权,非商业转载请在文章页面明显位置给出原文连接.

Gstreamer基础教程10 - Streaming的更多相关文章

  1. 【GStreamer开发】GStreamer基础教程10——GStreamer工具

    目标 GStreamer提供了一系列方便使用的工具.这篇教程里不牵涉任何代码,但还是会讲一些有用的内容: 如何在命令行下建立一个pipeline--完全不使用C 如何找出一个element的Capab ...

  2. 【GStreamer开发】GStreamer基础教程14——常用的element

    目标 本教程给出了一系列开发中常用的element.它们包括大杂烩般的eleemnt(比如playbin2)以及一些调试时很有用的element. 简单来说,下面用gst-launch这个工具给出一个 ...

  3. GStreamer基础教程02 - 基本概念

    摘要 在 Gstreamer基础教程01 - Hello World中,我们介绍了如何快速的通过一个字符串创建一个简单的pipeline.为了能够更好的控制pipline中的element,我们需要单 ...

  4. GStreamer基础教程09 - Appsrc及Appsink

    摘要 在我们前面的文章中,我们的Pipline都是使用GStreamer自带的插件去产生/消费数据.在实际的情况中,我们的数据源可能没有相应的gstreamer插件,但我们又需要将数据发送到GStre ...

  5. GStreamer基础教程12 - 常用命令工具

    摘要 GStreamer提供了不同的命令行工具用于快速的查看信息以及验证Pipeline的是否能够正确运行,在平时的开发过程中,我们也优先使用GStreamer的命令行工具验证,再将Pipeline集 ...

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

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

  7. 【GStreamer开发】GStreamer基础教程08——pipeline的快捷访问

    目标 GStreamer建立的pipeline不需要完全关闭.有多种方法可以让数据在任何时候送到pipeline中或者从pipeline中取出.本教程会展示: 如何把外部数据送到pipeline中 如 ...

  8. 【GStreamer开发】GStreamer基础教程07——多线程和Pad的有效性

    目标 GStreamer会自动处理多线程这部分,但在有些情况下,你需要手动对线程做解耦.本教程会教你怎样才能做到这一点,另外也展示了Pad的有效性.主要内容包括: 如何针对部分的pipeline建立一 ...

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

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

随机推荐

  1. [AWS] Serverless & Lambda

    因为Lambda 所以Serverless 进化过程 课程章节:https://edu.51cto.com//center/course/lesson/index?id=199646 作用和优势 ev ...

  2. 遗传编程(GA,genetic programming)算法初探,以及用遗传编程自动生成符合题解的正则表达式的实践

    1. 遗传编程简介 0x1:什么是遗传编程算法,和传统机器学习算法有什么区别 传统上,我们接触的机器学习算法,都是被设计为解决某一个某一类问题的确定性算法.对于这些机器学习算法来说,唯一的灵活性体现在 ...

  3. 【linux】【FastDFS】FastDFS安装

    前言 FastDFS是一个开源的轻量级分布式文件系统,由跟踪服务器(tracker server).存储服务器(storage server)和客户端(client)三个部分组成,主要解决了海量数据存 ...

  4. 【linux】【maven】maven及maven私服安装

    前言 系统环境:Centos7.jdk1.8 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的用户使用.当Maven需要下载构件的时候,它从私服请求,如 ...

  5. 前台提交数据到node服务器(get方式)

    .有两种办法,一种是表单提交,一种是ajax方式提交. 1.form提交  在前台模板文件上写: <form action="/reg" method="get&q ...

  6. APP自動化測試腳本1

    package com.lemon.day01; import java.net.MalformedURLException; import java.net.URL; import java.uti ...

  7. MyEclipse 中无法直接使用BaseEncoder问题

    首先 :点击项目---->build path--->configure Build Path 然后:java build path --->libraries--->JRE ...

  8. Android Studio [跑马灯]

    MainActivity package com.xdw.secondapp; import android.graphics.Paint; import android.support.v7.app ...

  9. 【THE LAST TIME】彻底吃透 JavaScript 执行机制

    前言 The last time, I have learned [THE LAST TIME]一直是我想写的一个系列,旨在厚积薄发,重温前端. 也是给自己的查缺补漏和技术分享. 欢迎大家多多评论指点 ...

  10. asp.net编译中出现 数据库 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\test1.mdf' 已存在。请选择其他数据库名称。

    关于asp.net编译中出现数据库 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\test1.mdf ...