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 keyboar