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.mzph.cn/bicheng/60974.shtml

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

相关文章

【微软:多模态基础模型】(1)从专家到通用助手

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

GRE做题笔记(零散的个人经验)

locomotive机车By 1813, the Luddite resistance had all but vanished. all but表示“几乎完全”的程度&#xff0c;或者表示排除piston活塞attributed to 归因于how a sportsperson accounted for their own experience of stress 运动员如何解释自己的压力经历 &#xff0c;…

【蓝桥杯算法】Java的基础API

1. BigInteger 的使用 1.1. 判素数 package 模板;import java.math.BigInteger; import java.util.Scanner;public class 判素数 {static Scanner in new Scanner(System.in);public static void main(String[] args) {int q in.nextInt();while (q-- > 0) {BigInteger …

【项目实战】基于 LLaMA-Factory 通过 LoRA 微调 Qwen2

【项目实战】基于 LLaMAFactory 通过 LoRA 微调 Qwen2 一、项目介绍二、环境准备1、环境准备2、安装LLaMa-Factory3、准备模型数据集3.1 模型准备3.2 数据集准备 三、微调1、启动webui2、选择参数3、训练 四、测试五、总结 一、项目介绍 LLaMA-Factory是一个由北京航空航天大学…

第23课-C++-红黑树的插入与旋转

&#x1f307;前言 红黑树是一种自平衡的二叉搜索树&#xff0c;因其出色的性能&#xff0c;广泛应用于实际中。Linux 内核中的 CFS 调度器便是一个使用红黑树的例子&#xff0c;这足以说明它的重要性。红黑树的实现通过红黑两种颜色的控制来维持平衡&#xff0c;并在必要时使…

基于 CentOS7.6 的 Docker 下载常用的容器(MySQLRedisMongoDB),解决拉取容器镜像失败问题

安装MySQL&Redis&MongoDB mysql选择是8版本&#xff0c;redis是选择4版本、mongoDB选择最新版&#xff0c;也可以根据自己的需要进行下载对应的版本&#xff0c;无非就是容器名:版本号 这样去拉去相关的容器镜像。如果你还不会在服务器中安装 docker&#xff0c;可以查…

C#/WinForm拖拽文件上传

一、首先创建一个上传文件的类&#xff0c;继承Control类&#xff0c;如下&#xff1a; public class UploadControl : Control{private Image _image;public UploadControl(){this.SetStyle(ControlStyles.UserPaint | //控件自行绘制&#xff0c;而不使用操作系统的绘制Cont…

ubuntu将firewall-config导出为.deb文件

firewall-config ubuntu是canonial 公司维护的&#xff0c;用wireshark测过&#xff0c;开机会给他们公司发遥测&#xff08;开了ufw阻塞所有连接也一样&#xff0c;canonial在里面把代码改了&#xff09;firewall-config是fedora(爱好者维护&#xff0c;公益版本)自带的防火墙…

蓝桥杯备考——算法

一、排序 冒泡排序、选择排序、插入排序、 快速排序、归并排序、桶排序 二、枚举 三、二分查找与二分答案 四、搜索&#xff08;DFS&#xff09; DFS&#xff08;DFS基础、回溯、剪枝、记忆化&#xff09; 1.DFS算法&#xff08;深度优先搜索算法&#xff09; 深度优先搜…

Javascript垃圾回收机制-运行机制(大厂内部培训版本)

前言 计算机基本组成&#xff1a; 我们编写的软件首先读取到内存&#xff0c;用于提供给 CPU 进行运算处理。 内存的读取和释放&#xff0c;决定了程序性能。 冯诺依曼结构 解释和编译 这两个概念怎么理解呢。 编译相当于事先已经完成了可以直接用。好比去饭店吃饭点完上…

ffmpeg+D3D实现的MFC音视频播放器,支持录像、截图、音视频播放、码流信息显示等功能

一、简介 本播放器是在vs2019 x86下开发&#xff0c;通过ffmpeg实现拉流解码功能&#xff0c;通过D3D实现视频的渲染功能。截图功能采用libjpeg实现&#xff0c;可以截取jpg图片&#xff0c;图片的默认保存路径是在C:\MYRecPath中。录像功能采用封装好的类Mp4Record实现&#x…

NodeJS 百度智能云文本转语音(实测)

现在文本转语音的技术已经非常完善了&#xff0c;尽管网络上有许多免费的工具&#xff0c;还是测试了专业的服务&#xff0c;选择了百度的TTS服务。 于是&#xff0c;在百度智能云注册和开通了文本转语音的服务&#xff0c;尝试使用NodeJS 实现文本转语音服务。但是百度的文档实…

信也科技和云杉网络的AI可观测性实践分享

1. 信也科技 2、云杉网络 2.1 中国移动

解析煤矿一张图

解析煤矿一张图 ​ 煤矿一张图是指通过数字化、智能化技术将煤矿的各项信息、数据和资源进行集中展示和管理&#xff0c;形成一个综合的可视化平台。这一平台将矿井的地理信息、设备状态、人员位置、安全生产、环境监测等信息整合成一个统一的“图形”&#xff0c;以便于管理者…

SpringBootTest常见错误解决

1.启动类所在包错误 问题 由于启动类所在包与需要自动注入的类的包不在一个包下&#xff1a; 启动类所在包&#xff1a; com.exmaple.test_02 但是对于需要注入的类却不在com.exmaple.test_02下或者其子包下&#xff0c;就会导致启动类无法扫描到该类&#xff0c;从而无法对…

Java 全栈知识体系

包含: Java 基础, Java 部分源码, JVM, Spring, Spring Boot, Spring Cloud, 数据库原理, MySQL, ElasticSearch, MongoDB, Docker, k8s, CI&CD, Linux, DevOps, 分布式, 中间件, 开发工具, Git, IDE, 源码阅读&#xff0c;读书笔记, 开源项目...

高效管理生产线:哪些项目管理工具最适合制造企业?

制造业的生产管理往往涉及复杂的流程和多部门协作&#xff0c;如何确保各环节顺利对接、信息准确传递&#xff0c;是每一家制造企业都在不断优化的问题。面对这些管理难题&#xff0c;越来越多的制造企业引入了项目管理软件&#xff0c;通过直观的任务分配、进度跟踪、数据反馈…

微信小程序 https://thirdwx.qlogo.cn 不在以下 downloadFile 合法域名列表中

授权登录后&#xff0c;拿到用户头像进行加载&#xff0c;但报错提示&#xff1a; https://thirdwx.qlogo.cn 不在以下 downloadFile 合法域名列表中 解决方法一&#xff08;未完全解决&#xff0c;临时处理&#xff09;&#xff1a;在微信开发者工具将不校验...勾上就可以访问…

Android - Pixel 6a 手机OS 由 Android 15 降级到 Android 14 操作记录

Pixel 6a 手机由 Android 14 升级到 Android 15了&#xff0c;但是由于一些原因又想降级回 Android 14&#xff0c; 能降吗&#xff1f;该怎么降级呢&#xff1f;本篇文章来记述实际操作过程&#xff0c;希望能给想做相同操作的人一些帮助。 答案当然是能降&#xff0c;而且我…

SOL链上Meme生态的崛起与未来#Dapp开发#链游#交易所#公链搭建

近年来&#xff0c;随着区块链技术的普及和NFT文化的流行&#xff0c;meme&#xff08;网络迷因&#xff09;逐渐成为区块链生态中的重要组成部分。meme不仅是一种互联网文化符号&#xff0c;更逐步渗透进了去中心化金融&#xff08;DeFi&#xff09;、NFT和元宇宙等多个领域&a…