文章目录

  • 1. Goal
  • 2. Introducion
  • 3. A network-resilient example
    • 3.1 Compile
    • 3.2 Code
  • 4.解析
  • 5.总结

1. Goal

直接从Internet播放媒体而不将其存储在本地称为流。 每当我们使用以http://开头的URI时,我们都会在整个教程中这样做。 本教程显示了流媒体时要记住的其他几点。 特别是:

  • 如何启用缓冲(缓解网络问题)
  • 如何从中断中恢复(时钟丢失)

2. Introducion

流式传输时,媒体块一旦从网络到达,便会解码并排队等待呈现。 这意味着,如果某个块被延迟(这在Internet上并不罕见),则演示队列可能会耗尽,媒体播放可能会停顿。
通用解决方案是建立一个“缓冲区”,即允许在开始播放之前将一定数量的媒体块排队。 这样,播放开始会延迟一点时间,但是,如果某些块延迟了,则再现不会受到影响,因为队列中有更多的块正在等待。
事实证明,该解决方案已经在GStreamer中实现,但是以前的教程并未从中受益。 一些元素(例如在playbin中找到的queue2和多队列)能够构建此缓冲区并发布有关缓冲区级别(队列状态)的总线消息。 如果缓冲区级别不够高(通常在低于100%时),则希望具有更大网络弹性的应用程序应收听这些消息并暂停播放。
为了实现多个sink(例如,音频接收器和视频接收器)之间的同步,使用了全局时钟。 该时钟由GStreamer在可以提供一个的所有元素中选择。 在某些情况下,例如,RTP源切换流或更改输出设备,此时钟可能会丢失,因此需要选择新的时钟。 这种情况主要发生在处理流式处理时,因此本教程将对此过程进行说明。
当时钟丢失时,应用程序会在bus上收到一条消息。 要选择一个新的,应用程序只需将pipeline设置为PAUSED,然后再次设置为PLAYING。

3. A network-resilient example

3.1 Compile

gcc basic-tutorial-12.c -o basic-tutorial-12 `pkg-config --cflags --libs gstreamer-1.0`

3.2 Code

#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);elsegst_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 ("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 -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;
}

4.解析

本教程唯一要做的就是对某些消息做出反应。 因此,初始化代码非常简单,并且现在应该是不言自明的。 唯一的新功能是实时流的检测:

/* 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,以指示这是实时流。 即使试图将pipeline设置为PLAYING,我们也将收到NO_PREROLL返回代码,因为状态更改是逐渐发生的(从NULL到READY,再到PAUSED,再到PLAYING)。
我们关心实时流,因为我们想为其禁用缓冲,因此请注意is_live变量中gst_element_set_state的结果。
现在,让我们回顾一下消息解析回调中有趣的部分:

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);elsegst_element_set_state (data->pipeline, GST_STATE_PLAYING);break;
}

首先,如果这是实时资源,请忽略缓冲消息。
我们使用gst_message_parse_buffering解析缓冲消息以检索缓冲级别。
然后,我们在控制台上打印缓冲级别,如果pipeline低于100%,则将pipeline设置为PAUSED。 否则,我们将pipeline设置为PLAYING。
在启动时,我们将看到在回放开始之前缓冲级别上升到100%,这是我们想要实现的目标。 如果稍后网络变得缓慢或无响应并且缓冲区耗尽,我们将收到低于100%的新缓冲消息,因此我们将再次暂停管道,直到建立了足够的缓冲区。

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,就可以选择一个新的时钟,并在必要时等待接收新的媒体块。


Ref

  • gst_message_parse_buffering
gst_message_parse_buffering(GstMessage *message, gint *percent)

从GstMessage中提取缓冲百分比。


5.总结

本教程描述了如何通过两个非常简单的预防措施为应用程序增加网络弹性:

  • 负责缓冲由管道发送的消息
  • 处理时钟丢失

处理这些消息改善了应用程序对网络问题的响应,提高了回放的整体流畅性。

Gstreamer基础教程12: Streamer流相关推荐

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

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

  2. GStreamer基础教程10——GStreamer工具

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

  3. 【GStreamer学习】之GStreamer基础教程

    目标 没有什么比在屏幕上打印出"Hello World"更能获得对软件库的第一印象了! 但是由于我们正在学习多媒体框架,所以我们将输出"Hello World!" ...

  4. Gstreamer基础教程13:Playback Speed

    文章目录 1.Goal 2.介绍 3. A Trick mode player 3.1 Compile 3.2 Code 4.Analyze 5.讨论 1.Goal 快进.倒放和慢动作都是所谓的技巧模 ...

  5. Gstreamer基础教程10: Gstreamer 工具

    文章目录 1. Goal 2. 介绍 3. gst-lanuch-1.0 3.1 Elements 3.2 Properties 3.3 Named elements 3.4 Pads 3.5 Cap ...

  6. Gstreamer基础教程10:GStreamer tools

    文章目录 目标 一.Introduction(简介) 二.gst-launch-1.0 1.Elements 2.Properties(属性) 3.Named elements(元素重命名) 4.Pa ...

  7. Java基础教程(12)--深入理解类

    一.方法的返回值   当我们在程序中调用方法时,虚拟机将会跳转到对应的方法中去执行.当以下几种情况发生时,虚拟机将会回到调用方法的语句并继续向下执行: 执行完方法中所有的语句: 遇到return语句: ...

  8. GStreamer基础教程04 - 动态连接Pipeline

    摘要 在以前的文章中,我们了解到了2种播放文件的方式:一种是在知道了文件的类型及编码方式后,手动创建所需Element并构造Pipeline:另一种是直接使用playbin,由playbin内部动态创 ...

  9. java io流 教程_Java基础教程:IO流与文件基础

    Java:IO流与文件基础 说明: 本章内容将会持续更新,大家可以关注一下并给我提供建议,谢谢啦. 走进流 什么是流 流:指的是从源到目的地的字节的有序序列. 在Java中,可以从其中读取一个字节序列 ...

最新文章

  1. matlab智能小车避障,Arduino智能小车系列教程4——超声波避障
  2. mysql半同步复制
  3. 索尼MOTO等压榨国内代工厂:员工宿舍像监狱
  4. Struts2的简单介绍
  5. werkzeug Request
  6. 大前端算法入门之二分查找
  7. ROS学习笔记6(理解ROS话题)
  8. Collections集合工具类的方法
  9. wlnmp+nginx+mysql+php集合包_LNMP(Linux+Nginx+MySQL+PHP)部署详解(一)
  10. java中数组下标越界对应的异常类是_Java 常见异常种类
  11. 数学分析典型方法pdf下载_硬质合金刀具常识及使用方法 pdf下载 0700
  12. 服务器SNMP协议测试
  13. CSS绘制三角形—border法
  14. 用C++写一个班级通讯录管理软件
  15. 深度系统官网linux安装打印机,在Deepin 20下安装brother打印机驱动及设置网络打印机...
  16. unity text颜色渐变
  17. v-model修饰符.lazy详解
  18. java 制作甘特图,看我如何用简单的步骤打造出复杂的web甘特图
  19. 数独解法-变形数独(第一讲:介绍)
  20. docker logs使用

热门文章

  1. mysql外网访问phpmyadmin_MYSQL如何用phpMyAdmin设置外部IP可以访问
  2. 整数在计算机中的表示
  3. IE浏览器确定兼容性模式
  4. MyEclipse老是提示 resetting selection 长耗时的问题
  5. Linux实战技巧--文件系统操作(二)--创建和删除目录(mkdir/rm)
  6. 解决在word中插入Mathtype公式后行距变大的问题(简单有效)
  7. 《临时笔记》一些深度学习中的英文术语的纪录
  8. 软件外包那些坑(一)
  9. 番茄工作法总结-第七章:团队
  10. html渐变色css3渐变,css3渐变