文章目录

  • 1.Goal
  • 2.介绍
  • 3. A Trick mode player
    • 3.1 Compile
    • 3.2 Code
  • 4.Analyze
  • 5.讨论

1.Goal

快进、倒放和慢动作都是所谓的技巧模式,它们都有修改正常播放速率的共同之处。本教程展示了如何实现这些效果,并在处理中添加frame-stepping。特别是,它显示

  • 如何改变播放速率,比正常速度快或慢,向前或向后
  • 如何逐帧推进视频

2.介绍

快进是一种以高于正常(预期)速度播放媒体的技术;而慢动作使用的速度低于预期的速度。反向回放做同样的事情,只是向后,从流的结尾到开始。
所有这些技术所做的就是改变播放速率,对于正常播放,它是一个等于1.0的变量,对于快速播放,它大于1.0(绝对值),对于慢速播放,它小于1.0(绝对值)。正向播放是正的,反向播放是负的。
Gstreamer提供了两种机制来更改回放速率:Step Events和Seek Events。Step Events除了更改后续的播放速率(仅为正值)也允许跳过给定数量的媒体。Seek Events允许跳到流中的任何位置,并设置正播放率和负播放率。
在基础教程4:时间管理搜索事件中,已经显示了时间,使用辅助函数来隐藏事件的复杂性。本教程将进一步说明如何使用这些事件。
Step Events是改变回放速率的一种更方便的方式,因此创建它们所需的参数数量减少了,然而,它们也有一些缺点,因此在本教程中使用Seek Events。Step Events只会影响sink(在pipeline的尾端),所以只有当pipeline的其余部分能够支持以不同的速度运行时。它们才会起作用,而Seek Events会贯穿整个pipeline,以便每个elements都能对它们做出反应。Step Events的好处是它们可以更快的采取行动。Step Events也无法更改回放的方向。
要使用这些事件,首先要创建它们,然后将它们传递到pipeline上,在pipeline中向上传播,直到到达可以处理它们的elements。如果一个事件被传递到像playbin这样的bin元素上,它将简单地将事件提供给所有sink,这将执行多个查找。常见的方法是通过video-sink或audio-sink检索playbin的sink,并将事件直接提供给sink。
Frame stepping是一种允许逐帧播放视频的技术。它是通过暂停pipeline来实现的,然后每次发送Step Events跳过一帧。

3. A Trick mode player

3.1 Compile

gcc basic-tutorial-13.c -o basic-tutorial-13 `pkg-config --cflags --libs gstreamer-1.0`

3.2 Code

#include <string.h>
#include <stdio.h>
#include <gst/gst.h>typedef struct _CustomData
{GstElement *pipeline;GstElement *video_sink;GMainLoop *loop;gboolean playing;             /* Playing or Paused */gdouble rate;                 /* Current playback rate (can be negative) */
} CustomData;/* Send seek event to change rate */
static void
send_seek_event (CustomData * data)
{gint64 position;GstEvent *seek_event;/* Obtain the current position, needed for the seek event */if (!gst_element_query_position (data->pipeline, GST_FORMAT_TIME, &position)) {g_printerr ("Unable to retrieve current position.\n");return;}/* Create the seek event */if (data->rate > 0) {seek_event =gst_event_new_seek (data->rate, GST_FORMAT_TIME,GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET,position, GST_SEEK_TYPE_END, 0);} else {seek_event =gst_event_new_seek (data->rate, GST_FORMAT_TIME,GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, 0,GST_SEEK_TYPE_SET, position);}if (data->video_sink == NULL) {/* If we have not done so, obtain the sink through which we will send the seek events */g_object_get (data->pipeline, "video-sink", &data->video_sink, NULL);}/* Send the event */gst_element_send_event (data->video_sink, seek_event);g_print ("Current rate: %g\n", data->rate);
}/* Process keyboard input */
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 '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);}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");break;case 'q':g_main_loop_quit (data->loop);break;default:break;}g_free (str);return TRUE;
}int
main (int argc, char *argv[])
{CustomData data;GstStateChangeReturn ret;GIOChannel *io_stdin;/* Initialize GStreamer */gst_init (&argc, &argv);/* Initialize our data structure */memset (&data, 0, sizeof (data));/* 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"" 'D' to toggle playback direction\n"" 'N' to move to next frame (in the current direction, better in PAUSE)\n"" 'Q' to quit\n");/* Build the pipeline */data.pipeline =gst_parse_launch("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm",NULL);/* Add a keyboard watch so we get notified of keystrokes */
#ifdef G_OS_WIN32io_stdin = g_io_channel_win32_new_fd (fileno (stdin));
#elseio_stdin = g_io_channel_unix_new (fileno (stdin));
#endifg_io_add_watch (io_stdin, G_IO_IN, (GIOFunc) handle_keyboard, &data);/* Start playing */ret = gst_element_set_state (data.pipeline, 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.pipeline);return -1;}data.playing = TRUE;data.rate = 1.0;/* Create a GLib Main Loop and set it to run */data.loop = g_main_loop_new (NULL, FALSE);g_main_loop_run (data.loop);/* Free resources */g_main_loop_unref (data.loop);g_io_channel_unref (io_stdin);gst_element_set_state (data.pipeline, GST_STATE_NULL);if (data.video_sink != NULL)gst_object_unref (data.video_sink);gst_object_unref (data.pipeline);return 0;
}

4.Analyze

在main函数中的初始化代码和之前一样:实例化了一个playbin管道,安装了一个I/O watch来跟踪按键,并执行一个GLib主循环.
下面是按键处理函数

/* Process keyboard input */
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;

暂停/播放切换由gst_element_set_state()处理,如之前的教程所示.

case 's':if (g_ascii_isupper (str[0])) {data->rate *= 2.0;} else {data->rate /= 2.0;}send_seek_event (data);break;
case 'd':data->rate *= -1.0;send_seek_event (data);break;

S和s将当前播放速度加倍或减半,d将当前播放方向反转.在这种两种情况下大会更新rate并调用send seek事件.

/* Send seek event to change rate */
static void send_seek_event (CustomData *data) {gint64 position;GstEvent *seek_event;/* Obtain the current position, needed for the seek event */if (!gst_element_query_position (data->pipeline, GST_FORMAT_TIME, &position)) {g_printerr ("Unable to retrieve current position.\n");return;}

此函数创建一个新的seek event, 并将其发送到pipeline以更新速率。首先,使用gst_element_query_position恢复到当前位置,这是必须的,因为seek event是跳转到流中的另一个位置,并且,由于我们并不想移动,所以要跳转到当前位置。使用step event会更简单,但是该事件的功能还不完全。

/* Create the seek event */
if (data->rate > 0) {seek_event = gst_event_new_seek (data->rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_END, -1);
} else {seek_event = gst_event_new_seek (data->rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, position);
}

seek event使用gst_event_new_seek()创建,其参数是新的速率,新的起始位置和新的停止位置。无论播放方向如何,开始位置必须小于停止位置,因此两个播放方向的处理不同。


GstEvent *gst_event_new_seek(gdouble rate, GstFormat format, GstSeekFlags flags, GstSeekType start_type, gint64 start, GstSeekType stop_type, gint64 stop);//GstSeekType
enum {GST_SEEK_TYPE_NONE, //no change in position is requiredGST_SEEK_TYPE_SET, // absolute position is requestedGST_SEEK_TYPE_END  // relative position to duration is requested
}

seek event以给定的速率配置从开始到停止之间的管道的播放,也称为播放段。
rate=1表示正常的播放速率,2表示2倍速,负值表示向后播放。不允许使用rate=0,应该使用PAUSE。
pipeline有一个默认的播放段,起始位置为0,停止位置为-1,速率为1.0。可以使用GST_QUERY_SEGMENT查询当前配置的播放段。
start_type和stop_type指定了如何在播放段中调整当前配置的start和stop字段。可以对最后配置的值进行相对或绝对的调整GST_SEEK_TYPE_NONE表示位置不该更新。
当rate为正并且启动已更新时,播放将从新配置的起始位置开始。
对于负速率,播放将从新配置的停止位置(如果有)开始。如果停止位置被更新,它必须同于-1(#GST_CLOCK_TIME_NONE)负速率。


if (data->video_sink == NULL) {/* If we have not done so, obtain the sink through which we will send the seek events */g_object_get (data->pipeline, "video-sink", &data->video_sink, NULL);
}

如简介中所述,为了避免执行多个seek event只被发送到一个sink,在本示例中sink为video sink。它是通过video-sink属性从playbin中获得的。它在初始化的时候被读取,因为实际的sink可能会根据媒体内容而变化,这直到管道是PLAYING和一些媒体被读取时才会知道。

// send the event
gst_element_send_event(data->video_sink, seek_event);

新的Event最终通过gst_element_send_event()发送到指定的sink。
回到按键处理程序,

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);}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");break;

使用gst_event_new_step()创建一个新的step event,它的参数指定了跳过的数量和新的速率。
为防止video_sink为空,从playbin中获取,和上面一样。


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

创建一个新的step event。它的目的是指示sink跳过的媒体量(以format表示),它可用来实现逐帧视频步进或快速魔术模式。
rate<=0.0是不允许的。rate=0的效果用PAUSE实现,rate<0.0的效果通过使用seek event反转播放的方向。
flush标志将在开始step操作之前清除pipeline中的任何pending数据。
intermediate标志用于指示该pipeline步进操作是更大步进操作的一部分。

5.讨论

本教程展示了:

  • 如何使用seek event更改播放速率,用gst_event_new_seek()进行创建,用gst_element_send_event()发送到管道
  • 如何使用gst_event_new_step()创建的step event逐帧推进视频

Gstreamer基础教程13:Playback Speed相关推荐

  1. 【GStreamer开发】GStreamer基础教程07——多线程和Pad的有效性

    目标 GStreamer会自动处理多线程这部分,但在有些情况下,你需要手动对线程做解耦.本教程会教你怎样才能做到这一点,另外也展示了Pad的有效性.主要内容包括: 如何针对部分的pipeline建立一 ...

  2. GStreamer基础教程10——GStreamer工具

    目标 GStreamer提供了一系列方便使用的工具.这篇教程里不牵涉任何代码,但还是会讲一些有用的内容: 如何在命令行下建立一个pipeline--完全不使用C 如何找出一个element的Capab ...

  3. ArcGIS二次开发基础教程(13):网络分析之最近设施分析

    ArcGIS二次开发基础教程(13):网络分析之最近设施分析 最近设施分析 /// <summary>/// Geodatabase function: open work space// ...

  4. gstreamer基础教程13-Playback speed

    索引:https://blog.csdn.net/knowledgebao/article/details/84621238 Goal 本章的目的是如何是播放实现一些特技,比如:快进,快退,慢放等.如 ...

  5. GStreamer基础教程07 - 播放速率控制

    摘要 在常见的媒体播放器中,通常可以看到快进,快退,慢放等功能,这部分功能被称为"特技模式(Trick Mode)",这些模式有个共同点:都通过修改播放的速率来达到相应的目的. 本 ...

  6. 【GStreamer学习】之GStreamer基础教程

    目标 没有什么比在屏幕上打印出"Hello World"更能获得对软件库的第一印象了! 但是由于我们正在学习多媒体框架,所以我们将输出"Hello World!" ...

  7. Gstreamer基础教程10: Gstreamer 工具

    文章目录 1. Goal 2. 介绍 3. gst-lanuch-1.0 3.1 Elements 3.2 Properties 3.3 Named elements 3.4 Pads 3.5 Cap ...

  8. Gstreamer基础教程10:GStreamer tools

    文章目录 目标 一.Introduction(简介) 二.gst-launch-1.0 1.Elements 2.Properties(属性) 3.Named elements(元素重命名) 4.Pa ...

  9. GStreamer基础教程04 - 动态连接Pipeline

    摘要 在以前的文章中,我们了解到了2种播放文件的方式:一种是在知道了文件的类型及编码方式后,手动创建所需Element并构造Pipeline:另一种是直接使用playbin,由playbin内部动态创 ...

最新文章

  1. 学习一下rails hash 的方法
  2. 关系运算符、逻辑 运算符与三元运算符
  3. hdu 3879(最小割模型求解最大权闭合图)
  4. 计算机应用基础自考,自考计算机应用基础
  5. Blazor The specified deps.json \bin\Debug\net5.0\BlazorWebApp.deps.json] does not exist
  6. 基于TCP协议的游戏代理接口测试工具<二>:工具架构与代理主体实现
  7. 科技「垦荒」,AI护虎
  8. workman与php通信
  9. 一种很厉害的AI学习方式
  10. Unity发布项目,记录日志并写入文件。
  11. 【nexusyum】CentOS7.x上用nexus搭建yum仓库
  12. 图森未来:营收增长与亏损扩大并行
  13. [附源码]计算机毕业设计springboot酒店客房管理信息系统
  14. YTU OJ Problem 2013
  15. 深度技术 GHOST XP SP3 快速装机专业版 V2013.03
  16. AD自动布地孔和不需要阻焊层
  17. c语言c++通讯录管理系统代码
  18. RK3568平台开发系列讲解(时间篇)蓝牙系统结构时间同步机制
  19. java数组释放空间函数_D4java基础(语句,函数,数组)
  20. 视频面试前这些准备工作,你做对了吗?

热门文章

  1. 西门子PLC能否通过以太网数据模块实现无线通讯?
  2. android sdk manager下载后怎么安装,【转载】一个不错的介绍配置Android SDK Manager安装的教程与注意事项...
  3. Python入门经典笔记之安装numpy和matplotlib遇到的问题
  4. 项目总监岗位职责与思考
  5. 英语Barklyite红宝石barklyite
  6. 28.SpringCloud
  7. Flex常用布局,了解一下
  8. SkyWalking调研与初步实践
  9. 计算机应用行距怎么弄,电脑行间距在哪里设置
  10. 一本通1034:计算三角形面积