mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-07 07:58:51 +00:00
queue2: Add use-tags-bitrate property
The use-tags-bitrate property makes queue2 look at tag events in the stream and extract a bitrate for the stream to use when calculating a duration for buffers that don't have one explicitly set. This lets queue2 sensibly buffer to a time threshold for any bytestream for which the general bitrate is known.
This commit is contained in:
parent
227c387b43
commit
c1f4920859
2 changed files with 91 additions and 9 deletions
|
@ -114,6 +114,7 @@ enum
|
||||||
#define DEFAULT_MAX_SIZE_BYTES (2 * 1024 * 1024) /* 2 MB */
|
#define DEFAULT_MAX_SIZE_BYTES (2 * 1024 * 1024) /* 2 MB */
|
||||||
#define DEFAULT_MAX_SIZE_TIME 2 * GST_SECOND /* 2 seconds */
|
#define DEFAULT_MAX_SIZE_TIME 2 * GST_SECOND /* 2 seconds */
|
||||||
#define DEFAULT_USE_BUFFERING FALSE
|
#define DEFAULT_USE_BUFFERING FALSE
|
||||||
|
#define DEFAULT_USE_TAGS_BITRATE FALSE
|
||||||
#define DEFAULT_USE_RATE_ESTIMATE TRUE
|
#define DEFAULT_USE_RATE_ESTIMATE TRUE
|
||||||
#define DEFAULT_LOW_PERCENT 10
|
#define DEFAULT_LOW_PERCENT 10
|
||||||
#define DEFAULT_HIGH_PERCENT 99
|
#define DEFAULT_HIGH_PERCENT 99
|
||||||
|
@ -130,6 +131,7 @@ enum
|
||||||
PROP_MAX_SIZE_BYTES,
|
PROP_MAX_SIZE_BYTES,
|
||||||
PROP_MAX_SIZE_TIME,
|
PROP_MAX_SIZE_TIME,
|
||||||
PROP_USE_BUFFERING,
|
PROP_USE_BUFFERING,
|
||||||
|
PROP_USE_TAGS_BITRATE,
|
||||||
PROP_USE_RATE_ESTIMATE,
|
PROP_USE_RATE_ESTIMATE,
|
||||||
PROP_LOW_PERCENT,
|
PROP_LOW_PERCENT,
|
||||||
PROP_HIGH_PERCENT,
|
PROP_HIGH_PERCENT,
|
||||||
|
@ -356,6 +358,12 @@ gst_queue2_class_init (GstQueue2Class * klass)
|
||||||
"Emit GST_MESSAGE_BUFFERING based on low-/high-percent thresholds",
|
"Emit GST_MESSAGE_BUFFERING based on low-/high-percent thresholds",
|
||||||
DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
|
DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
|
||||||
G_PARAM_STATIC_STRINGS));
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
g_object_class_install_property (gobject_class, PROP_USE_TAGS_BITRATE,
|
||||||
|
g_param_spec_boolean ("use-tags-bitrate", "Use bitrate from tags",
|
||||||
|
"Use a bitrate from upstream tags to estimate buffer duration if not provided",
|
||||||
|
DEFAULT_USE_TAGS_BITRATE,
|
||||||
|
G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
g_object_class_install_property (gobject_class, PROP_USE_RATE_ESTIMATE,
|
g_object_class_install_property (gobject_class, PROP_USE_RATE_ESTIMATE,
|
||||||
g_param_spec_boolean ("use-rate-estimate", "Use Rate Estimate",
|
g_param_spec_boolean ("use-rate-estimate", "Use Rate Estimate",
|
||||||
"Estimate the bitrate of the stream to calculate time level",
|
"Estimate the bitrate of the stream to calculate time level",
|
||||||
|
@ -783,13 +791,21 @@ apply_gap (GstQueue2 * queue, GstEvent * event,
|
||||||
/* take a buffer and update segment, updating the time level of the queue. */
|
/* take a buffer and update segment, updating the time level of the queue. */
|
||||||
static void
|
static void
|
||||||
apply_buffer (GstQueue2 * queue, GstBuffer * buffer, GstSegment * segment,
|
apply_buffer (GstQueue2 * queue, GstBuffer * buffer, GstSegment * segment,
|
||||||
gboolean is_sink)
|
guint64 size, gboolean is_sink)
|
||||||
{
|
{
|
||||||
GstClockTime duration, timestamp;
|
GstClockTime duration, timestamp;
|
||||||
|
|
||||||
timestamp = GST_BUFFER_DTS_OR_PTS (buffer);
|
timestamp = GST_BUFFER_DTS_OR_PTS (buffer);
|
||||||
duration = GST_BUFFER_DURATION (buffer);
|
duration = GST_BUFFER_DURATION (buffer);
|
||||||
|
|
||||||
|
/* If we have no duration, pick one from the bitrate if we can */
|
||||||
|
if (duration == GST_CLOCK_TIME_NONE && queue->use_tags_bitrate) {
|
||||||
|
guint bitrate =
|
||||||
|
is_sink ? queue->sink_tags_bitrate : queue->src_tags_bitrate;
|
||||||
|
if (bitrate)
|
||||||
|
duration = gst_util_uint64_scale (size, 8 * GST_SECOND, bitrate);
|
||||||
|
}
|
||||||
|
|
||||||
/* if no timestamp is set, assume it's continuous with the previous
|
/* if no timestamp is set, assume it's continuous with the previous
|
||||||
* time */
|
* time */
|
||||||
if (timestamp == GST_CLOCK_TIME_NONE)
|
if (timestamp == GST_CLOCK_TIME_NONE)
|
||||||
|
@ -813,10 +829,17 @@ apply_buffer (GstQueue2 * queue, GstBuffer * buffer, GstSegment * segment,
|
||||||
update_time_level (queue);
|
update_time_level (queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BufListData
|
||||||
|
{
|
||||||
|
GstClockTime timestamp;
|
||||||
|
guint bitrate;
|
||||||
|
};
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
buffer_list_apply_time (GstBuffer ** buf, guint idx, gpointer data)
|
buffer_list_apply_time (GstBuffer ** buf, guint idx, gpointer data)
|
||||||
{
|
{
|
||||||
GstClockTime *timestamp = data;
|
struct BufListData *bld = data;
|
||||||
|
GstClockTime *timestamp = &bld->timestamp;
|
||||||
GstClockTime btime;
|
GstClockTime btime;
|
||||||
|
|
||||||
GST_TRACE ("buffer %u has pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT
|
GST_TRACE ("buffer %u has pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT
|
||||||
|
@ -831,6 +854,13 @@ buffer_list_apply_time (GstBuffer ** buf, guint idx, gpointer data)
|
||||||
|
|
||||||
if (GST_BUFFER_DURATION_IS_VALID (*buf))
|
if (GST_BUFFER_DURATION_IS_VALID (*buf))
|
||||||
*timestamp += GST_BUFFER_DURATION (*buf);
|
*timestamp += GST_BUFFER_DURATION (*buf);
|
||||||
|
else if (bld->bitrate != 0) {
|
||||||
|
guint64 size = gst_buffer_get_size (*buf);
|
||||||
|
|
||||||
|
/* If we have no duration, pick one from the bitrate if we can */
|
||||||
|
*timestamp += gst_util_uint64_scale (bld->bitrate, 8 * GST_SECOND, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
GST_TRACE ("ts now %" GST_TIME_FORMAT, GST_TIME_ARGS (*timestamp));
|
GST_TRACE ("ts now %" GST_TIME_FORMAT, GST_TIME_ARGS (*timestamp));
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -841,17 +871,25 @@ static void
|
||||||
apply_buffer_list (GstQueue2 * queue, GstBufferList * buffer_list,
|
apply_buffer_list (GstQueue2 * queue, GstBufferList * buffer_list,
|
||||||
GstSegment * segment, gboolean is_sink)
|
GstSegment * segment, gboolean is_sink)
|
||||||
{
|
{
|
||||||
GstClockTime timestamp;
|
struct BufListData bld;
|
||||||
|
|
||||||
/* if no timestamp is set, assume it's continuous with the previous time */
|
/* if no timestamp is set, assume it's continuous with the previous time */
|
||||||
timestamp = segment->position;
|
bld.timestamp = segment->position;
|
||||||
|
|
||||||
gst_buffer_list_foreach (buffer_list, buffer_list_apply_time, ×tamp);
|
if (queue->use_tags_bitrate) {
|
||||||
|
if (is_sink)
|
||||||
|
bld.bitrate = queue->sink_tags_bitrate;
|
||||||
|
else
|
||||||
|
bld.bitrate = queue->src_tags_bitrate;
|
||||||
|
} else
|
||||||
|
bld.bitrate = 0;
|
||||||
|
|
||||||
|
gst_buffer_list_foreach (buffer_list, buffer_list_apply_time, &bld);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (queue, "last_stop updated to %" GST_TIME_FORMAT,
|
GST_DEBUG_OBJECT (queue, "last_stop updated to %" GST_TIME_FORMAT,
|
||||||
GST_TIME_ARGS (timestamp));
|
GST_TIME_ARGS (bld.timestamp));
|
||||||
|
|
||||||
segment->position = timestamp;
|
segment->position = bld.timestamp;
|
||||||
|
|
||||||
if (is_sink)
|
if (is_sink)
|
||||||
queue->sink_tainted = TRUE;
|
queue->sink_tainted = TRUE;
|
||||||
|
@ -2097,7 +2135,7 @@ gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item,
|
||||||
queue->bytes_in += size;
|
queue->bytes_in += size;
|
||||||
|
|
||||||
/* apply new buffer to segment stats */
|
/* apply new buffer to segment stats */
|
||||||
apply_buffer (queue, buffer, &queue->sink_segment, TRUE);
|
apply_buffer (queue, buffer, &queue->sink_segment, size, TRUE);
|
||||||
/* update the byterate stats */
|
/* update the byterate stats */
|
||||||
update_in_rates (queue);
|
update_in_rates (queue);
|
||||||
|
|
||||||
|
@ -2269,7 +2307,7 @@ gst_queue2_locked_dequeue (GstQueue2 * queue, GstQueue2ItemType * item_type)
|
||||||
}
|
}
|
||||||
queue->bytes_out += size;
|
queue->bytes_out += size;
|
||||||
|
|
||||||
apply_buffer (queue, buffer, &queue->src_segment, FALSE);
|
apply_buffer (queue, buffer, &queue->src_segment, size, FALSE);
|
||||||
/* update the byterate stats */
|
/* update the byterate stats */
|
||||||
update_out_rates (queue);
|
update_out_rates (queue);
|
||||||
/* update the buffering */
|
/* update the buffering */
|
||||||
|
@ -2404,6 +2442,7 @@ gst_queue2_handle_sink_event (GstPad * pad, GstObject * parent,
|
||||||
queue->is_eos = FALSE;
|
queue->is_eos = FALSE;
|
||||||
queue->unexpected = FALSE;
|
queue->unexpected = FALSE;
|
||||||
queue->seeking = FALSE;
|
queue->seeking = FALSE;
|
||||||
|
queue->src_tags_bitrate = queue->sink_tags_bitrate = 0;
|
||||||
/* reset rate counters */
|
/* reset rate counters */
|
||||||
reset_rate_timer (queue);
|
reset_rate_timer (queue);
|
||||||
gst_pad_start_task (queue->srcpad, (GstTaskFunction) gst_queue2_loop,
|
gst_pad_start_task (queue->srcpad, (GstTaskFunction) gst_queue2_loop,
|
||||||
|
@ -2416,12 +2455,29 @@ gst_queue2_handle_sink_event (GstPad * pad, GstObject * parent,
|
||||||
queue->unexpected = FALSE;
|
queue->unexpected = FALSE;
|
||||||
queue->sinkresult = GST_FLOW_OK;
|
queue->sinkresult = GST_FLOW_OK;
|
||||||
queue->seeking = FALSE;
|
queue->seeking = FALSE;
|
||||||
|
queue->src_tags_bitrate = queue->sink_tags_bitrate = 0;
|
||||||
GST_QUEUE2_MUTEX_UNLOCK (queue);
|
GST_QUEUE2_MUTEX_UNLOCK (queue);
|
||||||
|
|
||||||
gst_event_unref (event);
|
gst_event_unref (event);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case GST_EVENT_TAG:{
|
||||||
|
if (queue->use_tags_bitrate) {
|
||||||
|
GstTagList *tags;
|
||||||
|
guint bitrate;
|
||||||
|
|
||||||
|
gst_event_parse_tag (event, &tags);
|
||||||
|
if (gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &bitrate) ||
|
||||||
|
gst_tag_list_get_uint (tags, GST_TAG_NOMINAL_BITRATE, &bitrate)) {
|
||||||
|
GST_QUEUE2_MUTEX_LOCK (queue);
|
||||||
|
queue->sink_tags_bitrate = bitrate;
|
||||||
|
GST_QUEUE2_MUTEX_UNLOCK (queue);
|
||||||
|
GST_LOG_OBJECT (queue, "Sink pad bitrate from tags now %u", bitrate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Fall-through */
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
if (GST_EVENT_IS_SERIALIZED (event)) {
|
if (GST_EVENT_IS_SERIALIZED (event)) {
|
||||||
/* serialized events go in the queue */
|
/* serialized events go in the queue */
|
||||||
|
@ -2793,6 +2849,22 @@ next:
|
||||||
GstEvent *event = GST_EVENT_CAST (data);
|
GstEvent *event = GST_EVENT_CAST (data);
|
||||||
GstEventType type = GST_EVENT_TYPE (event);
|
GstEventType type = GST_EVENT_TYPE (event);
|
||||||
|
|
||||||
|
if (type == GST_EVENT_TAG) {
|
||||||
|
if (queue->use_tags_bitrate) {
|
||||||
|
GstTagList *tags;
|
||||||
|
guint bitrate;
|
||||||
|
|
||||||
|
gst_event_parse_tag (event, &tags);
|
||||||
|
if (gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &bitrate) ||
|
||||||
|
gst_tag_list_get_uint (tags, GST_TAG_NOMINAL_BITRATE, &bitrate)) {
|
||||||
|
GST_QUEUE2_MUTEX_LOCK (queue);
|
||||||
|
queue->src_tags_bitrate = bitrate;
|
||||||
|
GST_QUEUE2_MUTEX_UNLOCK (queue);
|
||||||
|
GST_LOG_OBJECT (queue, "src pad bitrate from tags now %u", bitrate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gst_pad_push_event (queue->srcpad, event);
|
gst_pad_push_event (queue->srcpad, event);
|
||||||
|
|
||||||
/* if we're EOS, return EOS so that the task pauses. */
|
/* if we're EOS, return EOS so that the task pauses. */
|
||||||
|
@ -3600,6 +3672,9 @@ gst_queue2_set_property (GObject * object,
|
||||||
update_buffering (queue);
|
update_buffering (queue);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case PROP_USE_TAGS_BITRATE:
|
||||||
|
queue->use_tags_bitrate = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
case PROP_USE_RATE_ESTIMATE:
|
case PROP_USE_RATE_ESTIMATE:
|
||||||
queue->use_rate_estimate = g_value_get_boolean (value);
|
queue->use_rate_estimate = g_value_get_boolean (value);
|
||||||
break;
|
break;
|
||||||
|
@ -3657,6 +3732,9 @@ gst_queue2_get_property (GObject * object,
|
||||||
case PROP_USE_BUFFERING:
|
case PROP_USE_BUFFERING:
|
||||||
g_value_set_boolean (value, queue->use_buffering);
|
g_value_set_boolean (value, queue->use_buffering);
|
||||||
break;
|
break;
|
||||||
|
case PROP_USE_TAGS_BITRATE:
|
||||||
|
g_value_set_boolean (value, queue->use_tags_bitrate);
|
||||||
|
break;
|
||||||
case PROP_USE_RATE_ESTIMATE:
|
case PROP_USE_RATE_ESTIMATE:
|
||||||
g_value_set_boolean (value, queue->use_rate_estimate);
|
g_value_set_boolean (value, queue->use_rate_estimate);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -87,6 +87,9 @@ struct _GstQueue2
|
||||||
GstClockTime sinktime, srctime;
|
GstClockTime sinktime, srctime;
|
||||||
/* TRUE if either position needs to be recalculated */
|
/* TRUE if either position needs to be recalculated */
|
||||||
gboolean sink_tainted, src_tainted;
|
gboolean sink_tainted, src_tainted;
|
||||||
|
/* Bitrates taken from tags */
|
||||||
|
guint sink_tags_bitrate;
|
||||||
|
guint src_tags_bitrate;
|
||||||
|
|
||||||
/* flowreturn when srcpad is paused */
|
/* flowreturn when srcpad is paused */
|
||||||
GstFlowReturn srcresult;
|
GstFlowReturn srcresult;
|
||||||
|
@ -103,6 +106,7 @@ struct _GstQueue2
|
||||||
GstQueue2Size cur_level; /* currently in the queue */
|
GstQueue2Size cur_level; /* currently in the queue */
|
||||||
GstQueue2Size max_level; /* max. amount of data allowed in the queue */
|
GstQueue2Size max_level; /* max. amount of data allowed in the queue */
|
||||||
gboolean use_buffering;
|
gboolean use_buffering;
|
||||||
|
gboolean use_tags_bitrate;
|
||||||
gboolean use_rate_estimate;
|
gboolean use_rate_estimate;
|
||||||
GstClockTime buffering_interval;
|
GstClockTime buffering_interval;
|
||||||
gint low_percent; /* low/high watermarks for buffering */
|
gint low_percent; /* low/high watermarks for buffering */
|
||||||
|
|
Loading…
Reference in a new issue