英文原文:https://gstreamer.freedesktop.org/documentation/plugin-development/basics/states.html
状态描述了element是否被初始化,是否已经准备好传送数据以及目前是否正在处理数据。Gstreamer中定义了4个状态:
从现在开始,我们将简单的用"NULL", "READY", "PAUSED"和"PLAYING"来指代。
GST_STATE_NULL是一个element的默认状态。在此状态下,所有运行时资源都未被分配,所有运行时所依赖的库都未被加载,很明显,此时不能处理数据。
GST_STATE_READY是一个element在GST_STATE_NULL后的下一个状态。在READY状态,一个element的所有默认资源(运行时库及运行时内存)会被分配。然而,此时的element还没有分配及定义任何流特定的资源。当从NULL状态转换到READY状态(GST_STATE_CHANGE_NULL_TO_READY),一个element应该分配所有非流特定资源,且加载运行时库(若有的话)。当从READY状态转换到NULL状态(GST_STATE_CHANGE_READY_TO_NULL),一个element应该卸载这些库,并释放所有分配的资源。这些资源是指,如硬件设备等。注意到,文件一般是流,这些应该被当作流特定资源,所以,它们在此状态下不应该被分配。
GST_STATE_PAUSED是一个element准备接收并处理数据的状态。对于大部分的element,这个状态相当于PLAYING状态,唯一的例外是sink element。sink element只接收数据的一个buffer,然后阻塞。在此时,pipeline已经预滚好,并准备立即渲染数据。
GST_STATE_PLAYING是一个element能进入的是最高级状态。对于大部分element,这个状态相当于PAUSED状态,它们接收并处理事件和数据。只有sink element在PAUSED状态和PLAYING状态有所不同,sink element实际上是渲染输入的数据,如音频输出到一个声卡或在一个图像sink中渲染视频图像。
如果你用一个基础类,你将很少需要自己处理状态。你所需要做的,只是覆写基础类中的start()和stop()虚函数(根据不同的基础将以不同的方式调用)。其它的事情,基础类将为你处理好。
然而,如果你的element不是从一个已经创建好的基础类,而是从GstElement或其它的未基于基础类构建的类型中派生,你很有可能需要创建实现你自己的状态转变函数以在状态发生改变进被调用。由于这里并没有整流或分流的基础类,所以,如果你的插件是一个整流器或一个分流器,这样做是非常有必要的。
通过一个虚函数指针,一个element可以在状态发生改变时被通知。在这个函数里,element能够初始化任何element所需要的特殊数据,并且从一个状态转变到另一个状态失败是有可能的。
对于未处理的状态转变,不进行g_assert;这是GstElement基础类所关心的事。
static GstStateChangeReturn
gst_my_filter_change_state (GstElement *element, GstStateChange transition);
static void
gst_my_filter_class_init (GstMyFilterClass *klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
element_class->change_state = gst_my_filter_change_state;
}
static GstStateChangeReturn
gst_my_filter_change_state (GstElement *element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstMyFilter *filter = GST_MY_FILTER (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
if (!gst_my_filter_allocate_memory (filter))
return GST_STATE_CHANGE_FAILURE;
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_READY_TO_NULL:
gst_my_filter_free_memory (filter);
break;
default:
break;
}
return ret;
}
注意到,向前(NULL=>READY, READY=>PAUSED, PAUSED=>PLAYING)和向后(PLAYING=>PAUSED, PAUSED=>READY, READY=>NULL)的状态转变是在两个分开的模块中处理的。其中向后的状态转变处理,是在调用了父类的状态转变函数后进行的。这对于多线程同时访问处理是非常有必要的。
这样处理的原因是,在状态向后转变的情况下,当你的插件的chain函数(例如)仍然在另一个线程访问这些资源时,你不会想销毁已分配的资源。是否你的chain函数可能运行与element的状态非常相关,它依赖你插件中的pads,及这些pads的状态。pads状态的处理是在GstElement类的状态改变函数中,包括资源锁,这是为什么在销毁已分配的资源前锁住是非常必要的。