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");
  // 重新执行 seek
  gst_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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

320页PDF | 集团IT蓝图总体规划报告-德勤(限免下载)

一、前言 这份报告是集团IT蓝图总体规划报告-德勤。在报告中详细阐述了德勤为某集团制定的全面IT蓝图总体规划&#xff0c;包括了集团信息化目标蓝图、IT应用规划、数据规划、IT集成架构、IT基础设施规划以及IT治理体系规划等关键领域&#xff0c;旨在为集团未来的信息化发展提…

Python毕业设计选题:基于django+vue的二手物品交易系统

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 店铺管理 二手物品管理 广告管理 留言反馈 订单…

Android CoordinatorLayout:打造高效交互界面的利器

目录 一、CoordinatorLayout 介绍及特点 二、使用方法 2.1 创建 CoordinatorLayout 布局 2.2 添加需要协调的子视图 2.3 自定义 Behavior 三、结语 相关推荐 在Android开发中&#xff0c;面对复杂多变的用户界面需求&#xff0c;CoordinatorLayout以其强大的交互管理能力…

docker-hub 无法访问,使用windows魔法拉取docker images再上传到linux docker环境中

云机的服务器是可以docker拉取镜像的&#xff0c;但是本地的虚拟机、物理服务器等网络环境不好的情况&#xff0c;是无法访问docker-hub的&#xff0c;即使更换了docker镜像源国内源也无法使用。 本文章使用 在魔法网络环境下的windows&#xff0c;下载docker images后&#xf…

在Ubuntu22.04上源码构建ROS noetic环境

Ubuntu22.04上源码构建ROS noetic 起因准备环境创建工作目录并下载源码安装编译依赖包安装ros_comm和rosconsole包的两个补丁并修改pluginlib包的CMakeLists的编译器版本编译安装ROS noetic和ros_test验证 起因 最近在研究VINS-Mono从ROS移植到ROS2&#xff0c;发现在编写feat…

【linux学习指南】VSCode部署Ubantu云服务器,与Xshell进行本地通信文件编写

文章目录 &#x1f4dd;前言&#x1f320; 步骤&#x1f309;测试同步 &#x1f6a9;总结 &#x1f4dd;前言 本文目的是讲使用Vscode连接Ubantu,与本地Xshell建立通信同步文件编写。 查看本机系统相关信息&#xff1a; cat /etc/lsb*DISTRIB_IDUbuntu: 表示这是 Ubuntu 发行…

【JavaSE线程知识总结】

多线程 一.创建线程1.多线程创建方式一(Thread)2.多线程创键方式二(Runnable)3.线程创建方式三 二.线程安全问题解决办法1.使用同步代码块synchornized 2 .使用Lock解决线程安全问题 三.总结 线程就是程序内部的一条执行流程 一.创建线程 常用的方法 Thread.currentThread()…

用OMS进行 OceanBase 租户间数据迁移的测评

基本概念 OceanBase迁移服务&#xff08;&#xff0c;简称OMS&#xff09;&#xff0c;可以让用户在同构或异构 RDBMS 与OceanBase 数据库之间进行数据交互&#xff0c;支持数据的在线迁移&#xff0c;以及实时增量同步的复制功能。 OMS 提供了可视化的集中管控平台&#xff…

第一个 Flutter 项目(1)共46节

前端开发工具vs code&#xff0c;安装Flutter sdk&#xff0c;如果你的下载速度比较慢&#xff0c;可以选择这个&#x1f604; flutter sdk 解压码&#xff1a;stwq 配置可以看这Flutter 新建工程一直等待 解决办法-CSDN博客 如果你是新的 Flutter 开发者&#xff0c;我们建…

POI实现根据PPTX模板渲染PPT

目录 1、前言 2、了解pptx文件结构 3、POI组件 3.1、引入依赖 3.2、常见的类 3.3、实现原理 3.4、关键代码片段 3.4.1、获取ppt实例 3.4.2、获取每页幻灯片 3.4.3、循环遍历幻灯片处理 3.4.3.1、文本 3.4.3.2、饼图 3.4.3.3、柱状图 3.4.3.4、表格 3.4.3.5、本地…

高级数据结构——hash表与布隆过滤器

文章目录 hash表与布隆过滤器1. hash函数2. 选择hash函数3. 散列冲突3.1 负载因子3.2 冲突解决3. STL中的散列表 4. 布隆过滤器4.1 背景1. 应用场景2. 常见的处理场景&#xff1a; 4.2 布隆过滤器构成4.3 原理4.4 应用分析4.5 要点 5. 分布式一致性hash5.1 缓存失效问题 6. 大数…

小程序19-微信小程序的样式和组件介绍

在小程序中不能使用 HTML 标签&#xff0c;也就没有 DOM 和 BOM&#xff0c;CSS 也仅支持部分选择器 小程序提供了 WXML 进行页面结构的编写&#xff0c;WXSS 进行页面的样式编写 WXML 提供了 view、text、image、navigator等标签构建页面结构&#xff0c;小程序中标签称为组件…

[Linux]多线程详解

多线程 1.线程的概念和理解1.1线程的优点1.2线程的缺点1.3线程的设计1.4线程 VS 进程 2.线程控制2.1线程等待2.2 线程终止2.3 线程分离 3.线程互斥3.1背景3.2抢票代码演示3.3保护公共资源&#xff08;加锁&#xff09;3.3.1创建锁/销毁锁3.3.2申请锁/尝试申请锁/解锁 3.4解决抢…

CSP-J 2024题解

省流&#xff1a;300->260 乐。 Poker: 我考场上寻思着会不会有人写成了 joker.in joker.out&#xff0c;结果真的有 joker Sol EZ problem&#xff0c;拿 set 搞一下就行了&#xff08;虽然我赛事没想到&#xff0c;用了 map&#xff09; Code #include <bits/std…

【miniMax开放平台-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…

python机器人Agent编程——多Agent框架的底层逻辑(上)

目录 一、前言二、两个核心概念2.1 Routines&#xff08;1&#xff09;清晰的Prompt&#xff08;2&#xff09;工具调用json schema自动生成&#xff08;3&#xff09;解析模型的toolcall指令&#xff08;4&#xff09;单Agent的循环决策与输出 PS.扩展阅读ps1.六自由度机器人相…

达梦 DG

监视器 switchover 关于达梦DG switchover的细节&#xff0c;以下是一些关键步骤和注意事项&#xff1a; • 切换前检查确认&#xff1a; • 确认数据库版本和DG架构&#xff0c;包括IP信息及切换角色前后的情况。 • 检查DG切换方式&#xff0c;是switch over还是fail ove…

blind-watermark - 水印绑定

文章目录 一、关于 blind-watermark安装 二、bash 中使用三、Python 调用1、基本使用2、attacks on Watermarked Image3、embed images4、embed array of bits 四、并发五、相关 Project 一、关于 blind-watermark Blind watermark 基于 DWT-DCT-SVD. github : https://githu…

从零开始的c++之旅——二叉搜索树

1、二叉搜索树概念 1. ⼆叉搜索树的概念 ⼆叉搜索树⼜称⼆叉排序树&#xff0c;它或者是⼀棵空树&#xff0c;或者是具有以下性质的⼆叉树: • 若它的左⼦树不为空&#xff0c;则左⼦树上所有结点的值都⼩于等于根结点的值 • 若它的右⼦树不为空&#xff0c;则右⼦树上所有结…

类与对象;

目录 一、认识类&#xff1b; 1、类的引入&#xff1b; 2、类的定义&#xff1b; 类的两种定义方式&#xff1a; 3、类的访问限定符及封装&#xff1b; 4、类的作用域&#xff1b; 5、类的实例化&#xff1b; 6、类对象模型&#xff1b; 计算类对象的大小&#xff1b; …