这篇翻译将会不断完善。有些不会翻译的地方会保留原文。开始。
######################################################################################################
协商
-----------
Capabilities 协商决定了Gstreamer 管道中数据流的格式,理想情况下,协商将信息从掌握信息一方传递到灵活的一方。受限于不灵活的一方。
基本原则
~~~~~~~~~~~
必须被遵守的简单原则
1) 上游给出建议的格式
2) 下游决定格式
以下4种消息被用于caps协商
1) GST_QUERY_CAPS : 得到可能的格式
2) GST_QUERY_ACCEPT_CAPS : 检测指定的格式是否可能
3) GST_EVENT_CAPS : 决定格式(下游)
4) GST_EVENT_RECONFIGURE : 告诉上游有了新的格式
查询
-------
Pad可以询问它的peer pad所支持的GstCaps.这一步就是CAPS查询。这些被支持的能力列表将被选出合适的用于数据传输
CAPS查询是递归的,建立可能的caps时elements必须考虑它的peers。因为最后的caps可能很大,filter可以被用来限制caps。那些仅仅匹配filter的caps才会成为最终caps。filter的顺序决定了最终返回的caps顺序(?)。
(in) "filter", GST_TYPE_CAPS (default NULL)
- 一个GstCaps用来过滤结果
(out) "caps", GST_TYPE_CAPS (default NULL)
- 结果caps
pad可以询问它的peer pad 是否支持指定caps。通过ACCEPT_CAPS查询。caps必须是固定的
ACCEPT_CAPS查询没有被要求递归工作。它可以简单的返回TRUE,只要在处理随后对应此caps的CAPS消息时返回success
(in) "caps", GST_TYPE_CAPS
- 待检查的GstCaps, 必须是固定的
(out) "result", G_TYPE_BOOLEAN (default FALSE)
- 如果 caps 被接受就该返回TRUE
事件
~~~~~~
但媒体格式协商完成.peer elements应该收到携带GstCaps的CAPS消息。该caps必须是固定
"caps", GST_TYPE_CAPS
- 协商好的 GstCaps, 必须固定
操作
~~~~~~~~~
GStreamer 有两个调度模式,push和pull,因此有两种不同的机制实现该目标。我们先挑更为普遍的push说起。
Push-mode协商
~~~~~~~~~~~~~~~~~~~~~
Push协商发生在elements想去push并且需要决定格式的时候。这被叫做下游协商,因为上游为下游决定了格式。这是最普遍的情况。
协商也会发生在但下游element想从上游接收新格式的时候。这被叫做上游协商。
基本的协商如下:
- GstCaps (见 part-caps.txt) 被引用计数在它作为消息参数被push之前。
- element应该针对接受到的CAPS消息重新设置自己,在处理消息之前。如果CAPS消息携带的数据类型不被接受,element应该拒绝这个消息。同样在chian函数中用返回GST_FLOW_NOT_NEGOTIATED的方法拒绝下一个buffer。
- 下游elements发送RECONFIGURE事件到上游,可以请求变化流格式,当上游elements收到RECONFIGURE事件将协商一个新的格式。
source pad开始协商的一般流程如下:
The general flow for a source pad starting the negotiation.
src sink
| |
| querycaps? |
|---------------->|
| caps |
从候选中选 |< - - - - - - - -|
则caps | |
| |
| |-.
| accepts? | |
cap A |---------------->| | optional
| yes | |
|< - - - - - - - -| |
| |-'
| send_event() |
发送 CAPS |---------------->| 接收 A, 重新设置 to
事件 A | | process type A.
| |
| push |
push buffer |---------------->| Process buffer of type A
| |
伪代码描述
[element 想创建buffer]
还没协商好格式
if not format
# 看看我们可以做什么
ourcaps = gst_pad_query_caps (srcpad)
# 看看我的peer可以对我的caps做过滤么(由此得到候选)
candidates = gst_pad_peer_query_caps (srcpad, ourcaps)
foreach每个候选
# 确保每个caps是固定的
fixedcaps = gst_pad_fixate_caps (srcpad, candidate)
# 看我的peer是否接受当前候选
if gst_pad_peer_accept_caps (srcpad, fixedcaps)
# 将caps存储为已协商的caps,这将调用setcaps函数(谁的setcaps函数?)
gst_pad_push_event (srcpad, gst_event_new_caps (fixedcaps))
break
endif
done
endif
#用ALLOCATION请求协商allocator/bufferpool
buffer = gst_buffer_new_allocate (NULL, size, 0);
#填充buffer然后push
sink pad开始协商的一般过程
src sink
| |
| accepts? |
|<----------------| type B
| yes |
|- - - - - - - - >|-.
| | | suggest B caps next
| |<'
| |
| push_event() |
mark .-|<----------------| 发送 RECONFIGURE 事件
renegotiate| | |
'>| |
| querycaps() |
renegotiate |---------------->|
| suggest B |
|< - - - - - - - -|
| |
| send_event() |
send CAPS |---------------->| 接受 type B, 重设 to
event B | | process type B.
| |
| push |
push buffer |---------------->| 处理B类型的buffer
| |
使用场景
videotestsrc ! xvimagesink
1) 谁来决定什么格式?
- 按照惯例,永远是src pad决定。sink pad在caps query中将想建议格式放在返回结果的前面。
- 因为是src决定,它会选择自己可以处理的格式,所以这一步仅仅会因为sink pad说自己能够接受该格式而随后它又不能处理该格式而失败。
2) 协商什么时候开始?
- 在src pad push之前,它决定一个格式类型,就像上面说的那样,然后用这个类型push一个caps事件。sink检查媒体类型并用这个格式设置自己
- src pad然后一般会发出一个ALLOCATION请求来和sink pad协商bufferpool。然后从内存池分配buffer在push给sink.因为sink pad已经接受了这个格式,所以它可以为这个格式创建一个内存池。
- 因为sink可以接受这个格式,所以它可以处理该格式
- since the sink stated in 1) it could accept the type, it will be able to
handle it.
3) sink如何请求另一个格式?
- sink 询问src pad能否接受新格式
- sink push RECONFIGURE事件到上游
- src接收到RECONFIGURE事件然后标记为renegotiation
- 下次push之前,src重新协商caps和buffer pool.sink将想要的格式放到caps查询结果的最前面。
videotestsrc ! queue ! xvimagesink
queue代理了所有的 accept和caps请求到peer pad.
- queue proxies all accept and caps queries to the other peer pad.
queue代理了bufferpool.
- queue proxies the bufferpool
queue代理了RECONFIGURE事件
- queue proxies the RECONFIGURE event
queue在队列中存储了CAPS事件。这意味着queue可以拥有不同类型的buffer.
- queue stores CAPS event in the queue. This means that the queue can contain
buffers with different types.
Pull模式的协商
~~~~~~~~~~~~~~~~~~~~~
解释
^^^^^^^^^
工作在pull模式下的管道和push模式有不一样的协商需求,push模式用于以下两种情况。
* 播放媒体文件,
A pipeline in pull mode has different negotiation needs than one
activated in push mode. Push mode is optimized for two use cases:
* Playback of media files, in which the demuxers and the decoders are
the points from which format information should disseminate to the
rest of the pipeline; and
* 从在线源录制,人们会在源element后放置capsfilter;caps信息从用户经过可能的源caps,到pipeline的sink
* Recording from live sources, in which users are accustomed to putting
a capsfilter directly after the source element; thus the caps
information flow proceeds from the user, through the potential caps
of the source, to the sinks of the pipeline.
pull模式有其他的典型应用:
In contrast, pull mode has other typical use cases:
* 从易损的源回放,如RTP,管道上更高的延迟能带来增加质量
* Playback from a lossy source, such as RTP, in which more knowledge
about the latency of the pipeline can increase quality; or
* 语音合成,API只会处理必要的采样,典型的受限于硬件驱动。
* Audio synthesis, in which audio APIs are tuned to producing only the
necessary number of samples, typically driven by a hardware interrupt
to fill a DMA buffer or a Jack[0] port buffer.
* 低延迟效果处理,
* Low-latency effects processing, whereby filters should be applied as
data is transferred from a ring buffer to a sink instead of
beforehand. For example, instead of using the internal alsasink
ringbuffer thread in push-mode wavsrc ! volume ! alsasink, placing
the volume inside the sound card writer thread via wavsrc !
audioringbuffer ! volume ! alsasink.
[0] http://jackit.sf.net
pull模式的问题在于sink得知道格式,这样通过gst_pad_pull_range()取数据的时候就可以知道要取多少了。这意味着在pull之前。sink必须决定协商格式。
回顾一下caps协商的原则,信息必须从有的那一方到无的那一方,我们看到两种使用情况有着不同的协商需求。
RTP和低延迟的回放都像普通回放一样,信息流到下游。
* RTP and low-latency playback are both like the normal playback case,
in which information flows downstream.
* In audio synthesis, the part of the pipeline that has the most
information is the sink, constrained by the capabilities of the graph
that feeds it. However the caps are not completely specified; at some
point the user has to intervene to choose the sample rate, at least.
This can be done externally to gstreamer, as in the jack elements, or
internally via a capsfilter, as is customary with live sources.
Given that sinks potentially need the input of sources, as in the RTP
case and at least as a filter in the synthesis case, there must be a
negotiation phase before the pull thread is activated. Also, given the
low latency offered by pull mode, we want to avoid capsnego from within
the pulling thread, in case it causes us to miss our scheduling
deadlines.
pull线程一般从PAUSED->PLAYING状态变化时开始,我们必须在此之前完成协商。
开始caps协商的时间就是在SCHEDULING请求完成之后,在sink开始pull线程之前。
机制
^^^^^^^^^
sink通过SCHEDULING请求检测上游element支持pull调度模式
The sink determines that the upstream elements support pull based scheduling by
doing a SCHEDULING query.
sink开始协商过程,先用gst_pad_query_caps取得自己的sink pads然后取得其peer 的src pads,两个pads计算交集。这个操作可以用gst_pad_get_allowed_caps()完成。也可以通过在它所有的src pad上调用get_allowed_caps()返回交集。这样,sink elements就知道整个管道的能力集。
sink element最后固定结果caps,如果需要,固定浮动的caps(带范围的caps).至此,从该sink pad的caps查询将返回固定的caps,这也意味着上游elements只能产生该格式的数据。
如果sink element不能设置它sink pad的caps。它应该在总线上抛出一个错误信息来指出协商错误。
但协商完成,sink pad和上游内部连接起来的pads被激活在pull模式。典型的,这个操作将触发下游的协商,被强制设置为协商过后的固定caps。
经过这些步骤,sink element从状态变化函数返回ASYNC。但sink收到第一个buffer时状态变为PAUSED。
After these steps, the sink element returns ASYNC from the state change
function. The state will commit to PAUSED when the first buffer is received in
the sink. This is needed to provide a consistent API to the applications that
expect ASYNC return values from sinks but it also allows us to perform the
remainder of the negotiation outside of the context of the pulling thread.
格式
~~~~~~~~
我们定义了三种样式的用于协商:
1) Fixed?: 不能选择输出格式
- Caps encoded in the stream
- A video/audio decoder
- 一般用 gst_pad_use_fixed_caps()
2) Transform
- Caps not modified (passthrough)
- can do caps transform based on element property
- fixed caps get transformed into fixed caps
- videobox
3) 动态?: 可以选择输出格式
- A converter element
- 决定于下游 caps, 需要做CAPS 请求来找到transform.
- usually prefers to use the identity transform
- 固定的caps可以被转换为不固定的caps.