*
* 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-queue2
+ * @title: queue2
*
* Data is queued until one of the limits specified by the
* #GstQueue2:max-size-buffers, #GstQueue2:max-size-bytes and/or
* The default queue size limits are 100 buffers, 2MB of data, or
* two seconds worth of data, whichever is reached first.
*
- * If you set temp-tmpl to a value such as /tmp/gstreamer-XXXXXX, the element
+ * If you set temp-template to a value such as /tmp/gstreamer-XXXXXX, the element
* will allocate a random free filename and buffer data in the file.
* By using this, it will buffer the entire stream data on the file independently
* of the queue size limits, they will only be used for buffering statistics.
*
- * Since 0.10.24, setting the temp-location property with a filename is deprecated
- * because it's impossible to securely open a temporary file in this way. The
- * property will still be used to notify the application of the allocated
- * filename, though.
- *
- * Last reviewed on 2009-07-10 (0.10.24)
+ * The temp-location property will be used to notify the application of the
+ * allocated filename.
*/
#ifdef HAVE_CONFIG_H
#include <unistd.h>
#endif
+#ifdef __BIONIC__ /* Android */
+#undef lseek
+#define lseek lseek64
+#undef off_t
+#define off_t guint64
+#include <fcntl.h>
+#endif
+
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
/* other defines */
#define DEFAULT_BUFFER_SIZE 4096
-#define QUEUE_IS_USING_TEMP_FILE(queue) ((queue)->temp_location_set || (queue)->temp_template != NULL)
+#define QUEUE_IS_USING_TEMP_FILE(queue) ((queue)->temp_template != NULL)
#define QUEUE_IS_USING_RING_BUFFER(queue) ((queue)->ring_buffer_max_size != 0) /* for consistency with the above macro */
#define QUEUE_IS_USING_QUEUE(queue) (!QUEUE_IS_USING_TEMP_FILE(queue) && !QUEUE_IS_USING_RING_BUFFER (queue))
#define DEFAULT_MAX_SIZE_BYTES (2 * 1024 * 1024) /* 2 MB */
#define DEFAULT_MAX_SIZE_TIME 2 * GST_SECOND /* 2 seconds */
#define DEFAULT_USE_BUFFERING FALSE
+#define DEFAULT_USE_TAGS_BITRATE FALSE
#define DEFAULT_USE_RATE_ESTIMATE TRUE
#define DEFAULT_LOW_PERCENT 10
#define DEFAULT_HIGH_PERCENT 99
+#define DEFAULT_LOW_WATERMARK 0.01
+#define DEFAULT_HIGH_WATERMARK 0.99
#define DEFAULT_TEMP_REMOVE TRUE
#define DEFAULT_RING_BUFFER_MAX_SIZE 0
PROP_MAX_SIZE_BYTES,
PROP_MAX_SIZE_TIME,
PROP_USE_BUFFERING,
+ PROP_USE_TAGS_BITRATE,
PROP_USE_RATE_ESTIMATE,
PROP_LOW_PERCENT,
PROP_HIGH_PERCENT,
+ PROP_LOW_WATERMARK,
+ PROP_HIGH_WATERMARK,
PROP_TEMP_TEMPLATE,
PROP_TEMP_LOCATION,
PROP_TEMP_REMOVE,
PROP_RING_BUFFER_MAX_SIZE,
+ PROP_AVG_IN_RATE,
PROP_LAST
};
+/* Explanation for buffer levels and percentages:
+ *
+ * The buffering_level functions here return a value in a normalized range
+ * that specifies the queue's current fill level. The range goes from 0 to
+ * MAX_BUFFERING_LEVEL. The low/high watermarks also use this same range.
+ *
+ * This is not to be confused with the buffering_percent value, which is
+ * a *relative* quantity - relative to the low/high watermarks.
+ * buffering_percent = 0% means buffering_level is at the low watermark.
+ * buffering_percent = 100% means buffering_level is at the high watermark.
+ * buffering_percent is used for determining if the fill level has reached
+ * the high watermark, and for producing BUFFERING messages. This value
+ * always uses a 0..100 range (since it is a percentage).
+ *
+ * To avoid future confusions, whenever "buffering level" is mentioned, it
+ * refers to the absolute level which is in the 0..MAX_BUFFERING_LEVEL
+ * range. Whenever "buffering_percent" is mentioned, it refers to the
+ * percentage value that is relative to the low/high watermark. */
+
+/* Using a buffering level range of 0..1000000 to allow for a
+ * resolution in ppm (1 ppm = 0.0001%) */
+#define MAX_BUFFERING_LEVEL 1000000
+
+/* How much 1% makes up in the buffer level range */
+#define BUF_LEVEL_PERCENT_FACTOR ((MAX_BUFFERING_LEVEL) / 100)
+
#define GST_QUEUE2_CLEAR_LEVEL(l) G_STMT_START { \
l.buffers = 0; \
l.bytes = 0; \
} \
} G_STMT_END
+#define SET_PERCENT(q, perc) G_STMT_START { \
+ if (perc != q->buffering_percent) { \
+ q->buffering_percent = perc; \
+ q->percent_changed = TRUE; \
+ GST_DEBUG_OBJECT (q, "buffering %d percent", perc); \
+ get_buffering_stats (q, perc, &q->mode, &q->avg_in, &q->avg_out, \
+ &q->buffering_left); \
+ } \
+} G_STMT_END
+
#define _do_init \
GST_DEBUG_CATEGORY_INIT (queue_debug, "queue2", 0, "queue element"); \
GST_DEBUG_CATEGORY_INIT (queue_dataflow, "queue2_dataflow", 0, \
static GstFlowReturn gst_queue2_push_one (GstQueue2 * queue);
static void gst_queue2_loop (GstPad * pad);
-static gboolean gst_queue2_handle_sink_event (GstPad * pad, GstObject * parent,
- GstEvent * event);
+static GstFlowReturn gst_queue2_handle_sink_event (GstPad * pad,
+ GstObject * parent, GstEvent * event);
static gboolean gst_queue2_handle_sink_query (GstPad * pad, GstObject * parent,
GstQuery * query);
static gboolean gst_queue2_is_filled (GstQueue2 * queue);
static void update_cur_level (GstQueue2 * queue, GstQueue2Range * range);
+static void update_in_rates (GstQueue2 * queue, gboolean force);
+static GstMessage *gst_queue2_get_buffering_message (GstQueue2 * queue);
+static void gst_queue2_post_buffering (GstQueue2 * queue);
typedef enum
{
GST_QUEUE2_ITEM_TYPE_UNKNOWN = 0,
GST_QUEUE2_ITEM_TYPE_BUFFER,
GST_QUEUE2_ITEM_TYPE_BUFFER_LIST,
- GST_QUEUE2_ITEM_TYPE_EVENT
+ GST_QUEUE2_ITEM_TYPE_EVENT,
+ GST_QUEUE2_ITEM_TYPE_QUERY
} GstQueue2ItemType;
+typedef struct
+{
+ GstQueue2ItemType type;
+ GstMiniObject *item;
+} GstQueue2Item;
+
/* static guint gst_queue2_signals[LAST_SIGNAL] = { 0 }; */
static void
g_param_spec_uint ("max-size-bytes", "Max. size (kB)",
"Max. amount of data in the queue (bytes, 0=disable)",
0, G_MAXUINT, DEFAULT_MAX_SIZE_BYTES,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_MAX_SIZE_BUFFERS,
g_param_spec_uint ("max-size-buffers", "Max. size (buffers)",
"Max. number of buffers in the queue (0=disable)", 0, G_MAXUINT,
DEFAULT_MAX_SIZE_BUFFERS,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_MAX_SIZE_TIME,
g_param_spec_uint64 ("max-size-time", "Max. size (ns)",
"Max. amount of data in the queue (in ns, 0=disable)", 0, G_MAXUINT64,
- DEFAULT_MAX_SIZE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ DEFAULT_MAX_SIZE_TIME, G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
+ G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_USE_BUFFERING,
g_param_spec_boolean ("use-buffering", "Use buffering",
"Emit GST_MESSAGE_BUFFERING based on low-/high-percent thresholds",
- DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
+ 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_param_spec_boolean ("use-rate-estimate", "Use Rate Estimate",
"Estimate the bitrate of the stream to calculate time level",
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_LOW_PERCENT,
g_param_spec_int ("low-percent", "Low percent",
- "Low threshold for buffering to start. Only used if use-buffering is True",
- 0, 100, DEFAULT_LOW_PERCENT,
+ "Low threshold for buffering to start. Only used if use-buffering is True "
+ "(Deprecated: use low-watermark instead)",
+ 0, 100, DEFAULT_LOW_WATERMARK * 100,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_HIGH_PERCENT,
g_param_spec_int ("high-percent", "High percent",
+ "High threshold for buffering to finish. Only used if use-buffering is True "
+ "(Deprecated: use high-watermark instead)",
+ 0, 100, DEFAULT_HIGH_WATERMARK * 100,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_LOW_WATERMARK,
+ g_param_spec_double ("low-watermark", "Low watermark",
+ "Low threshold for buffering to start. Only used if use-buffering is True",
+ 0.0, 1.0, DEFAULT_LOW_WATERMARK,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_HIGH_WATERMARK,
+ g_param_spec_double ("high-watermark", "High watermark",
"High threshold for buffering to finish. Only used if use-buffering is True",
- 0, 100, DEFAULT_HIGH_PERCENT,
+ 0.0, 1.0, DEFAULT_HIGH_WATERMARK,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_TEMP_TEMPLATE,
g_object_class_install_property (gobject_class, PROP_TEMP_LOCATION,
g_param_spec_string ("temp-location", "Temporary File Location",
- "Location to store temporary files in (Deprecated: Only read this "
- "property, use temp-template to configure the name template)",
- NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ "Location to store temporary files in (Only read this property, "
+ "use temp-template to configure the name template)",
+ NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GstQueue2:temp-remove
*
* When temp-template is set, remove the temporary file when going to READY.
- *
- * Since: 0.10.26
*/
g_object_class_install_property (gobject_class, PROP_TEMP_REMOVE,
g_param_spec_boolean ("temp-remove", "Remove the Temporary File",
*
* The maximum size of the ring buffer in bytes. If set to 0, the ring
* buffer is disabled. Default 0.
- *
- * Since: 0.10.31
*/
g_object_class_install_property (gobject_class, PROP_RING_BUFFER_MAX_SIZE,
g_param_spec_uint64 ("ring-buffer-max-size",
0, G_MAXUINT64, DEFAULT_RING_BUFFER_MAX_SIZE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstQueue2:avg-in-rate
+ *
+ * The average input data rate.
+ */
+ g_object_class_install_property (gobject_class, PROP_AVG_IN_RATE,
+ g_param_spec_int64 ("avg-in-rate", "Input data rate (bytes/s)",
+ "Average input data rate (bytes/s)",
+ 0, G_MAXINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
/* set several parent class virtual functions */
gobject_class->finalize = gst_queue2_finalize;
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&srctemplate));
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&sinktemplate));
+ gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
+ gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
gst_element_class_set_static_metadata (gstelement_class, "Queue 2",
"Generic",
GST_DEBUG_FUNCPTR (gst_queue2_chain_list));
gst_pad_set_activatemode_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue2_sink_activate_mode));
- gst_pad_set_event_function (queue->sinkpad,
+ gst_pad_set_event_full_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue2_handle_sink_event));
gst_pad_set_query_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue2_handle_sink_query));
queue->max_level.rate_time = DEFAULT_MAX_SIZE_TIME;
queue->use_buffering = DEFAULT_USE_BUFFERING;
queue->use_rate_estimate = DEFAULT_USE_RATE_ESTIMATE;
- queue->low_percent = DEFAULT_LOW_PERCENT;
- queue->high_percent = DEFAULT_HIGH_PERCENT;
+ queue->low_watermark = DEFAULT_LOW_WATERMARK * MAX_BUFFERING_LEVEL;
+ queue->high_watermark = DEFAULT_HIGH_WATERMARK * MAX_BUFFERING_LEVEL;
gst_segment_init (&queue->sink_segment, GST_FORMAT_TIME);
gst_segment_init (&queue->src_segment, GST_FORMAT_TIME);
g_cond_init (&queue->item_del);
g_queue_init (&queue->queue);
+ g_cond_init (&queue->query_handled);
+ queue->last_query = FALSE;
+
+ g_mutex_init (&queue->buffering_post_lock);
queue->buffering_percent = 100;
/* tempfile related */
queue->temp_template = NULL;
queue->temp_location = NULL;
- queue->temp_location_set = FALSE;
queue->temp_remove = DEFAULT_TEMP_REMOVE;
queue->ring_buffer = NULL;
GST_DEBUG_OBJECT (queue, "finalizing queue");
while (!g_queue_is_empty (&queue->queue)) {
- GstMiniObject *data = g_queue_pop_head (&queue->queue);
+ GstQueue2Item *qitem = g_queue_pop_head (&queue->queue);
- gst_mini_object_unref (data);
+ if (qitem->type != GST_QUEUE2_ITEM_TYPE_QUERY)
+ gst_mini_object_unref (qitem->item);
+ g_slice_free (GstQueue2Item, qitem);
}
+ queue->last_query = FALSE;
g_queue_clear (&queue->queue);
g_mutex_clear (&queue->qlock);
+ g_mutex_clear (&queue->buffering_post_lock);
g_cond_clear (&queue->item_add);
g_cond_clear (&queue->item_del);
+ g_cond_clear (&queue->query_handled);
g_timer_destroy (queue->in_timer);
g_timer_destroy (queue->out_timer);
/* make a new range for @offset or reuse an existing range */
static GstQueue2Range *
-add_range (GstQueue2 * queue, guint64 offset)
+add_range (GstQueue2 * queue, guint64 offset, gboolean update_existing)
{
GstQueue2Range *range, *prev, *next;
GST_DEBUG_OBJECT (queue,
"reusing range %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT, range->offset,
range->writing_pos);
- range->writing_pos = offset;
+ if (update_existing && range->writing_pos != offset) {
+ GST_DEBUG_OBJECT (queue, "updating range writing position to "
+ "%" G_GUINT64_FORMAT, offset);
+ range->writing_pos = offset;
+ }
} else {
GST_DEBUG_OBJECT (queue,
"new range %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT, offset, offset);
/* get rid of all the current ranges */
clean_ranges (queue);
/* make a range for offset 0 */
- queue->current = add_range (queue, 0);
+ queue->current = add_range (queue, 0, TRUE);
}
/* calculate the diff between running time on the sink and src of the queue.
gst_event_copy_segment (event, segment);
if (segment->format == GST_FORMAT_BYTES) {
- if (!QUEUE_IS_USING_QUEUE (queue)) {
+ if (!QUEUE_IS_USING_QUEUE (queue) && is_sink) {
/* start is where we'll be getting from and as such writing next */
- queue->current = add_range (queue, segment->start);
- /* update the stats for this range */
- update_cur_level (queue, queue->current);
+ queue->current = add_range (queue, segment->start, TRUE);
}
}
/* now configure the values, we use these to track timestamps on the
* sinkpad. */
if (segment->format != GST_FORMAT_TIME) {
- /* non-time format, pretent the current time segment is closed with a
+ /* non-time format, pretend the current time segment is closed with a
* 0 start and unknown stop time. */
segment->format = GST_FORMAT_TIME;
segment->start = 0;
update_time_level (queue);
}
+static void
+apply_gap (GstQueue2 * queue, GstEvent * event,
+ GstSegment * segment, gboolean is_sink)
+{
+ GstClockTime timestamp;
+ GstClockTime duration;
+
+ gst_event_parse_gap (event, ×tamp, &duration);
+
+ if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+
+ if (GST_CLOCK_TIME_IS_VALID (duration)) {
+ timestamp += duration;
+ }
+
+ segment->position = timestamp;
+
+ if (is_sink)
+ queue->sink_tainted = TRUE;
+ else
+ queue->src_tainted = TRUE;
+
+ /* calc diff with other end */
+ update_time_level (queue);
+ }
+}
+
/* take a buffer and update segment, updating the time level of the queue. */
static void
apply_buffer (GstQueue2 * queue, GstBuffer * buffer, GstSegment * segment,
- gboolean is_sink)
+ guint64 size, gboolean is_sink)
{
GstClockTime duration, timestamp;
- timestamp = GST_BUFFER_TIMESTAMP (buffer);
+ timestamp = GST_BUFFER_DTS_OR_PTS (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
* time */
if (timestamp == GST_CLOCK_TIME_NONE)
update_time_level (queue);
}
+struct BufListData
+{
+ GstClockTime timestamp;
+ guint bitrate;
+};
+
static gboolean
buffer_list_apply_time (GstBuffer ** buf, guint idx, gpointer data)
{
- GstClockTime *timestamp = data;
+ struct BufListData *bld = data;
+ GstClockTime *timestamp = &bld->timestamp;
+ GstClockTime btime;
- GST_TRACE ("buffer %u has ts %" GST_TIME_FORMAT
+ GST_TRACE ("buffer %u has pts %" GST_TIME_FORMAT " dts %" GST_TIME_FORMAT
" duration %" GST_TIME_FORMAT, idx,
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (*buf)),
+ GST_TIME_ARGS (GST_BUFFER_PTS (*buf)),
+ GST_TIME_ARGS (GST_BUFFER_DTS (*buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (*buf)));
- if (GST_BUFFER_TIMESTAMP_IS_VALID (*buf))
- *timestamp = GST_BUFFER_TIMESTAMP (*buf);
+ btime = GST_BUFFER_DTS_OR_PTS (*buf);
+ if (GST_CLOCK_TIME_IS_VALID (btime))
+ *timestamp = btime;
if (GST_BUFFER_DURATION_IS_VALID (*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));
return TRUE;
apply_buffer_list (GstQueue2 * queue, GstBufferList * buffer_list,
GstSegment * segment, gboolean is_sink)
{
- GstClockTime timestamp;
+ struct BufListData bld;
/* 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_TIME_ARGS (timestamp));
+ GST_TIME_ARGS (bld.timestamp));
- segment->position = timestamp;
+ segment->position = bld.timestamp;
if (is_sink)
queue->sink_tainted = TRUE;
update_time_level (queue);
}
-static void
-update_buffering (GstQueue2 * queue)
+static inline gint
+normalize_to_buffering_level (guint64 cur_level, guint64 max_level,
+ guint64 alt_max)
{
- gint64 percent;
- gboolean post = FALSE;
+ guint64 p;
- if (queue->high_percent <= 0)
- return;
+ if (max_level == 0)
+ return 0;
+
+ if (alt_max > 0)
+ p = gst_util_uint64_scale (cur_level, MAX_BUFFERING_LEVEL,
+ MIN (max_level, alt_max));
+ else
+ p = gst_util_uint64_scale (cur_level, MAX_BUFFERING_LEVEL, max_level);
+
+ return MIN (p, MAX_BUFFERING_LEVEL);
+}
-#define GET_PERCENT(format,alt_max) ((queue->max_level.format) > 0 ? (queue->cur_level.format) * 100 / ((alt_max) > 0 ? MIN ((alt_max), (queue->max_level.format)) : (queue->max_level.format)) : 0)
+static gboolean
+get_buffering_level (GstQueue2 * queue, gboolean * is_buffering,
+ gint * buffering_level)
+{
+ gint buflevel, buflevel2;
+
+ if (queue->high_watermark <= 0) {
+ if (buffering_level)
+ *buffering_level = MAX_BUFFERING_LEVEL;
+ if (is_buffering)
+ *is_buffering = FALSE;
+ return FALSE;
+ }
+#define GET_BUFFER_LEVEL_FOR_QUANTITY(format,alt_max) \
+ normalize_to_buffering_level (queue->cur_level.format,queue->max_level.format,(alt_max))
if (queue->is_eos) {
/* on EOS we are always 100% full, we set the var here so that it we can
* reuse the logic below to stop buffering */
- percent = 100;
+ buflevel = MAX_BUFFERING_LEVEL;
GST_LOG_OBJECT (queue, "we are EOS");
} else {
- /* figure out the percent we are filled, we take the max of all formats. */
+ GST_LOG_OBJECT (queue,
+ "Cur level bytes/time/buffers %u/%" GST_TIME_FORMAT "/%u",
+ queue->cur_level.bytes, GST_TIME_ARGS (queue->cur_level.time),
+ queue->cur_level.buffers);
+ /* figure out the buffering level we are filled, we take the max of all formats. */
if (!QUEUE_IS_USING_RING_BUFFER (queue)) {
- percent = GET_PERCENT (bytes, 0);
+ buflevel = GET_BUFFER_LEVEL_FOR_QUANTITY (bytes, 0);
} else {
guint64 rb_size = queue->ring_buffer_max_size;
- percent = GET_PERCENT (bytes, rb_size);
+ buflevel = GET_BUFFER_LEVEL_FOR_QUANTITY (bytes, rb_size);
}
- percent = MAX (percent, GET_PERCENT (time, 0));
- percent = MAX (percent, GET_PERCENT (buffers, 0));
+
+ buflevel2 = GET_BUFFER_LEVEL_FOR_QUANTITY (time, 0);
+ buflevel = MAX (buflevel, buflevel2);
+
+ buflevel2 = GET_BUFFER_LEVEL_FOR_QUANTITY (buffers, 0);
+ buflevel = MAX (buflevel, buflevel2);
/* also apply the rate estimate when we need to */
- if (queue->use_rate_estimate)
- percent = MAX (percent, GET_PERCENT (rate_time, 0));
+ if (queue->use_rate_estimate) {
+ buflevel2 = GET_BUFFER_LEVEL_FOR_QUANTITY (rate_time, 0);
+ buflevel = MAX (buflevel, buflevel2);
+ }
+
+ /* Don't get to 0% unless we're really empty */
+ if (queue->cur_level.bytes > 0)
+ buflevel = MAX (1, buflevel);
}
+#undef GET_BUFFER_LEVEL_FOR_QUANTITY
- if (queue->is_buffering) {
- post = TRUE;
- /* if we were buffering see if we reached the high watermark */
- if (percent >= queue->high_percent)
- queue->is_buffering = FALSE;
- } else {
- /* we were not buffering, check if we need to start buffering if we drop
- * below the low threshold */
- if (percent < queue->low_percent) {
- queue->is_buffering = TRUE;
- queue->buffering_iteration++;
- post = TRUE;
+ if (is_buffering)
+ *is_buffering = queue->is_buffering;
+
+ if (buffering_level)
+ *buffering_level = buflevel;
+
+ GST_DEBUG_OBJECT (queue, "buffering %d, level %d", queue->is_buffering,
+ buflevel);
+
+ return TRUE;
+}
+
+static gint
+convert_to_buffering_percent (GstQueue2 * queue, gint buffering_level)
+{
+ int percent;
+
+ /* scale so that if buffering_level equals the high watermark,
+ * the percentage is 100% */
+ percent = buffering_level * 100 / queue->high_watermark;
+ /* clip */
+ if (percent > 100)
+ percent = 100;
+
+ return percent;
+}
+
+static void
+get_buffering_stats (GstQueue2 * queue, gint percent, GstBufferingMode * mode,
+ gint * avg_in, gint * avg_out, gint64 * buffering_left)
+{
+ if (mode) {
+ if (!QUEUE_IS_USING_QUEUE (queue)) {
+ if (QUEUE_IS_USING_RING_BUFFER (queue))
+ *mode = GST_BUFFERING_TIMESHIFT;
+ else
+ *mode = GST_BUFFERING_DOWNLOAD;
+ } else {
+ *mode = GST_BUFFERING_STREAM;
}
}
- if (post) {
- GstMessage *message;
- GstBufferingMode mode;
- gint64 buffering_left = -1;
- /* scale to high percent so that it becomes the 100% mark */
- percent = percent * 100 / queue->high_percent;
- /* clip */
- if (percent > 100)
- percent = 100;
+ if (avg_in)
+ *avg_in = queue->byte_in_rate;
+ if (avg_out)
+ *avg_out = queue->byte_out_rate;
- if (percent != queue->buffering_percent) {
- queue->buffering_percent = percent;
+ if (buffering_left) {
+ *buffering_left = (percent == 100 ? 0 : -1);
- if (!QUEUE_IS_USING_QUEUE (queue)) {
- gint64 duration;
-
- if (QUEUE_IS_USING_RING_BUFFER (queue))
- mode = GST_BUFFERING_TIMESHIFT;
- else
- mode = GST_BUFFERING_DOWNLOAD;
-
- if (queue->byte_in_rate > 0) {
- if (gst_pad_peer_query_duration (queue->sinkpad, GST_FORMAT_BYTES,
- &duration)) {
- buffering_left =
- (gdouble) ((duration -
- queue->current->writing_pos) * 1000) / queue->byte_in_rate;
- }
- } else {
- buffering_left = G_MAXINT64;
- }
- } else {
- mode = GST_BUFFERING_STREAM;
- }
+ if (queue->use_rate_estimate) {
+ guint64 max, cur;
- GST_DEBUG_OBJECT (queue, "buffering %d percent", (gint) percent);
- message = gst_message_new_buffering (GST_OBJECT_CAST (queue),
- (gint) percent);
- gst_message_set_buffering_stats (message, mode,
- queue->byte_in_rate, queue->byte_out_rate, buffering_left);
+ max = queue->max_level.rate_time;
+ cur = queue->cur_level.rate_time;
- gst_element_post_message (GST_ELEMENT_CAST (queue), message);
+ if (percent != 100 && max > cur)
+ *buffering_left = (max - cur) / 1000000;
}
- } else {
- GST_DEBUG_OBJECT (queue, "filled %d percent", (gint) percent);
+ }
+}
+
+/* Called with the lock taken */
+static GstMessage *
+gst_queue2_get_buffering_message (GstQueue2 * queue)
+{
+ GstMessage *msg = NULL;
+
+ if (queue->percent_changed) {
+ gint percent = queue->buffering_percent;
+
+ queue->percent_changed = FALSE;
+
+ GST_DEBUG_OBJECT (queue, "Going to post buffering: %d%%", percent);
+ msg = gst_message_new_buffering (GST_OBJECT_CAST (queue), percent);
+
+ gst_message_set_buffering_stats (msg, queue->mode, queue->avg_in,
+ queue->avg_out, queue->buffering_left);
}
-#undef GET_PERCENT
+ return msg;
+}
+
+static void
+gst_queue2_post_buffering (GstQueue2 * queue)
+{
+ GstMessage *msg = NULL;
+
+ g_mutex_lock (&queue->buffering_post_lock);
+ GST_QUEUE2_MUTEX_LOCK (queue);
+ msg = gst_queue2_get_buffering_message (queue);
+ GST_QUEUE2_MUTEX_UNLOCK (queue);
+
+ if (msg != NULL)
+ gst_element_post_message (GST_ELEMENT_CAST (queue), msg);
+
+ g_mutex_unlock (&queue->buffering_post_lock);
+}
+
+static void
+update_buffering (GstQueue2 * queue)
+{
+ gint buffering_level, percent;
+
+ /* Ensure the variables used to calculate buffering state are up-to-date. */
+ if (queue->current)
+ update_cur_level (queue, queue->current);
+ update_in_rates (queue, FALSE);
+
+ if (!get_buffering_level (queue, NULL, &buffering_level))
+ return;
+
+ percent = convert_to_buffering_percent (queue, buffering_level);
+
+ if (queue->is_buffering) {
+ /* if we were buffering see if we reached the high watermark */
+ if (percent >= 100)
+ queue->is_buffering = FALSE;
+
+ SET_PERCENT (queue, percent);
+ } else {
+ /* we were not buffering, check if we need to start buffering if we drop
+ * below the low threshold */
+ if (buffering_level < queue->low_watermark) {
+ queue->is_buffering = TRUE;
+ SET_PERCENT (queue, percent);
+ }
+ }
}
static void
queue->byte_in_rate = 0.0;
queue->byte_in_period = 0;
queue->byte_out_rate = 0.0;
+ queue->last_update_in_rates_elapsed = 0.0;
queue->last_in_elapsed = 0.0;
queue->last_out_elapsed = 0.0;
queue->in_timer_started = FALSE;
#define AVG_OUT(avg,val) ((avg) * 3.0 + (val)) / 4.0
static void
-update_in_rates (GstQueue2 * queue)
+update_in_rates (GstQueue2 * queue, gboolean force)
{
gdouble elapsed, period;
gdouble byte_in_rate;
return;
}
- elapsed = g_timer_elapsed (queue->in_timer, NULL);
+ queue->last_update_in_rates_elapsed = elapsed =
+ g_timer_elapsed (queue->in_timer, NULL);
/* recalc after each interval. */
- if (queue->last_in_elapsed + RATE_INTERVAL < elapsed) {
+ if (force || queue->last_in_elapsed + RATE_INTERVAL < elapsed) {
period = elapsed - queue->last_in_elapsed;
GST_DEBUG_OBJECT (queue,
queue->seeking = TRUE;
GST_QUEUE2_MUTEX_UNLOCK (queue);
+ debug_ranges (queue);
+
GST_DEBUG_OBJECT (queue, "Seeking to %" G_GUINT64_FORMAT, offset);
event =
res = gst_pad_push_event (queue->sinkpad, event);
GST_QUEUE2_MUTEX_LOCK (queue);
- if (res)
- queue->current = add_range (queue, offset);
+ if (res) {
+ /* Between us sending the seek event and re-acquiring the lock, the source
+ * thread might already have pushed data and moved along the range's
+ * writing_pos beyond the seek offset. In that case we don't want to set
+ * the writing position back to the requested seek position, as it would
+ * cause data to be written to the wrong offset in the file or ring buffer.
+ * We still do the add_range call to switch the current range to the
+ * requested range, or create one if one doesn't exist yet. */
+ queue->current = add_range (queue, offset, FALSE);
+ }
return res;
}
+/* get the threshold for when we decide to seek rather than wait */
+static guint64
+get_seek_threshold (GstQueue2 * queue)
+{
+ guint64 threshold;
+
+ /* FIXME, find a good threshold based on the incoming rate. */
+ threshold = 1024 * 512;
+
+ if (QUEUE_IS_USING_RING_BUFFER (queue)) {
+ threshold = MIN (threshold,
+ QUEUE_MAX_BYTES (queue) - queue->cur_level.bytes);
+ }
+ return threshold;
+}
+
/* see if there is enough data in the file to read a full buffer */
static gboolean
gst_queue2_have_data (GstQueue2 * queue, guint64 offset, guint length)
" len %u", offset, length);
/* we don't have the range, see how far away we are */
if (!queue->is_eos && queue->current) {
- /* FIXME, find a good threshold based on the incoming rate. */
- guint64 threshold = 1024 * 512;
+ guint64 threshold = get_seek_threshold (queue);
- if (QUEUE_IS_USING_RING_BUFFER (queue)) {
- guint64 distance;
-
- distance = QUEUE_MAX_BYTES (queue) - queue->cur_level.bytes;
- /* don't wait for the complete buffer to fill */
- distance = MIN (distance, threshold);
-
- if (offset >= queue->current->offset && offset <=
- queue->current->writing_pos + distance) {
- GST_INFO_OBJECT (queue,
- "requested data is within range, wait for data");
- return FALSE;
- }
- } else if (offset < queue->current->writing_pos + threshold) {
- update_cur_pos (queue, queue->current, offset + length);
- GST_INFO_OBJECT (queue, "wait for data");
+ if (offset >= queue->current->offset && offset <=
+ queue->current->writing_pos + threshold) {
+ GST_INFO_OBJECT (queue,
+ "requested data is within range, wait for data");
return FALSE;
}
}
else
buf = *buffer;
- gst_buffer_map (buf, &info, GST_MAP_WRITE);
+ if (!gst_buffer_map (buf, &info, GST_MAP_WRITE))
+ goto buffer_write_fail;
data = info.data;
GST_DEBUG_OBJECT (queue, "Reading %u bytes from %" G_GUINT64_FORMAT, length,
}
if (read_length == 0) {
- if (QUEUE_IS_USING_RING_BUFFER (queue)
- && queue->current->max_reading_pos > rpos) {
- /* protect cached data (data between offset and max_reading_pos)
- * and update current level */
+ if (QUEUE_IS_USING_RING_BUFFER (queue)) {
GST_DEBUG_OBJECT (queue,
- "protecting cached data [%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT
- "]", rpos, queue->current->max_reading_pos);
- queue->current->max_reading_pos = rpos;
- update_cur_level (queue, queue->current);
+ "update current position [%" G_GUINT64_FORMAT "-%"
+ G_GUINT64_FORMAT "]", rpos, queue->current->max_reading_pos);
+ update_cur_pos (queue, queue->current, rpos);
+ GST_QUEUE2_SIGNAL_DEL (queue);
}
+
+ if (queue->use_buffering)
+ update_buffering (queue);
+
GST_DEBUG_OBJECT (queue, "waiting for add");
GST_QUEUE2_WAIT_ADD_CHECK (queue, queue->srcresult, out_flushing);
continue;
gst_buffer_unref (buf);
return ret;
}
+buffer_write_fail:
+ {
+ GST_ELEMENT_ERROR (queue, RESOURCE, WRITE, (NULL),
+ ("Can't write to buffer"));
+ if (*buffer == NULL)
+ gst_buffer_unref (buf);
+ return GST_FLOW_ERROR;
+ }
}
/* should be called with QUEUE_LOCK */
GST_DEBUG_OBJECT (queue, "opening temp file %s", queue->temp_template);
- /* we have two cases:
- * - temp_location was set to something !NULL (Deprecated). in this case we
- * open the specified filename.
- * - temp_template was set, allocate a filename and open that filename
- */
- if (!queue->temp_location_set) {
- /* nothing to do */
- if (queue->temp_template == NULL)
- goto no_directory;
-
- /* make copy of the template, we don't want to change this */
- name = g_strdup (queue->temp_template);
- fd = g_mkstemp (name);
- if (fd == -1)
- goto mkstemp_failed;
-
- /* open the file for update/writing */
- queue->temp_file = fdopen (fd, "wb+");
- /* error creating file */
- if (queue->temp_file == NULL)
- goto open_failed;
-
- g_free (queue->temp_location);
- queue->temp_location = name;
+ /* If temp_template was set, allocate a filename and open that file */
- GST_QUEUE2_MUTEX_UNLOCK (queue);
+ /* nothing to do */
+ if (queue->temp_template == NULL)
+ goto no_directory;
- /* we can't emit the notify with the lock */
- g_object_notify (G_OBJECT (queue), "temp-location");
+ /* make copy of the template, we don't want to change this */
+ name = g_strdup (queue->temp_template);
+
+#ifdef __BIONIC__
+ fd = g_mkstemp_full (name, O_RDWR | O_LARGEFILE, S_IRUSR | S_IWUSR);
+#else
+ fd = g_mkstemp (name);
+#endif
+
+ if (fd == -1)
+ goto mkstemp_failed;
+
+ /* open the file for update/writing */
+ queue->temp_file = fdopen (fd, "wb+");
+ /* error creating file */
+ if (queue->temp_file == NULL)
+ goto open_failed;
+
+ g_free (queue->temp_location);
+ queue->temp_location = name;
+
+ GST_QUEUE2_MUTEX_UNLOCK (queue);
+
+ /* we can't emit the notify with the lock */
+ g_object_notify (G_OBJECT (queue), "temp-location");
+
+ GST_QUEUE2_MUTEX_LOCK (queue);
- GST_QUEUE2_MUTEX_LOCK (queue);
- } else {
- /* open the file for update/writing, this is deprecated but we still need to
- * support it for API/ABI compatibility */
- queue->temp_file = g_fopen (queue->temp_location, "wb+");
- /* error creating file */
- if (queue->temp_file == NULL)
- goto open_failed;
- }
GST_DEBUG_OBJECT (queue, "opened temp file %s", queue->temp_template);
return TRUE;
fflush (queue->temp_file);
fclose (queue->temp_file);
- if (queue->temp_remove)
- remove (queue->temp_location);
+ if (queue->temp_remove) {
+ if (remove (queue->temp_location) < 0) {
+ GST_WARNING_OBJECT (queue, "Failed to remove temporary file %s: %s",
+ queue->temp_location, g_strerror (errno));
+ }
+ }
queue->temp_file = NULL;
clean_ranges (queue);
}
static void
-gst_queue2_locked_flush (GstQueue2 * queue)
+gst_queue2_locked_flush (GstQueue2 * queue, gboolean full, gboolean clear_temp)
{
if (!QUEUE_IS_USING_QUEUE (queue)) {
- if (QUEUE_IS_USING_TEMP_FILE (queue))
+ if (QUEUE_IS_USING_TEMP_FILE (queue) && clear_temp)
gst_queue2_flush_temp_file (queue);
init_ranges (queue);
} else {
while (!g_queue_is_empty (&queue->queue)) {
- GstMiniObject *data = g_queue_pop_head (&queue->queue);
+ GstQueue2Item *qitem = g_queue_pop_head (&queue->queue);
+
+ if (!full && qitem->type == GST_QUEUE2_ITEM_TYPE_EVENT
+ && GST_EVENT_IS_STICKY (qitem->item)
+ && GST_EVENT_TYPE (qitem->item) != GST_EVENT_SEGMENT
+ && GST_EVENT_TYPE (qitem->item) != GST_EVENT_EOS) {
+ gst_pad_store_sticky_event (queue->srcpad,
+ GST_EVENT_CAST (qitem->item));
+ }
/* Then lose another reference because we are supposed to destroy that
data when flushing */
- gst_mini_object_unref (data);
+ if (qitem->type != GST_QUEUE2_ITEM_TYPE_QUERY)
+ gst_mini_object_unref (qitem->item);
+ g_slice_free (GstQueue2Item, qitem);
}
}
+ queue->last_query = FALSE;
+ g_cond_signal (&queue->query_handled);
GST_QUEUE2_CLEAR_LEVEL (queue->cur_level);
gst_segment_init (&queue->sink_segment, GST_FORMAT_TIME);
gst_segment_init (&queue->src_segment, GST_FORMAT_TIME);
guint size, rb_size;
guint64 writing_pos, new_writing_pos;
GstQueue2Range *range, *prev, *next;
+ gboolean do_seek = FALSE;
if (QUEUE_IS_USING_RING_BUFFER (queue))
writing_pos = queue->current->rb_writing_pos;
ring_buffer = queue->ring_buffer;
rb_size = queue->ring_buffer_max_size;
- gst_buffer_map (buffer, &info, GST_MAP_READ);
+ if (!gst_buffer_map (buffer, &info, GST_MAP_READ))
+ goto buffer_read_error;
size = info.size;
data = info.data;
GST_DEBUG_OBJECT (queue, "Writing %u bytes to %" G_GUINT64_FORMAT, size,
- GST_BUFFER_OFFSET (buffer));
+ writing_pos);
+
+ /* sanity check */
+ if (GST_BUFFER_OFFSET_IS_VALID (buffer) &&
+ GST_BUFFER_OFFSET (buffer) != queue->current->writing_pos) {
+ GST_WARNING_OBJECT (queue, "buffer offset does not match current writing "
+ "position! %" G_GINT64_FORMAT " != %" G_GINT64_FORMAT,
+ GST_BUFFER_OFFSET (buffer), queue->current->writing_pos);
+ }
while (size > 0) {
guint to_write;
guint64 range_data_start, range_data_end;
GstQueue2Range *range_to_destroy = NULL;
+ if (range == queue->current)
+ goto next_range;
+
range_data_start = range->rb_offset;
range_data_end = range->rb_writing_pos;
GST_DEBUG_OBJECT (queue, "merging ranges %" G_GUINT64_FORMAT,
next->writing_pos);
- /* remove the group, we could choose to not read the data in this range
- * again. This would involve us doing a seek to the current writing position
- * in the range. FIXME, It would probably make sense to do a seek when there
- * is a lot of data in the range we merged with to avoid reading it all
- * again. */
+ /* remove the group */
queue->current->next = next->next;
- g_slice_free (GstQueue2Range, next);
- debug_ranges (queue);
+ /* We use the threshold to decide if we want to do a seek or simply
+ * read the data again. If there is not so much data in the range we
+ * prefer to avoid to seek and read it again. */
+ if (next->writing_pos > new_writing_pos + get_seek_threshold (queue)) {
+ /* the new range had more data than the threshold, it's worth keeping
+ * it and doing a seek. */
+ new_writing_pos = next->writing_pos;
+ do_seek = TRUE;
+ }
+ g_slice_free (GstQueue2Range, next);
}
goto update_and_signal;
}
} else {
queue->current->writing_pos = writing_pos = new_writing_pos;
}
+ if (do_seek)
+ perform_seek_to_offset (queue, new_writing_pos);
+
update_cur_level (queue, queue->current);
/* update the buffering status */
- if (queue->use_buffering)
+ if (queue->use_buffering) {
+ GstMessage *msg;
update_buffering (queue);
+ msg = gst_queue2_get_buffering_message (queue);
+ if (msg) {
+ GST_QUEUE2_MUTEX_UNLOCK (queue);
+ g_mutex_lock (&queue->buffering_post_lock);
+ gst_element_post_message (GST_ELEMENT_CAST (queue), msg);
+ g_mutex_unlock (&queue->buffering_post_lock);
+ GST_QUEUE2_MUTEX_LOCK (queue);
+ }
+ }
GST_INFO_OBJECT (queue, "cur_level.bytes %u (max %" G_GUINT64_FORMAT ")",
queue->cur_level.bytes, QUEUE_MAX_BYTES (queue));
gst_buffer_unmap (buffer, &info);
return FALSE;
}
+buffer_read_error:
+ {
+ GST_ELEMENT_ERROR (queue, RESOURCE, READ, (NULL),
+ ("Can't read from buffer"));
+ return FALSE;
+ }
}
static gboolean
queue->bytes_in += size;
/* 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_in_rates (queue);
+ update_in_rates (queue, FALSE);
if (!QUEUE_IS_USING_QUEUE (queue)) {
/* FIXME - check return value? */
/* add buffer to the statistics */
if (QUEUE_IS_USING_QUEUE (queue)) {
- queue->cur_level.buffers++;
+ queue->cur_level.buffers += gst_buffer_list_length (buffer_list);
queue->cur_level.bytes += size;
}
queue->bytes_in += size;
apply_buffer_list (queue, buffer_list, &queue->sink_segment, TRUE);
/* update the byterate stats */
- update_in_rates (queue);
+ update_in_rates (queue, FALSE);
if (!QUEUE_IS_USING_QUEUE (queue)) {
gst_buffer_list_foreach (buffer_list, buffer_list_create_write, queue);
* filled and we can read all data from the queue. */
GST_DEBUG_OBJECT (queue, "we have EOS");
queue->is_eos = TRUE;
+ /* Force updating the input bitrate */
+ update_in_rates (queue, TRUE);
break;
case GST_EVENT_SEGMENT:
apply_segment (queue, event, &queue->sink_segment, TRUE);
* from downstream */
queue->unexpected = FALSE;
break;
+ case GST_EVENT_GAP:
+ apply_gap (queue, event, &queue->sink_segment, TRUE);
+ break;
case GST_EVENT_STREAM_START:
if (!QUEUE_IS_USING_QUEUE (queue)) {
gst_event_replace (&queue->stream_start_event, event);
item = NULL;
}
break;
+ case GST_EVENT_CAPS:{
+ GstCaps *caps;
+
+ gst_event_parse_caps (event, &caps);
+ GST_INFO ("got caps: %" GST_PTR_FORMAT, caps);
+
+ if (!QUEUE_IS_USING_QUEUE (queue)) {
+ GST_LOG ("Dropping caps event, not using queue");
+ gst_event_unref (event);
+ item = NULL;
+ }
+ break;
+ }
default:
if (!QUEUE_IS_USING_QUEUE (queue))
goto unexpected_event;
break;
}
+ } else if (GST_IS_QUERY (item)) {
+ /* Can't happen as we check that in the caller */
+ if (!QUEUE_IS_USING_QUEUE (queue))
+ g_assert_not_reached ();
} else {
g_warning ("Unexpected item %p added in queue %s (refcounting problem?)",
item, GST_OBJECT_NAME (queue));
update_buffering (queue);
if (QUEUE_IS_USING_QUEUE (queue)) {
- g_queue_push_tail (&queue->queue, item);
+ GstQueue2Item *qitem = g_slice_new (GstQueue2Item);
+ qitem->type = item_type;
+ qitem->item = item;
+ g_queue_push_tail (&queue->queue, qitem);
} else {
gst_mini_object_unref (GST_MINI_OBJECT_CAST (item));
}
/* ERRORS */
unexpected_event:
{
- g_warning
- ("Unexpected event of kind %s can't be added in temp file of queue %s ",
- gst_event_type_get_name (GST_EVENT_TYPE (item)),
- GST_OBJECT_NAME (queue));
+ gboolean is_custom = GST_EVENT_TYPE (item) < GST_EVENT_CUSTOM_UPSTREAM;
+
+ GST_WARNING_OBJECT (queue, "%s%s event can't be added to temp file: "
+ "%" GST_PTR_FORMAT, is_custom ? "Unexpected " : "",
+ GST_EVENT_TYPE_NAME (item), GST_EVENT_CAST (item));
gst_event_unref (GST_EVENT_CAST (item));
return;
}
{
GstMiniObject *item;
- if (!QUEUE_IS_USING_QUEUE (queue))
+ if (!QUEUE_IS_USING_QUEUE (queue)) {
item = gst_queue2_read_item_from_file (queue);
- else
- item = g_queue_pop_head (&queue->queue);
+ } else {
+ GstQueue2Item *qitem = g_queue_pop_head (&queue->queue);
+
+ if (qitem == NULL)
+ goto no_item;
+
+ item = qitem->item;
+ g_slice_free (GstQueue2Item, qitem);
+ }
if (item == NULL)
goto no_item;
}
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_out_rates (queue);
/* update the buffering */
case GST_EVENT_SEGMENT:
apply_segment (queue, event, &queue->src_segment, FALSE);
break;
+ case GST_EVENT_GAP:
+ apply_gap (queue, event, &queue->src_segment, FALSE);
+ break;
default:
break;
}
"retrieved buffer list %p from queue", buffer_list);
if (QUEUE_IS_USING_QUEUE (queue)) {
- queue->cur_level.buffers--;
+ queue->cur_level.buffers -= gst_buffer_list_length (buffer_list);
queue->cur_level.bytes -= size;
}
queue->bytes_out += size;
/* update the buffering */
if (queue->use_buffering)
update_buffering (queue);
-
+ } else if (GST_IS_QUERY (item)) {
+ GST_CAT_LOG_OBJECT (queue_dataflow, queue,
+ "retrieved query %p from queue", item);
+ *item_type = GST_QUEUE2_ITEM_TYPE_QUERY;
} else {
g_warning
("Unexpected item %p dequeued from queue %s (refcounting problem?)",
}
}
-static gboolean
+static GstFlowReturn
gst_queue2_handle_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event)
{
+ gboolean ret = TRUE;
GstQueue2 *queue;
queue = GST_QUEUE2 (parent);
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "received flush start event");
if (GST_PAD_MODE (queue->srcpad) == GST_PAD_MODE_PUSH) {
/* forward event */
- gst_pad_push_event (queue->srcpad, event);
+ ret = gst_pad_push_event (queue->srcpad, event);
/* now unblock the chain function */
GST_QUEUE2_MUTEX_LOCK (queue);
* flush_start downstream. */
gst_pad_pause_task (queue->srcpad);
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "loop stopped");
+
+ GST_QUEUE2_MUTEX_LOCK (queue);
+ queue->last_query = FALSE;
+ g_cond_signal (&queue->query_handled);
+ GST_QUEUE2_MUTEX_UNLOCK (queue);
} else {
GST_QUEUE2_MUTEX_LOCK (queue);
/* flush the sink pad */
queue->sinkresult = GST_FLOW_FLUSHING;
GST_QUEUE2_SIGNAL_DEL (queue);
+ queue->last_query = FALSE;
+ g_cond_signal (&queue->query_handled);
GST_QUEUE2_MUTEX_UNLOCK (queue);
gst_event_unref (event);
}
- goto done;
+ break;
}
case GST_EVENT_FLUSH_STOP:
{
if (GST_PAD_MODE (queue->srcpad) == GST_PAD_MODE_PUSH) {
/* forward event */
- gst_pad_push_event (queue->srcpad, event);
+ ret = gst_pad_push_event (queue->srcpad, event);
GST_QUEUE2_MUTEX_LOCK (queue);
- gst_queue2_locked_flush (queue);
+ gst_queue2_locked_flush (queue, FALSE, TRUE);
queue->srcresult = GST_FLOW_OK;
queue->sinkresult = GST_FLOW_OK;
queue->is_eos = FALSE;
queue->unexpected = FALSE;
queue->seeking = FALSE;
+ queue->src_tags_bitrate = queue->sink_tags_bitrate = 0;
/* reset rate counters */
reset_rate_timer (queue);
gst_pad_start_task (queue->srcpad, (GstTaskFunction) gst_queue2_loop,
- queue->srcpad);
+ queue->srcpad, NULL);
GST_QUEUE2_MUTEX_UNLOCK (queue);
} else {
GST_QUEUE2_MUTEX_LOCK (queue);
queue->is_eos = FALSE;
queue->unexpected = FALSE;
queue->sinkresult = GST_FLOW_OK;
+ queue->seeking = FALSE;
+ queue->src_tags_bitrate = queue->sink_tags_bitrate = 0;
GST_QUEUE2_MUTEX_UNLOCK (queue);
gst_event_unref (event);
}
- goto done;
+ 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:
if (GST_EVENT_IS_SERIALIZED (event)) {
/* serialized events go in the queue */
GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->sinkresult, out_flushing);
+ if (queue->srcresult != GST_FLOW_OK) {
+ /* Errors in sticky event pushing are no problem and ignored here
+ * as they will cause more meaningful errors during data flow.
+ * For EOS events, that are not followed by data flow, we still
+ * return FALSE here though and report an error.
+ */
+ if (!GST_EVENT_IS_STICKY (event)) {
+ goto out_flow_error;
+ } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
+ if (queue->srcresult == GST_FLOW_NOT_LINKED
+ || queue->srcresult < GST_FLOW_EOS) {
+ GST_ELEMENT_FLOW_ERROR (queue, queue->srcresult);
+ }
+ goto out_flow_error;
+ }
+ }
/* refuse more events on EOS */
if (queue->is_eos)
goto out_eos;
gst_queue2_locked_enqueue (queue, event, GST_QUEUE2_ITEM_TYPE_EVENT);
GST_QUEUE2_MUTEX_UNLOCK (queue);
+ gst_queue2_post_buffering (queue);
} else {
/* non-serialized events are passed upstream. */
- gst_pad_push_event (queue->srcpad, event);
+ ret = gst_pad_push_event (queue->srcpad, event);
}
break;
}
-done:
- return TRUE;
+ if (ret == FALSE)
+ return GST_FLOW_ERROR;
+ return GST_FLOW_OK;
/* ERRORS */
out_flushing:
GST_DEBUG_OBJECT (queue, "refusing event, we are flushing");
GST_QUEUE2_MUTEX_UNLOCK (queue);
gst_event_unref (event);
- return FALSE;
+ return GST_FLOW_FLUSHING;
}
out_eos:
{
GST_DEBUG_OBJECT (queue, "refusing event, we are EOS");
GST_QUEUE2_MUTEX_UNLOCK (queue);
gst_event_unref (event);
- return FALSE;
+ return GST_FLOW_EOS;
+ }
+out_flow_error:
+ {
+ GST_LOG_OBJECT (queue,
+ "refusing event, we have a downstream flow error: %s",
+ gst_flow_get_name (queue->srcresult));
+ GST_QUEUE2_MUTEX_UNLOCK (queue);
+ gst_event_unref (event);
+ return queue->srcresult;
}
}
gst_queue2_handle_sink_query (GstPad * pad, GstObject * parent,
GstQuery * query)
{
+ GstQueue2 *queue;
gboolean res;
+ queue = GST_QUEUE2 (parent);
+
switch (GST_QUERY_TYPE (query)) {
default:
if (GST_QUERY_IS_SERIALIZED (query)) {
- GST_WARNING_OBJECT (pad, "unhandled serialized query");
- res = FALSE;
+ GST_CAT_LOG_OBJECT (queue_dataflow, queue, "received query %p", query);
+ /* serialized events go in the queue. We need to be certain that we
+ * don't cause deadlocks waiting for the query return value. We check if
+ * the queue is empty (nothing is blocking downstream and the query can
+ * be pushed for sure) or we are not buffering. If we are buffering,
+ * the pipeline waits to unblock downstream until our queue fills up
+ * completely, which can not happen if we block on the query..
+ * Therefore we only potentially block when we are not buffering. */
+ GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->sinkresult, out_flushing);
+ if (QUEUE_IS_USING_QUEUE (queue) && (gst_queue2_is_empty (queue)
+ || !queue->use_buffering)) {
+ if (!g_atomic_int_get (&queue->downstream_may_block)) {
+ gst_queue2_locked_enqueue (queue, query,
+ GST_QUEUE2_ITEM_TYPE_QUERY);
+
+ STATUS (queue, queue->sinkpad, "wait for QUERY");
+ while (queue->sinkresult == GST_FLOW_OK &&
+ queue->last_handled_query != query)
+ g_cond_wait (&queue->query_handled, &queue->qlock);
+ queue->last_handled_query = NULL;
+ if (queue->sinkresult != GST_FLOW_OK)
+ goto out_flushing;
+ res = queue->last_query;
+ } else {
+ GST_DEBUG_OBJECT (queue, "refusing query, downstream might block");
+ res = FALSE;
+ }
+ } else {
+ GST_DEBUG_OBJECT (queue,
+ "refusing query, we are not using the queue");
+ res = FALSE;
+ }
+ GST_QUEUE2_MUTEX_UNLOCK (queue);
+ gst_queue2_post_buffering (queue);
} else {
res = gst_pad_query_default (pad, parent, query);
}
break;
}
return res;
+
+ /* ERRORS */
+out_flushing:
+ {
+ GST_DEBUG_OBJECT (queue, "refusing query, we are flushing");
+ GST_QUEUE2_MUTEX_UNLOCK (queue);
+ return FALSE;
+ }
}
static gboolean
/* put buffer in queue now */
gst_queue2_locked_enqueue (queue, item, item_type);
GST_QUEUE2_MUTEX_UNLOCK (queue);
+ gst_queue2_post_buffering (queue);
return GST_FLOW_OK;
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"dropping EOS buffer list %p", data);
gst_buffer_list_unref (GST_BUFFER_LIST_CAST (data));
+ } else if (*item_type == GST_QUEUE2_ITEM_TYPE_QUERY) {
+ queue->last_query = FALSE;
+ g_cond_signal (&queue->query_handled);
+ GST_CAT_LOG_OBJECT (queue_dataflow, queue, "dropping EOS query %p", data);
}
}
/* no more items in the queue. Set the unexpected flag so that upstream
static GstFlowReturn
gst_queue2_push_one (GstQueue2 * queue)
{
- GstFlowReturn result = GST_FLOW_OK;
+ GstFlowReturn result = queue->srcresult;
GstMiniObject *data;
GstQueue2ItemType item_type;
goto no_item;
next:
+ STATUS (queue, queue->srcpad, "We have something dequeud");
+ g_atomic_int_set (&queue->downstream_may_block,
+ item_type == GST_QUEUE2_ITEM_TYPE_BUFFER ||
+ item_type == GST_QUEUE2_ITEM_TYPE_BUFFER_LIST);
GST_QUEUE2_MUTEX_UNLOCK (queue);
+ gst_queue2_post_buffering (queue);
if (item_type == GST_QUEUE2_ITEM_TYPE_BUFFER) {
GstBuffer *buffer;
-#if 0
- GstCaps *caps;
-#endif
buffer = GST_BUFFER_CAST (data);
-#if 0
- caps = GST_BUFFER_CAPS (buffer);
-#endif
-
-#if 0
- /* set caps before pushing the buffer so that core does not try to do
- * something fancy to check if this is possible. */
- if (caps && caps != GST_PAD_CAPS (queue->srcpad))
- gst_pad_set_caps (queue->srcpad, caps);
-#endif
result = gst_pad_push (queue->srcpad, buffer);
+ g_atomic_int_set (&queue->downstream_may_block, 0);
/* need to check for srcresult here as well */
GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->srcresult, out_flushing);
GstEvent *event = GST_EVENT_CAST (data);
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);
/* if we're EOS, return EOS so that the task pauses. */
GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->srcresult, out_flushing);
} else if (item_type == GST_QUEUE2_ITEM_TYPE_BUFFER_LIST) {
GstBufferList *buffer_list;
-#if 0
- GstBuffer *first_buf;
- GstCaps *caps;
-#endif
buffer_list = GST_BUFFER_LIST_CAST (data);
-#if 0
- first_buf = gst_buffer_list_get (buffer_list, 0);
- caps = (first_buf != NULL) ? GST_BUFFER_CAPS (first_buf) : NULL;
-
- /* set caps before pushing the buffer so that core does not try to do
- * something fancy to check if this is possible. */
- if (caps && caps != GST_PAD_CAPS (queue->srcpad))
- gst_pad_set_caps (queue->srcpad, caps);
-#endif
-
result = gst_pad_push_list (queue->srcpad, buffer_list);
+ g_atomic_int_set (&queue->downstream_may_block, 0);
/* need to check for srcresult here as well */
GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->srcresult, out_flushing);
* to the caller so that the task function does not shut down */
result = GST_FLOW_OK;
}
+ } else if (item_type == GST_QUEUE2_ITEM_TYPE_QUERY) {
+ GstQuery *query = GST_QUERY_CAST (data);
+
+ GST_LOG_OBJECT (queue->srcpad, "Peering query %p", query);
+ queue->last_handled_query = query;
+ queue->last_query = gst_pad_peer_query (queue->srcpad, query);
+ GST_LOG_OBJECT (queue->srcpad, "Peered query");
+ GST_CAT_LOG_OBJECT (queue_dataflow, queue,
+ "did query %p, return %d", query, queue->last_query);
+ g_cond_signal (&queue->query_handled);
+ GST_QUEUE2_MUTEX_LOCK_CHECK (queue, queue->srcresult, out_flushing);
+ result = GST_FLOW_OK;
}
return result;
goto out_flushing;
GST_QUEUE2_MUTEX_UNLOCK (queue);
+ gst_queue2_post_buffering (queue);
return;
GstFlowReturn ret = queue->srcresult;
gst_pad_pause_task (queue->srcpad);
+ if (ret == GST_FLOW_FLUSHING) {
+ gst_queue2_locked_flush (queue, FALSE, FALSE);
+ } else {
+ GST_QUEUE2_SIGNAL_DEL (queue);
+ queue->last_query = FALSE;
+ g_cond_signal (&queue->query_handled);
+ }
GST_QUEUE2_MUTEX_UNLOCK (queue);
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"pause task, reason: %s", gst_flow_get_name (queue->srcresult));
/* 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_ERROR (queue, STREAM, FAILED,
- (_("Internal data flow error.")),
- ("streaming task paused, reason %s (%d)",
- gst_flow_get_name (ret), ret));
+ GST_ELEMENT_FLOW_ERROR (queue, ret);
gst_pad_push_event (queue->srcpad, gst_event_new_eos ());
}
return;
gst_event_unref (event);
}
break;
+ case GST_EVENT_RECONFIGURE:
+ GST_QUEUE2_MUTEX_LOCK (queue);
+ /* assume downstream is linked now and try to push again */
+ if (queue->srcresult == GST_FLOW_NOT_LINKED) {
+ queue->srcresult = GST_FLOW_OK;
+ queue->sinkresult = GST_FLOW_OK;
+ if (GST_PAD_MODE (pad) == GST_PAD_MODE_PUSH) {
+ gst_pad_start_task (pad, (GstTaskFunction) gst_queue2_loop, pad,
+ NULL);
+ }
+ }
+ GST_QUEUE2_MUTEX_UNLOCK (queue);
+
+ res = gst_pad_push_event (queue->sinkpad, event);
+ break;
default:
res = gst_pad_push_event (queue->sinkpad, event);
break;
switch (format) {
case GST_FORMAT_BYTES:
peer_pos -= queue->cur_level.bytes;
+ if (peer_pos < 0) /* Clamp result to 0 */
+ peer_pos = 0;
break;
case GST_FORMAT_TIME:
peer_pos -= queue->cur_level.time;
+ if (peer_pos < 0) /* Clamp result to 0 */
+ peer_pos = 0;
break;
default:
GST_WARNING_OBJECT (queue, "dropping query in %s format, don't "
}
case GST_QUERY_BUFFERING:
{
- GstFormat format;
+ gint percent;
+ gboolean is_buffering;
+ GstBufferingMode mode;
+ gint avg_in, avg_out;
+ gint64 buffering_left;
GST_DEBUG_OBJECT (queue, "query buffering");
- /* FIXME - is this condition correct? what should ring buffer do? */
- if (QUEUE_IS_USING_QUEUE (queue)) {
- /* no temp file, just forward to the peer */
- if (!gst_pad_peer_query (queue->sinkpad, query))
- goto peer_failed;
- GST_DEBUG_OBJECT (queue, "buffering forwarded to peer");
- } else {
+ get_buffering_level (queue, &is_buffering, &percent);
+ percent = convert_to_buffering_percent (queue, percent);
+ gst_query_set_buffering_percent (query, is_buffering, percent);
+
+ get_buffering_stats (queue, percent, &mode, &avg_in, &avg_out,
+ &buffering_left);
+ gst_query_set_buffering_stats (query, mode, avg_in, avg_out,
+ buffering_left);
+
+ if (!QUEUE_IS_USING_QUEUE (queue)) {
+ /* add ranges for download and ringbuffer buffering */
+ GstFormat format;
gint64 start, stop, range_start, range_stop;
guint64 writing_pos;
- gint percent;
- gint64 estimated_total, buffering_left;
+ gint64 estimated_total;
gint64 duration;
- gboolean peer_res, is_buffering, is_eos;
- gdouble byte_in_rate, byte_out_rate;
+ gboolean peer_res, is_eos;
GstQueue2Range *queued_ranges;
/* we need a current download region */
return FALSE;
writing_pos = queue->current->writing_pos;
- byte_in_rate = queue->byte_in_rate;
- byte_out_rate = queue->byte_out_rate;
- is_buffering = queue->is_buffering;
is_eos = queue->is_eos;
- percent = queue->buffering_percent;
if (is_eos) {
/* we're EOS, we know the duration in bytes now */
GST_FORMAT_BYTES, &duration);
}
+ GST_DEBUG_OBJECT (queue, "percent %d, duration %" G_GINT64_FORMAT
+ ", writing %" G_GINT64_FORMAT, percent, duration, writing_pos);
+
/* calculate remaining and total download time */
- if (peer_res && byte_in_rate > 0.0) {
- estimated_total = (duration * 1000) / byte_in_rate;
- buffering_left = ((duration - writing_pos) * 1000) / byte_in_rate;
- } else {
+ if (peer_res && avg_in > 0.0)
+ estimated_total = ((duration - writing_pos) * 1000) / avg_in;
+ else
estimated_total = -1;
- buffering_left = -1;
- }
- GST_DEBUG_OBJECT (queue, "estimated %" G_GINT64_FORMAT ", left %"
- G_GINT64_FORMAT, estimated_total, buffering_left);
+
+ GST_DEBUG_OBJECT (queue, "estimated-total %" G_GINT64_FORMAT,
+ estimated_total);
gst_query_parse_buffering_range (query, &format, NULL, NULL, NULL);
if (!peer_res)
goto peer_failed;
- GST_DEBUG_OBJECT (queue,
- "duration %" G_GINT64_FORMAT ", writing %" G_GINT64_FORMAT,
- duration, writing_pos);
-
start = 0;
/* get our available data relative to the duration */
if (duration != -1)
- stop = GST_FORMAT_PERCENT_MAX * writing_pos / duration;
+ stop =
+ gst_util_uint64_scale (GST_FORMAT_PERCENT_MAX, writing_pos,
+ duration);
else
stop = -1;
break;
range_stop = 0;
break;
}
- range_start = 100 * queued_ranges->offset / duration;
- range_stop = 100 * queued_ranges->writing_pos / duration;
+ range_start =
+ gst_util_uint64_scale (GST_FORMAT_PERCENT_MAX,
+ queued_ranges->offset, duration);
+ range_stop =
+ gst_util_uint64_scale (GST_FORMAT_PERCENT_MAX,
+ queued_ranges->writing_pos, duration);
break;
case GST_FORMAT_BYTES:
range_start = queued_ranges->offset;
gst_query_add_buffering_range (query, range_start, range_stop);
}
- gst_query_set_buffering_percent (query, is_buffering, percent);
gst_query_set_buffering_range (query, format, start, stop,
estimated_total);
- gst_query_set_buffering_stats (query, GST_BUFFERING_DOWNLOAD,
- byte_in_rate, byte_out_rate, buffering_left);
}
break;
}
gboolean pull_mode;
GstSchedulingFlags flags = 0;
+ if (!gst_pad_peer_query (queue->sinkpad, query))
+ goto peer_failed;
+
+ gst_query_parse_scheduling (query, &flags, NULL, NULL, NULL);
+
/* we can operate in pull mode when we are using a tempfile */
pull_mode = !QUEUE_IS_USING_QUEUE (queue);
if (gst_pad_peer_query_duration (queue->sinkpad, GST_FORMAT_BYTES,
&upstream_size)) {
GST_INFO_OBJECT (queue, "upstream size: %" G_GINT64_FORMAT, upstream_size);
- queue->upstream_size = upstream_size;
+
+ /* upstream_size can be negative but queue->upstream_size is unsigned.
+ * Prevent setting negative values to it (the query can return -1) */
+ if (upstream_size >= 0)
+ queue->upstream_size = upstream_size;
+ else
+ queue->upstream_size = 0;
}
}
/* FIXME - function will block when the range is not yet available */
ret = gst_queue2_create_read (queue, offset, length, buffer);
GST_QUEUE2_MUTEX_UNLOCK (queue);
+ gst_queue2_post_buffering (queue);
return ret;
GST_DEBUG_OBJECT (queue, "deactivating push mode");
queue->srcresult = GST_FLOW_FLUSHING;
queue->sinkresult = GST_FLOW_FLUSHING;
- gst_queue2_locked_flush (queue);
+ GST_QUEUE2_SIGNAL_DEL (queue);
GST_QUEUE2_MUTEX_UNLOCK (queue);
+
+ /* wait until it is unblocked and clean up */
+ GST_PAD_STREAM_LOCK (pad);
+ GST_QUEUE2_MUTEX_LOCK (queue);
+ gst_queue2_locked_flush (queue, TRUE, FALSE);
+ GST_QUEUE2_MUTEX_UNLOCK (queue);
+ GST_PAD_STREAM_UNLOCK (pad);
}
result = TRUE;
break;
queue->sinkresult = GST_FLOW_OK;
queue->is_eos = FALSE;
queue->unexpected = FALSE;
- result = gst_pad_start_task (pad, (GstTaskFunction) gst_queue2_loop, pad);
+ result =
+ gst_pad_start_task (pad, (GstTaskFunction) gst_queue2_loop, pad, NULL);
GST_QUEUE2_MUTEX_UNLOCK (queue);
} else {
/* unblock loop function */
/* step 2, make sure streaming finishes */
result = gst_pad_stop_task (pad);
+
+ GST_QUEUE2_MUTEX_LOCK (queue);
+ gst_queue2_locked_flush (queue, FALSE, FALSE);
+ GST_QUEUE2_MUTEX_UNLOCK (queue);
}
return result;
/* changing the capacity of the queue must wake up
* the _chain function, it might have more room now
* to store the buffer/event in the queue */
-#define QUEUE_CAPACITY_CHANGE(q)\
- GST_QUEUE2_SIGNAL_DEL (queue);
+#define QUEUE_CAPACITY_CHANGE(q) \
+ GST_QUEUE2_SIGNAL_DEL (queue); \
+ if (queue->use_buffering) \
+ update_buffering (queue);
/* Changing the minimum required fill level must
* wake up the _loop function as it might now
break;
case PROP_USE_BUFFERING:
queue->use_buffering = g_value_get_boolean (value);
+ if (!queue->use_buffering && queue->is_buffering) {
+ GST_DEBUG_OBJECT (queue, "Disabled buffering while buffering, "
+ "posting 100%% message");
+ SET_PERCENT (queue, 100);
+ queue->is_buffering = FALSE;
+ }
+
+ if (queue->use_buffering) {
+ queue->is_buffering = TRUE;
+ update_buffering (queue);
+ }
+ break;
+ case PROP_USE_TAGS_BITRATE:
+ queue->use_tags_bitrate = g_value_get_boolean (value);
break;
case PROP_USE_RATE_ESTIMATE:
queue->use_rate_estimate = g_value_get_boolean (value);
break;
case PROP_LOW_PERCENT:
- queue->low_percent = g_value_get_int (value);
+ queue->low_watermark = g_value_get_int (value) * BUF_LEVEL_PERCENT_FACTOR;
+ if (queue->is_buffering)
+ update_buffering (queue);
break;
case PROP_HIGH_PERCENT:
- queue->high_percent = g_value_get_int (value);
+ queue->high_watermark =
+ g_value_get_int (value) * BUF_LEVEL_PERCENT_FACTOR;
+ if (queue->is_buffering)
+ update_buffering (queue);
+ break;
+ case PROP_LOW_WATERMARK:
+ queue->low_watermark = g_value_get_double (value) * MAX_BUFFERING_LEVEL;
+ if (queue->is_buffering)
+ update_buffering (queue);
+ break;
+ case PROP_HIGH_WATERMARK:
+ queue->high_watermark = g_value_get_double (value) * MAX_BUFFERING_LEVEL;
+ if (queue->is_buffering)
+ update_buffering (queue);
break;
case PROP_TEMP_TEMPLATE:
gst_queue2_set_temp_template (queue, g_value_get_string (value));
break;
- case PROP_TEMP_LOCATION:
- g_free (queue->temp_location);
- queue->temp_location = g_value_dup_string (value);
- /* you can set the property back to NULL to make it use the temp-tmpl
- * property. */
- queue->temp_location_set = queue->temp_location != NULL;
- break;
case PROP_TEMP_REMOVE:
queue->temp_remove = g_value_get_boolean (value);
break;
}
GST_QUEUE2_MUTEX_UNLOCK (queue);
+ gst_queue2_post_buffering (queue);
}
static void
case PROP_USE_BUFFERING:
g_value_set_boolean (value, queue->use_buffering);
break;
+ case PROP_USE_TAGS_BITRATE:
+ g_value_set_boolean (value, queue->use_tags_bitrate);
+ break;
case PROP_USE_RATE_ESTIMATE:
g_value_set_boolean (value, queue->use_rate_estimate);
break;
case PROP_LOW_PERCENT:
- g_value_set_int (value, queue->low_percent);
+ g_value_set_int (value, queue->low_watermark / BUF_LEVEL_PERCENT_FACTOR);
break;
case PROP_HIGH_PERCENT:
- g_value_set_int (value, queue->high_percent);
+ g_value_set_int (value, queue->high_watermark / BUF_LEVEL_PERCENT_FACTOR);
+ break;
+ case PROP_LOW_WATERMARK:
+ g_value_set_double (value, queue->low_watermark /
+ (gdouble) MAX_BUFFERING_LEVEL);
+ break;
+ case PROP_HIGH_WATERMARK:
+ g_value_set_double (value, queue->high_watermark /
+ (gdouble) MAX_BUFFERING_LEVEL);
break;
case PROP_TEMP_TEMPLATE:
g_value_set_string (value, queue->temp_template);
case PROP_RING_BUFFER_MAX_SIZE:
g_value_set_uint64 (value, queue->ring_buffer_max_size);
break;
+ case PROP_AVG_IN_RATE:
+ {
+ gdouble in_rate = queue->byte_in_rate;
+
+ /* During the first RATE_INTERVAL, byte_in_rate will not have been
+ * calculated, so calculate it here. */
+ if (in_rate == 0.0 && queue->bytes_in
+ && queue->last_update_in_rates_elapsed > 0.0)
+ in_rate = queue->bytes_in / queue->last_update_in_rates_elapsed;
+
+ g_value_set_int64 (value, (gint64) in_rate);
+ break;
+ }
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;