gstreamer插件编写指南:事件:搜索、导航及更多
事件类型多种多样,但在流水线中的传播方式却只有两种:下游或上游。了解这两种方式的工作原理非常重要,因为如果流水线中的某个元素没有正确处理这些事件,管道的整个事件系统就会崩溃。在此,我们将尝试解释这些方法的工作原理,以及元素应如何实现这些方法。
下游事件
下游事件通过汇衬底的事件处理函数接收,该处理函数在创建 pad 时使用gst_pad_set_event_function ()
设置。
下游事件有两种传播方式:带内(与缓冲区流一起序列化)或带外(即时通过流水线,可能与处理缓冲区的流媒体线程不在同一线程,跳过流水线中正在处理或排队的缓冲区)。最常见的下游事件(SEGMENT、CAPS、TAG、EOS)都与缓冲流一起序列化。
下面是一个典型的事件功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static gboolean
gst_my_filter_sink_event (GstPad *pad, GstObject * parent, GstEvent * event)
{
GstMyFilter *filter;
gboolean ret;
filter = GST_MY_FILTER (parent);
...
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEGMENT:
/* maybe save and/or update the current segment (e.g. for output
* clipping) or convert the event into one in a different format
* (e.g. BYTES to TIME) or drop it and set a flag to send a segment
* event in a different format later */
ret = gst_pad_push_event (filter->src_pad, event);
break;
case GST_EVENT_EOS:
/* end-of-stream, we should close down all stream leftovers here */
gst_my_filter_stop_processing (filter);
ret = gst_pad_push_event (filter->src_pad, event);
break;
case GST_EVENT_FLUSH_STOP:
gst_my_filter_clear_temporary_buffers (filter);
ret = gst_pad_push_event (filter->src_pad, event);
break;
default:
ret = gst_pad_event_default (pad, parent, event);
break;
}
...
return ret;
}
如果你的元素是基于链的,那么你几乎总是需要实现一个 sink 事件函数,因为这就是通知你 SEGMENT、CAPS和流结束的方式。
如果你的元素完全基于循环,你可能需要也可能不需要汇入事件函数(因为元素正在驱动流水线,它会提前知道流的长度,或者通过gst_pad_pull_range()
的流返回值得到通知)。在某些情况下,即使是基于循环的元素也可能会接收到来自上游的事件(例如前面带有 id3demux 或 apedemux 元素的音频解码器,或从在自定义事件中发送有关流的附加信息的源输入的解流器,如 DVD 源)。
上游事件
上游事件由管道下游的某个元素生成(例如:视频汇可能会生成搜索事件,向上游元素通报鼠标指针的当前位置)。这也可能是应应用程序的要求间接发生的,例如,当应用程序在流水线上执行搜索操作时,该搜索请求将被传递给汇元素,而汇元素又会生成一个上游搜索事件。
最常见的上游事件是搜索事件、服务质量(QoS)和重新配置事件。
上游事件可以使用gst_pad_send_event
函数发送,该函数只需调用该 pad 的默认事件处理程序。pad 的默认事件处理器是gst_pad_event_default
,它基本上是将事件发送给内部链接 pad 的对应设备。因此,上游事件总是到达元素的源 pad,并由默认事件处理程序处理,除非你重载该处理程序自行处理。在某些特殊情况下,你必须这样做:
-
如果您的元素中有多个汇衬底。在这种情况下,您必须决定将事件发送到哪个汇衬底(如果不是全部的话)。
-
如果您需要在本地处理该事件。例如,您需要在向上游发送之前转换的搜索事件,或者您需要处理的 QoS 事件。
在该事件处理程序中进行的处理并不重要,但有一些重要的规则必须绝对遵守,因为一个坏掉的元素事件处理程序就会破坏整个管道事件处理。这些规则如下
-
始终使用默认的
gst_pad_event_default
方法处理你不会处理的事件。该方法会根据事件的不同,转发或删除事件。 -
如果要根据收到的事件生成新事件,请不要忘记
gst_event_unref
收到的事件。 -
事件处理函数应该返回 TRUE 或 FALSE,表示事件是否已被处理。除非您确实知道已经处理了该事件,否则切勿在处理程序中简单地返回 TRUE/FALSE。
-
请记住,事件处理程序可能会从与媒体流线程不同的线程调用,因此请确保在任何地方都使用了适当的锁定。
事件汇总
本章列出了当前使用的所有已定义事件,以及如何使用/解释这些事件。您可以使用 GST_EVENT_TYPE
宏查看某个事件的类型(如果您需要一个字符串用于调试,也可以使用 GST_EVENT_TYPE_NAME
)。
在本章中,我们将讨论以下事件:
-
流启动
-
Caps
-
Segment
-
标签(元数据)
-
数据流结束(EOS)
-
目录
-
Gap
-
刷新开始
-
刷新停止
-
服务质量(QOS)
-
请求搜索
-
导航
有关事件以及如何在各种情况下正确使用事件的更全面信息,请查阅 GStreamer 设计文档。本节仅作概括介绍。
流启动
待添加……
Caps
CAPS 事件包含接下来的缓冲区的格式描述。有关协商的更多信息,请参阅能力集协商。
Segment
向下游发送段事件是为了宣布数据流中有效时间戳的范围,以及如何将其转换为运行时间和数据流时间。段事件必须始终在第一个数据缓冲区之前和刷新之后发送(见上文)。
第一个段事件由驱动流水线的元素创建,例如以推模式运行的信号源或以拉模式运行的解复用器/解码器。然后,段事件沿着流水线向下传输,途中可能会发生转换(例如,解码器可能会接收 BYTES 格式的段事件,并根据平均比特率将其转换为 TIMES 格式的段事件)。
根据元素类型的不同,可以使用gst_pad_event_default ()
简单地转发事件,或者对事件进行解析并发送修改后的事件。最后一种情况适用于通常具有字节到时间转换概念的解复用器。它们的输入通常以字节为单位,因此传入的事件也会有一个以字节为单位的偏移量(GST_FORMAT_BYTES
)。然而,下游元件则希望以时间单位来分段事件,以便与流水线时钟同步。因此,解复用器和类似元素不应转发事件,而应解析、释放事件并向下游发送段事件(以时间单位GST_FORMAT_TIME
)。
段事件使用函数gst_event_new_segment ()
创建。有关其参数的详细信息,请参阅 API 参考和设计文档。
解析该事件的元素可以使用gst_event_parse_segment()
提取事件细节。元素可能会发现 GstSegment API 对跟踪当前段非常有用(例如,如果它们想将其用于对输出进行剪切)。
标签(元数据)
标签事件用于向下游发送,以显示从流数据中解析出的标签。目前,在将数据流从一种格式转码为另一种格式的过程中,这种方法可用于保留标记。标签在“标签(元数据和流信息)”中有大量讨论。大多数元素都会通过调用gst_pad_event_default ()
来转发事件。
标签事件是通过函数gst_event_new_tag ()
创建的,但更常见的情况是,元素会向下游发送一个标签事件,该事件会被汇元素转换成总线上的信息。所有这些函数都需要一个填写完整的标签列表作为参数,它们将拥有该标签列表的所有权。
解析该事件的元素可以使用函数gst_event_parse_tag ()
获取事件包含的标记列表。
数据流结束(EOS)
如果元素发送的数据流结束,就会发送数据流结束事件。接收到该事件的元素(来自上游,因此会在其 sinkpad 上接收到)通常只会处理任何缓冲数据(如果有的话),然后将事件进一步转发到下游。gst_pad_event_default ()
会处理所有这些工作,因此大多数元素都不需要支持该事件。明确需要关闭 EOS 资源的元素和 N 对 1 元素除外。请注意,流本身并不是一个应该在 EOS 时关闭的资源!应用程序可能会返回到 EOS 之前的某个点,然后继续播放。
EOS 事件没有属性,因此是 GStreamer 中最简单的事件之一。它通过gst_event_new_eos()
函数创建。
需要注意的是,只有驱动流水线的元素才能发送 EOS 事件。如果您的元素是基于链的,那么它就不是在驱动流水线。基于链的元素只需在数据流(或配置的数据段)结束时从其链函数返回 GST_FLOW_EOS
,然后驱动流水线的上游元素将负责发送 EOS 事件(或根据操作模式在总线上发布 SEGMENT_DONE
消息)。如果您要实现自己的源元素,也无需手动发送 EOS 事件,只需在创建或填充函数中返回 GST_FLOW_EOS
(假设您的元素源自 GstBaseSrc 或 GstPushSrc)即可。
目录
待添加……
Gap
待添加……
刷新开始
如果流水线中的所有缓冲区和缓存都要清空,就会向下游(在推模式下)或上游(在拉模式下)发送 flush start 事件。例如,“队列”元素在收到该事件时会清空其内部缓冲区列表。文件汇元素(如 “filesink”)收到此事件后,将清空内核到磁盘的缓存(fdatasync()
或fflush ()
)。gst_pad_event_default ()
就是这样做的,因此对于大多数元素来说,使用默认事件处理程序转发事件就足够了。
作为从流水线中刷新所有数据的副作用,该事件会使所有衬底拒绝数据,直到收到“刷新停止”信号(试图推送数据的元素会返回“FLUSHING_FLOW”并停止处理数据),从而解除媒体流线程的阻塞。
刷新启动事件是通过gst_event_new_flush_start ()
创建的。与 EOS 事件一样,它没有任何属性。通常只有驱动流水线的元素才会创建该事件,如以推送模式运行的源元素或基于拉模式的解复用器/解码器。
刷新停止
刷新停止事件由驱动流水线的元素在刷新启动后发送,并告知下游的衬底和元素应再次接受事件和缓冲区(不过在缓冲区之前至少会有一个 SEGMENT 事件)。
如果元素保留了流数据的临时缓存,则应在接收到 FLUSH-STOP 事件时(以及在其链函数接收到设置了 DISCONT 标志的缓冲区时)清除这些缓存。
刷新停止事件是通过gst_event_new_flush_stop ()
创建的。它有一个参数,用于控制流水线的运行时间是否应重置为 0。通常情况下,在刷新停止后,running_time
会被重置为 0。
服务质量(QOS)
QOS 事件包含有关流当前实时性能的报告。更多信息请参阅“服务质量 (QoS)”章节。
搜索请求
搜索事件用于向元素请求新的流位置。新位置支持多种格式设置(时间、字节或“默认单位”(表示视频帧、音频独立于信道的采样等))。寻址可以相对于文件结束或文件开始进行,通常发生在上游方向(下游搜索是通过发送 SEGMENT 事件完成的,该事件会为支持该功能的元素(如 filesink)提供适当的偏移量)。
接收搜索事件的元素应根据元素类型,或直接将其转发到上游(过滤器、解码器),或更改事件的格式然后转发(解耦器),或通过更改其内部流资源中的文件指针来处理事件(文件源、以拉模式驱动流水线的解复用器/解码器)或其他方式。
搜索事件是使用指定格式(时间、字节、单位)的位置建立的。它们通过函数gst_event_new_seek ()
创建。请注意,许多插件不支持从流的末端开始搜索。未驱动流水线和转发搜索请求的元素不应假定搜索成功或实际发生,而应根据其接收到的 SEGMENT 事件进行操作。
解析该事件的元素可以使用gst_event_parse_seek()
来完成解析。
导航
导航事件由视频汇向上游发送,以告知上游元素鼠标指针的位置、鼠标指针是否点击以及点击的位置,或者按键是否被按下或释放。
所有这些信息都包含在事件结构中,可通过gst_event_get_structure ()
获取。
请查看 gst-plugins-good 中的 navigationtest 元素,了解如何从该事件中提取导航信息。