/**
* SECTION:gstbasesink
+ * @title: GstBaseSink
* @short_description: Base class for sink elements
* @see_also: #GstBaseTransform, #GstBaseSrc
*
* "Sink name",
* "Sink",
* "My Sink element",
- * "The author <my.sink@my.email>");
+ * "The author <my.sink@my.email>");
* }
* ]|
*
* should configure itself to process a specific media type.
*
* The #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() virtual methods
- * will be called when resources should be allocated. Any
+ * will be called when resources should be allocated. Any
* #GstBaseSinkClass.preroll(), #GstBaseSinkClass.render() and
* #GstBaseSinkClass.set_caps() function will be called between the
* #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() calls.
GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
#define GST_CAT_DEFAULT gst_base_sink_debug
-#define GST_BASE_SINK_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_SINK, GstBaseSinkPrivate))
-
#define GST_FLOW_STEP GST_FLOW_CUSTOM_ERROR
typedef struct
gboolean async_enabled;
GstClockTimeDiff ts_offset;
GstClockTime render_delay;
+ GstClockTime processing_deadline;
/* start, stop of current buffer, stream time, used to report position */
GstClockTime current_sstart;
GstClockTime last_left;
/* running averages go here these are done on running time */
- GstClockTime avg_pt;
- GstClockTime avg_duration;
- gdouble avg_rate;
- GstClockTime avg_in_diff;
-
- /* these are done on system time. avg_jitter and avg_render are
- * compared to eachother to see if the rendering time takes a
- * huge amount of the processing, If so we are flooded with
- * buffers. */
- GstClockTime last_left_systime;
- GstClockTime avg_jitter;
- GstClockTime start, stop;
- GstClockTime avg_render;
+ GstClockTime avg_pt, avg_in_diff;
+ gdouble avg_rate; /* average with infinite window */
/* number of rendered and dropped frames */
guint64 rendered;
/* latency stuff */
GstClockTime latency;
- /* if we already commited the state */
- gboolean commited;
+ /* if we already committed the state */
+ gboolean committed;
/* state change to playing ongoing */
gboolean to_playing;
GstClockTime rc_time;
GstClockTime rc_next;
gsize rc_accumulated;
+
+ gboolean drop_out_of_segment;
};
#define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
#define DEFAULT_ENABLE_LAST_SAMPLE TRUE
#define DEFAULT_THROTTLE_TIME 0
#define DEFAULT_MAX_BITRATE 0
+#define DEFAULT_DROP_OUT_OF_SEGMENT TRUE
+#define DEFAULT_PROCESSING_DEADLINE (20 * GST_MSECOND)
enum
{
PROP_RENDER_DELAY,
PROP_THROTTLE_TIME,
PROP_MAX_BITRATE,
+ PROP_PROCESSING_DEADLINE,
PROP_LAST
};
static GstElementClass *parent_class = NULL;
+static gint private_offset = 0;
static void gst_base_sink_class_init (GstBaseSinkClass * klass);
static void gst_base_sink_init (GstBaseSink * trans, gpointer g_class);
_type = g_type_register_static (GST_TYPE_ELEMENT,
"GstBaseSink", &base_sink_info, G_TYPE_FLAG_ABSTRACT);
+
+ private_offset =
+ g_type_add_instance_private (_type, sizeof (GstBaseSinkPrivate));
+
g_once_init_leave (&base_sink_type, _type);
}
return base_sink_type;
}
+static inline GstBaseSinkPrivate *
+gst_base_sink_get_instance_private (GstBaseSink * self)
+{
+ return (G_STRUCT_MEMBER_P (self, private_offset));
+}
+
static void gst_base_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_base_sink_get_property (GObject * object, guint prop_id,
gobject_class = G_OBJECT_CLASS (klass);
gstelement_class = GST_ELEMENT_CLASS (klass);
+ if (private_offset != 0)
+ g_type_class_adjust_private_offset (klass, &private_offset);
+
GST_DEBUG_CATEGORY_INIT (gst_base_sink_debug, "basesink", 0,
"basesink element");
- g_type_class_add_private (klass, sizeof (GstBaseSinkPrivate));
-
parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = gst_base_sink_finalize;
"The maximum bits per second to render (0 = disabled)", 0,
G_MAXUINT64, DEFAULT_MAX_BITRATE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstBaseSink:processing-deadline:
+ *
+ * Maximum amount of time (in nanoseconds) that the pipeline can take
+ * for processing the buffer. This is added to the latency of live
+ * pipelines.
+ *
+ * Since: 1.16
+ */
+ g_object_class_install_property (gobject_class, PROP_PROCESSING_DEADLINE,
+ g_param_spec_uint64 ("processing-deadline", "Processing deadline",
+ "Maximum processing deadline in nanoseconds", 0, G_MAXUINT64,
+ DEFAULT_PROCESSING_DEADLINE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
GstPadTemplate *pad_template;
GstBaseSinkPrivate *priv;
- basesink->priv = priv = GST_BASE_SINK_GET_PRIVATE (basesink);
+ basesink->priv = priv = gst_base_sink_get_instance_private (basesink);
pad_template =
gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
priv->async_enabled = DEFAULT_ASYNC;
priv->ts_offset = DEFAULT_TS_OFFSET;
priv->render_delay = DEFAULT_RENDER_DELAY;
+ priv->processing_deadline = DEFAULT_PROCESSING_DEADLINE;
priv->blocksize = DEFAULT_BLOCKSIZE;
priv->cached_clock_id = NULL;
g_atomic_int_set (&priv->enable_last_sample, DEFAULT_ENABLE_LAST_SAMPLE);
priv->throttle_time = DEFAULT_THROTTLE_TIME;
priv->max_bitrate = DEFAULT_MAX_BITRATE;
+ priv->drop_out_of_segment = DEFAULT_DROP_OUT_OF_SEGMENT;
+
GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_FLAG_SINK);
}
}
/**
+ * gst_base_sink_set_drop_out_of_segment:
+ * @sink: the sink
+ * @drop_out_of_segment: drop buffers outside the segment
+ *
+ * Configure @sink to drop buffers which are outside the current segment
+ *
+ * Since: 1.12
+ */
+void
+gst_base_sink_set_drop_out_of_segment (GstBaseSink * sink,
+ gboolean drop_out_of_segment)
+{
+ g_return_if_fail (GST_IS_BASE_SINK (sink));
+
+ GST_OBJECT_LOCK (sink);
+ sink->priv->drop_out_of_segment = drop_out_of_segment;
+ GST_OBJECT_UNLOCK (sink);
+
+}
+
+/**
+ * gst_base_sink_get_drop_out_of_segment:
+ * @sink: the sink
+ *
+ * Checks if @sink is currently configured to drop buffers which are outside
+ * the current segment
+ *
+ * Returns: %TRUE if the sink is configured to drop buffers outside the
+ * current segment.
+ *
+ * Since: 1.12
+ */
+gboolean
+gst_base_sink_get_drop_out_of_segment (GstBaseSink * sink)
+{
+ gboolean res;
+
+ g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
+
+ GST_OBJECT_LOCK (sink);
+ res = sink->priv->drop_out_of_segment;
+ GST_OBJECT_UNLOCK (sink);
+
+ return res;
+}
+
+/**
* gst_base_sink_set_max_lateness:
* @sink: the sink
* @max_lateness: the new max lateness value.
* gst_base_sink_get_max_lateness:
* @sink: the sink
*
- * Gets the max lateness value. See gst_base_sink_set_max_lateness for
+ * Gets the max lateness value. See gst_base_sink_set_max_lateness() for
* more details.
*
* Returns: The maximum time in nanoseconds that a buffer can be late
GstClockTime * max_latency)
{
gboolean l, us_live, res, have_latency;
- GstClockTime min, max, render_delay;
+ GstClockTime min, max, render_delay, processing_deadline;
GstQuery *query;
GstClockTime us_min, us_max;
l = sink->sync;
have_latency = sink->priv->have_latency;
render_delay = sink->priv->render_delay;
+ processing_deadline = sink->priv->processing_deadline;
GST_OBJECT_UNLOCK (sink);
/* assume no latency */
* values to create the complete latency. */
min = us_min;
max = us_max;
+
+ if (l) {
+ if (max == -1 || min + processing_deadline <= max)
+ min += processing_deadline;
+ else {
+ GST_ELEMENT_WARNING (sink, CORE, CLOCK,
+ (_("Pipeline construction is invalid, please add queues.")),
+ ("Not enough buffering available for "
+ " the processing deadline of %" GST_TIME_FORMAT
+ ", add enough queues to buffer %" GST_TIME_FORMAT
+ " additional data. Shortening processing latency to %"
+ GST_TIME_FORMAT ".",
+ GST_TIME_ARGS (processing_deadline),
+ GST_TIME_ARGS (min + processing_deadline - max),
+ GST_TIME_ARGS (max - min)));
+ min = max;
+ }
+ }
}
if (l) {
/* we need to add the render delay if we are live */
*
* Set the time that will be inserted between rendered buffers. This
* can be used to control the maximum buffers per second that the sink
- * will render.
+ * will render.
*/
void
gst_base_sink_set_throttle_time (GstBaseSink * sink, guint64 throttle)
* gst_base_sink_get_throttle_time:
* @sink: a #GstBaseSink
*
- * Get the time that will be inserted between frames to control the
+ * Get the time that will be inserted between frames to control the
* maximum buffers per second.
*
* Returns: the number of nanoseconds @sink will put between frames.
return res;
}
+/**
+ * gst_base_sink_set_processing_deadline:
+ * @sink: a #GstBaseSink
+ * @processing_deadline: the new processing deadline in nanoseconds.
+ *
+ * Maximum amount of time (in nanoseconds) that the pipeline can take
+ * for processing the buffer. This is added to the latency of live
+ * pipelines.
+ *
+ * This function is usually called by subclasses.
+ *
+ * Since: 1.16
+ */
+void
+gst_base_sink_set_processing_deadline (GstBaseSink * sink,
+ GstClockTime processing_deadline)
+{
+ GstClockTime old_processing_deadline;
+
+ g_return_if_fail (GST_IS_BASE_SINK (sink));
+
+ GST_OBJECT_LOCK (sink);
+ old_processing_deadline = sink->priv->processing_deadline;
+ sink->priv->processing_deadline = processing_deadline;
+ GST_LOG_OBJECT (sink, "set render processing_deadline to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (processing_deadline));
+ GST_OBJECT_UNLOCK (sink);
+
+ if (processing_deadline != old_processing_deadline) {
+ GST_DEBUG_OBJECT (sink, "posting latency changed");
+ gst_element_post_message (GST_ELEMENT_CAST (sink),
+ gst_message_new_latency (GST_OBJECT_CAST (sink)));
+ }
+}
+
+/**
+ * gst_base_sink_get_processing_deadline:
+ * @sink: a #GstBaseSink
+ *
+ * Get the processing deadline of @sink. see
+ * gst_base_sink_set_processing_deadline() for more information about
+ * the processing deadline.
+ *
+ * Returns: the processing deadline
+ *
+ * Since: 1.16
+ */
+GstClockTime
+gst_base_sink_get_processing_deadline (GstBaseSink * sink)
+{
+ GstClockTimeDiff res;
+
+ g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
+
+ GST_OBJECT_LOCK (sink);
+ res = sink->priv->processing_deadline;
+ GST_OBJECT_UNLOCK (sink);
+
+ return res;
+}
+
static void
gst_base_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
case PROP_MAX_BITRATE:
gst_base_sink_set_max_bitrate (sink, g_value_get_uint64 (value));
break;
+ case PROP_PROCESSING_DEADLINE:
+ gst_base_sink_set_processing_deadline (sink, g_value_get_uint64 (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_MAX_BITRATE:
g_value_set_uint64 (value, gst_base_sink_get_max_bitrate (sink));
break;
+ case PROP_PROCESSING_DEADLINE:
+ g_value_set_uint64 (value, gst_base_sink_get_processing_deadline (sink));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
switch (pending) {
case GST_STATE_PLAYING:
{
- GST_DEBUG_OBJECT (basesink, "commiting state to PLAYING");
+ GST_DEBUG_OBJECT (basesink, "committing state to PLAYING");
basesink->need_preroll = FALSE;
post_async_done = TRUE;
- basesink->priv->commited = TRUE;
+ basesink->priv->committed = TRUE;
post_playing = TRUE;
/* post PAUSED too when we were READY */
if (current == GST_STATE_READY) {
break;
}
case GST_STATE_PAUSED:
- GST_DEBUG_OBJECT (basesink, "commiting state to PAUSED");
+ GST_DEBUG_OBJECT (basesink, "committing state to PAUSED");
post_paused = TRUE;
post_async_done = TRUE;
- basesink->priv->commited = TRUE;
+ basesink->priv->committed = TRUE;
post_pending = GST_STATE_VOID_PENDING;
break;
case GST_STATE_READY:
}
rstart = rstop = rnext = priv->eos_rtime;
- *do_sync = rstart != -1;
+ *do_sync = GST_CLOCK_TIME_IS_VALID (rstart);
GST_DEBUG_OBJECT (basesink, "sync times for EOS %" GST_TIME_FORMAT,
GST_TIME_ARGS (rstart));
/* if we are stepping, we end now */
/* else do buffer sync code */
GstBuffer *buffer = GST_BUFFER_CAST (obj);
- /* just get the times to see if we need syncing, if the start returns -1 we
- * don't sync. */
+ /* just get the times to see if we need syncing, if the retuned start is -1
+ * we don't sync. */
if (bclass->get_times)
bclass->get_times (basesink, buffer, &start, &stop);
time += base_time;
/* Re-use existing clockid if available */
- /* FIXME: Casting to GstClockEntry only works because the types
- * are the same */
if (G_LIKELY (sink->priv->cached_clock_id != NULL
- && GST_CLOCK_ENTRY_CLOCK ((GstClockEntry *) sink->
- priv->cached_clock_id) == clock)) {
+ && gst_clock_id_uses_clock (sink->priv->cached_clock_id, clock))) {
if (!gst_clock_single_shot_id_reinit (clock, sink->priv->cached_clock_id,
time)) {
gst_clock_id_unref (sink->priv->cached_clock_id);
* against the clock it must unblock when going from PLAYING to the PAUSED state
* and call this method before continuing to render the remaining data.
*
+ * If the #GstBaseSinkClass.render() method can block on something else than
+ * the clock, it must also be ready to unblock immediately on
+ * the #GstBaseSinkClass.unlock() method and cause the
+ * #GstBaseSinkClass.render() method to immediately call this function.
+ * In this case, the subclass must be prepared to continue rendering where it
+ * left off if this function returns %GST_FLOW_OK.
+ *
* This function will block until a state change to PLAYING happens (in which
* case this function returns %GST_FLOW_OK) or the processing must be stopped due
* to a state change to READY or a FLUSH event (in which case this function
}
stopping:
{
- GST_DEBUG_OBJECT (sink, "stopping while commiting state");
+ GST_DEBUG_OBJECT (sink, "stopping while committing state");
return GST_FLOW_FLUSHING;
}
preroll_failed:
goto flushing;
/* retry if we got unscheduled, which means we did not reach the timeout
- * yet. if some other error occures, we continue. */
+ * yet. if some other error occurs, we continue. */
} while (status == GST_CLOCK_UNSCHEDULED);
GST_DEBUG_OBJECT (sink, "end of stream");
priv->eos_rtime = (do_sync ? rnext : GST_CLOCK_TIME_NONE);
/* calculate inter frame spacing */
- if (G_UNLIKELY (priv->prev_rstart != -1 && priv->prev_rstart < rstart)) {
+ if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->prev_rstart) &&
+ priv->prev_rstart < rstart)) {
GstClockTime in_diff;
in_diff = rstart - priv->prev_rstart;
}
priv->prev_rstart = rstart;
- if (G_UNLIKELY (priv->earliest_in_time != -1
- && rstart < priv->earliest_in_time))
+ if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->earliest_in_time) &&
+ rstart < priv->earliest_in_time))
goto qos_dropped;
again:
* trick mode or key-unit mode. Otherwise the buffer durations will be
* meaningless as frames are being dropped in-between without updating the
* durations. */
- if (GST_CLOCK_TIME_IS_VALID (stop)
- && !(sink->segment.flags & (GST_SEGMENT_FLAG_TRICKMODE |
- GST_SEGMENT_FLAG_TRICKMODE_KEY_UNITS)) && stop != start)
- duration = stop - start;
- else
- duration = priv->avg_in_diff;
+ duration = priv->avg_in_diff;
/* if we have the time when the last buffer left us, calculate
* processing time */
GST_TIME_ARGS (entered), GST_TIME_ARGS (left), GST_TIME_ARGS (pt),
GST_TIME_ARGS (duration), jitter);
- GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, "avg_duration: %" GST_TIME_FORMAT
- ", avg_pt: %" GST_TIME_FORMAT ", avg_rate: %g",
- GST_TIME_ARGS (priv->avg_duration), GST_TIME_ARGS (priv->avg_pt),
- priv->avg_rate);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink,
+ "avg_pt: %" GST_TIME_FORMAT ", avg_rate: %g",
+ GST_TIME_ARGS (priv->avg_pt), priv->avg_rate);
/* collect running averages. for first observations, we copy the
* values */
- if (!GST_CLOCK_TIME_IS_VALID (priv->avg_duration))
- priv->avg_duration = duration;
- else
- priv->avg_duration = UPDATE_RUNNING_AVG (priv->avg_duration, duration);
-
if (!GST_CLOCK_TIME_IS_VALID (priv->avg_pt))
priv->avg_pt = pt;
else
priv->avg_pt = UPDATE_RUNNING_AVG (priv->avg_pt, pt);
- if (priv->avg_duration != 0)
+ if (duration != -1 && duration != 0) {
rate =
gst_guint64_to_gdouble (priv->avg_pt) /
- gst_guint64_to_gdouble (priv->avg_duration);
- else
+ gst_guint64_to_gdouble (duration);
+ } else {
rate = 1.0;
+ }
if (GST_CLOCK_TIME_IS_VALID (priv->last_left)) {
if (dropped || priv->avg_rate < 0.0) {
}
GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink,
- "updated: avg_duration: %" GST_TIME_FORMAT ", avg_pt: %" GST_TIME_FORMAT
- ", avg_rate: %g", GST_TIME_ARGS (priv->avg_duration),
- GST_TIME_ARGS (priv->avg_pt), priv->avg_rate);
+ "updated: avg_pt: %" GST_TIME_FORMAT
+ ", avg_rate: %g", GST_TIME_ARGS (priv->avg_pt), priv->avg_rate);
if (priv->avg_rate >= 0.0) {
priv->prev_rstart = GST_CLOCK_TIME_NONE;
priv->earliest_in_time = GST_CLOCK_TIME_NONE;
priv->last_left = GST_CLOCK_TIME_NONE;
- priv->avg_duration = GST_CLOCK_TIME_NONE;
priv->avg_pt = GST_CLOCK_TIME_NONE;
priv->avg_rate = -1.0;
- priv->avg_render = GST_CLOCK_TIME_NONE;
priv->avg_in_diff = GST_CLOCK_TIME_NONE;
priv->rendered = 0;
priv->dropped = 0;
}
}
-/* called before and after calling the render vmethod. It keeps track of how
- * much time was spent in the render method and is used to check if we are
- * flooded */
-static void
-gst_base_sink_do_render_stats (GstBaseSink * basesink, gboolean start)
-{
- GstBaseSinkPrivate *priv;
-
- priv = basesink->priv;
-
- if (start) {
- priv->start = gst_util_get_timestamp ();
- } else {
- GstClockTime elapsed;
-
- priv->stop = gst_util_get_timestamp ();
-
- elapsed = GST_CLOCK_DIFF (priv->start, priv->stop);
-
- if (!GST_CLOCK_TIME_IS_VALID (priv->avg_render))
- priv->avg_render = elapsed;
- else
- priv->avg_render = UPDATE_RUNNING_AVG (priv->avg_render, elapsed);
-
- GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
- "avg_render: %" GST_TIME_FORMAT, GST_TIME_ARGS (priv->avg_render));
- }
-}
-
static void
gst_base_sink_update_start_time (GstBaseSink * basesink)
{
return res;
}
-static gboolean
-count_list_bytes (GstBuffer ** buffer, guint idx, GstBaseSinkPrivate * priv)
-{
- priv->rc_accumulated += gst_buffer_get_size (*buffer);
- return TRUE;
-}
-
/* with STREAM_LOCK, PREROLL_LOCK
*
* Takes a buffer and compare the timestamps with the last segment.
GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
GstSegment *segment;
GstBuffer *sync_buf;
- gint do_qos;
gboolean late, step_end, prepared = FALSE;
if (G_UNLIKELY (basesink->flushing))
", end: %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end));
/* a dropped buffer does not participate in anything. Buffer can only be
- * dropped if their PTS falls completly outside the segment, while we sync
+ * dropped if their PTS falls completely outside the segment, while we sync
* preferably on DTS */
if (GST_CLOCK_TIME_IS_VALID (start) && (segment->format == GST_FORMAT_TIME)) {
GstClockTime pts = GST_BUFFER_PTS (sync_buf);
pts_end = pts + (end - start);
if (G_UNLIKELY (!gst_segment_clip (segment,
- GST_FORMAT_TIME, pts, pts_end, NULL, NULL)))
+ GST_FORMAT_TIME, pts, pts_end, NULL, NULL)
+ && priv->drop_out_of_segment))
goto out_of_segment;
}
}
}
+ /* We are about to prepare the first frame, make sure we have prerolled
+ * already. This prevent nesting prepare/render calls. */
+ ret = gst_base_sink_do_preroll (basesink, obj);
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto preroll_failed;
+
if (G_UNLIKELY (late))
goto dropped;
goto dropped;
if (priv->max_bitrate) {
- if (is_list) {
- gst_buffer_list_foreach (GST_BUFFER_LIST_CAST (obj),
- (GstBufferListFunc) count_list_bytes, priv);
- } else {
- priv->rc_accumulated += gst_buffer_get_size (GST_BUFFER_CAST (obj));
- }
+ gsize size;
+
+ if (is_list)
+ size = gst_buffer_list_calculate_size (GST_BUFFER_LIST_CAST (obj));
+ else
+ size = gst_buffer_get_size (GST_BUFFER_CAST (obj));
+
+ priv->rc_accumulated += size;
priv->rc_next = priv->rc_time + gst_util_uint64_scale (priv->rc_accumulated,
8 * GST_SECOND, priv->max_bitrate);
}
- /* read once, to get same value before and after */
- do_qos = g_atomic_int_get (&priv->qos_enabled);
-
GST_DEBUG_OBJECT (basesink, "rendering object %p", obj);
- /* record rendering time for QoS and stats */
- if (do_qos)
- gst_base_sink_do_render_stats (basesink, TRUE);
-
if (!is_list) {
/* For buffer lists do not set last buffer for now. */
gst_base_sink_set_last_buffer (basesink, GST_BUFFER_CAST (obj));
gst_base_sink_set_last_buffer_list (basesink, buffer_list);
}
- if (do_qos)
- gst_base_sink_do_render_stats (basesink, FALSE);
-
if (ret == GST_FLOW_STEP)
goto again;
}
goto done;
}
+preroll_failed:
+ {
+ GST_DEBUG_OBJECT (basesink, "preroll failed: %s", gst_flow_get_name (ret));
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
+ return ret;
+ }
}
/* with STREAM_LOCK
}
} else if (result == GST_FLOW_NOT_LINKED || result <= GST_FLOW_EOS) {
/* for fatal errors we post an error message, post the error
- * first so the app knows about the error first.
+ * first so the app knows about the error first.
* wrong-state is not a fatal error because it happens due to
* flushing and posting an error message in that case is the
* wrong thing to do, e.g. when basesrc is doing a flushing
if (!gst_pad_peer_query (pad, query)) {
gst_query_unref (query);
- GST_DEBUG_OBJECT (basesink, "peer query faild, no pull mode");
+ GST_DEBUG_OBJECT (basesink, "peer query failed, no pull mode");
goto fallback;
}
}
if (forward) {
+ GST_DEBUG_OBJECT (basesink, "sending event %p %" GST_PTR_FORMAT, event,
+ event);
result = gst_pad_push_event (pad, event);
} else {
/* not forwarded, unref the event */
gst_object_unref (pad);
- GST_DEBUG_OBJECT (basesink, "handled event %p %" GST_PTR_FORMAT ": %d", event,
- event, result);
+ GST_DEBUG_OBJECT (basesink, "handled event: %d", result);
return result;
}
GstSegment *segment;
GstClockTime now, latency;
GstClockTimeDiff base_time;
- gint64 time, base, duration;
+ gint64 time, base, offset, duration;
gdouble rate;
gint64 last;
gboolean last_seen, with_clock, in_paused;
else
time = 0;
+ if (GST_CLOCK_TIME_IS_VALID (segment->offset))
+ offset = segment->offset;
+ else
+ offset = 0;
+
if (GST_CLOCK_TIME_IS_VALID (segment->stop))
duration = segment->stop - segment->start;
else
if (rate < 0.0)
time += duration;
- *cur = time + gst_guint64_to_gdouble (now - base_time) * rate;
+ *cur = time + offset + gst_guint64_to_gdouble (now - base_time) * rate;
/* never report more than last seen position */
if (last != -1) {
priv->received_eos = FALSE;
gst_base_sink_reset_qos (basesink);
priv->rc_next = -1;
- priv->commited = FALSE;
+ priv->committed = FALSE;
priv->call_preroll = TRUE;
priv->current_step.valid = FALSE;
priv->pending_step.valid = FALSE;
basesink->need_preroll = TRUE;
basesink->playing_async = TRUE;
priv->call_preroll = TRUE;
- priv->commited = FALSE;
+ priv->committed = FALSE;
if (priv->async_enabled) {
GST_DEBUG_OBJECT (basesink, "doing async state change");
ret = GST_STATE_CHANGE_ASYNC;
"PLAYING to PAUSED, we are not prerolled");
basesink->playing_async = TRUE;
basesink->need_preroll = TRUE;
- priv->commited = FALSE;
+ priv->committed = FALSE;
priv->call_preroll = TRUE;
if (priv->async_enabled) {
GST_DEBUG_OBJECT (basesink, "doing async state change");
gst_base_sink_set_last_buffer_list (basesink, NULL);
priv->call_preroll = FALSE;
- if (!priv->commited) {
+ if (!priv->committed) {
if (priv->async_enabled) {
GST_DEBUG_OBJECT (basesink, "PAUSED to READY, posting async-done");
gst_message_new_async_done (GST_OBJECT_CAST (basesink),
GST_CLOCK_TIME_NONE));
}
- priv->commited = TRUE;
+ priv->committed = TRUE;
} else {
GST_DEBUG_OBJECT (basesink, "PAUSED to READY, don't need_preroll");
}