GStreamer 简明教程(九):Seek 与跳帧

系列文章目录

  • GStreamer 简明教程(一):环境搭建,运行 Basic Tutorial 1 Hello world!
  • GStreamer 简明教程(二):基本概念介绍,Element 和 Pipeline
  • GStreamer 简明教程(三):动态调整 Pipeline
  • GStreamer 简明教程(四):Seek 以及获取文件时长
  • GStreamer 简明教程(五):Pad 相关概念介绍,Pad Capabilities/Templates
  • GStreamer 简明教程(六):利用 Tee 复制流数据,巧用 Queue 实现多线程
  • GStreamer 简明教程(七):实现管道的动态数据流
  • GStreamer 简明教程(八):常用工具介绍

文章目录

  • 系列文章目录
  • 前言
  • Seek Events
  • Step Events
  • Show me the code
  • 参考


前言

本文对 Basic tutorial 13: Playback speed 内容进行说明,重点是理解 GStreamer 中 seek events 和 step events。

Seek Events

在看视频的过程中,用户拖动滑动条跳转到指定播放位置是基本需求,在 GStreamer 中我们可以像 pipeline 发送一个 seek event 来实现。

GStreamer 中创建 seek events 接口原型如下:

GstEvent *
gst_event_new_seek (gdouble rate, GstFormat format, GstSeekFlags flags,GstSeekType start_type, gint64 start, GstSeekType stop_type, gint64 stop);

让我详细解释各个参数:

  1. rate (gdouble):

    • 播放速率倍数
    • 1.0 表示正常速度
    • 2.0 表示双倍速度
    • 负值表示倒放
    • 0.5 表示半速播放
  2. format (GstFormat):

    • 指定查找的格式单位
    • 常用值包括:
      • GST_FORMAT_TIME: 时间格式(纳秒)
      • GST_FORMAT_BYTES: 字节格式
      • GST_FORMAT_DEFAULT: 默认格式(如对音频来说是采样数)
  3. flags (GstSeekFlags):

    • seek 操作的标志位组合
    • 常用标志:
      • GST_SEEK_FLAG_FLUSH: 清空管道中的数据
      • GST_SEEK_FLAG_ACCURATE: 精确定位
      • GST_SEEK_FLAG_KEY_UNIT: 定位到关键帧
      • GST_SEEK_FLAG_SEGMENT: 执行片段播放
  4. start_type (GstSeekType):

    • 定义如何解释 start 参数
    • 常用值:
      • GST_SEEK_TYPE_NONE: 忽略起始位置
      • GST_SEEK_TYPE_SET: 绝对位置
      • GST_SEEK_TYPE_CUR: 相对当前位置
  5. start (gint64):

    • 开始位置的值
    • 具体含义取决于 format 和 start_type
  6. stop_type (GstSeekType):

    • 定义如何解释 stop 参数
    • 与 start_type 使用相同的值
  7. stop (gint64):

    • 结束位置的值
    • 具体含义取决于 format 和 stop_type

使用示例:

// 跳转到视频的 2 秒位置
GstEvent *seek_event = gst_event_new_seek(1.0,                    // 正常播放速度GST_FORMAT_TIME,        // 使用时间格式GST_SEEK_FLAG_FLUSH,    // 清空管道GST_SEEK_TYPE_SET,      // 绝对位置2 * GST_SECOND,         // 开始位置:2秒GST_SEEK_TYPE_NONE,     // 不设置结束位置0                       // 结束位置(未使用)
);

在 GStreamer - Seeking 中对一些细节内容做了补充,大家有兴趣可以自己过一遍,我这里罗列几个我感兴趣的点:

  1. Seek 可以指定一个时间段进行播放,如果 flag 中不包含 GST_SEEK_FLAG_SEGMENT,那么片段播放结束后返回 GST_MESSAGE_EOS(“message::eos”),如果包含那么返回的是 GST_MESSAGE_SEGMENT_DONE(“mesaage::segment-done”)。我们可以监听 GST_MESSAGE_SEGMENT_DONE 消息,重新再发送一个 seek 时间以便循环播放某个片段。
  2. 对于一个 Pipeline ,我们向其 Sink 节点发送 seek 事件即可,seek 事件会通过管道向上游传播,直到到达源元素(source element)。你当然可以向一个 bin 发送 seek 事件,默认情况下它的所有 sink 节点都会收到 seek 事件。
  3. Trick mode flags 可以跳过一些帧,这在某些场景下是有用的,例如:
    • GST_SEEK_FLAG_TRICKMODE_KEY_UNITS: 只解码/显示关键帧
    • GST_SEEK_FLAG_TRICKMODE_FORWARD_PREDICTED: 跳过 B 帧
    • GST_SEEK_FLAG_TRICKMODE_NO_AUDIO: 不解码音频

Step Events

在某些场景我们需要精细控制回放,比如在视频编辑软件中进行逐帧查看,或者在某些诊断和测试应用中使用。这时候我们使用 step event 来控制精确的步进播放,即逐帧或逐块地处理媒体流。创建 step event 接口原型如下:

GstEvent *
gst_event_new_step (GstFormat format, guint64 amount, gdouble rate, gboolean flush, gboolean intermediate)

函数参数说明:

  1. GstFormat format: 指定步进的格式,常见的格式包括:

    • GST_FORMAT_TIME: 时间格式(纳秒)
    • GST_FORMAT_BUFFERS: 缓冲区数量
    • GST_FORMAT_DEFAULT: 默认格式(帧数)
  2. guint64 amount: 指定步进的数量(根据 format 参数解释)

    • 如果 format 是 GST_FORMAT_TIME,则表示纳秒
    • 如果 format 是 GST_FORMAT_BUFFERS,则表示缓冲区数量
  3. gdouble rate: 指定步进的速率

    • 1.0 表示正常速度
    • 1.0 表示快进

    • <1.0 表示慢放
  4. gboolean flush: 是否在步进前清空管道

    • TRUE: 清空管道中的数据
    • FALSE: 保留管道中的数据
  5. gboolean intermediate: 是否为中间步进

    • TRUE: 表示这是一系列步进中的一个
    • FALSE: 表示这是单独的步进

使用示例:

// 创建一个步进事件,向前移动 1 秒(1000000000 纳秒)
GstEvent *step_event = gst_event_new_step (GST_FORMAT_TIME,      // 使用时间格式1000000000,          // 1秒 (纳秒单位)1.0,                 // 正常速度TRUE,                // 清空管道FALSE                // 非中间步进
);// 创建一个步进事件,向前移动 1 帧
GstEvent *step_event = gst_event_new_step (GST_FORMAT_BUFFERS,     // 使用时间格式1,          			// 1 帧1.0,                 	// 正常速度TRUE,                	// 清空管道FALSE                	// 非中间步进
);

主要用途:

  1. 实现帧步进功能(逐帧播放)
  2. 实现快进/跳跃播放,例如快进 5s
  3. 在视频编辑应用中进行精确定位
  4. 用于调试和分析媒体流

通常我们会在视频暂停的时候发送 step event 进行跳帧,另外注意到 step event 也有一个 rate 参数,实际体验下来这个 rate 并不是播放速度,而是计算步进的倍率。比如 amount = 1s ,rate = 2.0 时,发送这个 step event 后实际快进了 2s。

此外,step event 只会影响 sink 节点元素,而 seek event 则会影响整个 pipeline,每个元素都会对 seek event 做出反应,但 step event 速度更快。当你只对 video sink 节点进行快进后,你会发现音画发生了不同步,因为 audio sink 节点没有快进,因此可以同时对 video sink 和 audio sink 发送 step event 来避免音画不同步的问题。

Show me the code

本文的代码在 Basic tutorial 13: Playback speed 基础上做了一些修改和添加,主要是为了说明循环播放和跳转下一个 5s 要如何实现。详细代码参考 my_examples/basic-tutorial-13.c

这里对代码中重要部分进行说明

  /* Print usage map */g_print ("USAGE: Choose one of the following options, then press enter:\n"" 'P' to toggle between PAUSE and PLAY\n"" 'S' to increase playback speed, 's' to decrease playback speed\n"" 'k' to seek with segment [0, 10] and play looping\n"" 'D' to toggle playback direction\n"" 'N' to move to next frame (in the current direction, better in PAUSE)\n"" 'n' to move to 5s \n"" 'Q' to quit\n");

这里对程序功能进行说明,主要包含以下功能:

  1. ‘P’: 播放/暂停切换
  2. ‘S/s’: 调整播放速度(S增加,s减少)
  3. ‘k’: 在0-10秒区间循环播放
  4. ‘D’: 切换播放方向
  5. ‘N’: 下一帧(在当前方向,暂停状态下效果更好)
  6. ‘n’: 跳转5秒
  7. ‘Q’: 退出程序

注意,你需要在命令的执行窗口输入这些参数,而不是在播放窗口。

data.pipeline =gst_parse_launch("playbin uri=file:///Users/user/Documents/work/测试视频/video_1280x720_30fps_180sec.mp4",NULL);

上述代码中构建了一个 pipeline,播放本地文件,文件路径根据你自己电脑上的情况进行修改即可。

g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) handle_keyboard, &data);

这行代码是在设置键盘输入的监听器(事件处理),具体分解如下:

g_io_add_watch 是 GLib 库中的一个函数,用于添加对 I/O 事件的监听。它有三个主要参数:

  1. io_stdin:输入源(这里是标准输入,即键盘输入)
  2. G_IO_IN:表示监听输入事件
  3. handle_keyboard:回调函数,当有键盘输入时会调用这个函数
  4. &data:传递给回调函数的数据

当用户在键盘上输入内容时,handle_keyboard 函数会被触发,从而处理用户的输入命令。这是实现交互式命令行界面的关键部分,使程序能够响应用户的键盘输入。那么看看各个事件都在做什么。

static gboolean
handle_keyboard (GIOChannel * source, GIOCondition cond, CustomData * data)
{gchar *str = NULL;if (g_io_channel_read_line (source, &str, NULL, NULL,NULL) != G_IO_STATUS_NORMAL) {return TRUE;}switch (g_ascii_tolower (str[0])) {case 'p':data->playing = !data->playing;gst_element_set_state (data->pipeline,data->playing ? GST_STATE_PLAYING : GST_STATE_PAUSED);g_print ("Setting state to %s\n", data->playing ? "PLAYING" : "PAUSE");break;case 's':if (g_ascii_isupper (str[0])) {data->rate *= 2.0;} else {data->rate /= 2.0;}send_seek_event (data);break;case 'k':send_segment_seek_event(data);break;case 'd':data->rate *= -1.0;send_seek_event (data);break;case 'n':if (data->video_sink == NULL) {/* If we have not done so, obtain the sink through which we will send the step events */g_object_get (data->pipeline, "video-sink", &data->video_sink, NULL);}if(data->audio_sink == NULL) {g_object_get (data->pipeline, "audio-sink", &data->audio_sink, NULL);}if (g_ascii_isupper (str[0])) {gst_element_send_event (data->video_sink,gst_event_new_step (GST_FORMAT_BUFFERS, 1, ABS (data->rate), TRUE,FALSE));g_print ("Stepping one frame\n");}else {gst_element_send_event (data->video_sink,gst_event_new_step (GST_FORMAT_TIME, 5 * GST_SECOND, ABS (data->rate), TRUE,FALSE));gst_element_send_event (data->audio_sink,gst_event_new_step (GST_FORMAT_TIME, 5 * GST_SECOND, ABS (data->rate), TRUE,FALSE));g_print ("Stepping 5s\n");}break;case 'q':g_main_loop_quit (data->loop);break;default:break;}

这是一个键盘事件处理函数,针对不同的按键执行不同的操作:

  1. ‘p/P’:播放/暂停切换
  • 切换 data->playing 的状态
  • 通过 gst_element_set_state 设置播放器状态(PLAYING 或 PAUSED)
  1. ‘s/S’:调整播放速度
  • 大写’S’:速度翻倍(速率×2)
  • 小写’s’:速度减半(速率÷2)
  • 调用 send_seek_event 应用新的速率
  1. ‘k/K’:设置片段循环播放
  • 调用 send_segment_seek_event 进行片段循环播放
  1. ‘d/D’:改变播放方向
  • 将速率乘以-1(改变正负号)
  • 调用 send_seek_event 应用新的方向
  1. ‘n/N’:帧进/时间进
  • 首次使用时获取视频和音频接收器(sink)
  • 大写’N’:前进一帧(gst_event_new_step with GST_FORMAT_BUFFERS)
  • 小写’n’:前进5秒(gst_event_new_step with GST_FORMAT_TIME)
  1. ‘q/Q’:退出程序
  • 调用 g_main_loop_quit 退出主循环
GstBus *bus = gst_element_get_bus (data.pipeline);gst_bus_add_signal_watch(bus);g_signal_connect(G_OBJECT(bus), "message::segment-done", (GCallback)segment_done_callback, &data);

为了实现片段循环播放,我们这里监听 “message::segment-done” 信号,如果收到了信息,则调用 segment_done_callback

static void segment_done_callback(GstBus *bus, GstMessage *msg, CustomData *data) {g_print("Segment done, seeking again\n");// 重新执行 seekgst_element_seek(data->pipeline,1.0,GST_FORMAT_TIME,GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT,GST_SEEK_TYPE_SET,0 * GST_SECOND,GST_SEEK_TYPE_SET,5 * GST_SECOND);data->segment_loop_count++;g_print("segment loop count:%d\n", data->segment_loop_count);
}

segment_done_callback 函数中,我们重新发送一个 seek event,范围是 [0, 5s],并且仍然带着 GST_SEEK_FLAG_SEGMENT,如此一来当播放片段结束后, GStreamer 会发送一个 “message::segment-done” 信号,然后又会触发回调函数,如此一直循环。

其他代码大家都比较熟悉了,这里就不再赘述了。

参考

  • Basic tutorial 13: Playback speed
  • GStreamer - Seeking
  • my_examples/basic-tutorial-13.c

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

ssm127基于SSM的乡镇篮球队管理系统+jsp(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;乡镇篮球队管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本乡镇篮球队管理…

C#获取视频第一帧_腾讯云媒体处理获取视频第一帧

一、 使用步骤&#xff1a; 第一步、腾讯云开启万象 第二步、安装Tencent.QCloud.Cos.Sdk 包 第三步、修改 腾讯云配置 图片存储目录配置 第四步、执行获取图片并保存 二、封装代码 using System.Text; using System.Threading.Tasks;using COSXML.Model.CI; using COSXML.A…

【数据分享】2003-2022年各省土地利用面积统计数据

数据介绍 2003-2022年各省土地利用面积统计数据数据时间2003-2008、2013、2015-2017、2019、2022数据类型excel数据指标土地调查面积/万公顷农用地面积/万公顷园林面积/万公顷牧草地面积/万公顷建设用地面积/万公顷居民点及工矿用地/万公顷交通用地/万公顷水利设施用地/万公顷…

任务调度工具Spring Test

Spring Task 是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑。 作用&#xff1a;定时自动执行某段Java代码 应用场景&#xff1a; 信用卡每月还款提醒 银行贷款每月还款提醒 火车票售票系统处理未支付订单 入职纪念日为用户发送通知 一.…

20 轮转数组

20 轮转数组 20.1 轮转数组解决方案 class Solution { public:void rotate(vector<int>& nums, int k) {int n nums.size();k k % n; // 如果 k 大于数组长度&#xff0c;取模减少不必要的旋转// 第一步&#xff1a;反转整个数组reverse(nums.begin(), nums.end(…

字符串相关题解

目录 字母异位词 最长公共前缀 博主主页&#xff1a;东洛的克莱斯韦克-CSDN博客 字母异位词 49. 字母异位词分组 - 力扣&#xff08;LeetCode&#xff09; 这道题更像一道语法题&#xff0c;考察对容器的掌握情况。如果按题目要求去模拟&#xff0c;不仅要分析每个字符串&am…

【微软:多模态基础模型】(3)视觉生成

欢迎关注【youcans的AGI学习笔记】原创作品 【微软&#xff1a;多模态基础模型】&#xff08;1&#xff09;从专家到通用助手 【微软&#xff1a;多模态基础模型】&#xff08;2&#xff09;视觉理解 【微软&#xff1a;多模态基础模型】&#xff08;3&#xff09;视觉生成 【微…

CentOS8 启动错误,enter emergency mode ,开机直接进入紧急救援模式,报错 Failed to mount /home 解决方法

先看现场问题截图&#xff1a; 1.根据提示 按 ctrld 输入 root 密码&#xff0c;进入系统。 2. 在紧急模式下运行&#xff1a;journalctl -xe &#xff0c;查看相关日志&#xff0c;找到关键点&#xff1a; Failed to mount /home 3.接着执行修复命令&#xff1a; xfs_repa…

2024140读书笔记|《作家榜名著:生如夏花·泰戈尔经典诗选》——你从世界的生命的溪流浮泛而下,终于停泊在我的心头

2024140读书笔记|《作家榜名著&#xff1a;生如夏花泰戈尔经典诗选》——你从世界的生命的溪流浮泛而下&#xff0c;终于停泊在我的心头 《作家榜名著&#xff1a;生如夏花泰戈尔经典诗选》[印]泰戈尔&#xff0c;郑振铎译&#xff0c;泰戈尔的诗有的清丽&#xff0c;有的童真&…

lenovo联想ThinkBook 14 G5 ABP(21JE)原装出厂Windows11系统恢复镜像包下载

适用机型 &#xff1a;【21JE】 链接&#xff1a;https://pan.baidu.com/s/1FUjwN8ZeaQ9qr3kNalSkYg?pwdqasf 提取码&#xff1a;qasf 联想原装出厂系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office办公软件、联想电脑管家、联想浏览…

MySQL 数据类型

数值类型 int类型 类型说明tinyint1字节&#xff0c;范围从-128到127&#xff08;有符号&#xff09;&#xff0c;0到255&#xff08;无符号&#xff09;smallint2字节&#xff0c;范围从-2^15到2^15-1&#xff08;有符号&#xff09;&#xff0c;0到2^16-1&#xff08;无符号…

【WPF】Prism学习(三)

Prism Commands 1.复合命令&#xff08;Composite Commanding&#xff09; 这段内容主要介绍了在应用程序中如何使用复合命令&#xff08;Composite Commands&#xff09;来实现多个视图模型&#xff08;ViewModels&#xff09;上的命令。以下是对这段内容的解释&#xff1a; …

用go语言后端开发速查

文章目录 一、发送请求和接收请求示例1.1 发送请求1.2 接收请求 二、发送form-data格式的数据示例 用go语言发送请求和接收请求的快速参考 一、发送请求和接收请求示例 1.1 发送请求 package mainimport ("bytes""encoding/json""fmt""ne…

SpringCloud Alibaba入门简介和Nacos服务注册和配置中心

前面已经把spring cloud相关的组件都一一学了个遍,现在有点小佩服自己…本来计划今天周末好好出去玩一圈,天气太热了,39了都,还是在办公室学习吧,进行下面的springCloud Alibaba 学习吧…不废话了赶快进入正体 1. SpringCloud Alibaba入门简介 1.1 why会出现SpringCloud alib…

如何让Excel公式中的参数实现动态引用

如果你想成为Excel函数高手&#xff0c;仅仅掌握VLOOKUP和Countif等函数是远远不够的&#xff0c;起码你得学会使用INDIRECT函数&#xff0c;熟练掌握INDIRECT函数能让你从一个初学者晋级为高手&#xff0c;学会它就好比孙悟空掌握了72般变化的基本功&#xff0c;你说厉不厉害。…

【流量分析】常见webshell流量分析

免责声明&#xff1a;本文仅作分享&#xff01; 对于常见的webshell工具&#xff0c;就要知攻善防&#xff1b;后门脚本的执行导致webshell的连接&#xff0c;对于默认的脚本要了解&#xff0c;才能更清晰&#xff0c;更方便应对。 &#xff08;这里仅针对部分后门代码进行流量…

车载诊断架构 --- 关于DTC的开始检测条件

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所有人的看法和评价都是暂时的,只有自己的经历是伴随一生的,几乎所有的担忧和畏惧,都是来源于自己的想象,只有你真的去做了,才会发现有多快乐。…

掌握 Spring Boot 的最佳方法 – 学习路线图

在企业界&#xff0c;人们说“Java 永垂不朽&#xff01;”。但为什么呢&#xff1f;Java 仍然是开发企业应用程序的主要平台之一。大型公司使用企业应用程序来赚钱。这些应用程序具有高可靠性要求和庞大的代码库。根据Java开发人员生产力报告&#xff0c;62% 的受访开发人员使…

《操作系统 - 清华大学》3 -3:连续内存分配:内存碎片与分区的动态分配

文章目录 0. 概述1. 内存碎片问题2. 动态分配3. 首次适配算法4. 最优适配算法5. 最差适配算法 0. 概述 内存分配是操作系统管理过程中很重要的环节&#xff0c;首先需要考虑的是一块连续区域分配的过程&#xff0c;这个过程中会有很多问题&#xff0c;首先比较关注的一个问题是…

MySQL学习/复习3约束

一、表的常用约束 二、null、not null 三、默认值default 3.1default与null 四、注释commen 注意事项&#xff1a;desc查不到注释 五、zerofill 5.1填充0以控制宽度 六、primary_key 6.1复合主键 七、auto_increment 7.1last_insert_id() 八、unique 8.1unique与primary_key …