代码位置:gstreamer-1.20.3\plugins\elements\GstQueue.c
queue直到达到指定的限制之一,任何将更多缓冲区推入队列的尝试都会阻塞推入线程,直到有更多空间可用。queue将在 source pad 上创建一个新线程,以解耦 sink 和 source pad 上的处理。
您可以通过属性读取队列目前数据量。您可以通过连接到 notify::current-level-buffers 信号来跟踪更改(就像所有信号都将从流线程发出)。默认大小限制为 200 个GstBuffer、10MB 数据或一秒数据,以先达到者为准。如前所述,默认情况下,当达到指定的最大值(字节、时间、缓冲区)之一时,队列会阻塞,您也可以设置leaky属性来指定它应该丢弃新的(即值为upstream)或旧的(即值为downstream)缓冲区而不是阻塞。这当队列的数据少于指定的最小阈值要求时发出信号(默认情况下:当队列为空时)。这队列满时发出信号。这两个信号都是从流线程中发出的。
1、初始回调函数:
static void
gst_queue_init (GstQueue * queue)
{
...
//sink pad初始化
queue->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
gst_pad_set_chain_function (queue->sinkpad, gst_queue_chain);//push函数
gst_pad_set_chain_list_function (queue->sinkpad, gst_queue_chain_list);//push list函数
gst_pad_set_activatemode_function (queue->sinkpad,gst_queue_sink_activate_mode);//激活调用函数
gst_pad_set_event_full_function (queue->sinkpad, gst_queue_handle_sink_event);//事件处理
gst_pad_set_query_function (queue->sinkpad, gst_queue_handle_sink_query);//查询事件
GST_PAD_SET_PROXY_CAPS (queue->sinkpad);
gst_element_add_pad (GST_ELEMENT (queue), queue->sinkpad);
//source pad初始化
queue->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
gst_pad_set_activatemode_function (queue->srcpad,gst_queue_src_activate_mode);//激活调用函数
gst_pad_set_event_function (queue->srcpad, gst_queue_handle_src_event);//事件处理
gst_pad_set_query_function (queue->srcpad, gst_queue_handle_src_query);//查询事件
GST_PAD_SET_PROXY_CAPS (queue->srcpad);
gst_element_add_pad (GST_ELEMENT (queue), queue->srcpad);
...
2、基本函数解释:
static GstFlowReturn gst_queue_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer); //push buffer调用函数
static GstFlowReturn gst_queue_chain_list (GstPad * pad, GstObject * parent, GstBufferList * buffer_list);//push buffer list调用函数
static GstFlowReturn gst_queue_push_one (GstQueue * queue);//push单个Buffer,在gst_queue_loop中执行
static void gst_queue_loop (GstPad * pad);
static GstFlowReturn gst_queue_handle_sink_event (GstPad * pad, GstObject * parent, GstEvent * event);
static gboolean gst_queue_handle_sink_query (GstPad * pad, GstObject * parent, GstQuery * query);
static gboolean gst_queue_handle_src_event (GstPad * pad, GstObject * parent, GstEvent * event);
static gboolean gst_queue_handle_src_query (GstPad * pad, GstObject * parent,GstQuery * query);
static void gst_queue_locked_flush (GstQueue * queue, gboolean full);
static gboolean gst_queue_src_activate_mode (GstPad * pad, GstObject * parent,GstPadMode mode, gboolean active);
static gboolean gst_queue_sink_activate_mode (GstPad * pad, GstObject * parent,GstPadMode mode, gboolean active);
static gboolean gst_queue_is_empty (GstQueue * queue);
static gboolean gst_queue_is_filled (GstQueue * queue);
3、启动线程任务
static gboolean
gst_queue_handle_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
gboolean res = TRUE;
GstQueue *queue = GST_QUEUE (parent);
#ifndef GST_DISABLE_GST_DEBUG
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "got event %p (%d)",event, GST_EVENT_TYPE (event));
#endif
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_RECONFIGURE:
GST_QUEUE_MUTEX_LOCK (queue);
if (queue->srcresult == GST_FLOW_NOT_LINKED) {
/* when we got not linked, assume downstream is linked again now and we can try to start pushing again */
queue->srcresult = GST_FLOW_OK;
gst_pad_start_task (pad, (GstTaskFunction) gst_queue_loop, pad, NULL);
}
GST_QUEUE_MUTEX_UNLOCK (queue);
res = gst_pad_push_event (queue->sinkpad, event);
break;
default:
res = gst_pad_event_default (pad, parent, event);
break;
}
return res;
}
4、队列循环向下一个GstElement推送数据
static void
gst_queue_loop (GstPad * pad)
{
GstQueue *queue;
GstFlowReturn ret;
queue = (GstQueue *) GST_PAD_PARENT (pad);
/* have to lock for thread-safety */
GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);
//队列为空
while (gst_queue_is_empty (queue)) {
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "queue is empty");
if (!queue->silent) {
GST_QUEUE_MUTEX_UNLOCK (queue);
g_signal_emit (queue, gst_queue_signals[SIGNAL_UNDERRUN], 0);
GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);
}
/* we recheck, the signal could have changed the thresholds */
while (gst_queue_is_empty (queue)) {
GST_QUEUE_WAIT_ADD_CHECK (queue, out_flushing);
}
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "queue is not empty");
if (!queue->silent) {
GST_QUEUE_MUTEX_UNLOCK (queue);
g_signal_emit (queue, gst_queue_signals[SIGNAL_RUNNING], 0);
g_signal_emit (queue, gst_queue_signals[SIGNAL_PUSHING], 0);
GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);
}
}
ret = gst_queue_push_one (queue);
queue->srcresult = ret;
if (ret != GST_FLOW_OK)
goto out_flushing;
GST_QUEUE_MUTEX_UNLOCK (queue);
return;
/* ERRORS */
out_flushing: {
gboolean eos = queue->eos;
GstFlowReturn ret = queue->srcresult;
gst_pad_pause_task (queue->srcpad);
GST_CAT_LOG_OBJECT (queue_dataflow, queue,"pause task, reason: %s", gst_flow_get_name (ret));
if (ret == GST_FLOW_FLUSHING) {
gst_queue_locked_flush (queue, FALSE);
} else {
GST_QUEUE_SIGNAL_DEL (queue);
queue->last_query = FALSE;
g_cond_signal (&queue->query_handled);
}
GST_QUEUE_MUTEX_UNLOCK (queue);
/* let app know about us giving up if upstream is not expected to do so EOS is already taken care of elsewhere */
if (eos && (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS)) {
GST_ELEMENT_FLOW_ERROR (queue, ret);
gst_pad_push_event (queue->srcpad, gst_event_new_eos ());
}
return;
}
}
5、数据出队,push到下一个GstElement
/* dequeue an item from the queue an push it downstream. This functions returns
* the result of the push. */
static GstFlowReturn
gst_queue_push_one (GstQueue * queue) {
GstFlowReturn result = queue->srcresult;
GstMiniObject *data;
gboolean is_list;
data = gst_queue_locked_dequeue (queue);
if (data == NULL)
goto no_item;
next:
is_list = GST_IS_BUFFER_LIST (data);
if (GST_IS_BUFFER (data) || is_list) {
if (!is_list) {
GstBuffer *buffer;
buffer = GST_BUFFER_CAST (data);
if (queue->head_needs_discont) {
GstBuffer *subbuffer = gst_buffer_make_writable (buffer);
if (subbuffer) {
buffer = subbuffer;
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
} else {
GST_DEBUG_OBJECT (queue, "Could not mark buffer as DISCONT");
}
queue->head_needs_discont = FALSE;
}
GST_QUEUE_MUTEX_UNLOCK (queue);
result = gst_pad_push (queue->srcpad, buffer);
} else {
GstBufferList *buffer_list;
buffer_list = GST_BUFFER_LIST_CAST (data);
if (queue->head_needs_discont) {
buffer_list = gst_buffer_list_make_writable (buffer_list);
gst_buffer_list_foreach (buffer_list, discont_first_buffer, queue);
queue->head_needs_discont = FALSE;
}
GST_QUEUE_MUTEX_UNLOCK (queue);
result = gst_pad_push_list (queue->srcpad, buffer_list);
}
/* need to check for srcresult here as well */
GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);
if (result == GST_FLOW_EOS) {
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "got EOS from downstream");
/* stop pushing buffers, we dequeue all items until we see an item that we can push again, which is EOS or SEGMENT. If there is nothing in the queue we can push, we set a flag to make the sinkpad refuse more buffers with an EOS return value. */
while ((data = gst_queue_locked_dequeue (queue))) {
if (GST_IS_BUFFER (data)) {
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"dropping EOS buffer %p", data);
gst_buffer_unref (GST_BUFFER_CAST (data));
} else if (GST_IS_BUFFER_LIST (data)) {
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"dropping EOS buffer list %p", data);
gst_buffer_list_unref (GST_BUFFER_LIST_CAST (data));
} else if (GST_IS_EVENT (data)) {
GstEvent *event = GST_EVENT_CAST (data);
GstEventType type = GST_EVENT_TYPE (event);
if (type == GST_EVENT_EOS || type == GST_EVENT_SEGMENT
|| type == GST_EVENT_STREAM_START) {
/* we found a pushable item in the queue, push it out */
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"pushing pushable event %s after EOS",
GST_EVENT_TYPE_NAME (event));
goto next;
}
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"dropping EOS event %p", event);
gst_event_unref (event);
} else if (GST_IS_QUERY (data)) {
GstQuery *query = GST_QUERY_CAST (data);
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"dropping query %p because of EOS", query);
queue->last_query = FALSE;
g_cond_signal (&queue->query_handled);
}
}
/* no more items in the queue. Set the unexpected flag so that upstream make us refuse any more buffers on the sinkpad. Since we will still accept EOS and SEGMENT we return _FLOW_OK to the caller so that the task function does not shut down. */
queue->unexpected = TRUE;
result = GST_FLOW_OK;
}
} else if (GST_IS_EVENT (data)) {
GstEvent *event = GST_EVENT_CAST (data);
GstEventType type = GST_EVENT_TYPE (event);
GST_QUEUE_MUTEX_UNLOCK (queue);
gst_pad_push_event (queue->srcpad, event);
GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);
/* if we're EOS, return EOS so that the task pauses. */
if (type == GST_EVENT_EOS) {
GST_CAT_LOG_OBJECT (queue_dataflow, queue,"pushed EOS event %p, return EOS", event);
result = GST_FLOW_EOS;
}
} else if (GST_IS_QUERY (data)) {
GstQuery *query = GST_QUERY_CAST (data);
gboolean ret;
GST_QUEUE_MUTEX_UNLOCK (queue);
ret = gst_pad_peer_query (queue->srcpad, query);
GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing_query);
queue->last_query = ret;
queue->last_handled_query = query;
g_cond_signal (&queue->query_handled);
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "did query %p, return %d", query, queue->last_query);
}
return result;
/* ERRORS */
no_item: {
GST_CAT_ERROR_OBJECT (queue_dataflow, queue,
"exit because we have no item in the queue");
return GST_FLOW_ERROR;
}
out_flushing: {
GstFlowReturn ret = queue->srcresult;
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"exit because task paused, reason: %s", gst_flow_get_name (ret));
return ret;
}
out_flushing_query: {
GstFlowReturn ret = queue->srcresult;
queue->last_query = FALSE;
g_cond_signal (&queue->query_handled);
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"exit because task paused, reason: %s", gst_flow_get_name (ret));
return ret;
}
}
6、上一个GstElement PUSH数据入队
static GstFlowReturn
gst_queue_chain_list (GstPad * pad, GstObject * parent,
GstBufferList * buffer_list)
{
return gst_queue_chain_buffer_or_list (pad, parent,
GST_MINI_OBJECT_CAST (buffer_list), TRUE);
}
static GstFlowReturn
gst_queue_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
return gst_queue_chain_buffer_or_list (pad, parent,
GST_MINI_OBJECT_CAST (buffer), FALSE);
}
7、
gst_queue_chain_buffer_or_list具体enque相关处理
static GstFlowReturn
gst_queue_chain_buffer_or_list (GstPad * pad, GstObject * parent,
GstMiniObject * obj, gboolean is_list) {
GstQueue *queue;
queue = GST_QUEUE_CAST (parent);
/* we have to lock the queue since we span threads */
GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);
/* when we received EOS, we refuse any more data */
if (queue->eos)
goto out_eos;
if (queue->unexpected)
goto out_unexpected;
if (!is_list) {
GstClockTime duration, timestamp;
GstBuffer *buffer = GST_BUFFER_CAST (obj);
timestamp = GST_BUFFER_DTS_OR_PTS (buffer);
duration = GST_BUFFER_DURATION (buffer);
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "received buffer %p of size %"
G_GSIZE_FORMAT ", time %" GST_TIME_FORMAT ", duration %"
GST_TIME_FORMAT, buffer, gst_buffer_get_size (buffer),
GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
} else {
GST_CAT_LOG_OBJECT (queue_dataflow, queue,"received buffer list %p with %u buffers", obj,gst_buffer_list_length (GST_BUFFER_LIST_CAST (obj)));
}
/* We make space available if we're "full" according to whatever the user defined as "full". Note that this only applies to buffers. We always handle events and they don't count in our statistics. */
while (gst_queue_is_filled (queue)) {
if (!queue->silent) {
GST_QUEUE_MUTEX_UNLOCK (queue);
g_signal_emit (queue, gst_queue_signals[SIGNAL_OVERRUN], 0);
GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);
/* we recheck, the signal could have changed the thresholds */
if (!gst_queue_is_filled (queue))
break;
}
/* how are we going to make space for this buffer? */
switch (queue->leaky) {
case GST_QUEUE_LEAK_UPSTREAM:
/* next buffer needs to get a DISCONT flag */
queue->tail_needs_discont = TRUE;
/* leak current buffer */
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,"queue is full, leaking buffer on upstream end");
/* now we can clean up and exit right away */
goto out_unref;
case GST_QUEUE_LEAK_DOWNSTREAM:
gst_queue_leak_downstream (queue);
break;
default:
g_warning ("Unknown leaky type, using default");
/* fall-through */
case GST_QUEUE_NO_LEAK: {
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,"queue is full, waiting for free space");
/* don't leak. Instead, wait for space to be available for as long as the queue is filled, wait till an item was deleted. */
while (gst_queue_is_filled (queue)) {
GST_QUEUE_WAIT_DEL_CHECK (queue, out_flushing);
}
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "queue is not full");
if (!queue->silent) {
GST_QUEUE_MUTEX_UNLOCK (queue);
g_signal_emit (queue, gst_queue_signals[SIGNAL_RUNNING], 0);
GST_QUEUE_MUTEX_LOCK_CHECK (queue, out_flushing);
}
break;
}
}
}
//是否不连续Buffer
if (queue->tail_needs_discont) {
if (!is_list) {
GstBuffer *buffer = GST_BUFFER_CAST (obj);
GstBuffer *subbuffer = gst_buffer_make_writable (buffer);
if (subbuffer) {
buffer = subbuffer;
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
} else {
GST_DEBUG_OBJECT (queue, "Could not mark buffer as DISCONT");
}
obj = GST_MINI_OBJECT_CAST (buffer);
} else {
GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj);
buffer_list = gst_buffer_list_make_writable (buffer_list);
gst_buffer_list_foreach (buffer_list, discont_first_buffer, queue);
obj = GST_MINI_OBJECT_CAST (buffer_list);
}
queue->tail_needs_discont = FALSE;
}
/* put buffer in queue now */
if (is_list)
gst_queue_locked_enqueue_buffer_list (queue, obj);
else
gst_queue_locked_enqueue_buffer (queue, obj);
GST_QUEUE_MUTEX_UNLOCK (queue);
return GST_FLOW_OK;
/* special conditions */
out_unref: {
GST_QUEUE_MUTEX_UNLOCK (queue);
gst_mini_object_unref (obj);
return GST_FLOW_OK;
}
out_flushing: {
GstFlowReturn ret = queue->srcresult;
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"exit because task paused, reason: %s", gst_flow_get_name (ret));
GST_QUEUE_MUTEX_UNLOCK (queue);
gst_mini_object_unref (obj);
return ret;
}
out_eos: {
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "exit because we received EOS");
GST_QUEUE_MUTEX_UNLOCK (queue);
gst_mini_object_unref (obj);
return GST_FLOW_EOS;
}
out_unexpected: {
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "exit because we received EOS");
GST_QUEUE_MUTEX_UNLOCK (queue);
gst_mini_object_unref (obj);
return GST_FLOW_EOS;
}
}