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

news2024/11/18 3:37:43

系列文章目录

  • 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

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

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

相关文章

量子计算与人工智能的交汇:科技未来的新引擎

引言 在当今飞速发展的科技世界&#xff0c;人工智能&#xff08;AI&#xff09;和量子计算无疑是最受瞩目的两大前沿领域。人工智能凭借其在数据处理、模式识别以及自动化决策中的强大能力&#xff0c;已经成为推动各行业数字化转型的重要力量。而量子计算则通过颠覆传统计算机…

【Oracle篇】掌握SQL Tuning Advisor优化工具:从工具使用到SQL优化的全方位指南(第六篇,总共七篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

变分自编码器(VAE, Variational Autoencoder)

代码说明 VAE 模型结构&#xff1a; 编码器将输入数据&#xff08;如 MNIST 图像&#xff09;映射到潜在空间&#xff0c;生成均值 (mu) 和对数方差 (logvar)。 通过重新参数化技巧 (reparameterize) 从正态分布中采样潜在向量 z。 解码器将潜在向量 z 映射回原始空间&#xf…

DataWorks on EMR StarRocks,打造标准湖仓新范式

在大数据领域&#xff0c;数据仓库和实时分析系统扮演着至关重要的角色。DataWorks 基于大数据引擎&#xff0c;为数据仓库/数据湖/湖仓一体等解决方案提供统一的全链路大数据开发治理平台&#xff0c;为用户带来智能化的数据开发和分析体验。而阿里云提供的 EMR Serverless St…

Redis系列之底层数据结构ZipList

Redis系列之底层数据结构ZipList 实验环境 Redis 6.0 什么是Ziplist&#xff1f; Ziplist&#xff0c;压缩列表&#xff0c;这种数据结构会根据存入数据的类型和大小&#xff0c;分配大小不同的空间&#xff0c;所以是为了节省内存而采用的。因为这种数据结构是一种完整连续…

【FPGA开发】AXI-Stream总线协议解读

文章目录 AXI-Stream概述协议中一些定义字节定义流的定义 数据流类别字节流连续对齐流连续不对齐流稀疏流 协议的信号信号列表 文章为个人理解整理&#xff0c;如有错误&#xff0c;欢迎指正&#xff01; 参考文献 ARM官方手册 《IHI0051B》 AXI-Stream概述 协议中一些定义 A…

c# 调用c++ 的dll 出现找不到函数入口点

今天在调用一个设备的dll文件时遇到了一点波折&#xff0c;因为多c 不熟悉&#xff0c;调用过程张出现了找不到函数入口点&#xff0c;一般我们使用c# 调用c 文件&#xff0c;还是比较简单。 [DllImport("AtnDll2.dll",CharSet CharSet.Ansi)]public static extern …

L11.【LeetCode笔记】有效的括号

目录 1.题目 2.分析 理解题意 解决方法 草稿代码 ​编辑 逐一排错 1.当字符串为"["时,分析代码 2.当字符串为"()]"时,分析代码 正确代码(isValid函数部分) 提交结果 3.代码优化 1.题目 https://leetcode.cn/problems/valid-parentheses/descri…

Unity类银河战士恶魔城学习总结(P129 Craft UI 合成面板UI)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了合成面板的UI设置 UI_CraftWindow.cs 字段作用&#xff1a; UI 组件&#xff1a; itemName / itemDescription / icon&#…

Python酷库之旅-第三方库Pandas(221)

目录 一、用法精讲 1036、pandas.DatetimeIndex.to_pydatetime方法 1036-1、语法 1036-2、参数 1036-3、功能 1036-4、返回值 1036-5、说明 1036-6、用法 1036-6-1、数据准备 1036-6-2、代码示例 1036-6-3、结果输出 1037、pandas.DatetimeIndex.to_series方法 10…

通过JS实现下载图片到本地教程分享

今天分享的这个方法我之前自己试了一下&#xff0c;感觉还行&#xff0c;原理就是通过<a>标签的新增属性实现的&#xff0c;然后可以强制触发下载功能&#xff0c;废话不多说&#xff0c;直接上教程。 首先在HTML写下面的代码: <a href"img.jpg" download…

二、神经网络基础与搭建

神经网络基础 前言一、神经网络1.1 基本概念1.2 工作原理 二、激活函数2.1 sigmoid激活函数2.1.1 公式2.1.2 注意事项 2.2 tanh激活函数2.2.1 公式2.2.2 注意事项 2.3 ReLU激活函数2.3.1 公式2.3.2 注意事项 2.4 SoftMax激活函数2.4.1 公式2.4.2 Softmax的性质2.4.3 Softmax的应…

Unreal engine5实现类似鬼泣5维吉尔二段跳

系列文章目录 文章目录 系列文章目录前言一、实现思路二、具体使用蓝图状态机蓝图接口三、中间遇到的问题 前言 先看下使用Unreal engine5实现二段跳的效果 一、实现思路 在Unreal Engine 5 (UE5) 中使用蓝图系统实现类似于《鬼泣5》中维吉尔的二段跳效果&#xff0c;可以通…

RAG经验论文《FACTS About Building Retrieval Augmented Generation-based Chatbots》笔记

《FACTS About Building Retrieval Augmented Generation-based Chatbots》是2024年7月英伟达的团队发表的基于RAG的聊天机器人构建的文章。 这篇论文在待读列表很长时间了&#xff0c;一直没有读&#xff0c;看题目以为FACTS是总结的一些事实经验&#xff0c;阅读过才发现FAC…

Java 简单家居开关系统

1.需求&#xff1a; 面向对象编程实现智能家居控制系统&#xff08;简单的开关&#xff09; 2.实现思路 1.定义设备类&#xff1a;创建设备对象代表家里的设备 JD类&#xff1a; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;D…

任务调度工具Spring Test

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

SAFETY LAYERS IN ALIGNED LARGE LANGUAGEMODELS: THE KEY TO LLM SECURITY

目录 概要 背景 大语言模型对齐 对齐大语言模型中的过度拒绝 微调攻击 研究设置 问题定义 对齐的大语言模型 大语言模型的提示模板 安全层的存在和定位 安全层的存在性 1.从余弦相似度说明 2.从向量之间角度差异说明 3.与预训练LLM对比说明 安全层的定位 1.推理…

netcore Kafka

一、新建项目KafakDemo <ItemGroup><PackageReference Include"Confluent.Kafka" Version"2.6.0" /></ItemGroup> 二、Program.cs using Confluent.Kafka; using System; using System.Threading; using System.Threading.Tasks;names…

在k8s上部署Crunchy Postgres for Kubernetes

目录 一、前言二、安装Crunchy Postgres for Kubernetes三、部署一个简单的postgres集群四、增加pgbouncer五、数据备份六、备份恢复七、postgres配置参数八、数据导入九、权限管理 一、前言 Crunchy Postgres可以帮助我们在k8s上快速部署一个高可用、具有自动备份和恢复功能的…

函数指针示例

目录&#xff1a; 代码&#xff1a; main.c #include <stdio.h> #include <stdlib.h>int Max(int x, int y); int Min(int x, int y);int main(int argc, char**argv) {int x,y;scanf("%d",&x);scanf("%d",&y);int select;printf(&q…