gstreamer中,element和pad都可以处于不同的状态。 pad的状态与element的状态相关联,因此状态的设计主要集中在element状态。
一个element可以处于 4 种状态: NULL、READY、PAUSED 和 PLAYING。 当一个元素最初被实例化时,它处于NULL状态。
状态定义
gstreamer/gst/gstelement.h
typedef enum {
GST_STATE_VOID_PENDING = 0,
GST_STATE_NULL = 1,
GST_STATE_READY = 2,
GST_STATE_PAUSED = 3,
GST_STATE_PLAYING = 4
} GstState;
NULL
:这是元素的初始状态。READY
:element应该准备好进入PAUSED
。PAUSED
:element应该准备好接受和处理数据。 但是,sink element只接受一个buffer然后阻塞。PLAYING
:与PAUSED
相同,但live sources
和sinks除外,sinks接受并render数据,Live sources
产生数据。
我们将序列 NULL→PLAYING
称为向上状态变化,将 PLAYING→NULL
称为向下状态变化。
状态转换
可能会发生以下状态更改:
NULL -> READY:
- element必须检查它需要的资源是否可用。
Device sinks
和Device sources
通常会尝试探测设备以限制其上限。如果需要打开设备,则该元素就会打开设备。
READY -> PAUSED:
- element pads被激活,以便在暂停状态下接收数据。Streaming线程这时候已启动。
- 某些element可能需要在返回 ASYNC后有足够信息时才完成状态改变。当sink收到第一个缓冲区或 EOS 事件或者preroll事件时,需要返回 ASYNC并完成状态更改。当处于暂停状态时,sink也会阻塞数据流。
- 管道将 running_time 重置为 0。
Live sources
返回 NO_PREROLL 并且不生成数据。
PAUSED -> PLAYING:
- 大多数element忽略此状态改变。
- pipeline选择一个时钟并将其分配给所有子节点,然后再将它们设置为 PLAYING。这意味着它只允许在
PLAYING
状态下同步时钟。 - pipeline使用clock和 running_time 来计算 base_time。在执行状态更改时,此 base_time 会分发给所有子节点。
- sink elements在这个状态改变时会停止阻塞在preroll buffer或事件,并开始render数据。
- sink可以在
PLAYING
状态下发布EOS
消息。不在PLAYING
状态时不允许发布EOS
。 - 在
PAUSED
或PLAYING
中流式传输时,可以创建和删除一些pad。 - Live sources开始生成数据并返回
SUCCESS
。
PLAYING -> PAUSED:
-
大多数元素忽略此状态改变。
-
pipeline根据最后选择的clock和 base_time 计算 running_time。它存储此信息以在返回到
PLAYING
状态时继续播放。 -
sink取消阻塞任何
clock wait
调用。 -
当sink没有待播放的buffer时,它会从此状态的改变返回
ASYNC
,并在收到新buffer或 EOS 事件时完成状态更改。 -
任何排队的 EOS 消息都将被删除,因为它们将在返回
PLAYING
状态时重新发布。 EOS 消息在 GstBins 中排队。 -
Live sources停止生成数据并返回
NO_PREROLL
。
PAUSED -> READY:
- Sinks取消block在
preroll
中的任何等待。 - elements取消阻止devices上的任何等待。
chain
或get_range()
函数返回FLUSHING
。- element pads被停用,因此无法进行流式处理,并且所有流式处理线程都被停止。
- sink会忘记所有协商的格式
- element删除所有
sometimes pads
READY -> NULL:
- element关闭devices。
- element重置所有内部状态。
状态变量
#define GST_STATE(elem) (GST_ELEMENT_CAST(elem)->current_state)
#define GST_STATE_NEXT(elem) (GST_ELEMENT_CAST(elem)->next_state)
#define GST_STATE_PENDING(elem) (GST_ELEMENT_CAST(elem)->pending_state)
#define GST_STATE_TARGET(elem) (GST_ELEMENT_CAST(elem)->target_state)
#define GST_STATE_RETURN(elem) (GST_ELEMENT_CAST(elem)->last_return)
一个element有 5 个受object LOCK
保护的状态变量:
-
STATE
- 总是反映元素的当前状态。
-
STATE_NEXT
- 反映元素将进入的下一个状态。
-
STATE_PENDING
- 始终反映元素将要达到的最终状态。当最终状态涉及经历多个状态更改时,这与 STATE_NEXT 不同,例如从 PLAYING -> NULL。
-
STATE_TARGET
- 是应用程序设置的元素应该进入的最终状态。当元素进行状态转换时,在ASYNC状态更改期间,STATE_PENDING 可能与 STATE_TARGET 不同。
-
STATE_RETURN
- 反映状态改变的最后一个返回值。
如果element已经处于正确状态,则 STATE_NEXT 和 STATE_PENDING 可以是 VOID_PENDING。
一个element有一个特殊的锁来防止 set_state() 的并发调用,称为 STATE_LOCK
。
在元素上设置状态
GST_API
GstStateChangeReturn gst_element_set_state (GstElement *element, GstState state);
可以使用 _element_set_state() 改变元素的状态。当改变一个元素的状态时,所有的中间状态也将被设置在元素上,直到最终的期望状态被设置。
set_state() 函数可以返回 3 个可能的值:
GST_STATE_FAILURE
:状态改变由于某种原因失败。该插件应该已经在总线上发布了一条带有信息的错误消息。GST_STATE_SUCCESS
:状态改变成功完成。GST_STATE_ASYNC
:状态改变将在稍后完成。当元素需要很长时间来执行状态改变或需要接收第一个buffer才能完成状态改变(preroll)的sink时,可能会发生这种情况。GST_STATE_NO_PREROLL
:状态改变成功完成,但元素将无法在 PAUSED 状态下产生数据。
在 ASYNC 状态改变的情况下,可以在当前状态更改完成之前进入下一个状态,但是,element只会在完成前一个 ASYNC 状态更改之前进入下一个状态。收到 ASYNC 返回值后,可以使用 element_get_state() 来轮询元素的状态。如果轮询返回 SUCCESS,则element使用 set_state() 将状态更改为最后请求的状态。
设置element的状态时,将 STATE_PENDING 设置为所需的状态。然后调用element的状态更改函数,该函数的结果用于更新 STATE 和 STATE_RETURN 字段、STATE_NEXT、STATE_PENDING 和 STATE_RETURN 字段。如果函数返回 ASYNC,则此结果会立即返回给调用者。
获取元素的状态
get_state() 函数接受 3 个参数,两个指针将保存当前和pending状态,一个 GstClockTime 保存timeout值。该函数返回一个 GstElementStateReturn。
GST_API
GstStateChangeReturn gst_element_get_state (GstElement * element,
GstState * state,
GstState * pending,
GstClockTime timeout);
-
如果element返回
SUCCESS
给之前的 set_state() 函数,则get_state函数将返回element上设置的最后一个状态或者pending状态值中的 VOID_PENDING。正常get_state函数返回GST_STATE_SUCCESS
。 -
如果element返回
NO_PREROLL
给之前的 set_state() 函数,则get_state函数将返回element上设置的最后一个状态以及挂起状态值中的 VOID_PENDING。正常get_state函数返回GST_STATE_NO_PREROLL
。 -
如果element返回
FAILURE
到先前的 _set_state() 调用,则get_state函数将返回FAILURE
,状态设置为element的当前状态,pending状态设置为最后一次调用 _set_state() 时使用的值。 -
如果element将 ASYNC 返回到先前的 _set_state() 调用,则get_state函数将等待element完成其状态更改,直至 GstClockTime 中指定的时间量。
- 如果element在指定时间内未完成状态更改,则get_state函数将返回 ASYNC,状态设置为当前状态,pending状态设置为pending状态。
- 如果element在指定的超时时间内完成状态更改,则get_state函数返回更新后的状态和 VOID_PENDING 作为挂起状态。
- 如果element在指定的超时时间内由于错误而中止 ASYNC 状态更改,则get_state函数返回 FAILURE,状态设置为最后一次成功状态,挂起设置为最后一次尝试。该元素还应该在总线上发布一条错误消息,其中包含有关该问题的更多信息。
在元素中实现状态
这部分理解的有点模糊
向上状态变化
无论是否达到 STATE_PENDING
,向上状态改变总是返回 ASYNC
:
Element:
A -> B => SUCCESS
commit state
A -> B => ASYNC
no commit state
element commits state ASYNC
A -> B while ASYNC
update STATE_PENDING state
no commit state
no change_state() called on element
向下状态变化
如果最终状态为 ASYNC
,则向下状态改变仅返回 ASYNC
。 这是为了确保当您只想关闭一个element时,不需要等待一个element完成preroll
或其他 ASYNC
状态改变。
Element:
A -> B => SUCCESS
commit state
A -> B => ASYNC not final state
commit state on behalf of element
A -> B => ASYNC final state
element will commit ASYNC
Locking overview (element)
Element committing SUCCESS
- 在
set_state()
中获取 STATE_LOCK - 改变状态如果成功则调用
commit state
commit state
调用change_state()
到下一个状态改变。- 如果达到最终状态,堆栈展开并将结果返回给
set_state()
和调用者。
set_state(element) change_state (element) commit_state
| | |
| | |
STATE_LOCK | |
| | |
|------------------------>| |
| | |
| | (do state change) |
| | |
| | if `SUCCESS` |
| |---------------------->|
| | | post message
| |<----------------------| if (!final) change_state (next)
| | | else SIGNAL
| | |
|<------------------------| |
| `SUCCESS`
STATE_UNLOCK
|
`SUCCESS`
Element committing ASYNC
- 在
set_state()
中获取 STATE_LOCK - 调用change state并返回 ASYNC
- ASYNC 返回给调用者。
- element在streaming线程中持有LOCK。
- element在streaming线程中调用
commit_state()
。 - commit state调用
change_state()
到下一个状态改变。
set_state(element) change_state (element) stream_thread commit_state (element)
| | | |
STATE_LOCK | | |
| | | |
|------------------------>| | |
| | | |
| | (start_task) | |
| | | |
| | STREAM_LOCK |
| | |... |
|<------------------------| | |
| ASYNC STREAM_UNLOCK |
STATE_UNLOCK | |
| .....sync........ STATE_LOCK |
ASYNC |----------------->|
| |
| |---> post_message()
| |---> if (!final) change_state (next)
| | else SIGNAL
|<-----------------|
STATE_UNLOCK
|
STREAM_LOCK
| ...
STREAM_UNLOCK