*
* 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:gstbasesink:
+ * SECTION:gstbasesink
+ * @title: GstBaseSink
* @short_description: Base class for sink elements
* @see_also: #GstBaseTransform, #GstBaseSrc
*
* #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:
- * |[
+ * |[<!-- language="C" -->
* static void
* my_element_class_init (GstMyElementClass *klass)
* {
* GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
*
* // 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));
- * // see #GstElementDetails
- * gst_element_class_set_details (gstelement_class, &details);
+ * // %GST_PAD_SINK and name "sink"
+ * 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>");
* }
* ]|
*
* #GstBaseSink will handle the prerolling correctly. This means that it will
- * return #GST_STATE_CHANGE_ASYNC from a state change to PAUSED until the first
+ * return %GST_STATE_CHANGE_ASYNC from a state change to PAUSED until the first
* buffer arrives in this element. The base class will call the
* #GstBaseSinkClass.preroll() vmethod with this preroll buffer and will then
* commit the state change to the next asynchronously pending state.
*
* When the element is set to PLAYING, #GstBaseSink will synchronise on the
* clock using the times returned from #GstBaseSinkClass.get_times(). If this
- * function returns #GST_CLOCK_TIME_NONE for the start time, no synchronisation
+ * function returns %GST_CLOCK_TIME_NONE for the start time, no synchronisation
* will be done. Synchronisation can be disabled entirely by setting the object
* #GstBaseSink:sync property to %FALSE.
*
* After synchronisation the virtual method #GstBaseSinkClass.render() will be
* called. Subclasses should minimally implement this method.
*
- * Since 0.10.3 subclasses that synchronise on the clock in the
- * #GstBaseSinkClass.render() method are supported as well. These classes
- * typically receive a buffer in the render method and can then potentially
- * block on the clock while rendering. A typical example is an audiosink.
- * Since 0.10.11 these subclasses can use gst_base_sink_wait_preroll() to
- * perform the blocking wait.
+ * Subclasses that synchronise on the clock in the #GstBaseSinkClass.render()
+ * method are supported as well. These classes typically receive a buffer in
+ * the render method and can then potentially block on the clock while
+ * rendering. A typical example is an audiosink.
+ * These subclasses can use gst_base_sink_wait_preroll() to perform the
+ * blocking wait.
*
* Upon receiving the EOS event in the PLAYING state, #GstBaseSink will wait
* for the clock to reach the time indicated by the stop time of the last
* element receives EOS in PAUSED, preroll completes, the event is queued and an
* EOS message is posted when going to PLAYING.
*
- * #GstBaseSink will internally use the #GST_EVENT_NEWSEGMENT events to schedule
+ * #GstBaseSink will internally use the %GST_EVENT_SEGMENT events to schedule
* synchronisation and clipping of buffers. Buffers that fall completely outside
* of the current segment are dropped. Buffers that fall partially in the
* segment are rendered (and prerolled). Subclasses should do any subbuffer
* clipping themselves when needed.
*
* #GstBaseSink will by default report the current playback position in
- * #GST_FORMAT_TIME based on the current clock time and segment information.
+ * %GST_FORMAT_TIME based on the current clock time and segment information.
* If no clock has been set on the element, the query will be forwarded
* upstream.
*
* 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.
*
* The #GstBaseSinkClass.event() virtual method will be called when an event is
- * received by #GstBaseSink. Normally this method should only be overriden by
+ * received by #GstBaseSink. Normally this method should only be overridden by
* very specific elements (such as file sinks) which need to handle the
* newsegment event specially.
*
* information can then be used by upstream elements to reduce their processing
* rate, for example.
*
- * Since 0.10.15 the #GstBaseSink:async property can be used to instruct the
- * sink to never perform an ASYNC state change. This feature is mostly usable
- * when dealing with non-synchronized streams or sparse streams.
- *
- * Last reviewed on 2007-08-29 (0.10.15)
+ * The #GstBaseSink:async property can be used to instruct the sink to never
+ * perform an ASYNC state change. This feature is mostly usable when dealing
+ * with non-synchronized streams or sparse streams.
*/
#ifdef HAVE_CONFIG_H
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;
gint enable_last_sample; /* atomic */
GstBuffer *last_buffer;
GstCaps *last_caps;
+ GstBufferList *last_buffer_list;
/* negotiated caps */
GstCaps *caps;
/* for throttling and QoS */
GstClockTime earliest_in_time;
GstClockTime throttle_time;
+
+ /* for rate control */
+ guint64 max_bitrate;
+ 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 UPDATE_RUNNING_AVG(avg,val) DO_RUNNING_AVG(avg,val,8)
/* the windows for these running averages are experimentally obtained.
- * possitive values get averaged more while negative values use a small
+ * positive values get averaged more while negative values use a small
* window so we can react faster to badness. */
#define UPDATE_RUNNING_AVG_P(avg,val) DO_RUNNING_AVG(avg,val,16)
#define UPDATE_RUNNING_AVG_N(avg,val) DO_RUNNING_AVG(avg,val,4)
#define DEFAULT_RENDER_DELAY 0
#define DEFAULT_ENABLE_LAST_SAMPLE TRUE
#define DEFAULT_THROTTLE_TIME 0
+#define DEFAULT_MAX_BITRATE 0
+#define DEFAULT_DROP_OUT_OF_SEGMENT TRUE
enum
{
PROP_BLOCKSIZE,
PROP_RENDER_DELAY,
PROP_THROTTLE_TIME,
+ PROP_MAX_BITRATE,
PROP_LAST
};
GstObject * parent, GstPadMode mode, gboolean active);
static gboolean gst_base_sink_default_event (GstBaseSink * basesink,
GstEvent * event);
-static GstFlowReturn gst_base_sink_default_wait_eos (GstBaseSink * basesink,
+static GstFlowReturn gst_base_sink_default_wait_event (GstBaseSink * basesink,
GstEvent * event);
static gboolean gst_base_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
/* check if an object was too late */
static gboolean gst_base_sink_is_too_late (GstBaseSink * basesink,
GstMiniObject * obj, GstClockTime rstart, GstClockTime rstop,
- GstClockReturn status, GstClockTimeDiff jitter);
+ GstClockReturn status, GstClockTimeDiff jitter, gboolean render);
static void
gst_base_sink_class_init (GstBaseSinkClass * klass)
/**
* GstBaseSink:async:
*
- * If set to #TRUE, the basesink will perform asynchronous state changes.
- * When set to #FALSE, the sink will not signal the parent when it prerolls.
+ * If set to %TRUE, the basesink will perform asynchronous state changes.
+ * When set to %FALSE, the sink will not signal the parent when it prerolls.
* Use this option when dealing with sparse streams or when synchronisation is
* not required.
- *
- * Since: 0.10.15
*/
g_object_class_install_property (gobject_class, PROP_ASYNC,
g_param_spec_boolean ("async", "Async",
* Controls the final synchronisation, a negative value will render the buffer
* earlier while a positive value delays playback. This property can be
* used to fix synchronisation in bad files.
- *
- * Since: 0.10.15
*/
g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
g_param_spec_int64 ("ts-offset", "TS Offset",
/**
* GstBaseSink:enable-last-sample:
*
- * Enable the last-sample property. If FALSE, basesink doesn't keep a
+ * Enable the last-sample property. If %FALSE, basesink doesn't keep a
* reference to the last buffer arrived and the last-sample property is always
- * set to NULL. This can be useful if you need buffers to be released as soon
+ * set to %NULL. This can be useful if you need buffers to be released as soon
* as possible, eg. if you're using a buffer pool.
- *
- * Since: 0.10.30
*/
g_object_class_install_property (gobject_class, PROP_ENABLE_LAST_SAMPLE,
g_param_spec_boolean ("enable-last-sample", "Enable Last Buffer",
*
* The last buffer that arrived in the sink and was used for preroll or for
* rendering. This property can be used to generate thumbnails. This property
- * can be NULL when the sink has not yet received a bufer.
- *
- * Since: 0.10.15
+ * can be %NULL when the sink has not yet received a buffer.
*/
g_object_class_install_property (gobject_class, PROP_LAST_SAMPLE,
g_param_spec_boxed ("last-sample", "Last Sample",
* GstBaseSink:blocksize:
*
* The amount of bytes to pull when operating in pull mode.
- *
- * Since: 0.10.22
*/
- /* FIXME 0.11: blocksize property should be int, otherwise min>max.. */
+ /* FIXME 2.0: blocksize property should be int, otherwise min>max.. */
g_object_class_install_property (gobject_class, PROP_BLOCKSIZE,
g_param_spec_uint ("blocksize", "Block size",
"Size in bytes to pull per buffer (0 = default)", 0, G_MAXUINT,
* The additional delay between synchronisation and actual rendering of the
* media. This property will add additional latency to the device in order to
* make other sinks compensate for the delay.
- *
- * Since: 0.10.22
*/
g_object_class_install_property (gobject_class, PROP_RENDER_DELAY,
g_param_spec_uint64 ("render-delay", "Render Delay",
* The time to insert between buffers. This property can be used to control
* the maximum amount of buffers per second to render. Setting this property
* to a value bigger than 0 will make the sink create THROTTLE QoS events.
- *
- * Since: 0.10.33
*/
g_object_class_install_property (gobject_class, PROP_THROTTLE_TIME,
g_param_spec_uint64 ("throttle-time", "Throttle time",
- "The time to keep between rendered buffers", 0, G_MAXUINT64,
- DEFAULT_THROTTLE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ "The time to keep between rendered buffers (0 = disabled)", 0,
+ G_MAXUINT64, DEFAULT_THROTTLE_TIME,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstBaseSink:max-bitrate:
+ *
+ * Control the maximum amount of bits that will be rendered per second.
+ * Setting this property to a value bigger than 0 will make the sink delay
+ * rendering of the buffers when it would exceed to max-bitrate.
+ *
+ * Since: 1.2
+ */
+ g_object_class_install_property (gobject_class, PROP_MAX_BITRATE,
+ g_param_spec_uint64 ("max-bitrate", "Max Bitrate",
+ "The maximum bits per second to render (0 = disabled)", 0,
+ G_MAXUINT64, DEFAULT_MAX_BITRATE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_default_get_times);
klass->query = GST_DEBUG_FUNCPTR (gst_base_sink_default_query);
klass->event = GST_DEBUG_FUNCPTR (gst_base_sink_default_event);
- klass->wait_eos = GST_DEBUG_FUNCPTR (gst_base_sink_default_wait_eos);
+ klass->wait_event = GST_DEBUG_FUNCPTR (gst_base_sink_default_wait_event);
/* Registering debug symbols for function pointers */
GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_fixate);
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);
}
* @sync: the new sync value.
*
* Configures @sink to synchronize on the clock or not. When
- * @sync is FALSE, incoming samples will be played as fast as
- * possible. If @sync is TRUE, the timestamps of the incomming
+ * @sync is %FALSE, incoming samples will be played as fast as
+ * possible. If @sync is %TRUE, the timestamps of the incoming
* buffers will be used to schedule the exact render time of its
* contents.
- *
- * Since: 0.10.4
*/
void
gst_base_sink_set_sync (GstBaseSink * sink, gboolean sync)
* Checks if @sink is currently configured to synchronize against the
* clock.
*
- * Returns: TRUE if the sink is configured to synchronize against the clock.
- *
- * Since: 0.10.4
+ * Returns: %TRUE if the sink is configured to synchronize against the clock.
*/
gboolean
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)
+{
+ GstBaseSinkPrivate *priv;
+
+ g_return_if_fail (GST_IS_BASE_SINK (sink));
+
+ priv = GST_BASE_SINK_GET_PRIVATE (sink);
+
+ GST_OBJECT_LOCK (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)
+{
+ GstBaseSinkPrivate *priv;
+ gboolean res;
+
+ g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
+
+ priv = GST_BASE_SINK_GET_PRIVATE (sink);
+
+ GST_OBJECT_LOCK (sink);
+ res = 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.
* used to decide if a buffer should be dropped or not based on the
* buffer timestamp and the current clock time. A value of -1 means
* an unlimited time.
- *
- * Since: 0.10.4
*/
void
gst_base_sink_set_max_lateness (GstBaseSink * sink, gint64 max_lateness)
* Returns: The maximum time in nanoseconds that a buffer can be late
* before it is dropped and not rendered. A value of -1 means an
* unlimited time.
- *
- * Since: 0.10.4
*/
gint64
gst_base_sink_get_max_lateness (GstBaseSink * sink)
* @enabled: the new qos value.
*
* Configures @sink to send Quality-of-Service events upstream.
- *
- * Since: 0.10.5
*/
void
gst_base_sink_set_qos_enabled (GstBaseSink * sink, gboolean enabled)
* Checks if @sink is currently configured to send Quality-of-Service events
* upstream.
*
- * Returns: TRUE if the sink is configured to perform Quality-of-Service.
- *
- * Since: 0.10.5
+ * Returns: %TRUE if the sink is configured to perform Quality-of-Service.
*/
gboolean
gst_base_sink_is_qos_enabled (GstBaseSink * sink)
* @sink: the sink
* @enabled: the new async value.
*
- * Configures @sink to perform all state changes asynchronusly. When async is
+ * Configures @sink to perform all state changes asynchronously. When async is
* disabled, the sink will immediately go to PAUSED instead of waiting for a
* preroll buffer. This feature is useful if the sink does not synchronize
* against the clock or when it is dealing with sparse streams.
- *
- * Since: 0.10.15
*/
void
gst_base_sink_set_async_enabled (GstBaseSink * sink, gboolean enabled)
* Checks if @sink is currently configured to perform asynchronous state
* changes to PAUSED.
*
- * Returns: TRUE if the sink is configured to perform asynchronous state
+ * Returns: %TRUE if the sink is configured to perform asynchronous state
* changes.
- *
- * Since: 0.10.15
*/
gboolean
gst_base_sink_is_async_enabled (GstBaseSink * sink)
* render buffers earlier than their timestamp. A positive value will delay
* rendering. This function can be used to fix playback of badly timestamped
* buffers.
- *
- * Since: 0.10.15
*/
void
gst_base_sink_set_ts_offset (GstBaseSink * sink, GstClockTimeDiff offset)
* Get the synchronisation offset of @sink.
*
* Returns: The synchronisation offset.
- *
- * Since: 0.10.15
*/
GstClockTimeDiff
gst_base_sink_get_ts_offset (GstBaseSink * sink)
*
* Free-function: gst_sample_unref
*
- * Returns: (transfer full): a #GstSample. gst_sample_unref() after usage.
- * This function returns NULL when no buffer has arrived in the sink yet
- * or when the sink is not in PAUSED or PLAYING.
- *
- * Since: 0.10.15
+ * Returns: (transfer full) (nullable): a #GstSample. gst_sample_unref() after
+ * usage. This function returns %NULL when no buffer has arrived in the
+ * sink yet or when the sink is not in PAUSED or PLAYING.
*/
GstSample *
gst_base_sink_get_last_sample (GstBaseSink * sink)
g_return_val_if_fail (GST_IS_BASE_SINK (sink), NULL);
GST_OBJECT_LOCK (sink);
- if (sink->priv->last_buffer) {
+ if (sink->priv->last_buffer_list) {
+ GstBuffer *first_buffer = NULL;
+
+ /* Set the first buffer in the list to last sample's buffer */
+ first_buffer = gst_buffer_list_get (sink->priv->last_buffer_list, 0);
+ res =
+ gst_sample_new (first_buffer, sink->priv->last_caps, &sink->segment,
+ NULL);
+ gst_sample_set_buffer_list (res, sink->priv->last_buffer_list);
+ } else if (sink->priv->last_buffer) {
res = gst_sample_new (sink->priv->last_buffer,
sink->priv->last_caps, &sink->segment, NULL);
}
}
}
+/* with OBJECT_LOCK */
+static void
+gst_base_sink_set_last_buffer_list_unlocked (GstBaseSink * sink,
+ GstBufferList * buffer_list)
+{
+ GstBufferList *old;
+
+ old = sink->priv->last_buffer_list;
+ if (G_LIKELY (old != buffer_list)) {
+ GST_DEBUG_OBJECT (sink, "setting last buffer list to %p", buffer_list);
+ if (G_LIKELY (buffer_list))
+ gst_mini_object_ref (GST_MINI_OBJECT_CAST (buffer_list));
+ sink->priv->last_buffer_list = buffer_list;
+ } else {
+ old = NULL;
+ }
+
+ /* avoid unreffing with the lock because cleanup code might want to take the
+ * lock too */
+ if (G_LIKELY (old)) {
+ GST_OBJECT_UNLOCK (sink);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (old));
+ GST_OBJECT_LOCK (sink);
+ }
+}
+
static void
gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
{
GST_OBJECT_UNLOCK (sink);
}
+static void
+gst_base_sink_set_last_buffer_list (GstBaseSink * sink,
+ GstBufferList * buffer_list)
+{
+ if (!g_atomic_int_get (&sink->priv->enable_last_sample))
+ return;
+
+ GST_OBJECT_LOCK (sink);
+ gst_base_sink_set_last_buffer_list_unlocked (sink, buffer_list);
+ GST_OBJECT_UNLOCK (sink);
+}
+
/**
* gst_base_sink_set_last_sample_enabled:
* @sink: the sink
*
* Configures @sink to store the last received sample in the last-sample
* property.
- *
- * Since: 0.10.30
*/
void
gst_base_sink_set_last_sample_enabled (GstBaseSink * sink, gboolean enabled)
!enabled, enabled) && !enabled) {
GST_OBJECT_LOCK (sink);
gst_base_sink_set_last_buffer_unlocked (sink, NULL);
+ gst_base_sink_set_last_buffer_list_unlocked (sink, NULL);
GST_OBJECT_UNLOCK (sink);
}
}
* Checks if @sink is currently configured to store the last received sample in
* the last-sample property.
*
- * Returns: TRUE if the sink is configured to store the last received sample.
- *
- * Since: 0.10.30
+ * Returns: %TRUE if the sink is configured to store the last received sample.
*/
gboolean
gst_base_sink_is_last_sample_enabled (GstBaseSink * sink)
* Get the currently configured latency.
*
* Returns: The configured latency.
- *
- * Since: 0.10.12
*/
GstClockTime
gst_base_sink_get_latency (GstBaseSink * sink)
* @max_latency: (out) (allow-none): the max latency of the upstream elements
*
* Query the sink for the latency parameters. The latency will be queried from
- * the upstream elements. @live will be TRUE if @sink is configured to
- * synchronize against the clock. @upstream_live will be TRUE if an upstream
+ * the upstream elements. @live will be %TRUE if @sink is configured to
+ * synchronize against the clock. @upstream_live will be %TRUE if an upstream
* element is live.
*
- * If both @live and @upstream_live are TRUE, the sink will want to compensate
+ * If both @live and @upstream_live are %TRUE, the sink will want to compensate
* for the latency introduced by the upstream elements by setting the
- * @min_latency to a strictly possitive value.
+ * @min_latency to a strictly positive value.
*
* This function is mostly used by subclasses.
*
- * Returns: TRUE if the query succeeded.
- *
- * Since: 0.10.12
+ * Returns: %TRUE if the query succeeded.
*/
gboolean
gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live,
}
if (l) {
/* we need to add the render delay if we are live */
- if (min != -1)
- min += render_delay;
+ min += render_delay;
if (max != -1)
max += render_delay;
}
* other sinks will adjust their latency to delay the rendering of their media.
*
* This function is usually called by subclasses.
- *
- * Since: 0.10.21
*/
void
gst_base_sink_set_render_delay (GstBaseSink * sink, GstClockTime delay)
* information about the render delay.
*
* Returns: the render delay of @sink.
- *
- * Since: 0.10.21
*/
GstClockTime
gst_base_sink_get_render_delay (GstBaseSink * sink)
*
* Set the number of bytes that the sink will pull when it is operating in pull
* mode.
- *
- * Since: 0.10.22
*/
-/* FIXME 0.11: blocksize property should be int, otherwise min>max.. */
+/* FIXME 2.0: blocksize property should be int, otherwise min>max.. */
void
gst_base_sink_set_blocksize (GstBaseSink * sink, guint blocksize)
{
* mode.
*
* Returns: the number of bytes @sink will pull in pull mode.
- *
- * Since: 0.10.22
*/
-/* FIXME 0.11: blocksize property should be int, otherwise min>max.. */
+/* FIXME 2.0: blocksize property should be int, otherwise min>max.. */
guint
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.
- *
- * Since: 0.10.33
+ * 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.
- *
- * Since: 0.10.33
*/
guint64
gst_base_sink_get_throttle_time (GstBaseSink * sink)
return res;
}
+/**
+ * gst_base_sink_set_max_bitrate:
+ * @sink: a #GstBaseSink
+ * @max_bitrate: the max_bitrate in bits per second
+ *
+ * Set the maximum amount of bits per second that the sink will render.
+ *
+ * Since: 1.2
+ */
+void
+gst_base_sink_set_max_bitrate (GstBaseSink * sink, guint64 max_bitrate)
+{
+ g_return_if_fail (GST_IS_BASE_SINK (sink));
+
+ GST_OBJECT_LOCK (sink);
+ sink->priv->max_bitrate = max_bitrate;
+ GST_LOG_OBJECT (sink, "set max_bitrate to %" G_GUINT64_FORMAT, max_bitrate);
+ GST_OBJECT_UNLOCK (sink);
+}
+
+/**
+ * gst_base_sink_get_max_bitrate:
+ * @sink: a #GstBaseSink
+ *
+ * Get the maximum amount of bits per second that the sink will render.
+ *
+ * Returns: the maximum number of bits per second @sink will render.
+ *
+ * Since: 1.2
+ */
+guint64
+gst_base_sink_get_max_bitrate (GstBaseSink * sink)
+{
+ guint64 res;
+
+ g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
+
+ GST_OBJECT_LOCK (sink);
+ res = sink->priv->max_bitrate;
+ 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_THROTTLE_TIME:
gst_base_sink_set_throttle_time (sink, g_value_get_uint64 (value));
break;
+ case PROP_MAX_BITRATE:
+ gst_base_sink_set_max_bitrate (sink, g_value_get_uint64 (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_value_set_int64 (value, gst_base_sink_get_ts_offset (sink));
break;
case PROP_LAST_SAMPLE:
- gst_value_take_buffer (value, gst_base_sink_get_last_sample (sink));
+ gst_value_take_sample (value, gst_base_sink_get_last_sample (sink));
break;
case PROP_ENABLE_LAST_SAMPLE:
g_value_set_boolean (value, gst_base_sink_is_last_sample_enabled (sink));
case PROP_THROTTLE_TIME:
g_value_set_uint64 (value, gst_base_sink_get_throttle_time (sink));
break;
+ case PROP_MAX_BITRATE:
+ g_value_set_uint64 (value, gst_base_sink_get_max_bitrate (sink));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
GST_CLOCK_TIME_NONE));
}
if (post_playing) {
+ if (post_paused) {
+ GstElementClass *klass;
+
+ klass = GST_ELEMENT_GET_CLASS (basesink);
+ basesink->have_preroll = TRUE;
+ /* after releasing this lock, the state change function
+ * can execute concurrently with this thread. There is nothing we do to
+ * prevent this for now. subclasses should be prepared to handle it. */
+ GST_BASE_SINK_PREROLL_UNLOCK (basesink);
+
+ if (klass->change_state)
+ klass->change_state (GST_ELEMENT_CAST (basesink),
+ GST_STATE_CHANGE_PAUSED_TO_PLAYING);
+
+ GST_BASE_SINK_PREROLL_LOCK (basesink);
+ /* state change function could have been executed and we could be
+ * flushing now */
+ if (G_UNLIKELY (basesink->flushing))
+ goto stopping_unlocked;
+ }
GST_DEBUG_OBJECT (basesink, "posting PLAYING state change message");
+ /* FIXME, we released the PREROLL lock above, it's possible that this
+ * message is not correct anymore when the element went back to PAUSED */
gst_element_post_message (GST_ELEMENT_CAST (basesink),
gst_message_new_state_changed (GST_OBJECT_CAST (basesink),
next, pending, GST_STATE_VOID_PENDING));
/* Depending on the state, set our vars. We get in this situation when the
* state change function got a change to update the state vars before the
* streaming thread did. This is fine but we need to make sure that we
- * update the need_preroll var since it was TRUE when we got here and might
- * become FALSE if we got to PLAYING. */
+ * update the need_preroll var since it was %TRUE when we got here and might
+ * become %FALSE if we got to PLAYING. */
GST_DEBUG_OBJECT (basesink, "nothing to commit, now in %s",
gst_element_state_get_name (current));
switch (current) {
GST_OBJECT_UNLOCK (basesink);
return TRUE;
}
+stopping_unlocked:
+ {
+ GST_OBJECT_LOCK (basesink);
+ goto stopping;
+ }
stopping:
{
/* app is going to READY */
current->start_start = segment->start;
if (current->format == GST_FORMAT_TIME) {
- end = current->start + current->amount;
+ /* calculate the running-time when the step operation should stop */
+ if (current->amount != -1)
+ end = current->start + current->amount;
+ else
+ end = -1;
+
if (!current->flush) {
+ gint64 position;
+
/* update the segment clipping regions for non-flushing seeks */
if (segment->rate > 0.0) {
- segment->stop = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
- segment->position = segment->stop;
+ if (end != -1)
+ position =
+ gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
+ end);
+ else
+ position = segment->stop;
+
+ segment->stop = position;
+ segment->position = position;
} else {
- gint64 position;
+ if (end != -1)
+ position =
+ gst_segment_position_from_running_time (segment, GST_FORMAT_TIME,
+ end);
+ else
+ position = segment->start;
- position = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
segment->time = position;
segment->start = position;
segment->position = position;
GST_DEBUG_OBJECT (sink, "step started at running_time %" GST_TIME_FORMAT,
GST_TIME_ARGS (current->start));
- if (current->amount == -1) {
- GST_DEBUG_OBJECT (sink, "step amount == -1, stop stepping");
- current->valid = FALSE;
- } else {
- GST_DEBUG_OBJECT (sink, "step amount: %" G_GUINT64_FORMAT ", format: %s, "
- "rate: %f", current->amount, gst_format_get_name (current->format),
- current->rate);
- }
+ GST_DEBUG_OBJECT (sink, "step amount: %" G_GUINT64_FORMAT ", format: %s, "
+ "rate: %f", current->amount, gst_format_get_name (current->format),
+ current->rate);
}
static void
{
gboolean step_end = FALSE;
+ /* stepping never stops */
+ if (current->amount == -1)
+ return FALSE;
+
/* see if we need to skip this buffer because of stepping */
switch (current->format) {
case GST_FORMAT_TIME:
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,
/* with STREAM_LOCK, PREROLL_LOCK
*
- * Returns TRUE if the object needs synchronisation and takes therefore
+ * Returns %TRUE if the object needs synchronisation and takes therefore
* part in prerolling.
*
* rsstart/rsstop contain the start/stop in stream time.
static gboolean
gst_base_sink_get_sync_times (GstBaseSink * basesink, GstMiniObject * obj,
GstClockTime * rsstart, GstClockTime * rsstop,
- GstClockTime * rrstart, GstClockTime * rrstop, gboolean * do_sync,
- gboolean * stepped, GstStepInfo * step, gboolean * step_end)
+ GstClockTime * rrstart, GstClockTime * rrstop, GstClockTime * rrnext,
+ gboolean * do_sync, gboolean * stepped, GstStepInfo * step,
+ gboolean * step_end)
{
GstBaseSinkClass *bclass;
GstClockTime start, stop; /* raw start/stop timestamps */
guint64 cstart, cstop; /* clipped raw timestamps */
- guint64 rstart, rstop; /* clipped timestamps converted to running time */
+ guint64 rstart, rstop, rnext; /* clipped timestamps converted to running time */
GstClockTime sstart, sstop; /* clipped timestamps converted to stream time */
GstFormat format;
GstBaseSinkPrivate *priv;
}
}
- rstart = rstop = priv->eos_rtime;
+ rstart = rstop = rnext = priv->eos_rtime;
*do_sync = rstart != -1;
GST_DEBUG_OBJECT (basesink, "sync times for EOS %" GST_TIME_FORMAT,
GST_TIME_ARGS (rstart));
GstClockTime timestamp, duration;
gst_event_parse_gap (event, ×tamp, &duration);
+ GST_DEBUG_OBJECT (basesink, "Got Gap time %" GST_TIME_FORMAT
+ " duration %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration));
+
if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
start = timestamp;
if (GST_CLOCK_TIME_IS_VALID (duration))
rstart = gst_segment_to_running_time (segment, format, cstart);
rstop = gst_segment_to_running_time (segment, format, cstop);
+ if (GST_CLOCK_TIME_IS_VALID (stop))
+ rnext = rstop;
+ else
+ rnext = rstart;
+
if (G_UNLIKELY (step->valid)) {
if (!(*step_end = handle_stepping (basesink, segment, step, &cstart, &cstop,
&rstart, &rstop))) {
*rsstop = sstop;
*rrstart = rstart;
*rrstop = rstop;
+ *rrnext = rnext;
/* buffers and EOS always need syncing and preroll */
return TRUE;
out_of_segment:
{
/* we usually clip in the chain function already but stepping could cause
- * the segment to be updated later. we return FALSE so that we don't try
+ * the segment to be updated later. we return %FALSE so that we don't try
* to sync on it. */
GST_LOG_OBJECT (basesink, "buffer skipped, not in segment");
return FALSE;
time += basesink->priv->latency;
- /* apply offset, be carefull for underflows */
+ /* apply offset, be careful for underflows */
ts_offset = basesink->priv->ts_offset;
if (ts_offset < 0) {
ts_offset = -ts_offset;
* gst_base_sink_wait_clock:
* @sink: the sink
* @time: the running_time to be reached
- * @jitter: (out) (allow-none): the jitter to be filled with time diff, or NULL
+ * @jitter: (out) (allow-none): the jitter to be filled with time diff, or %NULL
*
* This function will block until @time is reached. It is usually called by
* subclasses that use their own internal synchronisation.
*
- * If @time is not valid, no sycnhronisation is done and #GST_CLOCK_BADTIME is
+ * If @time is not valid, no synchronisation is done and %GST_CLOCK_BADTIME is
* returned. Likewise, if synchronisation is disabled in the element or there
- * is no clock, no synchronisation is done and #GST_CLOCK_BADTIME is returned.
+ * is no clock, no synchronisation is done and %GST_CLOCK_BADTIME is returned.
*
* This function should only be called with the PREROLL_LOCK held, like when
* receiving an EOS event in the #GstBaseSinkClass.event() vmethod or when
* return and is not adjusted with any latency or offset configured in the
* sink.
*
- * Since: 0.10.20
- *
* Returns: #GstClockReturn
*/
GstClockReturn
/* 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_ENTRY_CLOCK ((GstClockEntry *) 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
+ * 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
- * returns #GST_FLOW_FLUSHING).
+ * returns %GST_FLOW_FLUSHING).
*
* This function should only be called with the PREROLL_LOCK held, like in the
* render function.
*
- * Returns: #GST_FLOW_OK if the preroll completed and processing can
+ * Returns: %GST_FLOW_OK if the preroll completed and processing can
* continue. Any other return value should be returned from the render vmethod.
- *
- * Since: 0.10.11
*/
GstFlowReturn
gst_base_sink_wait_preroll (GstBaseSink * sink)
*
* This function should be called with the PREROLL_LOCK held.
*
- * Returns: #GST_FLOW_OK if the preroll completed and processing can
+ * Returns: %GST_FLOW_OK if the preroll completed and processing can
* continue. Any other return value should be returned from the render vmethod.
- *
- * Since: 0.10.22
*/
GstFlowReturn
gst_base_sink_do_preroll (GstBaseSink * sink, GstMiniObject * obj)
if (GST_IS_BUFFER_LIST (obj)) {
buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
+ gst_base_sink_set_last_buffer (sink, buf);
+ gst_base_sink_set_last_buffer_list (sink, GST_BUFFER_LIST_CAST (obj));
g_assert (NULL != buf);
} else if (GST_IS_BUFFER (obj)) {
buf = GST_BUFFER_CAST (obj);
/* For buffer lists do not set last buffer for now */
gst_base_sink_set_last_buffer (sink, buf);
- } else
+ gst_base_sink_set_last_buffer_list (sink, NULL);
+ } else {
buf = NULL;
+ }
if (buf) {
GST_DEBUG_OBJECT (sink, "preroll buffer %" GST_TIME_FORMAT,
* gst_base_sink_wait:
* @sink: the sink
* @time: the running_time to be reached
- * @jitter: (out) (allow-none): the jitter to be filled with time diff, or NULL
+ * @jitter: (out) (allow-none): the jitter to be filled with time diff, or %NULL
*
* This function will wait for preroll to complete and will then block until @time
* is reached. It is usually called by subclasses that use their own internal
* and will be adjusted with any latency and offset configured in the sink.
*
* Returns: #GstFlowReturn
- *
- * Since: 0.10.15
*/
GstFlowReturn
gst_base_sink_wait (GstBaseSink * sink, GstClockTime time,
* immediately returns GST_FLOW_OK.
*
* for objects that arrive later than max-lateness to be synchronized to the
- * clock have the @late boolean set to TRUE.
+ * clock have the @late boolean set to %TRUE.
*
* This function keeps a running average of the jitter (the diff between the
* clock time and the requested sync time). The jitter is negative for
GstClockTimeDiff jitter = 0;
gboolean syncable;
GstClockReturn status = GST_CLOCK_OK;
- GstClockTime rstart, rstop, sstart, sstop, stime;
+ GstClockTime rstart, rstop, rnext, sstart, sstop, stime;
gboolean do_sync;
GstBaseSinkPrivate *priv;
GstFlowReturn ret;
priv = basesink->priv;
do_step:
- sstart = sstop = rstart = rstop = GST_CLOCK_TIME_NONE;
+ sstart = sstop = rstart = rstop = rnext = GST_CLOCK_TIME_NONE;
do_sync = TRUE;
stepped = FALSE;
/* get timing information for this object against the render segment */
syncable = gst_base_sink_get_sync_times (basesink, obj,
- &sstart, &sstop, &rstart, &rstop, &do_sync, &stepped, current, step_end);
+ &sstart, &sstop, &rstart, &rstop, &rnext, &do_sync, &stepped, current,
+ step_end);
if (G_UNLIKELY (stepped))
goto step_skipped;
priv->current_rstop = (GST_CLOCK_TIME_IS_VALID (rstop) ? rstop : rstart);
/* save sync time for eos when the previous object needed sync */
- priv->eos_rtime = (do_sync ? priv->current_rstop : GST_CLOCK_TIME_NONE);
+ 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)) {
/* adjust for latency */
stime = gst_base_sink_adjust_time (basesink, rstart);
+ /* adjust for rate control */
+ if (priv->rc_next == -1 || (stime != -1 && stime >= priv->rc_next)) {
+ GST_DEBUG_OBJECT (basesink, "reset rc_time to time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (stime));
+ priv->rc_time = stime;
+ priv->rc_accumulated = 0;
+ } else {
+ GST_DEBUG_OBJECT (basesink, "rate control next %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (priv->rc_next));
+ stime = priv->rc_next;
+ }
+
/* preroll done, we can sync since we are in PLAYING now. */
GST_DEBUG_OBJECT (basesink, "possibly waiting for clock to reach %"
GST_TIME_FORMAT ", adjusted %" GST_TIME_FORMAT,
/* check if the object should be dropped */
*late = gst_base_sink_is_too_late (basesink, obj, rstart, rstop,
- status, jitter);
+ status, jitter, TRUE);
done:
return GST_FLOW_OK;
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 */
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;
*
* status and jitter contain the return values from the clock wait.
*
- * returns TRUE if the buffer was too late.
+ * returns %TRUE if the buffer was too late.
*/
static gboolean
gst_base_sink_is_too_late (GstBaseSink * basesink, GstMiniObject * obj,
GstClockTime rstart, GstClockTime rstop,
- GstClockReturn status, GstClockTimeDiff jitter)
+ GstClockReturn status, GstClockTimeDiff jitter, gboolean render)
{
gboolean late;
guint64 max_lateness;
}
done:
- if (!late || !GST_CLOCK_TIME_IS_VALID (priv->last_render_time)) {
+ if (render && (!late || !GST_CLOCK_TIME_IS_VALID (priv->last_render_time))) {
priv->last_render_time = rstart;
/* the next allowed input timestamp */
if (priv->throttle_time > 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)
+gst_base_sink_update_start_time (GstBaseSink * basesink)
{
- GstBaseSinkPrivate *priv;
-
- priv = basesink->priv;
-
- if (start) {
- priv->start = gst_util_get_timestamp ();
- } else {
- GstClockTime elapsed;
+ GstClock *clock;
- priv->stop = gst_util_get_timestamp ();
+ GST_OBJECT_LOCK (basesink);
+ if (GST_STATE (basesink) == GST_STATE_PLAYING
+ && (clock = GST_ELEMENT_CLOCK (basesink))) {
+ GstClockTime now;
- elapsed = GST_CLOCK_DIFF (priv->start, priv->stop);
+ gst_object_ref (clock);
+ GST_OBJECT_UNLOCK (basesink);
- if (!GST_CLOCK_TIME_IS_VALID (priv->avg_render))
- priv->avg_render = elapsed;
- else
- priv->avg_render = UPDATE_RUNNING_AVG (priv->avg_render, elapsed);
+ /* calculate the time when we stopped */
+ now = gst_clock_get_time (clock);
+ gst_object_unref (clock);
- GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
- "avg_render: %" GST_TIME_FORMAT, GST_TIME_ARGS (priv->avg_render));
+ GST_OBJECT_LOCK (basesink);
+ /* store the current running time */
+ if (GST_ELEMENT_START_TIME (basesink) != GST_CLOCK_TIME_NONE) {
+ if (now != GST_CLOCK_TIME_NONE)
+ GST_ELEMENT_START_TIME (basesink) =
+ now - GST_ELEMENT_CAST (basesink)->base_time;
+ else
+ GST_WARNING_OBJECT (basesink,
+ "Clock %s returned invalid time, can't calculate "
+ "running_time when going to the PAUSED state",
+ GST_OBJECT_NAME (clock));
+ }
+ GST_DEBUG_OBJECT (basesink,
+ "start_time=%" GST_TIME_FORMAT ", now=%" GST_TIME_FORMAT
+ ", base_time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GST_ELEMENT_START_TIME (basesink)),
+ GST_TIME_ARGS (now),
+ GST_TIME_ARGS (GST_ELEMENT_CAST (basesink)->base_time));
}
+ GST_OBJECT_UNLOCK (basesink);
}
static void
* prerolled buffer */
basesink->playing_async = TRUE;
if (basesink->priv->async_enabled) {
+ gst_base_sink_update_start_time (basesink);
gst_element_lost_state (GST_ELEMENT_CAST (basesink));
} else {
/* start time reset in above case as well;
basesink->priv->have_latency = TRUE;
}
gst_base_sink_set_last_buffer (basesink, NULL);
+ gst_base_sink_set_last_buffer_list (basesink, NULL);
GST_PAD_STREAM_UNLOCK (pad);
}
basesink->have_newsegment = FALSE;
if (reset_time) {
gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
+ GST_ELEMENT_START_TIME (basesink) = 0;
}
}
GST_OBJECT_UNLOCK (basesink);
}
static GstFlowReturn
-gst_base_sink_default_wait_eos (GstBaseSink * basesink, GstEvent * event)
+gst_base_sink_default_wait_event (GstBaseSink * basesink, GstEvent * event)
{
GstFlowReturn ret;
- gboolean late, step_end;
+ gboolean late, step_end = FALSE;
ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (event),
&late, &step_end);
return ret;
}
+static GstFlowReturn
+gst_base_sink_wait_event (GstBaseSink * basesink, GstEvent * event)
+{
+ GstFlowReturn ret;
+ GstBaseSinkClass *bclass;
+
+ bclass = GST_BASE_SINK_GET_CLASS (basesink);
+
+ if (G_LIKELY (bclass->wait_event))
+ ret = bclass->wait_event (basesink, event);
+ else
+ ret = GST_FLOW_NOT_SUPPORTED;
+
+ return ret;
+}
+
static gboolean
gst_base_sink_default_event (GstBaseSink * basesink, GstEvent * event)
{
basesink->priv->received_eos = TRUE;
/* wait for EOS */
- if (G_LIKELY (bclass->wait_eos)) {
- GstFlowReturn ret;
-
- ret = bclass->wait_eos (basesink, event);
- if (G_UNLIKELY (ret != GST_FLOW_OK)) {
- result = FALSE;
- goto done;
- }
+ if (G_UNLIKELY (gst_base_sink_wait_event (basesink,
+ event) != GST_FLOW_OK)) {
+ result = FALSE;
+ goto done;
}
/* the EOS event is completely handled so we mark
gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
break;
}
+ case GST_EVENT_STREAM_START:
+ {
+ GstMessage *message;
+ guint32 seqnum;
+ guint group_id;
+
+ seqnum = gst_event_get_seqnum (event);
+ GST_DEBUG_OBJECT (basesink, "Now posting STREAM_START (seqnum:%d)",
+ seqnum);
+ message = gst_message_new_stream_start (GST_OBJECT_CAST (basesink));
+ if (gst_event_parse_group_id (event, &group_id)) {
+ gst_message_set_group_id (message, group_id);
+ } else {
+ GST_FIXME_OBJECT (basesink, "stream-start event without group-id. "
+ "Consider implementing group-id handling in the upstream "
+ "elements");
+ }
+ gst_message_set_seqnum (message, seqnum);
+ gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
+ break;
+ }
case GST_EVENT_CAPS:
{
- GstCaps *caps;
+ GstCaps *caps, *current_caps;
GST_DEBUG_OBJECT (basesink, "caps %p", event);
gst_event_parse_caps (event, &caps);
- if (bclass->set_caps)
- result = bclass->set_caps (basesink, caps);
+ current_caps = gst_pad_get_current_caps (GST_BASE_SINK_PAD (basesink));
- if (result) {
- GST_OBJECT_LOCK (basesink);
- gst_caps_replace (&basesink->priv->caps, caps);
- GST_OBJECT_UNLOCK (basesink);
+ if (current_caps && gst_caps_is_equal (current_caps, caps)) {
+ GST_DEBUG_OBJECT (basesink,
+ "New caps equal to old ones: %" GST_PTR_FORMAT, caps);
+ } else {
+ if (bclass->set_caps)
+ result = bclass->set_caps (basesink, caps);
+
+ if (result) {
+ GST_OBJECT_LOCK (basesink);
+ gst_caps_replace (&basesink->priv->caps, caps);
+ GST_OBJECT_UNLOCK (basesink);
+ }
}
+ if (current_caps)
+ gst_caps_unref (current_caps);
break;
}
case GST_EVENT_SEGMENT:
/* the newsegment event is needed to bring the buffer timestamps to the
* stream time and to drop samples outside of the playback segment. */
gst_event_copy_segment (event, &basesink->segment);
- GST_DEBUG_OBJECT (basesink, "configured SEGMENT %" GST_SEGMENT_FORMAT,
+ GST_DEBUG_OBJECT (basesink, "configured segment %" GST_SEGMENT_FORMAT,
&basesink->segment);
basesink->have_newsegment = TRUE;
+ gst_base_sink_reset_qos (basesink);
GST_OBJECT_UNLOCK (basesink);
break;
+ case GST_EVENT_GAP:
+ {
+ if (G_UNLIKELY (gst_base_sink_wait_event (basesink,
+ event) != GST_FLOW_OK))
+ result = FALSE;
+ break;
+ }
case GST_EVENT_TAG:
{
GstTagList *taglist;
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.
{
GstBaseSinkClass *bclass;
GstBaseSinkPrivate *priv = basesink->priv;
- GstFlowReturn ret;
+ GstFlowReturn ret = GST_FLOW_OK;
GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
GstSegment *segment;
GstBuffer *sync_buf;
- gint do_qos;
- gboolean late, step_end;
+ gboolean late, step_end, prepared = FALSE;
if (G_UNLIKELY (basesink->flushing))
goto flushing;
goto was_eos;
if (is_list) {
- sync_buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
+ GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj);
+
+ if (gst_buffer_list_length (buffer_list) == 0)
+ goto empty_list;
+
+ sync_buf = gst_buffer_list_get (buffer_list, 0);
g_assert (NULL != sync_buf);
} else {
sync_buf = GST_BUFFER_CAST (obj);
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 completly 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;
}
- if (!is_list) {
- if (bclass->prepare) {
- ret = bclass->prepare (basesink, GST_BUFFER_CAST (obj));
- if (G_UNLIKELY (ret != GST_FLOW_OK))
- goto prepare_failed;
+ if (bclass->prepare || bclass->prepare_list) {
+ gboolean do_sync = TRUE, stepped = FALSE, syncable = TRUE;
+ GstClockTime sstart, sstop, rstart, rstop, rnext;
+ GstStepInfo *current;
+
+ late = FALSE;
+ step_end = FALSE;
+
+ current = &priv->current_step;
+ syncable =
+ gst_base_sink_get_sync_times (basesink, obj, &sstart, &sstop, &rstart,
+ &rstop, &rnext, &do_sync, &stepped, current, &step_end);
+
+ if (G_UNLIKELY (stepped))
+ goto dropped;
+
+ if (syncable && do_sync && gst_base_sink_get_sync (basesink)) {
+ GstClock *clock;
+
+ GST_OBJECT_LOCK (basesink);
+ clock = GST_ELEMENT_CLOCK (basesink);
+ if (clock && GST_STATE (basesink) == GST_STATE_PLAYING) {
+ GstClockTime base_time;
+ GstClockTime stime;
+ GstClockTime now;
+
+ base_time = GST_ELEMENT_CAST (basesink)->base_time;
+ stime = base_time + gst_base_sink_adjust_time (basesink, rstart);
+ now = gst_clock_get_time (clock);
+ GST_OBJECT_UNLOCK (basesink);
+
+ late =
+ gst_base_sink_is_too_late (basesink, obj, rstart, rstop,
+ GST_CLOCK_EARLY, GST_CLOCK_DIFF (stime, now), FALSE);
+ } else {
+ GST_OBJECT_UNLOCK (basesink);
+ }
}
- } else {
- if (bclass->prepare_list) {
- ret = bclass->prepare_list (basesink, GST_BUFFER_LIST_CAST (obj));
- if (G_UNLIKELY (ret != GST_FLOW_OK))
- goto prepare_failed;
+
+ /* 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;
+
+ if (!is_list) {
+ if (bclass->prepare) {
+ ret = bclass->prepare (basesink, GST_BUFFER_CAST (obj));
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto prepare_failed;
+ }
+ } else {
+ if (bclass->prepare_list) {
+ ret = bclass->prepare_list (basesink, GST_BUFFER_LIST_CAST (obj));
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto prepare_failed;
+ }
}
+
+ prepared = TRUE;
}
again:
if (G_UNLIKELY (ret != GST_FLOW_OK))
goto sync_failed;
+ /* Don't skip if prepare() was called on time */
+ late = late && !prepared;
+
/* drop late buffers unconditionally, let's hope it's unlikely */
if (G_UNLIKELY (late))
goto dropped;
- /* read once, to get same value before and after */
- do_qos = g_atomic_int_get (&priv->qos_enabled);
+ 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));
+ }
+ priv->rc_next = priv->rc_time + gst_util_uint64_scale (priv->rc_accumulated,
+ 8 * GST_SECOND, priv->max_bitrate);
+ }
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, NULL);
if (bclass->render)
ret = bclass->render (basesink, GST_BUFFER_CAST (obj));
} else {
+ GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj);
+
if (bclass->render_list)
- ret = bclass->render_list (basesink, GST_BUFFER_LIST_CAST (obj));
- }
+ ret = bclass->render_list (basesink, buffer_list);
- if (do_qos)
- gst_base_sink_do_render_stats (basesink, FALSE);
+ /* Set the first buffer and buffer list to be included in last sample */
+ gst_base_sink_set_last_buffer (basesink, sync_buf);
+ gst_base_sink_set_last_buffer_list (basesink, buffer_list);
+ }
if (ret == GST_FLOW_STEP)
goto again;
gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
return GST_FLOW_EOS;
}
+empty_list:
+ {
+ GST_DEBUG_OBJECT (basesink, "buffer list with no buffers");
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
+ return GST_FLOW_OK;
+ }
out_of_segment:
{
GST_DEBUG_OBJECT (basesink, "dropping buffer, out of clipping segment");
}
goto done;
}
+preroll_failed:
+ {
+ GST_DEBUG_OBJECT (basesink, "preroll failed: %s", gst_flow_get_name (ret));
+ return ret;
+ }
}
/* with STREAM_LOCK
guint i, len;
GstBuffer *buffer;
- GST_INFO_OBJECT (pad, "chaining each group in list as a merged buffer");
+ GST_LOG_OBJECT (pad, "chaining each buffer in list");
len = gst_buffer_list_length (list);
* seek format, adjust by the relative seek offset and then convert back to
* the processing format
*/
- GstSeekType cur_type, stop_type;
- gint64 cur, stop;
+ GstSeekType start_type, stop_type;
+ gint64 start, stop;
GstSeekFlags flags;
GstFormat seek_format;
gdouble rate;
gboolean res = TRUE;
gst_event_parse_seek (event, &rate, &seek_format, &flags,
- &cur_type, &cur, &stop_type, &stop);
+ &start_type, &start, &stop_type, &stop);
if (seek_format == segment->format) {
gst_segment_do_seek (segment, rate, seek_format, flags,
- cur_type, cur, stop_type, stop, &update);
+ start_type, start, stop_type, stop, &update);
return TRUE;
}
- if (cur_type != GST_SEEK_TYPE_NONE) {
- /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
+ if (start_type != GST_SEEK_TYPE_NONE) {
+ /* FIXME: Handle seek_end by converting the input segment vals */
res =
- gst_pad_query_convert (sink->sinkpad, seek_format, cur, segment->format,
- &cur);
- cur_type = GST_SEEK_TYPE_SET;
+ gst_pad_query_convert (sink->sinkpad, seek_format, start,
+ segment->format, &start);
+ start_type = GST_SEEK_TYPE_SET;
}
if (res && stop_type != GST_SEEK_TYPE_NONE) {
- /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
+ /* FIXME: Handle seek_end by converting the input segment vals */
res =
gst_pad_query_convert (sink->sinkpad, seek_format, stop,
segment->format, &stop);
}
/* And finally, configure our output segment in the desired format */
- gst_segment_do_seek (segment, rate, segment->format, flags, cur_type, cur,
+ gst_segment_do_seek (segment, rate, segment->format, flags, start_type, start,
stop_type, stop, &update);
if (!res)
gdouble rate;
GstFormat seek_format, dest_format;
GstSeekFlags flags;
- GstSeekType cur_type, stop_type;
+ GstSeekType start_type, stop_type;
gboolean seekseg_configured = FALSE;
- gint64 cur, stop;
+ gint64 start, stop;
gboolean update, res = TRUE;
GstSegment seeksegment;
if (event) {
GST_DEBUG_OBJECT (sink, "performing seek with event %p", event);
gst_event_parse_seek (event, &rate, &seek_format, &flags,
- &cur_type, &cur, &stop_type, &stop);
+ &start_type, &start, &stop_type, &stop);
flush = flags & GST_SEEK_FLAG_FLUSH;
} else {
/* The seek format matches our processing format, no need to ask the
* the subclass to configure the segment. */
gst_segment_do_seek (&seeksegment, rate, seek_format, flags,
- cur_type, cur, stop_type, stop, &update);
+ start_type, start, stop_type, stop, &update);
}
}
/* Else, no seek event passed, so we're just (re)starting the
res = gst_base_sink_default_do_seek (sink, &seeksegment);
}
-
if (flush) {
GST_DEBUG_OBJECT (sink, "stop flushing upstream");
gst_pad_push_event (pad, gst_event_new_flush_stop (TRUE));
res = FALSE;
}
+ GST_INFO_OBJECT (sink, "seeking done %d: %" GST_SEGMENT_FORMAT, res,
+ &seeksegment);
+
/* if successful seek, we update our real segment and push
* out the new segment. */
if (res) {
gst_segment_copy_into (&seeksegment, &sink->segment);
- if (sink->segment.flags & GST_SEEK_FLAG_SEGMENT) {
+ if (sink->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
gst_element_post_message (GST_ELEMENT (sink),
gst_message_new_segment_start (GST_OBJECT (sink),
sink->segment.format, sink->segment.position));
sink->playing_async = TRUE;
priv->pending_step.need_preroll = TRUE;
sink->need_preroll = FALSE;
+ gst_base_sink_update_start_time (sink);
gst_element_lost_state (GST_ELEMENT_CAST (sink));
} else {
sink->priv->have_latency = TRUE;
priv->eos_rtime = GST_CLOCK_TIME_NONE;
priv->call_preroll = TRUE;
gst_base_sink_set_last_buffer (sink, NULL);
+ gst_base_sink_set_last_buffer_list (sink, NULL);
gst_base_sink_reset_qos (sink);
if (sink->clock_id) {
gst_pad_pause_task (pad);
if (result == GST_FLOW_EOS) {
/* perform EOS logic */
- if (basesink->segment.flags & GST_SEEK_FLAG_SEGMENT) {
+ if (basesink->segment.flags & GST_SEGMENT_FLAG_SEGMENT) {
gst_element_post_message (GST_ELEMENT_CAST (basesink),
gst_message_new_segment_done (GST_OBJECT_CAST (basesink),
basesink->segment.format, basesink->segment.position));
+ gst_base_sink_event (pad, parent,
+ gst_event_new_segment_done (basesink->segment.format,
+ basesink->segment.position));
} else {
gst_base_sink_event (pad, parent, gst_event_new_eos ());
}
} 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;
result = FALSE;
/* this returns the intersection between our caps and the peer caps. If there
- * is no peer, it returns NULL and we can't operate in pull mode so we can
+ * is no peer, it returns %NULL and we can't operate in pull mode so we can
* fail the negotiation. */
caps = gst_pad_get_allowed_caps (GST_BASE_SINK_PAD (basesink));
if (caps == NULL || gst_caps_is_empty (caps))
}
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;
}
/* assume we will use the clock for getting the current position */
with_clock = TRUE;
- if (basesink->sync == FALSE)
+ if (!basesink->sync)
with_clock = FALSE;
/* and we need a clock */
rate = segment->rate * segment->applied_rate;
latency = basesink->priv->latency;
- if (oformat == GST_FORMAT_TIME) {
- gint64 start, stop;
-
- start = basesink->priv->current_sstart;
- stop = basesink->priv->current_sstop;
-
- if (in_paused) {
- /* in paused we use the last position as a lower bound */
- if (stop == -1 || segment->rate > 0.0)
- last = start;
- else
- last = stop;
- } else {
- /* in playing, use last stop time as upper bound */
- if (start == -1 || segment->rate > 0.0)
- last = stop;
- else
- last = start;
- }
- } else {
- /* convert last stop to stream time */
- last = gst_segment_to_stream_time (segment, oformat, segment->position);
- }
-
if (in_paused) {
/* in paused, use start_time */
base_time = GST_ELEMENT_START_TIME (basesink);
if (base_time == -1)
last_seen = TRUE;
+ if (oformat == GST_FORMAT_TIME) {
+ gint64 start, stop;
+
+ start = basesink->priv->current_sstart;
+ stop = basesink->priv->current_sstop;
+
+ if (last_seen) {
+ /* when we don't use the clock, we use the last position as a lower bound */
+ if (stop == -1 || segment->rate > 0.0)
+ last = start;
+ else
+ last = stop;
+
+ GST_DEBUG_OBJECT (basesink, "in PAUSED using last %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (last));
+ } else {
+ /* in playing and paused, use last stop time as upper bound */
+ if (start == -1 || segment->rate > 0.0)
+ last = stop;
+ else
+ last = start;
+
+ GST_DEBUG_OBJECT (basesink, "in PLAYING using last %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (last));
+ }
+ } else {
+ /* convert position to stream time */
+ last = gst_segment_to_stream_time (segment, oformat, segment->position);
+
+ GST_DEBUG_OBJECT (basesink, "in using last %" G_GINT64_FORMAT, last);
+ }
+
/* need to release the object lock before we can get the time,
* a clock might take the LOCK of the provider, which could be
* a basesink subclass. */
*cur = time + gst_guint64_to_gdouble (now - base_time) * rate;
- if (in_paused) {
- /* never report less than segment values in paused */
- if (last != -1)
- *cur = MAX (last, *cur);
- } else {
- /* never report more than last seen position in playing */
- if (last != -1)
+ /* never report more than last seen position */
+ if (last != -1) {
+ if (rate > 0.0)
*cur = MIN (last, *cur);
+ else
+ *cur = MAX (last, *cur);
}
GST_DEBUG_OBJECT (basesink,
case GST_QUERY_SEGMENT:
{
if (basesink->pad_mode == GST_PAD_MODE_PULL) {
- gst_query_set_segment (query, basesink->segment.rate,
- GST_FORMAT_TIME, basesink->segment.start, basesink->segment.stop);
+ GstFormat format;
+ gint64 start, stop;
+
+ format = basesink->segment.format;
+
+ start =
+ gst_segment_to_stream_time (&basesink->segment, format,
+ basesink->segment.start);
+ if ((stop = basesink->segment.stop) == -1)
+ stop = basesink->segment.duration;
+ else
+ stop = gst_segment_to_stream_time (&basesink->segment, format, stop);
+
+ gst_query_set_segment (query, basesink->segment.rate, format, start,
+ stop);
res = TRUE;
} else {
res = gst_pad_peer_query (basesink->sinkpad, query);
return res;
}
+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
gst_base_sink_default_query (GstBaseSink * basesink, GstQuery * query)
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_ALLOCATION:
{
+ gst_base_sink_drain (basesink);
if (bclass->propose_allocation)
res = bclass->propose_allocation (basesink, query);
else
gst_query_parse_accept_caps (query, &caps);
allowed = gst_base_sink_query_caps (basesink, basesink->sinkpad, NULL);
subset = gst_caps_is_subset (caps, allowed);
+ GST_DEBUG_OBJECT (basesink, "Checking if requested caps %" GST_PTR_FORMAT
+ " are a subset of pad caps %" GST_PTR_FORMAT " result %d", caps,
+ allowed, subset);
gst_caps_unref (allowed);
gst_query_set_accept_caps_result (query, subset);
res = TRUE;
break;
}
case GST_QUERY_DRAIN:
+ {
+ gst_base_sink_drain (basesink);
res = TRUE;
break;
+ }
+ case GST_QUERY_POSITION:
+ {
+ res = default_element_query (GST_ELEMENT (basesink), query);
+ break;
+ }
default:
res =
gst_pad_query_default (basesink->sinkpad, GST_OBJECT_CAST (basesink),
basesink->eos = FALSE;
priv->received_eos = FALSE;
gst_base_sink_reset_qos (basesink);
+ priv->rc_next = -1;
priv->commited = FALSE;
priv->call_preroll = TRUE;
priv->current_step.valid = FALSE;
if (bclass->unlock_stop)
bclass->unlock_stop (basesink);
- /* we need preroll again and we set the flag before unlocking the clockid
- * because if the clockid is unlocked before a current buffer expired, we
- * can use that buffer to preroll with */
- basesink->need_preroll = TRUE;
-
if (basesink->clock_id) {
GST_DEBUG_OBJECT (basesink, "unschedule clock");
gst_clock_id_unschedule (basesink->clock_id);
if (!gst_base_sink_needs_preroll (basesink)) {
GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED, we are prerolled");
basesink->playing_async = FALSE;
+ basesink->need_preroll = FALSE;
} else {
if (GST_STATE_TARGET (GST_ELEMENT (basesink)) <= GST_STATE_READY) {
GST_DEBUG_OBJECT (basesink, "element is <= READY");
GST_DEBUG_OBJECT (basesink,
"PLAYING to PAUSED, we are not prerolled");
basesink->playing_async = TRUE;
+ basesink->need_preroll = TRUE;
priv->commited = FALSE;
priv->call_preroll = TRUE;
if (priv->async_enabled) {
GST_OBJECT_UNLOCK (basesink);
gst_base_sink_set_last_buffer (basesink, NULL);
+ gst_base_sink_set_last_buffer_list (basesink, NULL);
priv->call_preroll = FALSE;
if (!priv->commited) {
}
}
gst_base_sink_set_last_buffer (basesink, NULL);
+ gst_base_sink_set_last_buffer_list (basesink, NULL);
priv->call_preroll = FALSE;
break;
default:
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: