GStreamer——教程——基础教程4:Time management

基础教程4:Time management(时间管理)

目标

本教程展示了如何使用GStreamer时间相关工具。特别是:

  • 如何查询管道以获取流位置或持续时间等信息。
  • 如何寻找(跳转)到流内的不同位置(时间)。

介绍

GstQuery是一种机制,允许向元素或 pad 询问 一条信息。在此示例中,我们询问 pipeline 是否正在寻找允许(某些来源,如直播,不允许查找)。如果它是允许的,那么,一旦movie 播放了十秒钟,我们使用查找跳到不同的位置。

在前面的教程中,一旦我们设置并运行了pipeline , 我们的主要功能只是等待接收 ERROR 或 EOS 通过 bus 。在这里,我们修改这个函数以定期唤醒并查询pipeline 中的流位置,以便我们可以打印它 屏幕。这类似于媒体播放器会做的事情,更新定期的用户界面。

最后,只要听歌时长发生变化,就会进行查询和更新。

寻求的例子

将此代码复制到名为basic-tutorial-4.c文本文件中(或找到它 在您的GStreamer安装中)。

basic-tutorial-4.c

#include <gst/gst.h>/* Structure to contain all our information, so we can pass it around */
typedef struct _CustomData {GstElement *playbin;  /* Our one and only element */gboolean playing;      /* Are we in the PLAYING state? */gboolean terminate;    /* Should we terminate execution? */gboolean seek_enabled; /* Is seeking enabled for this media? */gboolean seek_done;    /* Have we performed the seek already? */gint64 duration;       /* How long does this media last, in nanoseconds */
} CustomData;/* Forward definition of the message processing function */
static void handle_message (CustomData *data, GstMessage *msg);int main(int argc, char *argv[]) {CustomData data;GstBus *bus;GstMessage *msg;GstStateChangeReturn ret;data.playing = FALSE;data.terminate = FALSE;data.seek_enabled = FALSE;data.seek_done = FALSE;data.duration = GST_CLOCK_TIME_NONE;/* Initialize GStreamer */gst_init (&argc, &argv);/* Create the elements */data.playbin = gst_element_factory_make ("playbin", "playbin");if (!data.playbin) {g_printerr ("Not all elements could be created.\n");return -1;}/* Set the URI to play */g_object_set (data.playbin, "uri", "https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm", NULL);/* Start playing */ret = gst_element_set_state (data.playbin, GST_STATE_PLAYING);if (ret == GST_STATE_CHANGE_FAILURE) {g_printerr ("Unable to set the pipeline to the playing state.\n");gst_object_unref (data.playbin);return -1;}/* Listen to the bus */bus = gst_element_get_bus (data.playbin);do {msg = gst_bus_timed_pop_filtered (bus, 100 * GST_MSECOND,GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_DURATION);/* Parse message */if (msg != NULL) {handle_message (&data, msg);} else {/* We got no message, this means the timeout expired */if (data.playing) {gint64 current = -1;/* Query the current position of the stream */if (!gst_element_query_position (data.playbin, GST_FORMAT_TIME, &current)) {g_printerr ("Could not query current position.\n");}/* If we didn't know it yet, query the stream duration */if (!GST_CLOCK_TIME_IS_VALID (data.duration)) {if (!gst_element_query_duration (data.playbin, GST_FORMAT_TIME, &data.duration)) {g_printerr ("Could not query current duration.\n");}}/* Print current position and total duration */g_print ("Position %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT "\r",GST_TIME_ARGS (current), GST_TIME_ARGS (data.duration));/* If seeking is enabled, we have not done it yet, and the time is right, seek */if (data.seek_enabled && !data.seek_done && current > 10 * GST_SECOND) {g_print ("\nReached 10s, performing seek...\n");gst_element_seek_simple (data.playbin, GST_FORMAT_TIME,GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, 30 * GST_SECOND);data.seek_done = TRUE;}}}} while (!data.terminate);/* Free resources */gst_object_unref (bus);gst_element_set_state (data.playbin, GST_STATE_NULL);gst_object_unref (data.playbin);return 0;
}static void handle_message (CustomData *data, GstMessage *msg) {GError *err;gchar *debug_info;switch (GST_MESSAGE_TYPE (msg)) {case GST_MESSAGE_ERROR:gst_message_parse_error (msg, &err, &debug_info);g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");g_clear_error (&err);g_free (debug_info);data->terminate = TRUE;break;case GST_MESSAGE_EOS:g_print ("\nEnd-Of-Stream reached.\n");data->terminate = TRUE;break;case GST_MESSAGE_DURATION:/* The duration has changed, mark the current one as invalid */data->duration = GST_CLOCK_TIME_NONE;break;case GST_MESSAGE_STATE_CHANGED: {GstState old_state, new_state, pending_state;gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->playbin)) {g_print ("Pipeline state changed from %s to %s:\n",gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));/* Remember whether we are in the PLAYING state or not */data->playing = (new_state == GST_STATE_PLAYING);if (data->playing) {/* We just moved to PLAYING. Check if seeking is possible */GstQuery *query;gint64 start, end;query = gst_query_new_seeking (GST_FORMAT_TIME);if (gst_element_query (data->playbin, query)) {gst_query_parse_seeking (query, NULL, &data->seek_enabled, &start, &end);if (data->seek_enabled) {g_print ("Seeking is ENABLED from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT "\n",GST_TIME_ARGS (start), GST_TIME_ARGS (end));} else {g_print ("Seeking is DISABLED for this stream.\n");}}else {g_printerr ("Seeking query failed.");}gst_query_unref (query);}}} break;default:/* We should not reach here */g_printerr ("Unexpected message received.\n");break;}gst_message_unref (msg);
}

需要帮忙吗?

如果您需要帮助来编译此代码,请参阅为您的平台构建教程部分:Linux、Mac OS X或Windows,或在Linux上使用此特定命令:

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

如果您需要帮助来运行此代码,请参阅为您的平台运行教程部分:Linux、Mac OS X或Windows。

本教程打开一个窗口并显示一个带有音频的电影。媒体是从Internet获取的,因此窗口可能需要几秒钟才能出现,具体取决于您的连接速度。进入电影10秒后,它会跳到一个新位置

所需库:gstreamer-1.0

工作流

/* Structure to contain all our information, so we can pass it around */
typedef struct _CustomData {GstElement *playbin;  /* Our one and only element */gboolean playing;      /* Are we in the PLAYING state? */gboolean terminate;    /* Should we terminate execution? */gboolean seek_enabled; /* Is seeking enabled for this media? */gboolean seek_done;    /* Have we performed the seek already? */gint64 duration;       /* How long does this media last, in nanoseconds */
} CustomData;/* Forward definition of the message processing function */
static void handle_message (CustomData *data, GstMessage *msg);

我们首先定义一个结构来包含我们所有的信息,所以我们可以将其传递给其他函数。特别是,在这个例子中,我们 将消息处理代码移动到自己的函数中 handle_message因为它长得有点太大了。

然后我们构建一个由单个元素组成的管道 playbin,我们已经在基本教程1:Hello world!了解。playbin本身就是一个管道,在这种情况下,它是唯一的 元素,所以我们直接使用playbin元素。我们将跳过细节:剪辑的URI通过playbin提供给 URI属性和管道设置为播放状态。

msg = gst_bus_timed_pop_filtered (bus, 100 * GST_MSECOND,GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_DURATION);

以前我们没有提供超时 gst_bus_timed_pop_filtered(),这意味着它直到消息已收到。现在我们使用100毫秒的超时,所以,如果十分之一秒内没有收到消息,函数将返回 NULL。我们将使用这个逻辑来更新我们的"UI"。

请注意,所需的超时必须指定为GstClockTime,因此, 以纳秒为单位。表示不同时间单位的数字应该是 乘以GST_SECONDGST_MSECOND之类的宏 您的代码更具可读性。

如果我们收到消息,我们在handle_message函数中处理它 (下一小节),否则:

用户接口刷新

/* We got no message, this means the timeout expired */
if (data.playing) {

如果pipeline 处于PLAYING状态,则是刷新屏幕的时候了。 如果我们不在PLAYING状态,我们什么都不想做,因为大多数查询都会失败。

我们到达这里大约每秒10次,足够刷新了为我们的UI评分。我们将在屏幕上打印当前媒体位置,我们可以通过查询管道来学习。这涉及一个下一个小节将显示的几个步骤,但是,由于位置 和持续时间是常见的查询,GstElement提供更容易, 现成的替代品:

/* Query the current position of the stream */
if (!gst_element_query_position (data.pipeline, GST_FORMAT_TIME, &current)) {g_printerr ("Could not query current position.\n");
}

gst_element_query_position() 并直接向我们提供结果。

/* If we didn't know it yet, query the stream duration */
if (!GST_CLOCK_TIME_IS_VALID (data.duration)) {if (!gst_element_query_duration (data.pipeline, GST_FORMAT_TIME, &data.duration)) {g_printerr ("Could not query current duration.\n");}
}

现在是了解溪流长度的好时机 另一个GstElement辅助函数:gst_element_query_duration()

/* Print current position and total duration */
g_print ("Position %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT "\r",GST_TIME_ARGS (current), GST_TIME_ARGS (data.duration));

注意GST_TIME_FORMATGST_TIME_ARGS宏的用法提供GStreamer时间的用户友好表示。

/* If seeking is enabled, we have not done it yet, and the time is right, seek */
if (data.seek_enabled && !data.seek_done && current > 10 * GST_SECOND) {g_print ("\nReached 10s, performing seek...\n");gst_element_seek_simple (data.pipeline, GST_FORMAT_TIME,GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, 30 * GST_SECOND);data.seek_done = TRUE;
}

现在我们执行寻找,“simply”通过在pipeline上调用gst_element_seek_simple()。很多寻找的复杂性隐藏在这种方法中,这是一个很好的东西!

让我们回顾一下参数:

GST_FORMAT_TIME 表示我们正在指定目的地时间单位。其他查找格式使用不同的单位。

然后是 GstSeekFlags,让我们回顾一下最常见的:

GST_SEEK_FLAG_FLUSH:这将丢弃当前在pipeline 中的所有数据在寻找之前。pipeline 重新填充时可能会暂停一点新的数据开始出现,但大大增加了应用程序的“响应能力”。如果未提供此标志, “stale”数据可能会显示一段时间,直到新位置出现在pipeline 的末端。

GST_SEEK_FLAG_KEY_UNIT:对于大多数编码视频流,寻求任意位置是不可能的,但只能用于称为关键帧的某些帧。当这使用标志,查找实际上会移动到最近的关键帧和立即开始生成数据。如果不使用此标志,则pipeline 将在内部移动到最近的关键帧(它没有其他替代)但数据在达到请求之前不会显示 位置。最后一个选择更准确,但可能需要更长时间。

GST_SEEK_FLAG_ACCURATE:某些媒体剪辑不提供足够的索引信息,这意味着定位到任意位置很耗时。在这些情况下,GStreamer通常会估计要定位的位置,通常效果还不错。如果这种精度对你的案例来说不够好(你看到的定位没有到达你要求确切的时间),那么就提供这个标志。请注意,计算定位位置可能需要更长的时间(在某些文件上可能会非常长)。

最后,我们提供要定位的位置。由于我们要求使用GST_FORMAT_TIME,因此该值必须以纳秒为单位,因此为了简单起见,我们将时间表示为秒,然后再乘以GST_SECOND。

消息Pump

handle_message函数处理通过管道总线接收到的所有消息。错误和结束的处理与以前的教程相同,所以我们跳到有趣的部分:

case GST_MESSAGE_DURATION:/* The duration has changed, mark the current one as invalid */data->duration = GST_CLOCK_TIME_NONE;break;

每当流的持续时间发生变化时,此消息都会发布到总线上。在这里,我们简单地将当前持续时间标记为无效,以便稍后重新查询。

case GST_MESSAGE_STATE_CHANGED: {GstState old_state, new_state, pending_state;gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->pipeline)) {g_print ("Pipeline state changed from %s to %s:\n",gst_element_state_get_name (old_state), gst_element_state_get_name (new_state));/* Remember whether we are in the PLAYING state or not */data->playing = (new_state == GST_STATE_PLAYING);

查找和时间查询通常只在 PAUSEDPLAYING状态,因为所有元素都有机会接收信息并配置自己。在这里,我们使用playing 变量来跟踪管道是否处于PLAYING状态。 此外,如果我们刚刚进入PLAYING状态,我们将执行第一个查询。 我们询问管道是否允许在此流中查找:

if (data->playing) {/* We just moved to PLAYING. Check if seeking is possible */GstQuery *query;gint64 start, end;query = gst_query_new_seeking (GST_FORMAT_TIME);if (gst_element_query (data->pipeline, query)) {gst_query_parse_seeking (query, NULL, &data->seek_enabled, &start, &end);if (data->seek_enabled) {g_print ("Seeking is ENABLED from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT "\n",GST_TIME_ARGS (start), GST_TIME_ARGS (end));} else {g_print ("Seeking is DISABLED for this stream.\n");}}else {g_printerr ("Seeking query failed.");}gst_query_unref (query);
}

gst_query_new_seeking()以GST_FORMAT_TIME格式创建一个新的“seeking”类型查询对象。这表明我们有兴趣通过指定我们想要移动的新时间来进行定位。我们也可以选择询问GST_FORMAT_BYTES,然后定位到源文件内的特定字节位置,但这通常较少使用。

然后,这个查询对象通过gst_element_query()传递给管道。结果存储在同一个查询中,可以用gst_query_parse_seeking()轻松检索。它提取一个布尔值,表示是否允许定位,以及可能进行定位的范围。

当你完成查询对象的使用时,别忘了取消引用它。

就这样!有了这些知识,就可以构建一个媒体播放器,它可以根据当前流位置定期更新滑块,并通过移动滑块来实现定位!

结论

本教程显示:

  • 如何使用GstQuery查询管道信息

  • 如何使用gst_element_query_position()和gst_element_query_duration()获取常见信息,如位置和持续时间

  • 如何使用gst_element_seek_simple()定位到流中的任意位置

  • 所有这些操作可以在哪些状态下执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/28514.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM调优-推荐启动参数

JVM&#xff08;Java Virtual Machine&#xff09;调优是为了提高Java应用程序的性能和稳定性。以下是一些常用的JVM启动参数及其作用&#xff0c;这些参数可以帮助优化JVM性能&#xff1a; 1. 堆内存设置&#xff1a; - -Xms<size>: 设置初始堆大小。例如&#xff0…

python模块之codecs

python 模块codecs python对多国语言的处理是支持的很好的&#xff0c;它可以处理现在任意编码的字符&#xff0c;这里深入的研究一下python对多种不同语言的处理。 有一点需要清楚的是&#xff0c;当python要做编码转换的时候&#xff0c;会借助于内部的编码&#xff0c;转换…

数据结构与算法笔记:基础篇 -递归树:如何借助树来求解递归算法的时间复杂度?

概述 我们都知道&#xff0c;递归代码的时间复杂度分析起来很麻烦。在《排序(下)》哪里讲过&#xff0c;如何用递推公式&#xff0c;求解归并排序、快速排序的时间复杂度&#xff0c;但是有些情况&#xff0c;比如快排的平均时间复杂度的分析&#xff0c;用递推公式的话&#…

《天软股票特色因子定期报告》

最新《天软股票特色因子定期报告》&#xff08;2024-06&#xff09;&#xff0c;抢先发布 内容概要如下&#xff1a; 天软特色因子A08006&#xff08;近一月日度买卖压力2&#xff09;从行业角度分析&#xff0c;在电子设备、石油石化行业表现稳定&#xff0c;无论在有效性、区…

【名词解释】Unity中的3D物理系统:触发器

在Unity的3D物理系统中&#xff0c;触发器&#xff08;Trigger&#xff09;是一种特殊的碰撞体&#xff0c;用于检测物体进入或离开一个特定区域的事件&#xff0c;但它不会像普通碰撞体那样产生物理碰撞反应。触发器通常用于实现非物理交互&#xff0c;如检测玩家进入特定区域…

复星杏脉算法面经2024年5月16日面试

复星杏脉算法面经2024年5月 面试记录&#xff1a;3个部分1. 自己介绍 2. 问八股 3.代码题先自我介绍20分钟问问题1. 梯度爆炸怎么解决&#xff0c;三个解决方案&#xff1a;梯度裁剪&#xff08;Gradient Clipping&#xff09;正则化&#xff08;Regularization&#xff09;调整…

C11与C++11关于Atomic原子类型的异同

"The C11 atomics were almost copynpasted from C11. All the work was done for C, and C (sensibly) incorporated it wholesale." 上面这句话源自&#xff1a;C11 atomic variables and the kernel [LWN.net] 翻译过来就是&#xff1a; "C11 中的原子操作…

HTML 颜色名

HTML 颜色名 HTML 颜色名是一组预定义的颜色&#xff0c;可以在 HTML 和 CSS 中使用。这些颜色名易于记忆&#xff0c;方便开发者快速选择和使用。本文将详细介绍 HTML 颜色名&#xff0c;包括它们的用途、优点以及如何在网页设计中使用它们。 HTML 颜色名的用途 HTML 颜色名…

熱門開源項目推薦

熱門開源項目推薦&#xff1a;探索未來的技術前沿 開源軟件的興起為科技領域帶來了革命性的變化&#xff0c;不僅促進了技術的發展&#xff0c;還創造了一個開放和協作的環境&#xff0c;讓全球的開發者可以共同參與、創新和改進。近年來&#xff0c;開源大模型成為了技術社區…

时政|连续高温

危害 会对人的健康乃至生命安全产生严重影响&#xff0c;近年来&#xff0c;几乎每年都有因热致死的病例面对高温天气&#xff0c;不能仅仅止于调侃“天热”&#xff0c;止于变着花样表达自己的感受&#xff0c;还是要提高警惕&#xff0c;重视并防范高温导致的中暑、热痉挛、…

nginx+tomcat+nfs →web集群部署

nginxtomcatnfs →web集群部署 一.安装前介绍 NGINX是一个高性能的Web服务器和反向代理服务器。它能够处理静态内容&#xff0c;缓存请求结果&#xff0c;以及将请求转发给后端服务器。通过反向代理&#xff0c;NGINX能够实现请求的负载均衡、安全性增强、SSL加密等功能。此外…

Linux中文件查找相关命令比较

Linux中与文件定位的命令有find、locate、whereis、which&#xff0c;type。 一、find find命令最强&#xff0c;能搜索各种场景下的文件&#xff0c;需要配合相关参数&#xff0c;搜索速度慢。在文件系统中递归查找文件。 find /path/to/search -name "filename"…

第67集《摄大乘论》

《摄大乘论》&#xff0c;和尚尼慈悲、诸位法师、诸位居士&#xff0c;阿弥陀佛&#xff01;(阿弥陀佛&#xff01;)请大家打开《讲义》第二二六页&#xff0c;庚十、业。 这一大科是讲到法身的功德。我们从前面的学习&#xff0c;可以把法身的功德分两部分来作个总结&#xf…

位运算算法:编程世界中的魔法符号

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一. 常见位运算总结 二、常见位运算题目 2.1 位1的个数 2.2 比特数记位&#xff08;典型dp&#xff09; 2.3 汉明距离 2.4 只出现一次的数字&#xff08;1&#xff09; 2.5 只出…

【JVM】CMS 收集器的垃圾收集过程

CMS&#xff08;Concurrent Mark-Sweep&#xff09;收集器是Java虚拟机&#xff08;JVM&#xff09;中的一种垃圾收集器&#xff0c;它主要面向老年代&#xff08;Old Generation&#xff09;的垃圾回收。CMS收集器的目标是最小化垃圾收集的停顿时间&#xff0c;从而提高应用程…

OpenGL系列(六)变换

在三角形和纹理贴图示例中&#xff0c;顶点使用的是归一化设备坐标&#xff0c;在该坐标系下&#xff0c;顶点的每个轴的取值为-1到1&#xff0c;超出范围的顶点不可见。 基于归一化设备坐标的物体的形状随着设备的大小变换而变化&#xff0c;这里产生的第一个问题是&#xff0…

三极管的理解

三极管的放大使用 基极集电极之间可理解为电子扩展 电化学效应&#xff1b;产生载流子多少&#xff0c;从而射集间而流动大小 电化学效应&#xff0c;电子漂移现象&#xff0c;基极与集电极的电流的作用在于产生载流子 电流的流动&#xff0c;需要载流子&#xff0c;从而基极…

【教程】服务器数据一键备份脚本 backup.sh(新增支持COS/阿里云盘)

1、一键备份脚本 backup.sh 功能特点 支持 MySQL/MariaDB/Percona 的数据库全量备份或选择备份;支持指定目录或文件的备份;支持加密备份文件(需安装 openssl 命令,可选);支持上传至 Google Drive(需先安装 rclone 并配置,可选);支持上传至 腾讯云COS(需先安装 coscm…

Linux初识地址空间

前言 上一期我们对进程优先级、命令行参数以及环境和变量做了介绍&#xff01;以前我们就提到过一个问题有了运行队列为什么还要有优先级&#xff1f;本期将带你揭晓&#xff01; 本期内容介绍 虚拟地址空间的引入 虚拟地址空间的介绍 如何理解地址空间 为什么要有地址空间 如…

Elasticsearch:智能 RAG,获取周围分块(一)

作者&#xff1a;来自 Elastic Sunile Manjee 在检索增强生成 (RAG) 领域&#xff0c;一个持续存在的挑战是找到输入大型语言模型 (LLM) 的最佳数据量。数据太少会导致响应不足或不准确&#xff0c;而数据太多会导致答案模糊。这种微妙的平衡启发我开发了一个专注于智能分块和利…