gstreamer的collectpad是一类特殊的pad,这类pad工作于收集模式,用于管理控制若干个pad组成的pad集合的数据同步处理。大部分的合成器(muxer)均使用collectpad来收集音视频数据,并根据可重载的收集条件判断函数对不同pad之间的数据进行处理(或同步)。
由于collectpad中大部分处理函数均可重载(set_func),因此本文只讨论默认的处理函数。
2. 默认流程:
collectpad的简单流程如下图:
不同的pad工作与不同的线程中,当某一个pad有数据到来时,会对所有pad进行判断,看看是否可以满足收集条件,如果满足收集条件就向对应的element推送数据。如果不满足收集条件,就会将该线程挂起,等待其他线程的数据。
当某个pad处于挂起时,其他pad收到数据后,一样会对收集条件进行判断,如果满足条件,会将所有pad的数据推送至element,同时广播条件变量,唤醒所有挂起中的其他pad(线程)。
简单的函数调用关系如下:
3. 数据结构:
数据结构如下:一个_GstCollectPads中维护了一个_GstCollectData的链表,每个pad对应一个_GstCollectData,其中记录了pad中的数据的时间戳,buffer,已经对应pad的状态(如锁、等待等标志位),GstCollectPadsPrivate中则记录了collectpad中注册的各种事件回调函数,这里的回调函数都有接口可以进行重载。此外,GstCollectPadsPrivate还维护了线程间同步用的锁和条件变量。
/**
* GstCollectPads:
* @data: (element-type GstBase.CollectData): #GList of #GstCollectData managed
* by this #GstCollectPads.
*
* Collectpads object.
*/
struct _GstCollectPads {
/* 基类。 */
GstObject object;
/*< public >*/ /* with LOCK and/or STREAM_LOCK */
/* 所有PAD的集合。 */
/*
* GstCollectData:
* @collect: owner #GstCollectPads
* @pad: #GstPad managed by this data
* @buffer: currently queued buffer.
* @pos: position in the buffer
* @segment: last segment received.
* @dts: the signed version of the DTS converted to running time. To access
* this memeber, use %GST_COLLECT_PADS_DTS macro. (Since 1.6)
*
* Structure used by the collect_pads.
struct _GstCollectData
{
/* with STREAM_LOCK of @collect */
/* 指向回collectpad。 */
GstCollectPads *collect;
GstPad *pad;
GstBuffer *buffer;
guint pos;
GstSegment segment;
/*< private >*/
/* state: bitfield for easier extension;
* eos, flushing, new_segment, waiting */
GstCollectPadsStateFlags state;
GstCollectDataPrivate *priv;
union {
struct {
/*< public >*/
gint64 dts;
/*< private >*/
} abi;
gpointer _gst_reserved[GST_PADDING];
} ABI;
};
*/
GSList *data; /* list of CollectData items */
/*< private >*/
GRecMutex stream_lock; /* used to serialize collection among several streams */
GstCollectPadsPrivate *priv;
gpointer _gst_reserved[GST_PADDING];
};
4. 代码分析:
4.1 主入口函数:
主入口函数gst_collect_pads_chain,不同pad工作于不同线程中。代码分析如下:
/* For each buffer we receive we check if our collected condition is reached
* and if so we call the collected function. When this is done we check if
* data has been unqueued. If data is still queued we wait holding the stream
* lock to make sure no EOS event can happen while we are ready to be
* collected
*/
static GstFlowReturn
gst_collect_pads_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
GstCollectData *data;
GstCollectPads *pads;
GstFlowReturn ret;
GstBuffer **buffer_p;
guint32 cookie;
GST_DEBUG ("Got buffer for pad %s:%s", GST_DEBUG_PAD_NAME (pad));
/* some magic to get the managing collect_pads */
GST_OBJECT_LOCK (pad);
data = (GstCollectData *) gst_pad_get_element_private (pad);
if (G_UNLIKELY (data == NULL))
goto no_data;
ref_data (data);
GST_OBJECT_UNLOCK (pad);
pads = data->collect;
GST_COLLECT_PADS_STREAM_LOCK (pads);
/* 状态判断。 */
/* if not started, bail out */
if (G_UNLIKELY (!pads->priv->started))
goto not_started;
/* check if this pad is flushing */
if (G_UNLIKELY (GST_COLLECT_PADS_STATE_IS_SET (data,
GST_COLLECT_PADS_STATE_FLUSHING)))
goto flushing;
/* pad was EOS, we can refuse this data */
if (G_UNLIKELY (GST_COLLECT_PADS_STATE_IS_SET (data,
GST_COLLECT_PADS_STATE_EOS)))
goto eos;
/* see if we need to clip */
/* 数据前处理。 */
if (pads->priv->clip_func) {
GstBuffer *outbuf = NULL;