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,一经查实,立即删除!

相关文章

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

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

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

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

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

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

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"…

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

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

OpenGL系列(六)变换

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

三极管的理解

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

Linux初识地址空间

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

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

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

花钱就能过?PMP到底有没有用

在项目管理领域&#xff0c;PMP&#xff08;Project Management Professional&#xff09;认证常被看作是专业能力的金牌标准。 然而&#xff0c;伴随着这一认证的普及&#xff0c;也出现了一些质疑声&#xff0c;比如“PMP认证是否只是金钱和时间的投入就能获得的证书&#xf…

Mybatis工作流程和插件开发

在了解插件开发之前&#xff0c;我们先总体的来梳理一下Mybatis的大致执行流程&#xff1a; 1.new SqlSessionFactoryBuilder().build(inputStream):先根据配置文件&#xff08;包含了全局配置文件和映射配置文件&#xff09;初始化一个对象Configuration&#xff08;这里对象里…

Java | Leetcode Java题解之第155题最小栈

题目&#xff1a; 题解&#xff1a; class MinStack {Deque<Integer> xStack;Deque<Integer> minStack;public MinStack() {xStack new LinkedList<Integer>();minStack new LinkedList<Integer>();minStack.push(Integer.MAX_VALUE);}public void …

哈喽GPT-4o——对GPT-4o 文本创作的思考与看法

目录 用法1&#xff1a;创作小说用法2&#xff1a;创作散文用法3&#xff1a;创作诗歌1、古诗2、现代诗 用法4&#xff1a;创作儿童故事用法5&#xff1a;创作剧本 大家好&#xff0c;我是哪吒。 都说ChatGPT4o是目前文本创作的最强大模型&#xff0c;它都可以用于哪些方面的文…

虚拟化 之四 详解 jailhouse 使能和创建 Cell 的工作流程

完整的 Jailhouse 组件主要由内核模块(jailhouse.ko)、虚拟机管理程序固件(jailhouse*.bin)、管理工具(jailhouse 命令行程序及一些 Python 脚本)以及配置文件(.cell)这四部分组成。用户使用它们来启用虚拟机管理程序、创建 Cell、加载 inmate 二进制文件以及运行和停止…

CCPD数据集

官网&#xff1a;https://github.com/detectRecog/CCPD 其它介绍&#xff1a;https://blog.csdn.net/qianbin3200896/article/details/103009221 CCPD (Chinese City Parking Dataset, ECCV) provinces ["皖", "沪", "津", "渝", &…

跟着刘二大人学pytorch(第---13---节课之RNN高级篇)

文章目录 0 前言0.1 课程视频链接&#xff1a;0.2 课件下载地址&#xff1a; 1 本节课任务描述模型的处理过程训练循环初始化分类器是否使用GPU构造损失函数和优化器每个epoch所要花费的时间遍历每个epoch时进行训练和测试记录每次测试的准确率加入到列表中 具体实现&#xff0…

0基础可以直接学python自动化测试吗?

可以直接学习Python自动化测试&#xff0c;即使没有编程基础。Python是一种易于学习和上手的编程语言&#xff0c;也是自动化测试领域中最受欢迎的语言之一。下面是一篇超详细和规范的文章&#xff0c;将从零开始介绍Python自动化测试的基础知识。 1. 安装Python和开发环境 首…

项目进度和成本管理

1.如何正确标识出软件项目活动&#xff1f; 正确标识软件项目活动是项目管理中的一个重要步骤&#xff0c;它有助于确保项目的顺利进行和成功完成。以下是一些标识软件项目活动的步骤和建议&#xff1a; 项目范围定义&#xff1a;首先明确项目的目标和范围&#xff0c;这将帮助…

初识前端开发

目的 &#xff1a;开发一个网站 -前端开发&#xff1a;html 、css、javaScript -web框架&#xff1a;接收请求处理 -mysql数据库&#xff1a;存储数据的地方 快速上手&#xff1a; 基于Flask web框架让你快速搭建一个网站出来 学习&#xff1a; 基于Django框架 初识HTML …