X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=libs%2Fgst%2Fbase%2Fgstbasesink.c;h=4368c1a34c4627595ea5a2a98ecd281dd329be94;hb=5bf13cdd5314bc3c6c81bd620e712acdcab14eb2;hp=971d87a8184573c63731055a3aadb695a96ecaa6;hpb=37a6e8d6dfefde77a8c055d8ef85dca0d837ac87;p=platform%2Fupstream%2Fgstreamer.git diff --git a/libs/gst/base/gstbasesink.c b/libs/gst/base/gstbasesink.c index 971d87a..4368c1a 100644 --- a/libs/gst/base/gstbasesink.c +++ b/libs/gst/base/gstbasesink.c @@ -21,6 +21,7 @@ /** * SECTION:gstbasesink + * @title: GstBaseSink * @short_description: Base class for sink elements * @see_also: #GstBaseTransform, #GstBaseSrc * @@ -37,7 +38,7 @@ * #GstBaseSink provides support for exactly one sink pad, which should be * named "sink". A sink implementation (subclass of #GstBaseSink) should * install a pad template in its class_init function, like so: - * |[ + * |[ * static void * my_element_class_init (GstMyElementClass *klass) * { @@ -45,14 +46,13 @@ * * // sinktemplate should be a #GstStaticPadTemplate with direction * // %GST_PAD_SINK and name "sink" - * gst_element_class_add_pad_template (gstelement_class, - * gst_static_pad_template_get (&sinktemplate)); + * gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate); * * gst_element_class_set_static_metadata (gstelement_class, * "Sink name", * "Sink", * "My Sink element", - * "The author <my.sink@my.email>"); + * "The author "); * } * ]| * @@ -99,7 +99,7 @@ * 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. @@ -152,9 +152,6 @@ 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 @@ -181,6 +178,7 @@ struct _GstBaseSinkPrivate 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; @@ -202,19 +200,8 @@ struct _GstBaseSinkPrivate 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; @@ -223,8 +210,8 @@ struct _GstBaseSinkPrivate /* 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; @@ -270,6 +257,8 @@ struct _GstBaseSinkPrivate 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)) @@ -298,6 +287,8 @@ struct _GstBaseSinkPrivate #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 { @@ -313,10 +304,12 @@ 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); @@ -343,11 +336,21 @@ gst_base_sink_get_type (void) _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, @@ -415,11 +418,12 @@ gst_base_sink_class_init (GstBaseSinkClass * klass) 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; @@ -535,6 +539,20 @@ gst_base_sink_class_init (GstBaseSinkClass * klass) "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); @@ -630,7 +648,7 @@ gst_base_sink_init (GstBaseSink * basesink, gpointer g_class) 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"); @@ -661,12 +679,15 @@ gst_base_sink_init (GstBaseSink * basesink, gpointer g_class) 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); } @@ -728,6 +749,53 @@ gst_base_sink_get_sync (GstBaseSink * 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. @@ -751,7 +819,7 @@ gst_base_sink_set_max_lateness (GstBaseSink * sink, gint64 max_lateness) * 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 @@ -1100,7 +1168,7 @@ gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live, 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; @@ -1109,6 +1177,7 @@ gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live, 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 */ @@ -1132,6 +1201,24 @@ gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live, * 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 */ @@ -1283,7 +1370,7 @@ gst_base_sink_get_blocksize (GstBaseSink * sink) * * 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) @@ -1300,7 +1387,7 @@ 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. @@ -1363,6 +1450,67 @@ gst_base_sink_get_max_bitrate (GstBaseSink * sink) 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) @@ -1400,6 +1548,9 @@ gst_base_sink_set_property (GObject * object, guint prop_id, 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; @@ -1446,6 +1597,9 @@ gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value, 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; @@ -1487,11 +1641,11 @@ gst_base_sink_commit_state (GstBaseSink * basesink) 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) { @@ -1500,10 +1654,10 @@ gst_base_sink_commit_state (GstBaseSink * basesink) 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: @@ -1659,7 +1813,9 @@ start_stepping (GstBaseSink * sink, GstSegment * segment, /* update the segment clipping regions for non-flushing seeks */ if (segment->rate > 0.0) { if (end != -1) - position = gst_segment_to_position (segment, GST_FORMAT_TIME, end); + position = + gst_segment_position_from_running_time (segment, GST_FORMAT_TIME, + end); else position = segment->stop; @@ -1667,7 +1823,9 @@ start_stepping (GstBaseSink * sink, GstSegment * segment, segment->position = position; } else { if (end != -1) - position = gst_segment_to_position (segment, GST_FORMAT_TIME, end); + position = + gst_segment_position_from_running_time (segment, GST_FORMAT_TIME, + end); else position = segment->start; @@ -1803,10 +1961,14 @@ handle_stepping (GstBaseSink * sink, GstSegment * segment, step_end = TRUE; if (segment->rate > 0.0) { *rstart = end; - *cstart = gst_segment_to_position (segment, GST_FORMAT_TIME, end); + *cstart = + gst_segment_position_from_running_time (segment, GST_FORMAT_TIME, + end); } else { *rstop = end; - *cstop = gst_segment_to_position (segment, GST_FORMAT_TIME, end); + *cstop = + gst_segment_position_from_running_time (segment, GST_FORMAT_TIME, + end); } } GST_DEBUG_OBJECT (sink, @@ -1897,7 +2059,7 @@ again: } 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 */ @@ -1930,8 +2092,8 @@ again: /* 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); @@ -2124,11 +2286,8 @@ gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time, 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); @@ -2184,6 +2343,13 @@ no_clock: * 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 @@ -2317,7 +2483,7 @@ preroll_canceled: } stopping: { - GST_DEBUG_OBJECT (sink, "stopping while commiting state"); + GST_DEBUG_OBJECT (sink, "stopping while committing state"); return GST_FLOW_FLUSHING; } preroll_failed: @@ -2388,7 +2554,7 @@ gst_base_sink_wait (GstBaseSink * sink, GstClockTime time, 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"); @@ -2473,7 +2639,8 @@ do_step: 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; @@ -2489,8 +2656,8 @@ do_step: } 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: @@ -2661,11 +2828,11 @@ gst_base_sink_perform_qos (GstBaseSink * sink, gboolean dropped) left = start + jitter; } - /* calculate duration of the buffer */ - if (GST_CLOCK_TIME_IS_VALID (stop) && stop != start) - duration = stop - start; - else - duration = priv->avg_in_diff; + /* calculate duration of the buffer, only use buffer durations if not in + * trick mode or key-unit mode. Otherwise the buffer durations will be + * meaningless as frames are being dropped in-between without updating the + * durations. */ + duration = priv->avg_in_diff; /* if we have the time when the last buffer left us, calculate * processing time */ @@ -2686,29 +2853,24 @@ gst_base_sink_perform_qos (GstBaseSink * sink, gboolean dropped) 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) { @@ -2722,9 +2884,8 @@ gst_base_sink_perform_qos (GstBaseSink * sink, gboolean dropped) } 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) { @@ -2770,10 +2931,8 @@ gst_base_sink_reset_qos (GstBaseSink * sink) 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; @@ -2882,42 +3041,14 @@ no_timestamp: } } -/* 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) { GstClock *clock; GST_OBJECT_LOCK (basesink); - if ((clock = GST_ELEMENT_CLOCK (basesink))) { + if (GST_STATE (basesink) == GST_STATE_PLAYING + && (clock = GST_ELEMENT_CLOCK (basesink))) { GstClockTime now; gst_object_ref (clock); @@ -3315,13 +3446,6 @@ gst_base_sink_needs_preroll (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. @@ -3340,7 +3464,6 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad, 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)) @@ -3400,10 +3523,22 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad, GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT ", end: %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end)); - /* a dropped buffer does not participate in anything */ + /* a dropped buffer does not participate in anything. Buffer can only be + * 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); + GstClockTime pts_end = GST_CLOCK_TIME_NONE; + + if (!GST_CLOCK_TIME_IS_VALID (pts)) + pts = start; + + if (GST_CLOCK_TIME_IS_VALID (end)) + pts_end = pts + (end - start); + if (G_UNLIKELY (!gst_segment_clip (segment, - GST_FORMAT_TIME, start, end, NULL, NULL))) + GST_FORMAT_TIME, pts, pts_end, NULL, NULL) + && priv->drop_out_of_segment)) goto out_of_segment; } @@ -3423,7 +3558,7 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad, if (G_UNLIKELY (stepped)) goto dropped; - if (syncable && do_sync) { + if (syncable && do_sync && gst_base_sink_get_sync (basesink)) { GstClock *clock; GST_OBJECT_LOCK (basesink); @@ -3446,6 +3581,12 @@ gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad, } } + /* 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; @@ -3485,25 +3626,20 @@ again: 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)); @@ -3522,9 +3658,6 @@ again: 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; @@ -3619,6 +3752,12 @@ dropped: } 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 @@ -4066,14 +4205,12 @@ paused: } } 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 * seek. */ - GST_ELEMENT_ERROR (basesink, STREAM, FAILED, - (_("Internal data stream error.")), - ("stream stopped, reason %s", gst_flow_get_name (result))); + GST_ELEMENT_FLOW_ERROR (basesink, result); gst_base_sink_event (pad, parent, gst_event_new_eos ()); } return; @@ -4185,7 +4322,7 @@ gst_base_sink_pad_activate (GstPad * pad, GstObject * parent) 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; } @@ -4471,6 +4608,8 @@ gst_base_sink_send_event (GstElement * element, GstEvent * event) } 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 */ @@ -4479,8 +4618,7 @@ gst_base_sink_send_event (GstElement * element, GstEvent * 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; } @@ -4495,7 +4633,7 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format, 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; @@ -4547,6 +4685,11 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format, 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 @@ -4668,7 +4811,7 @@ gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format, 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) { @@ -4900,13 +5043,20 @@ static void gst_base_sink_drain (GstBaseSink * basesink) { GstBuffer *old; + GstBufferList *old_list; GST_OBJECT_LOCK (basesink); if ((old = basesink->priv->last_buffer)) basesink->priv->last_buffer = gst_buffer_copy_deep (old); + + if ((old_list = basesink->priv->last_buffer_list)) + basesink->priv->last_buffer_list = gst_buffer_list_copy_deep (old_list); GST_OBJECT_UNLOCK (basesink); + if (old) gst_buffer_unref (old); + if (old_list) + gst_mini_object_unref (GST_MINI_OBJECT_CAST (old_list)); } static gboolean @@ -5031,7 +5181,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) 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; @@ -5072,7 +5222,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) 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; @@ -5137,7 +5287,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) "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"); @@ -5174,7 +5324,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) 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"); @@ -5186,7 +5336,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) 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"); } @@ -5212,6 +5362,10 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) start_failed: { GST_DEBUG_OBJECT (basesink, "failed to start"); + /* subclass is supposed to post a message but we post one as a fallback + * just in case */ + GST_ELEMENT_ERROR (basesink, CORE, STATE_CHANGE, (NULL), + ("Failed to start")); return GST_STATE_CHANGE_FAILURE; } activate_failed: