mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-18 04:05:34 +00:00
appsrc: extract buffering level calculations
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5321>
This commit is contained in:
parent
aa515b0276
commit
4c13ccec16
4 changed files with 323 additions and 195 deletions
|
@ -101,6 +101,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "gstappsrc.h"
|
#include "gstappsrc.h"
|
||||||
|
#include "gstapputils.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
|
@ -178,11 +179,7 @@ struct _GstAppSrcPrivate
|
||||||
gboolean flushing;
|
gboolean flushing;
|
||||||
gboolean started;
|
gboolean started;
|
||||||
gboolean is_eos;
|
gboolean is_eos;
|
||||||
guint64 queued_bytes, queued_buffers;
|
GstQueueStatusInfo queue_status_info;
|
||||||
/* Used to calculate the current time level */
|
|
||||||
GstClockTime last_in_running_time, last_out_running_time;
|
|
||||||
/* Updated based on the above whenever they change */
|
|
||||||
GstClockTime queued_time;
|
|
||||||
guint64 offset;
|
guint64 offset;
|
||||||
GstAppStreamType current_type;
|
GstAppStreamType current_type;
|
||||||
|
|
||||||
|
@ -795,11 +792,7 @@ gst_app_src_flush_queued (GstAppSrc * src, gboolean retain_last_caps)
|
||||||
gst_queue_array_clear (priv->delayed_events);
|
gst_queue_array_clear (priv->delayed_events);
|
||||||
priv->pushed_buffer = FALSE;
|
priv->pushed_buffer = FALSE;
|
||||||
|
|
||||||
priv->queued_bytes = 0;
|
gst_queue_status_info_reset (&priv->queue_status_info);
|
||||||
priv->queued_buffers = 0;
|
|
||||||
priv->queued_time = 0;
|
|
||||||
priv->last_in_running_time = GST_CLOCK_TIME_NONE;
|
|
||||||
priv->last_out_running_time = GST_CLOCK_TIME_NONE;
|
|
||||||
priv->need_discont_upstream = FALSE;
|
priv->need_discont_upstream = FALSE;
|
||||||
priv->need_discont_downstream = FALSE;
|
priv->need_discont_downstream = FALSE;
|
||||||
}
|
}
|
||||||
|
@ -1384,92 +1377,18 @@ gst_app_src_update_queued_pop (GstAppSrc * appsrc, GstMiniObject * item,
|
||||||
gboolean update_offset)
|
gboolean update_offset)
|
||||||
{
|
{
|
||||||
GstAppSrcPrivate *priv = appsrc->priv;
|
GstAppSrcPrivate *priv = appsrc->priv;
|
||||||
guint buf_size = 0;
|
guint64 old_queued_bytes = priv->queue_status_info.queued_bytes;
|
||||||
guint n_buffers = 0;
|
guint64 bytes_dequeued;
|
||||||
GstClockTime end_buffer_ts = GST_CLOCK_TIME_NONE;
|
|
||||||
|
|
||||||
if (GST_IS_BUFFER (item)) {
|
gst_queue_status_info_pop (&priv->queue_status_info, item,
|
||||||
GstBuffer *buf = GST_BUFFER_CAST (item);
|
&priv->current_segment, &priv->last_segment, GST_OBJECT_CAST (appsrc));
|
||||||
buf_size = gst_buffer_get_size (buf);
|
|
||||||
n_buffers = 1;
|
|
||||||
|
|
||||||
end_buffer_ts = GST_BUFFER_DTS_OR_PTS (buf);
|
bytes_dequeued = old_queued_bytes - priv->queue_status_info.queued_bytes;
|
||||||
if (end_buffer_ts != GST_CLOCK_TIME_NONE
|
|
||||||
&& GST_BUFFER_DURATION_IS_VALID (buf))
|
|
||||||
end_buffer_ts += GST_BUFFER_DURATION (buf);
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (appsrc, "have buffer %p of size %u", buf, buf_size);
|
|
||||||
} else if (GST_IS_BUFFER_LIST (item)) {
|
|
||||||
GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (item);
|
|
||||||
guint i;
|
|
||||||
|
|
||||||
n_buffers = gst_buffer_list_length (buffer_list);
|
|
||||||
|
|
||||||
for (i = 0; i < n_buffers; i++) {
|
|
||||||
GstBuffer *tmp = gst_buffer_list_get (buffer_list, i);
|
|
||||||
GstClockTime ts = GST_BUFFER_DTS_OR_PTS (tmp);
|
|
||||||
|
|
||||||
buf_size += gst_buffer_get_size (tmp);
|
|
||||||
/* Update to the last buffer's timestamp that is known */
|
|
||||||
if (ts != GST_CLOCK_TIME_NONE) {
|
|
||||||
end_buffer_ts = ts;
|
|
||||||
if (GST_BUFFER_DURATION_IS_VALID (tmp))
|
|
||||||
end_buffer_ts += GST_BUFFER_DURATION (tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->queued_bytes -= buf_size;
|
|
||||||
priv->queued_buffers -= n_buffers;
|
|
||||||
|
|
||||||
/* Update time level if working on a TIME segment */
|
|
||||||
if ((priv->current_segment.format == GST_FORMAT_TIME
|
|
||||||
|| (priv->current_segment.format == GST_FORMAT_UNDEFINED
|
|
||||||
&& priv->last_segment.format == GST_FORMAT_TIME))
|
|
||||||
&& end_buffer_ts != GST_CLOCK_TIME_NONE) {
|
|
||||||
const GstSegment *segment =
|
|
||||||
priv->current_segment.format ==
|
|
||||||
GST_FORMAT_TIME ? &priv->current_segment : &priv->last_segment;
|
|
||||||
|
|
||||||
/* Clip to the current segment boundaries */
|
|
||||||
if (segment->stop != -1 && end_buffer_ts > segment->stop)
|
|
||||||
end_buffer_ts = segment->stop;
|
|
||||||
else if (segment->start > end_buffer_ts)
|
|
||||||
end_buffer_ts = segment->start;
|
|
||||||
|
|
||||||
priv->last_out_running_time =
|
|
||||||
gst_segment_to_running_time (segment, GST_FORMAT_TIME, end_buffer_ts);
|
|
||||||
|
|
||||||
GST_TRACE_OBJECT (appsrc,
|
|
||||||
"Last in running time %" GST_TIME_FORMAT ", last out running time %"
|
|
||||||
GST_TIME_FORMAT, GST_TIME_ARGS (priv->last_in_running_time),
|
|
||||||
GST_TIME_ARGS (priv->last_out_running_time));
|
|
||||||
|
|
||||||
/* If timestamps on both sides are known, calculate the current
|
|
||||||
* fill level in time and consider the queue empty if the output
|
|
||||||
* running time is lower than the input one (i.e. some kind of reset
|
|
||||||
* has happened).
|
|
||||||
*/
|
|
||||||
if (priv->last_out_running_time != GST_CLOCK_TIME_NONE
|
|
||||||
&& priv->last_in_running_time != GST_CLOCK_TIME_NONE) {
|
|
||||||
if (priv->last_out_running_time > priv->last_in_running_time) {
|
|
||||||
priv->queued_time = 0;
|
|
||||||
} else {
|
|
||||||
priv->queued_time =
|
|
||||||
priv->last_in_running_time - priv->last_out_running_time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (appsrc,
|
|
||||||
"Currently queued: %" G_GUINT64_FORMAT " bytes, %" G_GUINT64_FORMAT
|
|
||||||
" buffers, %" GST_TIME_FORMAT, priv->queued_bytes,
|
|
||||||
priv->queued_buffers, GST_TIME_ARGS (priv->queued_time));
|
|
||||||
|
|
||||||
/* only update the offset when in random_access mode and when requested by
|
/* only update the offset when in random_access mode and when requested by
|
||||||
* the caller, i.e. not when just dropping the item */
|
* the caller, i.e. not when just dropping the item */
|
||||||
if (update_offset && priv->stream_type == GST_APP_STREAM_TYPE_RANDOM_ACCESS)
|
if (update_offset && priv->stream_type == GST_APP_STREAM_TYPE_RANDOM_ACCESS)
|
||||||
priv->offset += buf_size;
|
priv->offset += bytes_dequeued;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the currently queued bytes/buffers/time information for the item
|
/* Update the currently queued bytes/buffers/time information for the item
|
||||||
|
@ -1479,99 +1398,11 @@ static void
|
||||||
gst_app_src_update_queued_push (GstAppSrc * appsrc, GstMiniObject * item)
|
gst_app_src_update_queued_push (GstAppSrc * appsrc, GstMiniObject * item)
|
||||||
{
|
{
|
||||||
GstAppSrcPrivate *priv = appsrc->priv;
|
GstAppSrcPrivate *priv = appsrc->priv;
|
||||||
GstClockTime start_buffer_ts = GST_CLOCK_TIME_NONE;
|
gst_queue_status_info_push (&priv->queue_status_info, item,
|
||||||
GstClockTime end_buffer_ts = GST_CLOCK_TIME_NONE;
|
&priv->last_segment, GST_OBJECT_CAST (appsrc));
|
||||||
guint buf_size = 0;
|
|
||||||
guint n_buffers = 0;
|
|
||||||
|
|
||||||
if (GST_IS_BUFFER (item)) {
|
|
||||||
GstBuffer *buf = GST_BUFFER_CAST (item);
|
|
||||||
|
|
||||||
buf_size = gst_buffer_get_size (buf);
|
|
||||||
n_buffers = 1;
|
|
||||||
|
|
||||||
start_buffer_ts = end_buffer_ts = GST_BUFFER_DTS_OR_PTS (buf);
|
|
||||||
if (end_buffer_ts != GST_CLOCK_TIME_NONE
|
|
||||||
&& GST_BUFFER_DURATION_IS_VALID (buf))
|
|
||||||
end_buffer_ts += GST_BUFFER_DURATION (buf);
|
|
||||||
} else if (GST_IS_BUFFER_LIST (item)) {
|
|
||||||
GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (item);
|
|
||||||
guint i;
|
|
||||||
|
|
||||||
n_buffers = gst_buffer_list_length (buffer_list);
|
|
||||||
|
|
||||||
for (i = 0; i < n_buffers; i++) {
|
|
||||||
GstBuffer *tmp = gst_buffer_list_get (buffer_list, i);
|
|
||||||
GstClockTime ts = GST_BUFFER_DTS_OR_PTS (tmp);
|
|
||||||
|
|
||||||
buf_size += gst_buffer_get_size (tmp);
|
|
||||||
|
|
||||||
if (ts != GST_CLOCK_TIME_NONE) {
|
|
||||||
if (start_buffer_ts == GST_CLOCK_TIME_NONE)
|
|
||||||
start_buffer_ts = ts;
|
|
||||||
end_buffer_ts = ts;
|
|
||||||
if (GST_BUFFER_DURATION_IS_VALID (tmp))
|
|
||||||
end_buffer_ts += GST_BUFFER_DURATION (tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->queued_bytes += buf_size;
|
|
||||||
priv->queued_buffers += n_buffers;
|
|
||||||
|
|
||||||
/* Update time level if working on a TIME segment */
|
|
||||||
if (priv->last_segment.format == GST_FORMAT_TIME
|
|
||||||
&& end_buffer_ts != GST_CLOCK_TIME_NONE) {
|
|
||||||
/* Clip to the last segment boundaries */
|
|
||||||
if (priv->last_segment.stop != -1
|
|
||||||
&& end_buffer_ts > priv->last_segment.stop)
|
|
||||||
end_buffer_ts = priv->last_segment.stop;
|
|
||||||
else if (priv->last_segment.start > end_buffer_ts)
|
|
||||||
end_buffer_ts = priv->last_segment.start;
|
|
||||||
|
|
||||||
priv->last_in_running_time =
|
|
||||||
gst_segment_to_running_time (&priv->last_segment, GST_FORMAT_TIME,
|
|
||||||
end_buffer_ts);
|
|
||||||
|
|
||||||
/* If this is the only buffer then we can directly update the queued time
|
|
||||||
* here. This is especially useful if this was the first buffer because
|
|
||||||
* otherwise we would have to wait until it is actually unqueued to know
|
|
||||||
* the queued duration */
|
|
||||||
if (priv->queued_buffers == 1) {
|
|
||||||
if (priv->last_segment.stop != -1
|
|
||||||
&& start_buffer_ts > priv->last_segment.stop)
|
|
||||||
start_buffer_ts = priv->last_segment.stop;
|
|
||||||
else if (priv->last_segment.start > start_buffer_ts)
|
|
||||||
start_buffer_ts = priv->last_segment.start;
|
|
||||||
|
|
||||||
priv->last_out_running_time =
|
|
||||||
gst_segment_to_running_time (&priv->last_segment, GST_FORMAT_TIME,
|
|
||||||
start_buffer_ts);
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_TRACE_OBJECT (appsrc,
|
|
||||||
"Last in running time %" GST_TIME_FORMAT ", last out running time %"
|
|
||||||
GST_TIME_FORMAT, GST_TIME_ARGS (priv->last_in_running_time),
|
|
||||||
GST_TIME_ARGS (priv->last_out_running_time));
|
|
||||||
|
|
||||||
if (priv->last_out_running_time != GST_CLOCK_TIME_NONE
|
|
||||||
&& priv->last_in_running_time != GST_CLOCK_TIME_NONE) {
|
|
||||||
if (priv->last_out_running_time > priv->last_in_running_time) {
|
|
||||||
priv->queued_time = 0;
|
|
||||||
} else {
|
|
||||||
priv->queued_time =
|
|
||||||
priv->last_in_running_time - priv->last_out_running_time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (appsrc,
|
|
||||||
"Currently queued: %" G_GUINT64_FORMAT " bytes, %" G_GUINT64_FORMAT
|
|
||||||
" buffers, %" GST_TIME_FORMAT, priv->queued_bytes, priv->queued_buffers,
|
|
||||||
GST_TIME_ARGS (priv->queued_time));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if @obj should be send after the CAPS and SEGMENT events */
|
/* check if @obj should be sent after the CAPS and SEGMENT events */
|
||||||
static gboolean
|
static gboolean
|
||||||
needs_segment (GstMiniObject * obj)
|
needs_segment (GstMiniObject * obj)
|
||||||
{
|
{
|
||||||
|
@ -1828,11 +1659,11 @@ gst_app_src_create (GstBaseSrc * bsrc, guint64 offset, guint size,
|
||||||
/* see if we go lower than the min-percent */
|
/* see if we go lower than the min-percent */
|
||||||
if (priv->min_percent) {
|
if (priv->min_percent) {
|
||||||
if ((priv->max_bytes
|
if ((priv->max_bytes
|
||||||
&& priv->queued_bytes * 100 / priv->max_bytes <=
|
&& priv->queue_status_info.queued_bytes * 100 /
|
||||||
priv->min_percent) || (priv->max_buffers
|
priv->max_bytes <= priv->min_percent) || (priv->max_buffers
|
||||||
&& priv->queued_buffers * 100 / priv->max_buffers <=
|
&& priv->queue_status_info.queued_buffers * 100 /
|
||||||
priv->min_percent) || (priv->max_time
|
priv->max_buffers <= priv->min_percent) || (priv->max_time
|
||||||
&& priv->queued_time * 100 / priv->max_time <=
|
&& priv->queue_status_info.queued_time * 100 / priv->max_time <=
|
||||||
priv->min_percent)) {
|
priv->min_percent)) {
|
||||||
/* ignore flushing state, we got a buffer and we will return it now.
|
/* ignore flushing state, we got a buffer and we will return it now.
|
||||||
* Errors will be handled in the next round */
|
* Errors will be handled in the next round */
|
||||||
|
@ -2211,7 +2042,7 @@ gst_app_src_get_current_level_bytes (GstAppSrc * appsrc)
|
||||||
priv = appsrc->priv;
|
priv = appsrc->priv;
|
||||||
|
|
||||||
GST_OBJECT_LOCK (appsrc);
|
GST_OBJECT_LOCK (appsrc);
|
||||||
queued = priv->queued_bytes;
|
queued = priv->queue_status_info.queued_bytes;
|
||||||
GST_DEBUG_OBJECT (appsrc, "current level bytes is %" G_GUINT64_FORMAT,
|
GST_DEBUG_OBJECT (appsrc, "current level bytes is %" G_GUINT64_FORMAT,
|
||||||
queued);
|
queued);
|
||||||
GST_OBJECT_UNLOCK (appsrc);
|
GST_OBJECT_UNLOCK (appsrc);
|
||||||
|
@ -2299,7 +2130,7 @@ gst_app_src_get_current_level_buffers (GstAppSrc * appsrc)
|
||||||
priv = appsrc->priv;
|
priv = appsrc->priv;
|
||||||
|
|
||||||
GST_OBJECT_LOCK (appsrc);
|
GST_OBJECT_LOCK (appsrc);
|
||||||
queued = priv->queued_buffers;
|
queued = priv->queue_status_info.queued_buffers;
|
||||||
GST_DEBUG_OBJECT (appsrc, "current level buffers is %" G_GUINT64_FORMAT,
|
GST_DEBUG_OBJECT (appsrc, "current level buffers is %" G_GUINT64_FORMAT,
|
||||||
queued);
|
queued);
|
||||||
GST_OBJECT_UNLOCK (appsrc);
|
GST_OBJECT_UNLOCK (appsrc);
|
||||||
|
@ -2388,7 +2219,7 @@ gst_app_src_get_current_level_time (GstAppSrc * appsrc)
|
||||||
priv = appsrc->priv;
|
priv = appsrc->priv;
|
||||||
|
|
||||||
GST_OBJECT_LOCK (appsrc);
|
GST_OBJECT_LOCK (appsrc);
|
||||||
queued = priv->queued_time;
|
queued = priv->queue_status_info.queued_time;
|
||||||
GST_DEBUG_OBJECT (appsrc, "current level time is %" GST_TIME_FORMAT,
|
GST_DEBUG_OBJECT (appsrc, "current level time is %" GST_TIME_FORMAT,
|
||||||
GST_TIME_ARGS (queued));
|
GST_TIME_ARGS (queued));
|
||||||
GST_OBJECT_UNLOCK (appsrc);
|
GST_OBJECT_UNLOCK (appsrc);
|
||||||
|
@ -2631,16 +2462,16 @@ gst_app_src_push_internal (GstAppSrc * appsrc, GstBuffer * buffer,
|
||||||
if (priv->is_eos)
|
if (priv->is_eos)
|
||||||
goto eos;
|
goto eos;
|
||||||
|
|
||||||
if ((priv->max_bytes && priv->queued_bytes >= priv->max_bytes) ||
|
if (gst_queue_status_info_is_full (&priv->queue_status_info,
|
||||||
(priv->max_buffers && priv->queued_buffers >= priv->max_buffers) ||
|
priv->max_buffers, priv->max_bytes, priv->max_time)) {
|
||||||
(priv->max_time && priv->queued_time >= priv->max_time)) {
|
|
||||||
GST_DEBUG_OBJECT (appsrc,
|
GST_DEBUG_OBJECT (appsrc,
|
||||||
"queue filled (queued %" G_GUINT64_FORMAT " bytes, max %"
|
"queue filled (queued %" G_GUINT64_FORMAT " bytes, max %"
|
||||||
G_GUINT64_FORMAT " bytes, " "queued %" G_GUINT64_FORMAT
|
G_GUINT64_FORMAT " bytes, " "queued %" G_GUINT64_FORMAT
|
||||||
" buffers, max %" G_GUINT64_FORMAT " buffers, " "queued %"
|
" buffers, max %" G_GUINT64_FORMAT " buffers, " "queued %"
|
||||||
GST_TIME_FORMAT " time, max %" GST_TIME_FORMAT " time)",
|
GST_TIME_FORMAT " time, max %" GST_TIME_FORMAT " time)",
|
||||||
priv->queued_bytes, priv->max_bytes, priv->queued_buffers,
|
priv->queue_status_info.queued_bytes, priv->max_bytes,
|
||||||
priv->max_buffers, GST_TIME_ARGS (priv->queued_time),
|
priv->queue_status_info.queued_buffers, priv->max_buffers,
|
||||||
|
GST_TIME_ARGS (priv->queue_status_info.queued_time),
|
||||||
GST_TIME_ARGS (priv->max_time));
|
GST_TIME_ARGS (priv->max_time));
|
||||||
|
|
||||||
if (first) {
|
if (first) {
|
||||||
|
|
250
subprojects/gst-plugins-base/gst-libs/gst/app/gstapputils.c
Normal file
250
subprojects/gst-plugins-base/gst-libs/gst/app/gstapputils.c
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gstapputils.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_queue_status_info_reset (GstQueueStatusInfo * info)
|
||||||
|
{
|
||||||
|
g_return_if_fail (info != NULL);
|
||||||
|
|
||||||
|
info->queued_bytes = 0;
|
||||||
|
info->queued_buffers = 0;
|
||||||
|
info->queued_time = 0;
|
||||||
|
info->num_events = 0;
|
||||||
|
info->last_in_running_time = GST_CLOCK_TIME_NONE;
|
||||||
|
info->last_out_running_time = GST_CLOCK_TIME_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gst_queue_status_info_is_full (const GstQueueStatusInfo * info,
|
||||||
|
guint64 max_buffers, guint64 max_bytes, GstClockTime max_time)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (info != NULL, FALSE);
|
||||||
|
|
||||||
|
return (max_buffers > 0 && info->queued_buffers >= max_buffers)
|
||||||
|
|| (max_bytes > 0 && info->queued_bytes >= max_bytes)
|
||||||
|
|| (max_time > 0 && info->queued_time >= max_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_queue_status_info_push_event (GstQueueStatusInfo * info)
|
||||||
|
{
|
||||||
|
g_return_if_fail (info != NULL);
|
||||||
|
|
||||||
|
info->num_events++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the currently queued bytes/buffers/time information for the item
|
||||||
|
* that was just added to the queue.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_queue_status_info_push (GstQueueStatusInfo * info, GstMiniObject * item,
|
||||||
|
GstSegment * last_segment, GstObject * log_context)
|
||||||
|
{
|
||||||
|
GstClockTime start_buffer_ts = GST_CLOCK_TIME_NONE;
|
||||||
|
GstClockTime end_buffer_ts = GST_CLOCK_TIME_NONE;
|
||||||
|
guint buf_size = 0;
|
||||||
|
guint n_buffers = 0;
|
||||||
|
|
||||||
|
g_return_if_fail (info != NULL);
|
||||||
|
|
||||||
|
if (GST_IS_EVENT (item)) {
|
||||||
|
info->num_events++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GST_IS_BUFFER (item)) {
|
||||||
|
GstBuffer *buf = GST_BUFFER_CAST (item);
|
||||||
|
|
||||||
|
buf_size = gst_buffer_get_size (buf);
|
||||||
|
n_buffers = 1;
|
||||||
|
|
||||||
|
start_buffer_ts = end_buffer_ts = GST_BUFFER_DTS_OR_PTS (buf);
|
||||||
|
if (end_buffer_ts != GST_CLOCK_TIME_NONE
|
||||||
|
&& GST_BUFFER_DURATION_IS_VALID (buf))
|
||||||
|
end_buffer_ts += GST_BUFFER_DURATION (buf);
|
||||||
|
} else if (GST_IS_BUFFER_LIST (item)) {
|
||||||
|
GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (item);
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
n_buffers = gst_buffer_list_length (buffer_list);
|
||||||
|
|
||||||
|
for (i = 0; i < n_buffers; i++) {
|
||||||
|
GstBuffer *tmp = gst_buffer_list_get (buffer_list, i);
|
||||||
|
GstClockTime ts = GST_BUFFER_DTS_OR_PTS (tmp);
|
||||||
|
|
||||||
|
buf_size += gst_buffer_get_size (tmp);
|
||||||
|
|
||||||
|
if (ts != GST_CLOCK_TIME_NONE) {
|
||||||
|
if (start_buffer_ts == GST_CLOCK_TIME_NONE)
|
||||||
|
start_buffer_ts = ts;
|
||||||
|
end_buffer_ts = ts;
|
||||||
|
if (GST_BUFFER_DURATION_IS_VALID (tmp))
|
||||||
|
end_buffer_ts += GST_BUFFER_DURATION (tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info->queued_bytes += buf_size;
|
||||||
|
info->queued_buffers += n_buffers;
|
||||||
|
|
||||||
|
/* Update time level if working on a TIME segment */
|
||||||
|
if (last_segment->format == GST_FORMAT_TIME
|
||||||
|
&& end_buffer_ts != GST_CLOCK_TIME_NONE) {
|
||||||
|
/* Clip to the last segment boundaries */
|
||||||
|
if (last_segment->stop != -1 && end_buffer_ts > last_segment->stop)
|
||||||
|
end_buffer_ts = last_segment->stop;
|
||||||
|
else if (last_segment->start > end_buffer_ts)
|
||||||
|
end_buffer_ts = last_segment->start;
|
||||||
|
|
||||||
|
info->last_in_running_time =
|
||||||
|
gst_segment_to_running_time (last_segment, GST_FORMAT_TIME,
|
||||||
|
end_buffer_ts);
|
||||||
|
|
||||||
|
/* If this is the only buffer then we can directly update the queued time
|
||||||
|
* here. This is especially useful if this was the first buffer because
|
||||||
|
* otherwise we would have to wait until it is actually unqueued to know
|
||||||
|
* the queued duration */
|
||||||
|
if (info->queued_buffers == 1) {
|
||||||
|
if (last_segment->stop != -1 && start_buffer_ts > last_segment->stop)
|
||||||
|
start_buffer_ts = last_segment->stop;
|
||||||
|
else if (last_segment->start > start_buffer_ts)
|
||||||
|
start_buffer_ts = last_segment->start;
|
||||||
|
|
||||||
|
info->last_out_running_time =
|
||||||
|
gst_segment_to_running_time (last_segment, GST_FORMAT_TIME,
|
||||||
|
start_buffer_ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_TRACE_OBJECT (log_context,
|
||||||
|
"Last in running time %" GST_TIME_FORMAT ", last out running time %"
|
||||||
|
GST_TIME_FORMAT, GST_TIME_ARGS (info->last_in_running_time),
|
||||||
|
GST_TIME_ARGS (info->last_out_running_time));
|
||||||
|
|
||||||
|
if (info->last_out_running_time != GST_CLOCK_TIME_NONE
|
||||||
|
&& info->last_in_running_time != GST_CLOCK_TIME_NONE) {
|
||||||
|
if (info->last_out_running_time > info->last_in_running_time) {
|
||||||
|
info->queued_time = 0;
|
||||||
|
} else {
|
||||||
|
info->queued_time =
|
||||||
|
info->last_in_running_time - info->last_out_running_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (log_context,
|
||||||
|
"Currently queued: %" G_GUINT64_FORMAT " bytes, %" G_GUINT64_FORMAT
|
||||||
|
" buffers, %" GST_TIME_FORMAT, info->queued_bytes, info->queued_buffers,
|
||||||
|
GST_TIME_ARGS (info->queued_time));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_queue_status_info_pop (GstQueueStatusInfo * info, GstMiniObject * item,
|
||||||
|
GstSegment * current_segment, GstSegment * last_segment,
|
||||||
|
GstObject * log_context)
|
||||||
|
{
|
||||||
|
guint buf_size = 0;
|
||||||
|
guint n_buffers = 0;
|
||||||
|
GstClockTime end_buffer_ts = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
|
g_return_if_fail (info != NULL);
|
||||||
|
|
||||||
|
if (GST_IS_EVENT (item)) {
|
||||||
|
info->num_events--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GST_IS_BUFFER (item)) {
|
||||||
|
GstBuffer *buf = GST_BUFFER_CAST (item);
|
||||||
|
buf_size = gst_buffer_get_size (buf);
|
||||||
|
n_buffers = 1;
|
||||||
|
|
||||||
|
end_buffer_ts = GST_BUFFER_DTS_OR_PTS (buf);
|
||||||
|
if (end_buffer_ts != GST_CLOCK_TIME_NONE
|
||||||
|
&& GST_BUFFER_DURATION_IS_VALID (buf))
|
||||||
|
end_buffer_ts += GST_BUFFER_DURATION (buf);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (log_context, "have buffer %p of size %u", buf, buf_size);
|
||||||
|
} else if (GST_IS_BUFFER_LIST (item)) {
|
||||||
|
GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (item);
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
n_buffers = gst_buffer_list_length (buffer_list);
|
||||||
|
|
||||||
|
for (i = 0; i < n_buffers; i++) {
|
||||||
|
GstBuffer *tmp = gst_buffer_list_get (buffer_list, i);
|
||||||
|
GstClockTime ts = GST_BUFFER_DTS_OR_PTS (tmp);
|
||||||
|
|
||||||
|
buf_size += gst_buffer_get_size (tmp);
|
||||||
|
/* Update to the last buffer's timestamp that is known */
|
||||||
|
if (ts != GST_CLOCK_TIME_NONE) {
|
||||||
|
end_buffer_ts = ts;
|
||||||
|
if (GST_BUFFER_DURATION_IS_VALID (tmp))
|
||||||
|
end_buffer_ts += GST_BUFFER_DURATION (tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info->queued_bytes -= buf_size;
|
||||||
|
info->queued_buffers -= n_buffers;
|
||||||
|
|
||||||
|
/* Update time level if working on a TIME segment */
|
||||||
|
if ((current_segment->format == GST_FORMAT_TIME
|
||||||
|
|| (current_segment->format == GST_FORMAT_UNDEFINED
|
||||||
|
&& last_segment->format == GST_FORMAT_TIME))
|
||||||
|
&& end_buffer_ts != GST_CLOCK_TIME_NONE) {
|
||||||
|
const GstSegment *segment =
|
||||||
|
current_segment->format ==
|
||||||
|
GST_FORMAT_TIME ? current_segment : last_segment;
|
||||||
|
|
||||||
|
/* Clip to the current segment boundaries */
|
||||||
|
if (segment->stop != -1 && end_buffer_ts > segment->stop)
|
||||||
|
end_buffer_ts = segment->stop;
|
||||||
|
else if (segment->start > end_buffer_ts)
|
||||||
|
end_buffer_ts = segment->start;
|
||||||
|
|
||||||
|
info->last_out_running_time =
|
||||||
|
gst_segment_to_running_time (segment, GST_FORMAT_TIME, end_buffer_ts);
|
||||||
|
|
||||||
|
GST_TRACE_OBJECT (log_context,
|
||||||
|
"Last in running time %" GST_TIME_FORMAT ", last out running time %"
|
||||||
|
GST_TIME_FORMAT, GST_TIME_ARGS (info->last_in_running_time),
|
||||||
|
GST_TIME_ARGS (info->last_out_running_time));
|
||||||
|
|
||||||
|
/* If timestamps on both sides are known, calculate the current
|
||||||
|
* fill level in time and consider the queue empty if the output
|
||||||
|
* running time is lower than the input one (i.e. some kind of reset
|
||||||
|
* has happened).
|
||||||
|
*/
|
||||||
|
if (info->last_out_running_time != GST_CLOCK_TIME_NONE
|
||||||
|
&& info->last_in_running_time != GST_CLOCK_TIME_NONE) {
|
||||||
|
if (info->last_out_running_time > info->last_in_running_time) {
|
||||||
|
info->queued_time = 0;
|
||||||
|
} else {
|
||||||
|
info->queued_time =
|
||||||
|
info->last_in_running_time - info->last_out_running_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (log_context,
|
||||||
|
"Currently queued: %" G_GUINT64_FORMAT " bytes, %" G_GUINT64_FORMAT
|
||||||
|
" buffers, %" GST_TIME_FORMAT, info->queued_bytes,
|
||||||
|
info->queued_buffers, GST_TIME_ARGS (info->queued_time));
|
||||||
|
}
|
47
subprojects/gst-plugins-base/gst-libs/gst/app/gstapputils.h
Normal file
47
subprojects/gst-plugins-base/gst-libs/gst/app/gstapputils.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#ifndef _GST_APP_UTILS_H_
|
||||||
|
#define _GST_APP_UTILS_H_
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
typedef struct _GstQueueStatusInfo
|
||||||
|
{
|
||||||
|
guint64 queued_bytes, queued_buffers;
|
||||||
|
/* Used to calculate the current time level */
|
||||||
|
GstClockTime last_in_running_time, last_out_running_time;
|
||||||
|
/* Updated based on the above whenever they change */
|
||||||
|
GstClockTime queued_time;
|
||||||
|
guint num_events;
|
||||||
|
} GstQueueStatusInfo;
|
||||||
|
|
||||||
|
void gst_queue_status_info_reset (GstQueueStatusInfo * info);
|
||||||
|
|
||||||
|
gboolean gst_queue_status_info_is_full (const GstQueueStatusInfo * info,
|
||||||
|
guint64 max_buffers, guint64 max_bytes, GstClockTime max_time);
|
||||||
|
|
||||||
|
void gst_queue_status_info_push (GstQueueStatusInfo * info,
|
||||||
|
GstMiniObject * item, GstSegment * last_segment, GstObject * log_context);
|
||||||
|
|
||||||
|
void gst_queue_status_info_push_event (GstQueueStatusInfo * info);
|
||||||
|
|
||||||
|
void gst_queue_status_info_pop (GstQueueStatusInfo * info, GstMiniObject * item,
|
||||||
|
GstSegment * current_segment, GstSegment * last_segment,
|
||||||
|
GstObject * log_context);
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,4 +1,4 @@
|
||||||
app_sources = files(['gstappsrc.c', 'gstappsink.c'])
|
app_sources = files(['gstappsrc.c', 'gstappsink.c', 'gstapputils.c'])
|
||||||
|
|
||||||
app_mkenum_headers = files([
|
app_mkenum_headers = files([
|
||||||
'gstappsrc.h',
|
'gstappsrc.h',
|
||||||
|
|
Loading…
Reference in a new issue