/**
* SECTION:gstbasesink
* @short_description: Base class for sink elements
- * @see_also: #GstBaseTransform, #GstBaseSource
+ * @see_also: #GstBaseTransform, #GstBaseSrc
*
* #GstBaseSink is the base class for sink elements in GStreamer, such as
* xvimagesink or filesink. It is a layer on top of #GstElement that provides a
* #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 base_init function, like so:
- * <programlisting>
+ * |[
* static void
* my_element_base_init (gpointer g_class)
* {
* GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
- *
+ *
* // sinktemplate should be a #GstStaticPadTemplate with direction
* // #GST_PAD_SINK and name "sink"
* gst_element_class_add_pad_template (gstelement_class,
* // see #GstElementDetails
* gst_element_class_set_details (gstelement_class, &details);
* }
- * </programlisting>
+ * ]|
*
* #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
* buffer arrives in this element. The base class will call the
- * #GstBaseSink::preroll vmethod with this preroll buffer and will then commit
- * the state change to the next asynchronously pending state.
+ * #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 ::get_times. If this function returns
- * #GST_CLOCK_TIME_NONE for the start time, no synchronisation will be done.
- * Synchronisation can be disabled entirely by setting the object "sync"
- * property to %FALSE.
+ * clock using the times returned from #GstBaseSinkClass.get_times(). If this
+ * 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 #GstBaseSink::render will be called.
- * Subclasses should minimally implement this method.
+ * 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 ::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.
+ * 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.
*
* 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
- * ::get_times call before posting an EOS message. When the element receives
- * EOS in PAUSED, preroll completes, the event is queued and an EOS message is
- * posted when going to PLAYING.
+ * #GstBaseSinkClass.get_times() call before posting an EOS message. When the
+ * 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
* synchronisation and clipping of buffers. Buffers that fall completely outside
* If no clock has been set on the element, the query will be forwarded
* upstream.
*
- * The ::set_caps function will be called when the subclass should configure
- * itself to process a specific media type.
+ * The #GstBaseSinkClass.set_caps() function will be called when the subclass
+ * should configure itself to process a specific media type.
*
- * The ::start and ::stop virtual methods will be called when resources should
- * be allocated. Any ::preroll, ::render and ::set_caps function will be
- * called between the ::start and ::stop calls.
+ * The #GstBaseSinkClass.start() and #GstBaseSinkClass.stop() virtual methods
+ * 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 ::event virtual method will be called when an event is received by
- * #GstBaseSink. Normally this method should only be overriden by very specific
- * elements (such as file sinks) which need to handle the newsegment event
- * specially.
+ * The #GstBaseSinkClass.event() virtual method will be called when an event is
+ * received by #GstBaseSink. Normally this method should only be overriden by
+ * very specific elements (such as file sinks) which need to handle the
+ * newsegment event specially.
*
- * #GstBaseSink provides an overridable ::buffer_alloc function that can be
- * used by sinks that want to do reverse negotiation or to provide
- * custom buffers (hardware buffers for example) to upstream elements.
+ * #GstBaseSink provides an overridable #GstBaseSinkClass.buffer_alloc()
+ * function that can be used by sinks that want to do reverse negotiation or to
+ * provide custom buffers (hardware buffers for example) to upstream elements.
*
- * The ::unlock method is called when the elements should unblock any blocking
- * operations they perform in the ::render method. This is mostly useful when
- * the ::render method performs a blocking write on a file descriptor, for
- * example.
+ * The #GstBaseSinkClass.unlock() method is called when the elements should
+ * unblock any blocking operations they perform in the
+ * #GstBaseSinkClass.render() method. This is mostly useful when the
+ * #GstBaseSinkClass.render() method performs a blocking write on a file
+ * descriptor, for example.
*
- * The max-lateness property affects how the sink deals with buffers that
- * arrive too late in the sink. A buffer arrives too late in the sink when
- * the presentation time (as a combination of the last segment, buffer
+ * The #GstBaseSink:max-lateness property affects how the sink deals with
+ * buffers that arrive too late in the sink. A buffer arrives too late in the
+ * sink when the presentation time (as a combination of the last segment, buffer
* timestamp and element base_time) plus the duration is before the current
* time of the clock.
* If the frame is later than max-lateness, the sink will drop the buffer
* without calling the render method.
- * This feature is disabled if sync is disabled, the ::get-times method does
- * not return a valid start time or max-lateness is set to -1 (the default).
+ * This feature is disabled if sync is disabled, the
+ * #GstBaseSinkClass.get_times() method does not return a valid start time or
+ * max-lateness is set to -1 (the default).
* Subclasses can use gst_base_sink_set_max_lateness() to configure the
* max-lateness value.
*
- * The qos property will enable the quality-of-service features of the basesink
- * which gather statistics about the real-time performance of the clock
- * synchronisation. For each buffer received in the sink, statistics are
+ * The #GstBaseSink:qos property will enable the quality-of-service features of
+ * the basesink which gather statistics about the real-time performance of the
+ * clock synchronisation. For each buffer received in the sink, statistics are
* gathered and a QOS event is sent upstream with these numbers. This
* information can then be used by upstream elements to reduce their processing
* rate, for example.
*
- * Since 0.10.15 the 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.
+ * 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)
*/
# include "config.h"
#endif
+#include <gst/gst_private.h>
+
#include "gstbasesink.h"
#include <gst/gstmarshal.h>
-#include <gst/gst_private.h>
#include <gst/gst-i18n-lib.h>
GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
#define GST_BASE_SINK_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BASE_SINK, GstBaseSinkPrivate))
+#define GST_FLOW_STEP GST_FLOW_CUSTOM_ERROR
+
+typedef struct
+{
+ gboolean valid; /* if this info is valid */
+ guint32 seqnum; /* the seqnum of the STEP event */
+ GstFormat format; /* the format of the amount */
+ guint64 amount; /* the total amount of data to skip */
+ guint64 position; /* the position in the stepped data */
+ guint64 duration; /* the duration in time of the skipped data */
+ guint64 start; /* running_time of the start */
+ gdouble rate; /* rate of skipping */
+ gdouble start_rate; /* rate before skipping */
+ guint64 start_start; /* start position skipping */
+ guint64 start_stop; /* stop position skipping */
+ gboolean flush; /* if this was a flushing step */
+ gboolean intermediate; /* if this is an intermediate step */
+ gboolean need_preroll; /* if we need preroll after this step */
+} GstStepInfo;
+
/* FIXME, some stuff in ABI.data and other in Private...
* Make up your mind please.
*/
gboolean have_latency;
/* the last buffer we prerolled or rendered. Useful for making snapshots */
+ gint enable_last_buffer; /* atomic */
GstBuffer *last_buffer;
+
+ /* caps for pull based scheduling */
+ GstCaps *pull_caps;
+
+ /* blocksize for pulling */
+ guint blocksize;
+
+ gboolean discont;
+
+ /* seqnum of the stream */
+ guint32 seqnum;
+
+ gboolean call_preroll;
+ gboolean step_unlock;
+
+ /* we have a pending and a current step operation */
+ GstStepInfo current_step;
+ GstStepInfo pending_step;
+
+ /* Cached GstClockID */
+ GstClockID cached_clock_id;
};
#define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size))
#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)
+enum
+{
+ _PR_IS_NOTHING = 1 << 0,
+ _PR_IS_BUFFER = 1 << 1,
+ _PR_IS_BUFFERLIST = 1 << 2,
+ _PR_IS_EVENT = 1 << 3
+} PrivateObjectType;
+
+#define OBJ_IS_BUFFER(a) ((a) & _PR_IS_BUFFER)
+#define OBJ_IS_BUFFERLIST(a) ((a) & _PR_IS_BUFFERLIST)
+#define OBJ_IS_EVENT(a) ((a) & _PR_IS_EVENT)
+#define OBJ_IS_BUFFERFULL(a) ((a) & (_PR_IS_BUFFER | _PR_IS_BUFFERLIST))
+
/* BaseSink properties */
-#define DEFAULT_SIZE 1024
#define DEFAULT_CAN_ACTIVATE_PULL FALSE /* fixme: enable me */
#define DEFAULT_CAN_ACTIVATE_PUSH TRUE
-#define DEFAULT_PREROLL_QUEUE_LEN 0
-#define DEFAULT_SYNC TRUE
-#define DEFAULT_MAX_LATENESS -1
-#define DEFAULT_QOS FALSE
-#define DEFAULT_ASYNC TRUE
-#define DEFAULT_TS_OFFSET 0
+#define DEFAULT_PREROLL_QUEUE_LEN 0
+#define DEFAULT_SYNC TRUE
+#define DEFAULT_MAX_LATENESS -1
+#define DEFAULT_QOS FALSE
+#define DEFAULT_ASYNC TRUE
+#define DEFAULT_TS_OFFSET 0
+#define DEFAULT_BLOCKSIZE 4096
+#define DEFAULT_RENDER_DELAY 0
+#define DEFAULT_ENABLE_LAST_BUFFER TRUE
enum
{
PROP_QOS,
PROP_ASYNC,
PROP_TS_OFFSET,
+ PROP_ENABLE_LAST_BUFFER,
PROP_LAST_BUFFER,
+ PROP_BLOCKSIZE,
+ PROP_RENDER_DELAY,
PROP_LAST
};
GType
gst_base_sink_get_type (void)
{
- static GType base_sink_type = 0;
+ static volatile gsize base_sink_type = 0;
- if (G_UNLIKELY (base_sink_type == 0)) {
+ if (g_once_init_enter (&base_sink_type)) {
+ GType _type;
static const GTypeInfo base_sink_info = {
sizeof (GstBaseSinkClass),
NULL,
(GInstanceInitFunc) gst_base_sink_init,
};
- base_sink_type = g_type_register_static (GST_TYPE_ELEMENT,
+ _type = g_type_register_static (GST_TYPE_ELEMENT,
"GstBaseSink", &base_sink_info, G_TYPE_FLAG_ABSTRACT);
+ g_once_init_leave (&base_sink_type, _type);
}
return base_sink_type;
}
static gboolean gst_base_sink_send_event (GstElement * element,
GstEvent * event);
static gboolean gst_base_sink_query (GstElement * element, GstQuery * query);
+static const GstQueryType *gst_base_sink_get_query_types (GstElement * element);
static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink);
static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
GstPad * pad, gboolean flushing);
static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink,
gboolean active);
+static gboolean gst_base_sink_default_do_seek (GstBaseSink * sink,
+ GstSegment * segment);
+static gboolean gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
+ GstEvent * event, GstSegment * segment);
static GstStateChangeReturn gst_base_sink_change_state (GstElement * element,
GstStateChange transition);
static GstFlowReturn gst_base_sink_chain (GstPad * pad, GstBuffer * buffer);
+static GstFlowReturn gst_base_sink_chain_list (GstPad * pad,
+ GstBufferList * list);
+
static void gst_base_sink_loop (GstPad * pad);
static gboolean gst_base_sink_pad_activate (GstPad * pad);
static gboolean gst_base_sink_pad_activate_push (GstPad * pad, gboolean active);
static gboolean gst_base_sink_pad_activate_pull (GstPad * pad, gboolean active);
static gboolean gst_base_sink_event (GstPad * pad, GstEvent * event);
-static gboolean gst_base_sink_peer_query (GstBaseSink * sink, GstQuery * query);
+
+static gboolean gst_base_sink_negotiate_pull (GstBaseSink * basesink);
+static GstCaps *gst_base_sink_pad_getcaps (GstPad * pad);
+static gboolean gst_base_sink_pad_setcaps (GstPad * pad, GstCaps * caps);
+static void gst_base_sink_pad_fixate (GstPad * pad, GstCaps * caps);
+static GstFlowReturn gst_base_sink_pad_buffer_alloc (GstPad * pad,
+ guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
+
/* check if an object was too late */
static gboolean gst_base_sink_is_too_late (GstBaseSink * basesink,
GstMiniObject * obj, GstClockTime start, GstClockTime stop,
GstClockReturn status, GstClockTimeDiff jitter);
+static GstFlowReturn gst_base_sink_preroll_object (GstBaseSink * basesink,
+ guint8 obj_type, GstMiniObject * obj);
static void
gst_base_sink_class_init (GstBaseSinkClass * klass)
parent_class = g_type_class_peek_parent (klass);
- gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_base_sink_finalize);
- gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_base_sink_set_property);
- gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_base_sink_get_property);
+ gobject_class->finalize = gst_base_sink_finalize;
+ gobject_class->set_property = gst_base_sink_set_property;
+ gobject_class->get_property = gst_base_sink_get_property;
/* FIXME, this next value should be configured using an event from the
* upstream element, ie, the BUFFER_SIZE event. */
* GstBaseSink:ts-offset
*
* Controls the final synchronisation, a negative value will render the buffer
- * earlier while a positive value delays playback. This property can be
+ * earlier while a positive value delays playback. This property can be
* used to fix synchronisation in bad files.
*
* Since: 0.10.15
DEFAULT_TS_OFFSET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
+ * GstBaseSink:enable-last-buffer
+ *
+ * Enable the last-buffer property. If FALSE, basesink doesn't keep a
+ * reference to the last buffer arrived and the last-buffer property is always
+ * 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_BUFFER,
+ g_param_spec_boolean ("enable-last-buffer", "Enable Last Buffer",
+ "Enable the last-buffer property", DEFAULT_ENABLE_LAST_BUFFER,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ /**
* GstBaseSink:last-buffer
*
* The last buffer that arrived in the sink and was used for preroll or for
gst_param_spec_mini_object ("last-buffer", "Last Buffer",
"The last buffer received in the sink", GST_TYPE_BUFFER,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstBaseSink:blocksize
+ *
+ * The amount of bytes to pull when operating in pull mode.
+ *
+ * Since: 0.10.22
+ */
+ 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,
+ DEFAULT_BLOCKSIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstBaseSink:render-delay
+ *
+ * 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",
+ "Additional render delay of the sink in nanoseconds", 0, G_MAXUINT64,
+ DEFAULT_RENDER_DELAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_base_sink_change_state);
gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event);
gstelement_class->query = GST_DEBUG_FUNCPTR (gst_base_sink_query);
+ gstelement_class->get_query_types =
+ GST_DEBUG_FUNCPTR (gst_base_sink_get_query_types);
klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_get_caps);
klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps);
klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_get_times);
klass->activate_pull =
GST_DEBUG_FUNCPTR (gst_base_sink_default_activate_pull);
+
+ /* Registering debug symbols for function pointers */
+ GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_getcaps);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_setcaps);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_fixate);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_buffer_alloc);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_push);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_pull);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_event);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain);
+ GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain_list);
}
static GstCaps *
bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
bclass = GST_BASE_SINK_GET_CLASS (bsink);
- if (bclass->get_caps)
- caps = bclass->get_caps (bsink);
+ if (bsink->pad_mode == GST_ACTIVATE_PULL) {
+ /* if we are operating in pull mode we only accept the negotiated caps */
+ GST_OBJECT_LOCK (pad);
+ if ((caps = GST_PAD_CAPS (pad)))
+ gst_caps_ref (caps);
+ GST_OBJECT_UNLOCK (pad);
+ }
if (caps == NULL) {
- GstPadTemplate *pad_template;
+ if (bclass->get_caps)
+ caps = bclass->get_caps (bsink);
+
+ if (caps == NULL) {
+ GstPadTemplate *pad_template;
- pad_template =
- gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink");
- if (pad_template != NULL) {
- caps = gst_caps_ref (gst_pad_template_get_caps (pad_template));
+ pad_template =
+ gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass),
+ "sink");
+ if (pad_template != NULL) {
+ caps = gst_caps_ref (gst_pad_template_get_caps (pad_template));
+ }
}
}
gst_object_unref (bsink);
bsink = GST_BASE_SINK (gst_pad_get_parent (pad));
bclass = GST_BASE_SINK_GET_CLASS (bsink);
- if (bsink->pad_mode == GST_ACTIVATE_PULL) {
- GstPad *peer = gst_pad_get_peer (pad);
-
- if (peer)
- res = gst_pad_set_caps (peer, caps);
- else
- res = FALSE;
-
- if (!res)
- GST_DEBUG_OBJECT (bsink, "peer setcaps() failed");
- }
-
if (res && bclass->set_caps)
res = bclass->set_caps (bsink, caps);
basesink->sinkpad = gst_pad_new_from_template (pad_template, "sink");
- gst_pad_set_getcaps_function (basesink->sinkpad,
- GST_DEBUG_FUNCPTR (gst_base_sink_pad_getcaps));
- gst_pad_set_setcaps_function (basesink->sinkpad,
- GST_DEBUG_FUNCPTR (gst_base_sink_pad_setcaps));
- gst_pad_set_fixatecaps_function (basesink->sinkpad,
- GST_DEBUG_FUNCPTR (gst_base_sink_pad_fixate));
+ gst_pad_set_getcaps_function (basesink->sinkpad, gst_base_sink_pad_getcaps);
+ gst_pad_set_setcaps_function (basesink->sinkpad, gst_base_sink_pad_setcaps);
+ gst_pad_set_fixatecaps_function (basesink->sinkpad, gst_base_sink_pad_fixate);
gst_pad_set_bufferalloc_function (basesink->sinkpad,
- GST_DEBUG_FUNCPTR (gst_base_sink_pad_buffer_alloc));
- gst_pad_set_activate_function (basesink->sinkpad,
- GST_DEBUG_FUNCPTR (gst_base_sink_pad_activate));
+ gst_base_sink_pad_buffer_alloc);
+ gst_pad_set_activate_function (basesink->sinkpad, gst_base_sink_pad_activate);
gst_pad_set_activatepush_function (basesink->sinkpad,
- GST_DEBUG_FUNCPTR (gst_base_sink_pad_activate_push));
+ gst_base_sink_pad_activate_push);
gst_pad_set_activatepull_function (basesink->sinkpad,
- GST_DEBUG_FUNCPTR (gst_base_sink_pad_activate_pull));
- gst_pad_set_event_function (basesink->sinkpad,
- GST_DEBUG_FUNCPTR (gst_base_sink_event));
- gst_pad_set_chain_function (basesink->sinkpad,
- GST_DEBUG_FUNCPTR (gst_base_sink_chain));
+ gst_base_sink_pad_activate_pull);
+ gst_pad_set_event_function (basesink->sinkpad, gst_base_sink_event);
+ gst_pad_set_chain_function (basesink->sinkpad, gst_base_sink_chain);
+ gst_pad_set_chain_list_function (basesink->sinkpad, gst_base_sink_chain_list);
gst_element_add_pad (GST_ELEMENT_CAST (basesink), basesink->sinkpad);
basesink->pad_mode = GST_ACTIVATE_NONE;
g_atomic_int_set (&priv->qos_enabled, DEFAULT_QOS);
priv->async_enabled = DEFAULT_ASYNC;
priv->ts_offset = DEFAULT_TS_OFFSET;
- priv->render_delay = 0;
+ priv->render_delay = DEFAULT_RENDER_DELAY;
+ priv->blocksize = DEFAULT_BLOCKSIZE;
+ priv->cached_clock_id = NULL;
+ g_atomic_int_set (&priv->enable_last_buffer, DEFAULT_ENABLE_LAST_BUFFER);
GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_IS_SINK);
}
g_return_if_fail (GST_IS_BASE_SINK (sink));
GST_PAD_PREROLL_LOCK (sink->sinkpad);
- sink->priv->async_enabled = enabled;
+ g_atomic_int_set (&sink->priv->async_enabled, enabled);
GST_LOG_OBJECT (sink, "set async enabled to %d", enabled);
GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
}
g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
- GST_PAD_PREROLL_LOCK (sink->sinkpad);
- res = sink->priv->async_enabled;
- GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
+ res = g_atomic_int_get (&sink->priv->async_enabled);
return res;
}
* rendering. This property can be used to generate thumbnails.
*
* The #GstCaps on the buffer can be used to determine the type of the buffer.
- *
+ *
* Returns: a #GstBuffer. gst_buffer_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.
return res;
}
+/* with OBJECT_LOCK */
static void
-gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
+gst_base_sink_set_last_buffer_unlocked (GstBaseSink * sink, GstBuffer * buffer)
{
GstBuffer *old;
- if (buffer)
- gst_buffer_ref (buffer);
+ old = sink->priv->last_buffer;
+ if (G_LIKELY (old != buffer)) {
+ GST_DEBUG_OBJECT (sink, "setting last buffer to %p", buffer);
+ if (G_LIKELY (buffer))
+ gst_buffer_ref (buffer);
+ sink->priv->last_buffer = buffer;
+ } 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_buffer_unref (old);
+ GST_OBJECT_LOCK (sink);
+ }
+}
+
+static void
+gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer)
+{
+ if (!g_atomic_int_get (&sink->priv->enable_last_buffer))
+ return;
GST_OBJECT_LOCK (sink);
- old = sink->priv->last_buffer;
- sink->priv->last_buffer = buffer;
+ gst_base_sink_set_last_buffer_unlocked (sink, buffer);
GST_OBJECT_UNLOCK (sink);
+}
- if (old)
- gst_buffer_unref (old);
+/**
+ * gst_base_sink_set_last_buffer_enabled:
+ * @sink: the sink
+ * @enabled: the new enable-last-buffer value.
+ *
+ * Configures @sink to store the last received buffer in the last-buffer
+ * property.
+ *
+ * Since: 0.10.30
+ */
+void
+gst_base_sink_set_last_buffer_enabled (GstBaseSink * sink, gboolean enabled)
+{
+ g_return_if_fail (GST_IS_BASE_SINK (sink));
+
+ /* Only take lock if we change the value */
+ if (g_atomic_int_compare_and_exchange (&sink->priv->enable_last_buffer,
+ !enabled, enabled) && !enabled) {
+ GST_OBJECT_LOCK (sink);
+ gst_base_sink_set_last_buffer_unlocked (sink, NULL);
+ GST_OBJECT_UNLOCK (sink);
+ }
+}
+
+/**
+ * gst_base_sink_is_last_buffer_enabled:
+ * @sink: the sink
+ *
+ * Checks if @sink is currently configured to store the last received buffer in
+ * the last-buffer property.
+ *
+ * Returns: TRUE if the sink is configured to store the last received buffer.
+ *
+ * Since: 0.10.30
+ */
+gboolean
+gst_base_sink_is_last_buffer_enabled (GstBaseSink * sink)
+{
+ g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE);
+
+ return g_atomic_int_get (&sink->priv->enable_last_buffer);
}
/**
* 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
- * element is live.
+ * element is live.
*
* 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.
*
- * This function is mostly used by subclasses.
+ * This function is mostly used by subclasses.
*
* Returns: TRUE if the query succeeded.
*
query = gst_query_new_latency ();
/* ask the peer for the latency */
- if ((res = gst_base_sink_peer_query (sink, query))) {
+ if ((res = gst_pad_peer_query (sink->sinkpad, query))) {
/* get upstream min and max latency */
gst_query_parse_latency (query, &us_live, &us_min, &us_max);
* @sink: a #GstBaseSink
* @delay: the new delay
*
- * Set the render delay in @sink to @delay. The render delay is the time
+ * Set the render delay in @sink to @delay. The render delay is the time
* between actual rendering of a buffer and its synchronisation time. Some
* devices might delay media rendering which can be compensated for with this
- * function.
+ * function.
*
* After calling this function, this sink will report additional latency and
* other sinks will adjust their latency to delay the rendering of their media.
void
gst_base_sink_set_render_delay (GstBaseSink * sink, GstClockTime delay)
{
+ GstClockTime old_render_delay;
+
g_return_if_fail (GST_IS_BASE_SINK (sink));
GST_OBJECT_LOCK (sink);
+ old_render_delay = sink->priv->render_delay;
sink->priv->render_delay = delay;
GST_LOG_OBJECT (sink, "set render delay to %" GST_TIME_FORMAT,
GST_TIME_ARGS (delay));
GST_OBJECT_UNLOCK (sink);
+
+ if (delay != old_render_delay) {
+ GST_DEBUG_OBJECT (sink, "posting latency changed");
+ gst_element_post_message (GST_ELEMENT_CAST (sink),
+ gst_message_new_latency (GST_OBJECT_CAST (sink)));
+ }
}
/**
return res;
}
+/**
+ * gst_base_sink_set_blocksize:
+ * @sink: a #GstBaseSink
+ * @blocksize: the blocksize in bytes
+ *
+ * Set the number of bytes that the sink will pull when it is operating in pull
+ * mode.
+ *
+ * Since: 0.10.22
+ */
+void
+gst_base_sink_set_blocksize (GstBaseSink * sink, guint blocksize)
+{
+ g_return_if_fail (GST_IS_BASE_SINK (sink));
+
+ GST_OBJECT_LOCK (sink);
+ sink->priv->blocksize = blocksize;
+ GST_LOG_OBJECT (sink, "set blocksize to %u", blocksize);
+ GST_OBJECT_UNLOCK (sink);
+}
+
+/**
+ * gst_base_sink_get_blocksize:
+ * @sink: a #GstBaseSink
+ *
+ * Get the number of bytes that the sink will pull when it is operating in pull
+ * mode.
+ *
+ * Returns: the number of bytes @sink will pull in pull mode.
+ *
+ * Since: 0.10.22
+ */
+guint
+gst_base_sink_get_blocksize (GstBaseSink * sink)
+{
+ guint res;
+
+ g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0);
+
+ GST_OBJECT_LOCK (sink);
+ res = sink->priv->blocksize;
+ 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_PREROLL_QUEUE_LEN:
/* preroll lock necessary to serialize with finish_preroll */
GST_PAD_PREROLL_LOCK (sink->sinkpad);
- sink->preroll_queue_max_len = g_value_get_uint (value);
+ g_atomic_int_set (&sink->preroll_queue_max_len, g_value_get_uint (value));
GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
break;
case PROP_SYNC:
case PROP_TS_OFFSET:
gst_base_sink_set_ts_offset (sink, g_value_get_int64 (value));
break;
+ case PROP_BLOCKSIZE:
+ gst_base_sink_set_blocksize (sink, g_value_get_uint (value));
+ break;
+ case PROP_RENDER_DELAY:
+ gst_base_sink_set_render_delay (sink, g_value_get_uint64 (value));
+ break;
+ case PROP_ENABLE_LAST_BUFFER:
+ gst_base_sink_set_last_buffer_enabled (sink, g_value_get_boolean (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
switch (prop_id) {
case PROP_PREROLL_QUEUE_LEN:
- GST_PAD_PREROLL_LOCK (sink->sinkpad);
- g_value_set_uint (value, sink->preroll_queue_max_len);
- GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
+ g_value_set_uint (value, g_atomic_int_get (&sink->preroll_queue_max_len));
break;
case PROP_SYNC:
g_value_set_boolean (value, gst_base_sink_get_sync (sink));
case PROP_LAST_BUFFER:
gst_value_take_buffer (value, gst_base_sink_get_last_buffer (sink));
break;
+ case PROP_ENABLE_LAST_BUFFER:
+ g_value_set_boolean (value, gst_base_sink_is_last_buffer_enabled (sink));
+ break;
+ case PROP_BLOCKSIZE:
+ g_value_set_uint (value, gst_base_sink_get_blocksize (sink));
+ break;
+ case PROP_RENDER_DELAY:
+ g_value_set_uint64 (value, gst_base_sink_get_render_delay (sink));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
basesink->eos = FALSE;
basesink->priv->received_eos = FALSE;
basesink->have_preroll = FALSE;
+ basesink->priv->step_unlock = FALSE;
basesink->eos_queued = FALSE;
basesink->preroll_queued = 0;
basesink->buffers_queued = 0;
gboolean post_paused = FALSE;
gboolean post_async_done = FALSE;
gboolean post_playing = FALSE;
- gboolean sync;
/* we are certainly not playing async anymore now */
basesink->playing_async = FALSE;
next = GST_STATE_NEXT (basesink);
pending = GST_STATE_PENDING (basesink);
post_pending = pending;
- sync = basesink->sync;
switch (pending) {
case GST_STATE_PLAYING:
/* make sure we notify the subclass of async playing */
if (bclass->async_play) {
+ GST_WARNING_OBJECT (basesink, "deprecated async_play");
ret = bclass->async_play (basesink);
if (ret == GST_STATE_CHANGE_FAILURE)
goto async_failed;
}
}
+static void
+start_stepping (GstBaseSink * sink, GstSegment * segment,
+ GstStepInfo * pending, GstStepInfo * current)
+{
+ gint64 end;
+ GstMessage *message;
+
+ GST_DEBUG_OBJECT (sink, "update pending step");
+
+ GST_OBJECT_LOCK (sink);
+ memcpy (current, pending, sizeof (GstStepInfo));
+ pending->valid = FALSE;
+ GST_OBJECT_UNLOCK (sink);
+
+ /* post message first */
+ message =
+ gst_message_new_step_start (GST_OBJECT (sink), TRUE, current->format,
+ current->amount, current->rate, current->flush, current->intermediate);
+ gst_message_set_seqnum (message, current->seqnum);
+ gst_element_post_message (GST_ELEMENT (sink), message);
+
+ /* get the running time of where we paused and remember it */
+ current->start = gst_element_get_start_time (GST_ELEMENT_CAST (sink));
+ gst_segment_set_running_time (segment, GST_FORMAT_TIME, current->start);
+
+ /* set the new rate for the remainder of the segment */
+ current->start_rate = segment->rate;
+ segment->rate *= current->rate;
+ segment->abs_rate = ABS (segment->rate);
+
+ /* save values */
+ if (segment->rate > 0.0)
+ current->start_stop = segment->stop;
+ else
+ current->start_start = segment->start;
+
+ if (current->format == GST_FORMAT_TIME) {
+ end = current->start + current->amount;
+ if (!current->flush) {
+ /* 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->last_stop = segment->stop;
+ } else {
+ gint64 position;
+
+ position = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
+ segment->time = position;
+ segment->start = position;
+ segment->last_stop = position;
+ }
+ }
+ }
+
+ GST_DEBUG_OBJECT (sink,
+ "segment now rate %lf, applied rate %lf, "
+ "format GST_FORMAT_TIME, "
+ "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
+ ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT,
+ segment->rate, segment->applied_rate, GST_TIME_ARGS (segment->start),
+ GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time),
+ GST_TIME_ARGS (segment->accum));
+
+ 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);
+ }
+}
+
+static void
+stop_stepping (GstBaseSink * sink, GstSegment * segment,
+ GstStepInfo * current, gint64 rstart, gint64 rstop, gboolean eos)
+{
+ gint64 stop, position;
+ GstMessage *message;
+
+ GST_DEBUG_OBJECT (sink, "step complete");
+
+ if (segment->rate > 0.0)
+ stop = rstart;
+ else
+ stop = rstop;
+
+ GST_DEBUG_OBJECT (sink,
+ "step stop at running_time %" GST_TIME_FORMAT, GST_TIME_ARGS (stop));
+
+ if (stop == -1)
+ current->duration = current->position;
+ else
+ current->duration = stop - current->start;
+
+ GST_DEBUG_OBJECT (sink, "step elapsed running_time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (current->duration));
+
+ position = current->start + current->duration;
+
+ /* now move the segment to the new running time */
+ gst_segment_set_running_time (segment, GST_FORMAT_TIME, position);
+
+ if (current->flush) {
+ /* and remove the accumulated time we flushed, start time did not change */
+ segment->accum = current->start;
+ } else {
+ /* start time is now the stepped position */
+ gst_element_set_start_time (GST_ELEMENT_CAST (sink), position);
+ }
+
+ /* restore the previous rate */
+ segment->rate = current->start_rate;
+ segment->abs_rate = ABS (segment->rate);
+
+ if (segment->rate > 0.0)
+ segment->stop = current->start_stop;
+ else
+ segment->start = current->start_start;
+
+ /* the clip segment is used for position report in paused... */
+ memcpy (sink->abidata.ABI.clip_segment, segment, sizeof (GstSegment));
+
+ /* post the step done when we know the stepped duration in TIME */
+ message =
+ gst_message_new_step_done (GST_OBJECT_CAST (sink), current->format,
+ current->amount, current->rate, current->flush, current->intermediate,
+ current->duration, eos);
+ gst_message_set_seqnum (message, current->seqnum);
+ gst_element_post_message (GST_ELEMENT_CAST (sink), message);
+
+ if (!current->intermediate)
+ sink->need_preroll = current->need_preroll;
+
+ /* and the current step info finished and becomes invalid */
+ current->valid = FALSE;
+}
+
+static gboolean
+handle_stepping (GstBaseSink * sink, GstSegment * segment,
+ GstStepInfo * current, gint64 * cstart, gint64 * cstop, gint64 * rstart,
+ gint64 * rstop)
+{
+ gboolean step_end = FALSE;
+
+ /* see if we need to skip this buffer because of stepping */
+ switch (current->format) {
+ case GST_FORMAT_TIME:
+ {
+ guint64 end;
+ gint64 first, last;
+
+ if (segment->rate > 0.0) {
+ if (segment->stop == *cstop)
+ *rstop = *rstart + current->amount;
+
+ first = *rstart;
+ last = *rstop;
+ } else {
+ if (segment->start == *cstart)
+ *rstart = *rstop + current->amount;
+
+ first = *rstop;
+ last = *rstart;
+ }
+
+ end = current->start + current->amount;
+ current->position = first - current->start;
+
+ if (G_UNLIKELY (segment->abs_rate != 1.0))
+ current->position /= segment->abs_rate;
+
+ GST_DEBUG_OBJECT (sink,
+ "buffer: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (first), GST_TIME_ARGS (last));
+ GST_DEBUG_OBJECT (sink,
+ "got time step %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT "/%"
+ GST_TIME_FORMAT, GST_TIME_ARGS (current->position),
+ GST_TIME_ARGS (last - current->start),
+ GST_TIME_ARGS (current->amount));
+
+ if ((current->flush && current->position >= current->amount)
+ || last >= end) {
+ GST_DEBUG_OBJECT (sink, "step ended, we need clipping");
+ step_end = TRUE;
+ if (segment->rate > 0.0) {
+ *rstart = end;
+ *cstart = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
+ } else {
+ *rstop = end;
+ *cstop = gst_segment_to_position (segment, GST_FORMAT_TIME, end);
+ }
+ }
+ GST_DEBUG_OBJECT (sink,
+ "cstart %" GST_TIME_FORMAT ", rstart %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (*cstart), GST_TIME_ARGS (*rstart));
+ GST_DEBUG_OBJECT (sink,
+ "cstop %" GST_TIME_FORMAT ", rstop %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (*cstop), GST_TIME_ARGS (*rstop));
+ break;
+ }
+ case GST_FORMAT_BUFFERS:
+ GST_DEBUG_OBJECT (sink,
+ "got default step %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
+ current->position, current->amount);
+
+ if (current->position < current->amount) {
+ current->position++;
+ } else {
+ step_end = TRUE;
+ }
+ break;
+ case GST_FORMAT_DEFAULT:
+ default:
+ GST_DEBUG_OBJECT (sink,
+ "got unknown step %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
+ current->position, current->amount);
+ break;
+ }
+ return step_end;
+}
/* with STREAM_LOCK, PREROLL_LOCK
*
gst_base_sink_get_sync_times (GstBaseSink * basesink, GstMiniObject * obj,
GstClockTime * rsstart, GstClockTime * rsstop,
GstClockTime * rrstart, GstClockTime * rrstop, gboolean * do_sync,
- GstSegment * segment)
+ gboolean * stepped, GstSegment * segment, GstStepInfo * step,
+ gboolean * step_end, guint8 obj_type)
{
GstBaseSinkClass *bclass;
GstBuffer *buffer;
GstClockTime sstart, sstop; /* clipped timestamps converted to stream time */
GstFormat format;
GstBaseSinkPrivate *priv;
+ gboolean eos;
priv = basesink->priv;
/* start with nothing */
- start = stop = sstart = sstop = rstart = rstop = -1;
+ start = stop = GST_CLOCK_TIME_NONE;
- if (G_UNLIKELY (GST_IS_EVENT (obj))) {
+ if (G_UNLIKELY (OBJ_IS_EVENT (obj_type))) {
GstEvent *event = GST_EVENT_CAST (obj);
switch (GST_EVENT_TYPE (event)) {
{
if (basesink->segment.rate >= 0.0) {
sstart = sstop = priv->current_sstop;
- if (sstart == -1) {
+ if (!GST_CLOCK_TIME_IS_VALID (sstart)) {
/* we have not seen a buffer yet, use the segment values */
sstart = sstop = gst_segment_to_stream_time (&basesink->segment,
basesink->segment.format, basesink->segment.stop);
}
} else {
sstart = sstop = priv->current_sstart;
- if (sstart == -1) {
+ if (!GST_CLOCK_TIME_IS_VALID (sstart)) {
/* we have not seen a buffer yet, use the segment values */
sstart = sstop = gst_segment_to_stream_time (&basesink->segment,
basesink->segment.format, basesink->segment.start);
*do_sync = rstart != -1;
GST_DEBUG_OBJECT (basesink, "sync times for EOS %" GST_TIME_FORMAT,
GST_TIME_ARGS (rstart));
- goto done;
+ /* if we are stepping, we end now */
+ *step_end = step->valid;
+ eos = TRUE;
+ goto eos_done;
}
default:
/* other events do not need syncing */
}
}
+ eos = FALSE;
+
+again:
/* else do buffer sync code */
buffer = GST_BUFFER_CAST (obj);
bclass = GST_BASE_SINK_GET_CLASS (basesink);
- /* just get the times to see if we need syncing */
+ /* just get the times to see if we need syncing, if the start returns -1 we
+ * don't sync. */
if (bclass->get_times)
bclass->get_times (basesink, buffer, &start, &stop);
- if (start == -1) {
+ if (!GST_CLOCK_TIME_IS_VALID (start)) {
+ /* we don't need to sync but we still want to get the timestamps for
+ * tracking the position */
gst_base_sink_get_times (basesink, buffer, &start, &stop);
*do_sync = FALSE;
} else {
/* collect segment and format for code clarity */
format = segment->format;
- /* no timestamp clipping if we did not * get a TIME segment format */
+ /* no timestamp clipping if we did not get a TIME segment format */
if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
cstart = start;
cstop = stop;
/* do running and stream time in TIME format */
format = GST_FORMAT_TIME;
+ GST_LOG_OBJECT (basesink, "not time format, don't clip");
goto do_times;
}
- /* clip */
+ /* clip, only when we know about time */
if (G_UNLIKELY (!gst_segment_clip (segment, GST_FORMAT_TIME,
- (gint64) start, (gint64) stop, &cstart, &cstop)))
+ (gint64) start, (gint64) stop, &cstart, &cstop))) {
+ if (step->valid) {
+ GST_DEBUG_OBJECT (basesink, "step out of segment");
+ /* when we are stepping, pretend we're at the end of the segment */
+ if (segment->rate > 0.0) {
+ cstart = segment->stop;
+ cstop = segment->stop;
+ } else {
+ cstart = segment->start;
+ cstop = segment->start;
+ }
+ goto do_times;
+ }
goto out_of_segment;
+ }
if (G_UNLIKELY (start != cstart || stop != cstop)) {
GST_DEBUG_OBJECT (basesink, "clipped to: start %" GST_TIME_FORMAT
gst_segment_set_last_stop (segment, GST_FORMAT_TIME, cstart);
do_times:
+ rstart = gst_segment_to_running_time (segment, format, cstart);
+ rstop = gst_segment_to_running_time (segment, format, cstop);
+
+ if (G_UNLIKELY (step->valid)) {
+ if (!(*step_end = handle_stepping (basesink, segment, step, &cstart, &cstop,
+ &rstart, &rstop))) {
+ /* step is still busy, we discard data when we are flushing */
+ *stepped = step->flush;
+ GST_DEBUG_OBJECT (basesink, "stepping busy");
+ }
+ }
/* this can produce wrong values if we accumulated non-TIME segments. If this happens,
* upstream is behaving very badly */
sstart = gst_segment_to_stream_time (segment, format, cstart);
sstop = gst_segment_to_stream_time (segment, format, cstop);
- rstart = gst_segment_to_running_time (segment, format, cstart);
- rstop = gst_segment_to_running_time (segment, format, cstop);
-done:
+eos_done:
+ /* eos_done label only called when doing EOS, we also stop stepping then */
+ if (*step_end && step->flush) {
+ GST_DEBUG_OBJECT (basesink, "flushing step ended");
+ stop_stepping (basesink, segment, step, rstart, rstop, eos);
+ *step_end = FALSE;
+ /* re-determine running start times for adjusted segment
+ * (which has a flushed amount of running/accumulated time removed) */
+ if (!GST_IS_EVENT (obj)) {
+ GST_DEBUG_OBJECT (basesink, "refresh sync times");
+ goto again;
+ }
+ }
+
/* save times */
*rsstart = sstart;
*rsstop = sstop;
/* special cases */
out_of_segment:
{
- /* should not happen since we clip them in the chain function already,
- * we return FALSE so that we don't try to sync on it. */
- GST_ELEMENT_WARNING (basesink, STREAM, FAILED,
- (NULL), ("unexpected buffer out of segment found."));
+ /* 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
+ * to sync on it. */
GST_LOG_OBJECT (basesink, "buffer skipped, not in segment");
return FALSE;
}
}
/* with STREAM_LOCK, PREROLL_LOCK, LOCK
- * adjust a timestamp with the latency and timestamp offset */
+ * adjust a timestamp with the latency and timestamp offset. This function does
+ * not adjust for the render delay. */
static GstClockTime
gst_base_sink_adjust_time (GstBaseSink * basesink, GstClockTime time)
{
} else
time += ts_offset;
+ /* subtract the render delay again, which was included in the latency */
+ if (time > basesink->priv->render_delay)
+ time -= basesink->priv->render_delay;
+ else
+ time = 0;
+
return time;
}
-/* gst_base_sink_wait_clock:
+/**
+ * gst_base_sink_wait_clock:
* @sink: the sink
* @time: the running_time to be reached
* @jitter: the jitter to be filled with time diff (can be NULL)
* 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 ::event vmethod or when receiving a buffer in
- * the ::render vmethod.
+ * receiving an EOS event in the #GstBaseSinkClass.event() vmethod or when
+ * receiving a buffer in
+ * the #GstBaseSinkClass.render() vmethod.
*
* The @time argument should be the running_time of when this method should
* return and is not adjusted with any latency or offset configured in the
* Returns: #GstClockReturn
*/
GstClockReturn
-gst_base_sink_wait_clock (GstBaseSink * basesink, GstClockTime time,
+gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time,
GstClockTimeDiff * jitter)
{
- GstClockID id;
GstClockReturn ret;
GstClock *clock;
+ GstClockTime base_time;
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time)))
goto invalid_time;
- GST_OBJECT_LOCK (basesink);
- if (G_UNLIKELY (!basesink->sync))
+ GST_OBJECT_LOCK (sink);
+ if (G_UNLIKELY (!sink->sync))
goto no_sync;
- if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL))
+ if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (sink)) == NULL))
goto no_clock;
- /* add base_time to running_time to get the time against the clock */
- time += GST_ELEMENT_CAST (basesink)->base_time;
+ base_time = GST_ELEMENT_CAST (sink)->base_time;
+ GST_LOG_OBJECT (sink,
+ "time %" GST_TIME_FORMAT ", base_time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (time), GST_TIME_ARGS (base_time));
- id = gst_clock_new_single_shot_id (clock, time);
- GST_OBJECT_UNLOCK (basesink);
+ /* add base_time to running_time to get the time against the clock */
+ time += base_time;
+
+ /* Re-use existing clockid if available */
+ if (G_LIKELY (sink->priv->cached_clock_id != NULL)) {
+ if (!gst_clock_single_shot_id_reinit (clock, sink->priv->cached_clock_id,
+ time)) {
+ gst_clock_id_unref (sink->priv->cached_clock_id);
+ sink->priv->cached_clock_id = gst_clock_new_single_shot_id (clock, time);
+ }
+ } else
+ sink->priv->cached_clock_id = gst_clock_new_single_shot_id (clock, time);
+ GST_OBJECT_UNLOCK (sink);
/* A blocking wait is performed on the clock. We save the ClockID
- * so we can unlock the entry at any time. While we are blocking, we
+ * so we can unlock the entry at any time. While we are blocking, we
* release the PREROLL_LOCK so that other threads can interrupt the
* entry. */
- basesink->clock_id = id;
+ sink->clock_id = sink->priv->cached_clock_id;
/* release the preroll lock while waiting */
- GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
+ GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
- ret = gst_clock_id_wait (id, jitter);
+ ret = gst_clock_id_wait (sink->priv->cached_clock_id, jitter);
- GST_PAD_PREROLL_LOCK (basesink->sinkpad);
- gst_clock_id_unref (id);
- basesink->clock_id = NULL;
+ GST_PAD_PREROLL_LOCK (sink->sinkpad);
+ sink->clock_id = NULL;
return ret;
/* no syncing needed */
invalid_time:
{
- GST_DEBUG_OBJECT (basesink, "time not valid, no sync needed");
+ GST_DEBUG_OBJECT (sink, "time not valid, no sync needed");
return GST_CLOCK_BADTIME;
}
no_sync:
{
- GST_DEBUG_OBJECT (basesink, "sync disabled");
- GST_OBJECT_UNLOCK (basesink);
+ GST_DEBUG_OBJECT (sink, "sync disabled");
+ GST_OBJECT_UNLOCK (sink);
return GST_CLOCK_BADTIME;
}
no_clock:
{
- GST_DEBUG_OBJECT (basesink, "no clock, can't sync");
- GST_OBJECT_UNLOCK (basesink);
+ GST_DEBUG_OBJECT (sink, "no clock, can't sync");
+ GST_OBJECT_UNLOCK (sink);
return GST_CLOCK_BADTIME;
}
}
* gst_base_sink_wait_preroll:
* @sink: the sink
*
- * If the #GstBaseSinkClass::render method performs its own synchronisation 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 performs its own synchronisation
+ * 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.
*
* 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
sink->have_preroll = FALSE;
if (G_UNLIKELY (sink->flushing))
goto stopping;
+ if (G_UNLIKELY (sink->priv->step_unlock))
+ goto step_unlocked;
GST_DEBUG_OBJECT (sink, "continue after preroll");
return GST_FLOW_OK;
/* ERRORS */
stopping:
{
- GST_DEBUG_OBJECT (sink, "preroll interrupted");
+ GST_DEBUG_OBJECT (sink, "preroll interrupted because of flush");
return GST_FLOW_WRONG_STATE;
}
+step_unlocked:
+ {
+ sink->priv->step_unlock = FALSE;
+ GST_DEBUG_OBJECT (sink, "preroll interrupted because of step");
+ return GST_FLOW_STEP;
+ }
}
/**
- * gst_base_sink_wait_eos:
+ * gst_base_sink_do_preroll:
* @sink: the sink
- * @time: the running_time to be reached
- * @jitter: the jitter to be filled with time diff (can be NULL)
- *
- * This function will block until @time is reached. It is usually called by
- * subclasses that use their own internal synchronisation but want to let the
- * EOS be handled by the base class.
+ * @obj: the object that caused the preroll
*
- * This function should only be called with the PREROLL_LOCK held, like when
- * receiving an EOS event in the ::event vmethod.
+ * If the @sink spawns its own thread for pulling buffers from upstream it
+ * should call this method after it has pulled a buffer. If the element needed
+ * to preroll, this function will perform the preroll and will then block
+ * until the element state is changed.
*
- * The @time argument should be the running_time of when the EOS should happen
- * and will be adjusted with any latency and offset configured in the sink.
+ * This function should be called with the PREROLL_LOCK held.
*
- * Since 0.10.15
+ * Since 0.10.22
*
- * Returns: #GstFlowReturn
+ * Returns: #GST_FLOW_OK if the preroll completed and processing can
+ * continue. Any other return value should be returned from the render vmethod.
*/
GstFlowReturn
-gst_base_sink_wait_eos (GstBaseSink * sink, GstClockTime time,
- GstClockTimeDiff * jitter)
+gst_base_sink_do_preroll (GstBaseSink * sink, GstMiniObject * obj)
{
- GstClockReturn status;
GstFlowReturn ret;
- do {
- GstClockTime stime;
+ while (G_UNLIKELY (sink->need_preroll)) {
+ guint8 obj_type = _PR_IS_NOTHING;
+ GST_DEBUG_OBJECT (sink, "prerolling object %p", obj);
- GST_DEBUG_OBJECT (sink, "checking preroll");
+ if (G_LIKELY (GST_IS_BUFFER (obj)))
+ obj_type = _PR_IS_BUFFER;
+ else if (GST_IS_EVENT (obj))
+ obj_type = _PR_IS_EVENT;
+ else if (GST_IS_BUFFER_LIST (obj))
+ obj_type = _PR_IS_BUFFERLIST;
- /* first wait for the playing state before we can continue */
- if (G_UNLIKELY (sink->need_preroll)) {
- ret = gst_base_sink_wait_preroll (sink);
- if (ret != GST_FLOW_OK)
- goto flushing;
- }
+ ret = gst_base_sink_preroll_object (sink, obj_type, obj);
+ if (ret != GST_FLOW_OK)
+ goto preroll_failed;
+
+ /* need to recheck here because the commit state could have
+ * made us not need the preroll anymore */
+ if (G_LIKELY (sink->need_preroll)) {
+ /* block until the state changes, or we get a flush, or something */
+ ret = gst_base_sink_wait_preroll (sink);
+ if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_STEP))
+ goto preroll_failed;
+ }
+ }
+ return GST_FLOW_OK;
+
+ /* ERRORS */
+preroll_failed:
+ {
+ GST_DEBUG_OBJECT (sink, "preroll failed %d", ret);
+ return ret;
+ }
+}
+
+/**
+ * gst_base_sink_wait_eos:
+ * @sink: the sink
+ * @time: the running_time to be reached
+ * @jitter: the jitter to be filled with time diff (can be NULL)
+ *
+ * This function will block until @time is reached. It is usually called by
+ * subclasses that use their own internal synchronisation but want to let the
+ * EOS be handled by the base class.
+ *
+ * This function should only be called with the PREROLL_LOCK held, like when
+ * receiving an EOS event in the ::event vmethod.
+ *
+ * The @time argument should be the running_time of when the EOS should happen
+ * and will be adjusted with any latency and offset configured in the sink.
+ *
+ * Since 0.10.15
+ *
+ * Returns: #GstFlowReturn
+ */
+GstFlowReturn
+gst_base_sink_wait_eos (GstBaseSink * sink, GstClockTime time,
+ GstClockTimeDiff * jitter)
+{
+ GstClockReturn status;
+ GstFlowReturn ret;
+
+ do {
+ GstClockTime stime;
+
+ GST_DEBUG_OBJECT (sink, "checking preroll");
+
+ /* first wait for the playing state before we can continue */
+ while (G_UNLIKELY (sink->need_preroll)) {
+ ret = gst_base_sink_wait_preroll (sink);
+ if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_STEP))
+ goto flushing;
+ }
/* preroll done, we can sync since we are in PLAYING now. */
GST_DEBUG_OBJECT (sink, "possibly waiting for clock to reach %"
* because we don't interact with the device on EOS normally. */
stime = gst_base_sink_adjust_time (sink, time);
- /* wait for the clock, this can be interrupted because we got shut down or
+ /* wait for the clock, this can be interrupted because we got shut down or
* we PAUSED. */
status = gst_base_sink_wait_clock (sink, stime, jitter);
* Some objects do not need synchronisation (most events) and so this function
* immediatly returns GST_FLOW_OK.
*
- * for objects that arrive later than max-lateness to be synchronized to the
+ * for objects that arrive later than max-lateness to be synchronized to the
* clock have the @late boolean set to TRUE.
*
* This function keeps a running average of the jitter (the diff between the
*/
static GstFlowReturn
gst_base_sink_do_sync (GstBaseSink * basesink, GstPad * pad,
- GstMiniObject * obj, gboolean * late)
+ GstMiniObject * obj, gboolean * late, gboolean * step_end, guint8 obj_type)
{
- GstClockTimeDiff jitter;
+ GstClockTimeDiff jitter = 0;
gboolean syncable;
GstClockReturn status = GST_CLOCK_OK;
GstClockTime rstart, rstop, sstart, sstop, stime;
gboolean do_sync;
GstBaseSinkPrivate *priv;
+ GstFlowReturn ret;
+ GstStepInfo *current, *pending;
+ gboolean stepped;
priv = basesink->priv;
- sstart = sstop = rstart = rstop = -1;
+do_step:
+ sstart = sstop = rstart = rstop = GST_CLOCK_TIME_NONE;
do_sync = TRUE;
+ stepped = FALSE;
+
+ priv->current_rstart = GST_CLOCK_TIME_NONE;
- priv->current_rstart = -1;
+ /* get stepping info */
+ current = &priv->current_step;
+ pending = &priv->pending_step;
/* 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, &basesink->segment);
+ &sstart, &sstop, &rstart, &rstop, &do_sync, &stepped, &basesink->segment,
+ current, step_end, obj_type);
+
+ if (G_UNLIKELY (stepped))
+ goto step_skipped;
/* a syncable object needs to participate in preroll and
* clocking. All buffers and EOS are syncable. */
/* store timing info for current object */
priv->current_rstart = rstart;
- priv->current_rstop = (rstop != -1 ? rstop : rstart);
+ 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 : -1);
+ priv->eos_rtime = (do_sync ? priv->current_rstop : GST_CLOCK_TIME_NONE);
again:
/* first do preroll, this makes sure we commit our state
* to PAUSED and can continue to PLAYING. We cannot perform
- * any clock sync in PAUSED because there is no clock.
- */
- while (G_UNLIKELY (basesink->need_preroll)) {
- GST_DEBUG_OBJECT (basesink, "prerolling object %p", obj);
-
- if (G_LIKELY (basesink->playing_async)) {
- /* commit state */
- if (G_UNLIKELY (!gst_base_sink_commit_state (basesink)))
- goto stopping;
- }
+ * any clock sync in PAUSED because there is no clock. */
+ ret = gst_base_sink_do_preroll (basesink, obj);
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto preroll_failed;
- /* need to recheck here because the commit state could have
- * made us not need the preroll anymore */
- if (G_LIKELY (basesink->need_preroll)) {
- /* block until the state changes, or we get a flush, or something */
- if (gst_base_sink_wait_preroll (basesink) != GST_FLOW_OK)
- goto flushing;
- }
+ /* update the segment with a pending step if the current one is invalid and we
+ * have a new pending one. We only accept new step updates after a preroll */
+ if (G_UNLIKELY (pending->valid && !current->valid)) {
+ start_stepping (basesink, &basesink->segment, pending, current);
+ goto do_step;
}
/* After rendering we store the position of the last buffer so that we can use
* it to report the position. We need to take the lock here. */
GST_OBJECT_LOCK (basesink);
priv->current_sstart = sstart;
- priv->current_sstop = (sstop != -1 ? sstop : sstart);
+ priv->current_sstop = (GST_CLOCK_TIME_IS_VALID (sstop) ? sstop : sstart);
GST_OBJECT_UNLOCK (basesink);
if (!do_sync)
stime = gst_base_sink_adjust_time (basesink, rstart);
/* adjust for render-delay, avoid underflows */
- if (stime != -1) {
+ if (GST_CLOCK_TIME_IS_VALID (stime)) {
if (stime > priv->render_delay)
stime -= priv->render_delay;
else
* or sync is disabled with GST_CLOCK_BADTIME. */
status = gst_base_sink_wait_clock (basesink, stime, &jitter);
- GST_DEBUG_OBJECT (basesink, "clock returned %d", status);
+ GST_DEBUG_OBJECT (basesink, "clock returned %d, jitter %c%" GST_TIME_FORMAT,
+ status, (jitter < 0 ? '-' : ' '), GST_TIME_ARGS (ABS (jitter)));
/* invalid time, no clock or sync disabled, just render */
if (status == GST_CLOCK_BADTIME)
* we can try to preroll on the current buffer. */
if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED)) {
GST_DEBUG_OBJECT (basesink, "unscheduled, waiting some more");
+ priv->call_preroll = TRUE;
goto again;
}
return GST_FLOW_OK;
/* ERRORS */
+step_skipped:
+ {
+ GST_DEBUG_OBJECT (basesink, "skipped stepped object %p", obj);
+ *late = TRUE;
+ return GST_FLOW_OK;
+ }
not_syncable:
{
GST_DEBUG_OBJECT (basesink, "non syncable object %p", obj);
GST_DEBUG_OBJECT (basesink, "we are flushing");
return GST_FLOW_WRONG_STATE;
}
-stopping:
+preroll_failed:
{
- GST_DEBUG_OBJECT (basesink, "stopping while commiting state");
- return GST_FLOW_WRONG_STATE;
+ GST_DEBUG_OBJECT (basesink, "preroll failed");
+ *step_end = FALSE;
+ return ret;
}
}
start = priv->current_rstart;
+ if (priv->current_step.valid)
+ return;
+
/* if Quality-of-Service disabled, do nothing */
- if (!g_atomic_int_get (&priv->qos_enabled) || start == -1)
+ if (!g_atomic_int_get (&priv->qos_enabled) ||
+ !GST_CLOCK_TIME_IS_VALID (start))
return;
stop = priv->current_rstop;
}
/* calculate duration of the buffer */
- if (stop != -1)
+ if (GST_CLOCK_TIME_IS_VALID (stop))
duration = stop - start;
else
- duration = -1;
+ duration = GST_CLOCK_TIME_NONE;
/* if we have the time when the last buffer left us, calculate
* processing time */
- if (priv->last_left != -1) {
+ if (GST_CLOCK_TIME_IS_VALID (priv->last_left)) {
if (entered > priv->last_left) {
pt = entered - priv->last_left;
} else {
/* collect running averages. for first observations, we copy the
* values */
- if (priv->avg_duration == -1)
+ 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 (priv->avg_pt == -1)
+ if (!GST_CLOCK_TIME_IS_VALID (priv->avg_pt))
priv->avg_pt = pt;
else
priv->avg_pt = UPDATE_RUNNING_AVG (priv->avg_pt, pt);
else
rate = 0.0;
- if (priv->last_left != -1) {
+ if (GST_CLOCK_TIME_IS_VALID (priv->last_left)) {
if (dropped || priv->avg_rate < 0.0) {
priv->avg_rate = rate;
} else {
priv = sink->priv;
- priv->last_in_time = -1;
- priv->last_left = -1;
- priv->avg_duration = -1;
- priv->avg_pt = -1;
+ priv->last_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 = -1;
+ priv->avg_render = GST_CLOCK_TIME_NONE;
priv->rendered = 0;
priv->dropped = 0;
goto not_buffer;
/* can't do check if we don't have a timestamp */
- if (G_UNLIKELY (start == -1))
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (start)))
goto no_timestamp;
/* we can add a valid stop time */
- if (stop != -1)
+ if (GST_CLOCK_TIME_IS_VALID (stop))
max_lateness += stop;
else
max_lateness += start;
/* if the jitter bigger than duration and lateness we are too late */
if ((late = start + jitter > max_lateness)) {
- GST_DEBUG_OBJECT (basesink, "buffer is too late %" GST_TIME_FORMAT
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, basesink,
+ "buffer is too late %" GST_TIME_FORMAT
" > %" GST_TIME_FORMAT, GST_TIME_ARGS (start + jitter),
GST_TIME_ARGS (max_lateness));
- /* !!emergency!!, if we did not receive anything valid for more than a
+ /* !!emergency!!, if we did not receive anything valid for more than a
* second, render it anyway so the user sees something */
- if (priv->last_in_time && start - priv->last_in_time > GST_SECOND) {
+ if (GST_CLOCK_TIME_IS_VALID (priv->last_in_time) &&
+ start - priv->last_in_time > GST_SECOND) {
late = FALSE;
- GST_DEBUG_OBJECT (basesink,
+ GST_ELEMENT_WARNING (basesink, CORE, CLOCK,
+ (_("A lot of buffers are being dropped.")),
+ ("There may be a timestamping problem, or this computer is too slow."));
+ GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, basesink,
"**emergency** last buffer at %" GST_TIME_FORMAT " > GST_SECOND",
GST_TIME_ARGS (priv->last_in_time));
}
}
done:
- if (!late) {
+ if (!late || !GST_CLOCK_TIME_IS_VALID (priv->last_in_time)) {
priv->last_in_time = start;
}
return late;
elapsed = GST_CLOCK_DIFF (priv->start, priv->stop);
- if (priv->avg_render == -1)
+ if (!GST_CLOCK_TIME_IS_VALID (priv->avg_render))
priv->avg_render = elapsed;
else
priv->avg_render = UPDATE_RUNNING_AVG (priv->avg_render, elapsed);
*/
static GstFlowReturn
gst_base_sink_render_object (GstBaseSink * basesink, GstPad * pad,
- GstMiniObject * obj)
+ guint8 obj_type, gpointer obj)
{
- GstFlowReturn ret = GST_FLOW_OK;
+ GstFlowReturn ret;
GstBaseSinkClass *bclass;
- gboolean late = FALSE;
+ gboolean late, step_end;
+ gpointer sync_obj;
GstBaseSinkPrivate *priv;
priv = basesink->priv;
+ if (OBJ_IS_BUFFERLIST (obj_type)) {
+ /*
+ * If buffer list, use the first group buffer within the list
+ * for syncing
+ */
+ sync_obj = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0, 0);
+ g_assert (NULL != sync_obj);
+ } else {
+ sync_obj = obj;
+ }
+
+again:
+ late = FALSE;
+ step_end = FALSE;
+
/* synchronize this object, non syncable objects return OK
* immediatly. */
- ret = gst_base_sink_do_sync (basesink, pad, obj, &late);
+ ret =
+ gst_base_sink_do_sync (basesink, pad, sync_obj, &late, &step_end,
+ obj_type);
if (G_UNLIKELY (ret != GST_FLOW_OK))
goto sync_failed;
- /* and now render, event or buffer. */
- if (G_LIKELY (GST_IS_BUFFER (obj))) {
- GstBuffer *buf;
-
+ /* and now render, event or buffer/buffer list. */
+ if (G_LIKELY (OBJ_IS_BUFFERFULL (obj_type))) {
/* drop late buffers unconditionally, let's hope it's unlikely */
if (G_UNLIKELY (late))
goto dropped;
- buf = GST_BUFFER_CAST (obj);
-
- gst_base_sink_set_last_buffer (basesink, buf);
-
bclass = GST_BASE_SINK_GET_CLASS (basesink);
- if (G_LIKELY (bclass->render)) {
+ if (G_LIKELY ((OBJ_IS_BUFFERLIST (obj_type) && bclass->render_list) ||
+ (!OBJ_IS_BUFFERLIST (obj_type) && bclass->render))) {
gint do_qos;
/* read once, to get same value before and after */
do_qos = g_atomic_int_get (&priv->qos_enabled);
- GST_DEBUG_OBJECT (basesink, "rendering buffer %p", obj);
+ 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);
- ret = bclass->render (basesink, buf);
+ if (!OBJ_IS_BUFFERLIST (obj_type)) {
+ GstBuffer *buf;
- priv->rendered++;
+ /* For buffer lists do not set last buffer. Creating buffer
+ * with meaningful data can be done only with memcpy which will
+ * significantly affect performance */
+ buf = GST_BUFFER_CAST (obj);
+ gst_base_sink_set_last_buffer (basesink, buf);
+
+ ret = bclass->render (basesink, buf);
+ } else {
+ GstBufferList *buflist;
+
+ buflist = GST_BUFFER_LIST_CAST (obj);
+
+ ret = bclass->render_list (basesink, buflist);
+ }
if (do_qos)
gst_base_sink_do_render_stats (basesink, FALSE);
+
+ if (ret == GST_FLOW_STEP)
+ goto again;
+
+ if (G_UNLIKELY (basesink->flushing))
+ goto flushing;
+
+ priv->rendered++;
}
- } else {
+ } else if (G_LIKELY (OBJ_IS_EVENT (obj_type))) {
GstEvent *event = GST_EVENT_CAST (obj);
gboolean event_res = TRUE;
GstEventType type;
goto flushing;
if (G_LIKELY (event_res)) {
+ guint32 seqnum;
+
+ seqnum = basesink->priv->seqnum = gst_event_get_seqnum (event);
+ GST_DEBUG_OBJECT (basesink, "Got seqnum #%" G_GUINT32_FORMAT, seqnum);
+
switch (type) {
case GST_EVENT_EOS:
+ {
+ GstMessage *message;
+
/* the EOS event is completely handled so we mark
- * ourselves as being in the EOS state. eos is also
- * protected by the object lock so we can read it when
+ * ourselves as being in the EOS state. eos is also
+ * protected by the object lock so we can read it when
* answering the POSITION query. */
GST_OBJECT_LOCK (basesink);
basesink->eos = TRUE;
GST_OBJECT_UNLOCK (basesink);
+
/* ok, now we can post the message */
GST_DEBUG_OBJECT (basesink, "Now posting EOS");
- gst_element_post_message (GST_ELEMENT_CAST (basesink),
- gst_message_new_eos (GST_OBJECT_CAST (basesink)));
+
+ message = gst_message_new_eos (GST_OBJECT_CAST (basesink));
+ gst_message_set_seqnum (message, seqnum);
+ gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
break;
+ }
case GST_EVENT_NEWSEGMENT:
/* configure the segment */
gst_base_sink_configure_segment (basesink, pad, event,
&basesink->segment);
break;
+ case GST_EVENT_SINK_MESSAGE:{
+ GstMessage *msg = NULL;
+
+ gst_event_parse_sink_message (event, &msg);
+
+ if (msg)
+ gst_element_post_message (GST_ELEMENT_CAST (basesink), msg);
+ }
default:
break;
}
}
+ } else {
+ g_return_val_if_reached (GST_FLOW_ERROR);
}
done:
+ if (step_end) {
+ /* the step ended, check if we need to activate a new step */
+ GST_DEBUG_OBJECT (basesink, "step ended");
+ stop_stepping (basesink, &basesink->segment, &priv->current_step,
+ priv->current_rstart, priv->current_rstop, basesink->eos);
+ goto again;
+ }
+
gst_base_sink_perform_qos (basesink, late);
GST_DEBUG_OBJECT (basesink, "object unref after render %p", obj);
- gst_mini_object_unref (obj);
-
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
return ret;
/* ERRORS */
{
priv->dropped++;
GST_DEBUG_OBJECT (basesink, "buffer late, dropping");
+
+ if (g_atomic_int_get (&priv->qos_enabled)) {
+ GstMessage *qos_msg;
+ GstClockTime timestamp, duration;
+
+ timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (sync_obj));
+ duration = GST_BUFFER_DURATION (GST_BUFFER_CAST (sync_obj));
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
+ "qos: dropped buffer rt %" GST_TIME_FORMAT ", st %" GST_TIME_FORMAT
+ ", ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (priv->current_rstart),
+ GST_TIME_ARGS (priv->current_sstart), GST_TIME_ARGS (timestamp),
+ GST_TIME_ARGS (duration));
+ GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink,
+ "qos: rendered %" G_GUINT64_FORMAT ", dropped %" G_GUINT64_FORMAT,
+ priv->rendered, priv->dropped);
+
+ qos_msg =
+ gst_message_new_qos (GST_OBJECT_CAST (basesink), basesink->sync,
+ priv->current_rstart, priv->current_sstart, timestamp, duration);
+ gst_message_set_qos_values (qos_msg, priv->current_jitter, priv->avg_rate,
+ 1000000);
+ gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS, priv->rendered,
+ priv->dropped);
+ gst_element_post_message (GST_ELEMENT_CAST (basesink), qos_msg);
+ }
goto done;
}
flushing:
/* with STREAM_LOCK, PREROLL_LOCK
*
- * Perform preroll on the given object. For buffers this means
- * calling the preroll subclass method.
+ * Perform preroll on the given object. For buffers this means
+ * calling the preroll subclass method.
* If that succeeds, the state will be commited.
*
* function does not take ownership of obj.
*/
static GstFlowReturn
-gst_base_sink_preroll_object (GstBaseSink * basesink, GstPad * pad,
+gst_base_sink_preroll_object (GstBaseSink * basesink, guint8 obj_type,
GstMiniObject * obj)
{
GstFlowReturn ret;
- GST_DEBUG_OBJECT (basesink, "do preroll %p", obj);
+ GST_DEBUG_OBJECT (basesink, "prerolling object %p", obj);
/* if it's a buffer, we need to call the preroll method */
- if (G_LIKELY (GST_IS_BUFFER (obj))) {
+ if (G_LIKELY (OBJ_IS_BUFFERFULL (obj_type) && basesink->priv->call_preroll)) {
GstBaseSinkClass *bclass;
GstBuffer *buf;
GstClockTime timestamp;
- buf = GST_BUFFER_CAST (obj);
+ if (OBJ_IS_BUFFERLIST (obj_type)) {
+ buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0, 0);
+ g_assert (NULL != buf);
+ } else {
+ buf = GST_BUFFER_CAST (obj);
+ }
+
timestamp = GST_BUFFER_TIMESTAMP (buf);
GST_DEBUG_OBJECT (basesink, "preroll buffer %" GST_TIME_FORMAT,
GST_TIME_ARGS (timestamp));
- gst_base_sink_set_last_buffer (basesink, buf);
+ /*
+ * For buffer lists do not set last buffer. Creating buffer
+ * with meaningful data can be done only with memcpy which will
+ * significantly affect performance
+ */
+ if (!OBJ_IS_BUFFERLIST (obj_type)) {
+ gst_base_sink_set_last_buffer (basesink, buf);
+ }
bclass = GST_BASE_SINK_GET_CLASS (basesink);
if (bclass->preroll)
if ((ret = bclass->preroll (basesink, buf)) != GST_FLOW_OK)
goto preroll_failed;
+
+ basesink->priv->call_preroll = FALSE;
}
/* commit state */
}
}
-/* with STREAM_LOCK, PREROLL_LOCK
+/* with STREAM_LOCK, PREROLL_LOCK
*
* Queue an object for rendering.
* The first prerollable object queued will complete the preroll. If the
*/
static GstFlowReturn
gst_base_sink_queue_object_unlocked (GstBaseSink * basesink, GstPad * pad,
- GstMiniObject * obj, gboolean prerollable)
+ guint8 obj_type, gpointer obj, gboolean prerollable)
{
GstFlowReturn ret = GST_FLOW_OK;
gint length;
/* first prerollable item needs to finish the preroll */
if (length == 1) {
- ret = gst_base_sink_preroll_object (basesink, pad, obj);
+ ret = gst_base_sink_preroll_object (basesink, obj_type, obj);
if (G_UNLIKELY (ret != GST_FLOW_OK))
goto preroll_failed;
}
- /* need to recheck if we need preroll, commmit state during preroll
+ /* need to recheck if we need preroll, commmit state during preroll
* could have made us not need more preroll. */
if (G_UNLIKELY (basesink->need_preroll)) {
/* see if we can render now, if we can't add the object to the preroll
goto more_preroll;
}
}
-
/* we can start rendering (or blocking) the queued object
* if any. */
q = basesink->preroll_queue;
GST_DEBUG_OBJECT (basesink, "rendering queued object %p", o);
/* do something with the return value */
- ret = gst_base_sink_render_object (basesink, pad, o);
+ ret =
+ gst_base_sink_render_object (basesink, pad,
+ GST_IS_BUFFER (o) ? _PR_IS_BUFFER : _PR_IS_EVENT, o);
if (ret != GST_FLOW_OK)
goto dequeue_failed;
}
/* now render the object */
- ret = gst_base_sink_render_object (basesink, pad, obj);
+ ret = gst_base_sink_render_object (basesink, pad, obj_type, obj);
basesink->preroll_queued = 0;
return ret;
{
GST_DEBUG_OBJECT (basesink, "preroll failed, reason %s",
gst_flow_get_name (ret));
- gst_mini_object_unref (obj);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
return ret;
}
more_preroll:
{
GST_DEBUG_OBJECT (basesink, "rendering queued objects failed, reason %s",
gst_flow_get_name (ret));
- gst_mini_object_unref (obj);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
return ret;
}
}
* the queue.
*
* This function takes ownership of obj.
+ *
+ * Note: Only GstEvent seem to be passed to this private method
*/
static GstFlowReturn
gst_base_sink_queue_object (GstBaseSink * basesink, GstPad * pad,
if (G_UNLIKELY (basesink->priv->received_eos))
goto was_eos;
- ret = gst_base_sink_queue_object_unlocked (basesink, pad, obj, prerollable);
+ ret =
+ gst_base_sink_queue_object_unlocked (basesink, pad, _PR_IS_EVENT, obj,
+ prerollable);
GST_PAD_PREROLL_UNLOCK (pad);
return ret;
}
}
+static void
+gst_base_sink_flush_start (GstBaseSink * basesink, GstPad * pad)
+{
+ /* make sure we are not blocked on the clock also clear any pending
+ * eos state. */
+ gst_base_sink_set_flushing (basesink, pad, TRUE);
+
+ /* we grab the stream lock but that is not needed since setting the
+ * sink to flushing would make sure no state commit is being done
+ * anymore */
+ GST_PAD_STREAM_LOCK (pad);
+ gst_base_sink_reset_qos (basesink);
+ if (basesink->priv->async_enabled) {
+ /* and we need to commit our state again on the next
+ * prerolled buffer */
+ basesink->playing_async = TRUE;
+ gst_element_lost_state (GST_ELEMENT_CAST (basesink));
+ } else {
+ basesink->priv->have_latency = TRUE;
+ }
+ gst_base_sink_set_last_buffer (basesink, NULL);
+ GST_PAD_STREAM_UNLOCK (pad);
+}
+
+static void
+gst_base_sink_flush_stop (GstBaseSink * basesink, GstPad * pad)
+{
+ /* unset flushing so we can accept new data, this also flushes out any EOS
+ * event. */
+ gst_base_sink_set_flushing (basesink, pad, FALSE);
+
+ /* for position reporting */
+ GST_OBJECT_LOCK (basesink);
+ basesink->priv->current_sstart = GST_CLOCK_TIME_NONE;
+ basesink->priv->current_sstop = GST_CLOCK_TIME_NONE;
+ basesink->priv->eos_rtime = GST_CLOCK_TIME_NONE;
+ basesink->priv->call_preroll = TRUE;
+ basesink->priv->current_step.valid = FALSE;
+ basesink->priv->pending_step.valid = FALSE;
+ if (basesink->pad_mode == GST_ACTIVATE_PUSH) {
+ /* we need new segment info after the flush. */
+ basesink->have_newsegment = FALSE;
+ gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
+ gst_segment_init (basesink->abidata.ABI.clip_segment, GST_FORMAT_UNDEFINED);
+ }
+ GST_OBJECT_UNLOCK (basesink);
+}
+
static gboolean
gst_base_sink_event (GstPad * pad, GstEvent * event)
{
bclass = GST_BASE_SINK_GET_CLASS (basesink);
- GST_DEBUG_OBJECT (basesink, "event %p (%s)", event,
- GST_EVENT_TYPE_NAME (event));
+ GST_DEBUG_OBJECT (basesink, "reveived event %p %" GST_PTR_FORMAT, event,
+ event);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
gst_event_unref (event);
} else {
/* we set the received EOS flag here so that we can use it when testing if
- * we are prerolled and to refure more buffers. */
+ * we are prerolled and to refuse more buffers. */
basesink->priv->received_eos = TRUE;
/* EOS is a prerollable object, we call the unlocked version because it
* does not check the received_eos flag. */
ret = gst_base_sink_queue_object_unlocked (basesink, pad,
- GST_MINI_OBJECT_CAST (event), TRUE);
+ _PR_IS_EVENT, GST_MINI_OBJECT_CAST (event), TRUE);
if (G_UNLIKELY (ret != GST_FLOW_OK))
result = FALSE;
}
case GST_EVENT_NEWSEGMENT:
{
GstFlowReturn ret;
+ gboolean update;
GST_DEBUG_OBJECT (basesink, "newsegment %p", event);
if (G_UNLIKELY (basesink->flushing))
goto flushing;
- if (G_UNLIKELY (basesink->priv->received_eos)) {
+ gst_event_parse_new_segment_full (event, &update, NULL, NULL, NULL, NULL,
+ NULL, NULL);
+
+ if (G_UNLIKELY (basesink->priv->received_eos && !update)) {
/* we can't accept anything when we are EOS */
result = FALSE;
gst_event_unref (event);
} else {
/* the new segment is a non prerollable item and does not block anything,
- * we need to configure the current clipping segment and insert the event
+ * we need to configure the current clipping segment and insert the event
* in the queue to serialize it with the buffers for rendering. */
gst_base_sink_configure_segment (basesink, pad, event,
basesink->abidata.ABI.clip_segment);
ret =
gst_base_sink_queue_object_unlocked (basesink, pad,
- GST_MINI_OBJECT_CAST (event), FALSE);
+ _PR_IS_EVENT, GST_MINI_OBJECT_CAST (event), FALSE);
if (G_UNLIKELY (ret != GST_FLOW_OK))
result = FALSE;
else {
GST_DEBUG_OBJECT (basesink, "flush-start %p", event);
- /* make sure we are not blocked on the clock also clear any pending
- * eos state. */
- gst_base_sink_set_flushing (basesink, pad, TRUE);
-
- /* we grab the stream lock but that is not needed since setting the
- * sink to flushing would make sure no state commit is being done
- * anymore */
- GST_PAD_STREAM_LOCK (pad);
- gst_base_sink_reset_qos (basesink);
- if (basesink->priv->async_enabled) {
- /* and we need to commit our state again on the next
- * prerolled buffer */
- basesink->playing_async = TRUE;
- gst_element_lost_state (GST_ELEMENT_CAST (basesink));
- } else {
- basesink->priv->have_latency = TRUE;
- basesink->need_preroll = FALSE;
- }
- gst_base_sink_set_last_buffer (basesink, NULL);
- GST_PAD_STREAM_UNLOCK (pad);
+ gst_base_sink_flush_start (basesink, pad);
gst_event_unref (event);
break;
GST_DEBUG_OBJECT (basesink, "flush-stop %p", event);
- /* unset flushing so we can accept new data, this also flushes out any EOS
- * event. */
- gst_base_sink_set_flushing (basesink, pad, FALSE);
-
- /* for position reporting */
- GST_OBJECT_LOCK (basesink);
- basesink->priv->current_sstart = -1;
- basesink->priv->current_sstop = -1;
- basesink->priv->eos_rtime = -1;
- basesink->have_newsegment = FALSE;
- GST_OBJECT_UNLOCK (basesink);
-
- /* we need new segment info after the flush. */
- gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
- gst_segment_init (basesink->abidata.ABI.clip_segment,
- GST_FORMAT_UNDEFINED);
+ gst_base_sink_flush_stop (basesink, pad);
gst_event_unref (event);
break;
* 2) we are syncing on the clock
*/
is_prerolled = basesink->have_preroll || basesink->priv->received_eos;
- res = !is_prerolled && basesink->pad_mode != GST_ACTIVATE_PULL;
+ res = !is_prerolled;
+
GST_DEBUG_OBJECT (basesink, "have_preroll: %d, EOS: %d => needs preroll: %d",
basesink->have_preroll, basesink->priv->received_eos, res);
return res;
}
-/* with STREAM_LOCK, PREROLL_LOCK
+/* with STREAM_LOCK, PREROLL_LOCK
*
* Takes a buffer and compare the timestamps with the last segment.
* If the buffer falls outside of the segment boundaries, drop it.
*/
static GstFlowReturn
gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
- GstBuffer * buf)
+ guint8 obj_type, gpointer obj)
{
GstBaseSinkClass *bclass;
GstFlowReturn result;
GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
GstSegment *clip_segment;
+ GstBuffer *time_buf;
if (G_UNLIKELY (basesink->flushing))
goto flushing;
if (G_UNLIKELY (basesink->priv->received_eos))
goto was_eos;
+ if (OBJ_IS_BUFFERLIST (obj_type)) {
+ time_buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0, 0);
+ g_assert (NULL != time_buf);
+ } else {
+ time_buf = GST_BUFFER_CAST (obj);
+ }
+
/* for code clarity */
clip_segment = basesink->abidata.ABI.clip_segment;
/* check if the buffer needs to be dropped, we first ask the subclass for the
* start and end */
if (bclass->get_times)
- bclass->get_times (basesink, buf, &start, &end);
+ bclass->get_times (basesink, time_buf, &start, &end);
- if (start == -1) {
+ if (!GST_CLOCK_TIME_IS_VALID (start)) {
/* if the subclass does not want sync, we use our own values so that we at
* least clip the buffer to the segment */
- gst_base_sink_get_times (basesink, buf, &start, &end);
+ gst_base_sink_get_times (basesink, time_buf, &start, &end);
}
GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT
/* now we can process the buffer in the queue, this function takes ownership
* of the buffer */
result = gst_base_sink_queue_object_unlocked (basesink, pad,
- GST_MINI_OBJECT_CAST (buf), TRUE);
-
+ obj_type, obj, TRUE);
return result;
/* ERRORS */
flushing:
{
GST_DEBUG_OBJECT (basesink, "sink is flushing");
- gst_buffer_unref (buf);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
return GST_FLOW_WRONG_STATE;
}
was_eos:
{
GST_DEBUG_OBJECT (basesink,
"we are EOS, dropping object, return UNEXPECTED");
- gst_buffer_unref (buf);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
return GST_FLOW_UNEXPECTED;
}
out_of_segment:
{
GST_DEBUG_OBJECT (basesink, "dropping buffer, out of clipping segment");
- gst_buffer_unref (buf);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
return GST_FLOW_OK;
}
}
/* with STREAM_LOCK
*/
static GstFlowReturn
-gst_base_sink_chain (GstPad * pad, GstBuffer * buf)
+gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad,
+ guint8 obj_type, gpointer obj)
{
- GstBaseSink *basesink;
GstFlowReturn result;
- basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));
-
if (G_UNLIKELY (basesink->pad_mode != GST_ACTIVATE_PUSH))
goto wrong_mode;
GST_PAD_PREROLL_LOCK (pad);
- result = gst_base_sink_chain_unlocked (basesink, pad, buf);
+ result = gst_base_sink_chain_unlocked (basesink, pad, obj_type, obj);
GST_PAD_PREROLL_UNLOCK (pad);
done:
"Push on pad %s:%s, but it was not activated in push mode",
GST_DEBUG_PAD_NAME (pad));
GST_OBJECT_UNLOCK (pad);
- gst_buffer_unref (buf);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
/* we don't post an error message this will signal to the peer
* pushing that EOS is reached. */
result = GST_FLOW_UNEXPECTED;
}
}
-/* with STREAM_LOCK
- */
-static void
-gst_base_sink_loop (GstPad * pad)
+static GstFlowReturn
+gst_base_sink_chain (GstPad * pad, GstBuffer * buf)
{
GstBaseSink *basesink;
- GstBuffer *buf = NULL;
- GstFlowReturn result;
basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));
- g_assert (basesink->pad_mode == GST_ACTIVATE_PULL);
-
- GST_DEBUG_OBJECT (basesink, "pulling %" G_GUINT64_FORMAT ", %u",
- basesink->offset, (guint) DEFAULT_SIZE);
+ return gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER, buf);
+}
- result = gst_pad_pull_range (pad, basesink->offset, DEFAULT_SIZE, &buf);
- if (G_UNLIKELY (result != GST_FLOW_OK))
- goto paused;
+static GstFlowReturn
+gst_base_sink_chain_list (GstPad * pad, GstBufferList * list)
+{
+ GstBaseSink *basesink;
+ GstBaseSinkClass *bclass;
+ GstFlowReturn result;
- if (G_UNLIKELY (buf == NULL))
- goto no_buffer;
+ basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));
+ bclass = GST_BASE_SINK_GET_CLASS (basesink);
- basesink->offset += GST_BUFFER_SIZE (buf);
+ if (G_LIKELY (bclass->render_list)) {
+ result = gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFERLIST, list);
+ } else {
+ GstBufferListIterator *it;
+ GstBuffer *group;
- GST_PAD_PREROLL_LOCK (pad);
- result = gst_base_sink_chain_unlocked (basesink, pad, buf);
- GST_PAD_PREROLL_UNLOCK (pad);
- if (G_UNLIKELY (result != GST_FLOW_OK))
- goto paused;
+ GST_INFO_OBJECT (pad, "chaining each group in list as a merged buffer");
- return;
+ it = gst_buffer_list_iterate (list);
- /* ERRORS */
-paused:
- {
- GST_LOG_OBJECT (basesink, "pausing task, reason %s",
- gst_flow_get_name (result));
- gst_pad_pause_task (pad);
- /* fatal errors and NOT_LINKED cause EOS */
- if (GST_FLOW_IS_FATAL (result) || result == GST_FLOW_NOT_LINKED) {
- /* FIXME, we shouldn't post EOS when we are operating in segment mode */
- gst_base_sink_event (pad, gst_event_new_eos ());
- /* EOS does not cause an ERROR message */
- if (result != GST_FLOW_UNEXPECTED) {
- GST_ELEMENT_ERROR (basesink, STREAM, FAILED,
- (_("Internal data stream error.")),
- ("stream stopped, reason %s", gst_flow_get_name (result)));
- }
+ if (gst_buffer_list_iterator_next_group (it)) {
+ do {
+ group = gst_buffer_list_iterator_merge_group (it);
+ if (group == NULL) {
+ group = gst_buffer_new ();
+ GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining empty group");
+ } else {
+ GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining group");
+ }
+ result = gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER, group);
+ } while (result == GST_FLOW_OK
+ && gst_buffer_list_iterator_next_group (it));
+ } else {
+ GST_CAT_INFO_OBJECT (GST_CAT_SCHEDULING, pad, "chaining empty group");
+ result =
+ gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER,
+ gst_buffer_new ());
}
- return;
- }
-no_buffer:
- {
- GST_LOG_OBJECT (basesink, "no buffer, pausing");
- result = GST_FLOW_ERROR;
- goto paused;
+ gst_buffer_list_iterator_free (it);
+ gst_buffer_list_unref (list);
}
+ return result;
}
+
static gboolean
-gst_base_sink_set_flushing (GstBaseSink * basesink, GstPad * pad,
- gboolean flushing)
+gst_base_sink_default_do_seek (GstBaseSink * sink, GstSegment * segment)
{
- GstBaseSinkClass *bclass;
-
- bclass = GST_BASE_SINK_GET_CLASS (basesink);
+ gboolean res = TRUE;
- if (flushing) {
- /* unlock any subclasses, we need to do this before grabbing the
- * PREROLL_LOCK since we hold this lock before going into ::render. */
- if (bclass->unlock)
- bclass->unlock (basesink);
+ /* update our offset if the start/stop position was updated */
+ if (segment->format == GST_FORMAT_BYTES) {
+ segment->time = segment->start;
+ } else if (segment->start == 0) {
+ /* seek to start, we can implement a default for this. */
+ segment->time = 0;
+ } else {
+ res = FALSE;
+ GST_INFO_OBJECT (sink, "Can't do a default seek");
}
- GST_PAD_PREROLL_LOCK (pad);
- basesink->flushing = flushing;
- if (flushing) {
- /* step 1, now that we have the PREROLL lock, clear our unlock request */
- if (bclass->unlock_stop)
- bclass->unlock_stop (basesink);
-
- /* set need_preroll before we unblock the clock. If the clock is unblocked
- * before timing out, we can reuse the buffer for preroll. */
- basesink->need_preroll = TRUE;
+ return res;
+}
- /* step 2, unblock clock sync (if any) or any other blocking thing */
- if (basesink->clock_id) {
- gst_clock_id_unschedule (basesink->clock_id);
- }
+#define SEEK_TYPE_IS_RELATIVE(t) (((t) != GST_SEEK_TYPE_NONE) && ((t) != GST_SEEK_TYPE_SET))
+
+static gboolean
+gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink,
+ GstEvent * event, GstSegment * segment)
+{
+ /* By default, we try one of 2 things:
+ * - For absolute seek positions, convert the requested position to our
+ * configured processing format and place it in the output segment \
+ * - For relative seek positions, convert our current (input) values to the
+ * seek format, adjust by the relative seek offset and then convert back to
+ * the processing format
+ */
+ GstSeekType cur_type, stop_type;
+ gint64 cur, stop;
+ GstSeekFlags flags;
+ GstFormat seek_format, dest_format;
+ gdouble rate;
+ gboolean update;
+ gboolean res = TRUE;
+
+ gst_event_parse_seek (event, &rate, &seek_format, &flags,
+ &cur_type, &cur, &stop_type, &stop);
+ dest_format = segment->format;
+
+ if (seek_format == dest_format) {
+ gst_segment_set_seek (segment, rate, seek_format, flags,
+ cur_type, cur, 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 */
+ res =
+ gst_pad_query_convert (sink->sinkpad, seek_format, cur, &dest_format,
+ &cur);
+ cur_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 */
+ res =
+ gst_pad_query_convert (sink->sinkpad, seek_format, stop, &dest_format,
+ &stop);
+ stop_type = GST_SEEK_TYPE_SET;
+ }
+
+ /* And finally, configure our output segment in the desired format */
+ gst_segment_set_seek (segment, rate, dest_format, flags, cur_type, cur,
+ stop_type, stop, &update);
+
+ if (!res)
+ goto no_format;
+
+ return res;
+
+no_format:
+ {
+ GST_DEBUG_OBJECT (sink, "undefined format given, seek aborted.");
+ return FALSE;
+ }
+}
+
+/* perform a seek, only executed in pull mode */
+static gboolean
+gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event)
+{
+ gboolean flush;
+ gdouble rate;
+ GstFormat seek_format, dest_format;
+ GstSeekFlags flags;
+ GstSeekType cur_type, stop_type;
+ gboolean seekseg_configured = FALSE;
+ gint64 cur, stop;
+ gboolean update, res = TRUE;
+ GstSegment seeksegment;
+
+ dest_format = sink->segment.format;
+
+ 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);
+
+ flush = flags & GST_SEEK_FLAG_FLUSH;
+ } else {
+ GST_DEBUG_OBJECT (sink, "performing seek without event");
+ flush = FALSE;
+ }
+
+ if (flush) {
+ GST_DEBUG_OBJECT (sink, "flushing upstream");
+ gst_pad_push_event (pad, gst_event_new_flush_start ());
+ gst_base_sink_flush_start (sink, pad);
+ } else {
+ GST_DEBUG_OBJECT (sink, "pausing pulling thread");
+ }
+
+ GST_PAD_STREAM_LOCK (pad);
+
+ /* If we configured the seeksegment above, don't overwrite it now. Otherwise
+ * copy the current segment info into the temp segment that we can actually
+ * attempt the seek with. We only update the real segment if the seek suceeds. */
+ if (!seekseg_configured) {
+ memcpy (&seeksegment, &sink->segment, sizeof (GstSegment));
+
+ /* now configure the final seek segment */
+ if (event) {
+ if (sink->segment.format != seek_format) {
+ /* OK, here's where we give the subclass a chance to convert the relative
+ * seek into an absolute one in the processing format. We set up any
+ * absolute seek above, before taking the stream lock. */
+ if (!gst_base_sink_default_prepare_seek_segment (sink, event,
+ &seeksegment)) {
+ GST_DEBUG_OBJECT (sink,
+ "Preparing the seek failed after flushing. " "Aborting seek");
+ res = FALSE;
+ }
+ } else {
+ /* The seek format matches our processing format, no need to ask the
+ * the subclass to configure the segment. */
+ gst_segment_set_seek (&seeksegment, rate, seek_format, flags,
+ cur_type, cur, stop_type, stop, &update);
+ }
+ }
+ /* Else, no seek event passed, so we're just (re)starting the
+ current segment. */
+ }
+
+ if (res) {
+ GST_DEBUG_OBJECT (sink, "segment configured from %" G_GINT64_FORMAT
+ " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT,
+ seeksegment.start, seeksegment.stop, seeksegment.last_stop);
+
+ /* do the seek, segment.last_stop contains the new position. */
+ 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 ());
+ gst_base_sink_flush_stop (sink, pad);
+ } else if (res && sink->abidata.ABI.running) {
+ /* we are running the current segment and doing a non-flushing seek,
+ * close the segment first based on the last_stop. */
+ GST_DEBUG_OBJECT (sink, "closing running segment %" G_GINT64_FORMAT
+ " to %" G_GINT64_FORMAT, sink->segment.start, sink->segment.last_stop);
+ }
+
+ /* The subclass must have converted the segment to the processing format
+ * by now */
+ if (res && seeksegment.format != dest_format) {
+ GST_DEBUG_OBJECT (sink, "Subclass failed to prepare a seek segment "
+ "in the correct format. Aborting seek.");
+ res = FALSE;
+ }
+
+ /* if successfull seek, we update our real segment and push
+ * out the new segment. */
+ if (res) {
+ memcpy (&sink->segment, &seeksegment, sizeof (GstSegment));
+
+ if (sink->segment.flags & GST_SEEK_FLAG_SEGMENT) {
+ gst_element_post_message (GST_ELEMENT (sink),
+ gst_message_new_segment_start (GST_OBJECT (sink),
+ sink->segment.format, sink->segment.last_stop));
+ }
+ }
+
+ sink->priv->discont = TRUE;
+ sink->abidata.ABI.running = TRUE;
+
+ GST_PAD_STREAM_UNLOCK (pad);
+
+ return res;
+}
+
+static void
+set_step_info (GstBaseSink * sink, GstStepInfo * current, GstStepInfo * pending,
+ guint seqnum, GstFormat format, guint64 amount, gdouble rate,
+ gboolean flush, gboolean intermediate)
+{
+ GST_OBJECT_LOCK (sink);
+ pending->seqnum = seqnum;
+ pending->format = format;
+ pending->amount = amount;
+ pending->position = 0;
+ pending->rate = rate;
+ pending->flush = flush;
+ pending->intermediate = intermediate;
+ pending->valid = TRUE;
+ /* flush invalidates the current stepping segment */
+ if (flush)
+ current->valid = FALSE;
+ GST_OBJECT_UNLOCK (sink);
+}
+
+static gboolean
+gst_base_sink_perform_step (GstBaseSink * sink, GstPad * pad, GstEvent * event)
+{
+ GstBaseSinkPrivate *priv;
+ GstBaseSinkClass *bclass;
+ gboolean flush, intermediate;
+ gdouble rate;
+ GstFormat format;
+ guint64 amount;
+ guint seqnum;
+ GstStepInfo *pending, *current;
+ GstMessage *message;
+
+ bclass = GST_BASE_SINK_GET_CLASS (sink);
+ priv = sink->priv;
+
+ GST_DEBUG_OBJECT (sink, "performing step with event %p", event);
+
+ gst_event_parse_step (event, &format, &amount, &rate, &flush, &intermediate);
+ seqnum = gst_event_get_seqnum (event);
+
+ pending = &priv->pending_step;
+ current = &priv->current_step;
+
+ /* post message first */
+ message = gst_message_new_step_start (GST_OBJECT (sink), FALSE, format,
+ amount, rate, flush, intermediate);
+ gst_message_set_seqnum (message, seqnum);
+ gst_element_post_message (GST_ELEMENT (sink), message);
+
+ if (flush) {
+ /* we need to call ::unlock before locking PREROLL_LOCK
+ * since we lock it before going into ::render */
+ if (bclass->unlock)
+ bclass->unlock (sink);
+
+ GST_PAD_PREROLL_LOCK (sink->sinkpad);
+ /* now that we have the PREROLL lock, clear our unlock request */
+ if (bclass->unlock_stop)
+ bclass->unlock_stop (sink);
+
+ /* update the stepinfo and make it valid */
+ set_step_info (sink, current, pending, seqnum, format, amount, rate, flush,
+ intermediate);
+
+ if (sink->priv->async_enabled) {
+ /* and we need to commit our state again on the next
+ * prerolled buffer */
+ sink->playing_async = TRUE;
+ priv->pending_step.need_preroll = TRUE;
+ sink->need_preroll = FALSE;
+ gst_element_lost_state_full (GST_ELEMENT_CAST (sink), FALSE);
+ } else {
+ sink->priv->have_latency = TRUE;
+ sink->need_preroll = FALSE;
+ }
+ priv->current_sstart = GST_CLOCK_TIME_NONE;
+ priv->current_sstop = GST_CLOCK_TIME_NONE;
+ priv->eos_rtime = GST_CLOCK_TIME_NONE;
+ priv->call_preroll = TRUE;
+ gst_base_sink_set_last_buffer (sink, NULL);
+ gst_base_sink_reset_qos (sink);
+
+ if (sink->clock_id) {
+ gst_clock_id_unschedule (sink->clock_id);
+ }
+
+ if (sink->have_preroll) {
+ GST_DEBUG_OBJECT (sink, "signal waiter");
+ priv->step_unlock = TRUE;
+ GST_PAD_PREROLL_SIGNAL (sink->sinkpad);
+ }
+ GST_PAD_PREROLL_UNLOCK (sink->sinkpad);
+ } else {
+ /* update the stepinfo and make it valid */
+ set_step_info (sink, current, pending, seqnum, format, amount, rate, flush,
+ intermediate);
+ }
+
+ return TRUE;
+}
+
+/* with STREAM_LOCK
+ */
+static void
+gst_base_sink_loop (GstPad * pad)
+{
+ GstBaseSink *basesink;
+ GstBuffer *buf = NULL;
+ GstFlowReturn result;
+ guint blocksize;
+ guint64 offset;
+
+ basesink = GST_BASE_SINK (GST_OBJECT_PARENT (pad));
+
+ g_assert (basesink->pad_mode == GST_ACTIVATE_PULL);
+
+ if ((blocksize = basesink->priv->blocksize) == 0)
+ blocksize = -1;
+
+ offset = basesink->segment.last_stop;
+
+ GST_DEBUG_OBJECT (basesink, "pulling %" G_GUINT64_FORMAT ", %u",
+ offset, blocksize);
+
+ result = gst_pad_pull_range (pad, offset, blocksize, &buf);
+ if (G_UNLIKELY (result != GST_FLOW_OK))
+ goto paused;
+
+ if (G_UNLIKELY (buf == NULL))
+ goto no_buffer;
+
+ offset += GST_BUFFER_SIZE (buf);
+
+ gst_segment_set_last_stop (&basesink->segment, GST_FORMAT_BYTES, offset);
+
+ GST_PAD_PREROLL_LOCK (pad);
+ result = gst_base_sink_chain_unlocked (basesink, pad, _PR_IS_BUFFER, buf);
+ GST_PAD_PREROLL_UNLOCK (pad);
+ if (G_UNLIKELY (result != GST_FLOW_OK))
+ goto paused;
+
+ return;
+
+ /* ERRORS */
+paused:
+ {
+ GST_LOG_OBJECT (basesink, "pausing task, reason %s",
+ gst_flow_get_name (result));
+ gst_pad_pause_task (pad);
+ if (result == GST_FLOW_UNEXPECTED) {
+ /* perform EOS logic */
+ if (basesink->segment.flags & GST_SEEK_FLAG_SEGMENT) {
+ gst_element_post_message (GST_ELEMENT_CAST (basesink),
+ gst_message_new_segment_done (GST_OBJECT_CAST (basesink),
+ basesink->segment.format, basesink->segment.last_stop));
+ } else {
+ gst_base_sink_event (pad, gst_event_new_eos ());
+ }
+ } else if (result == GST_FLOW_NOT_LINKED || result <= GST_FLOW_UNEXPECTED) {
+ /* for fatal errors we post an error message, post the error
+ * 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_base_sink_event (pad, gst_event_new_eos ());
+ }
+ return;
+ }
+no_buffer:
+ {
+ GST_LOG_OBJECT (basesink, "no buffer, pausing");
+ GST_ELEMENT_ERROR (basesink, STREAM, FAILED,
+ (_("Internal data flow error.")), ("element returned NULL buffer"));
+ result = GST_FLOW_ERROR;
+ goto paused;
+ }
+}
+
+static gboolean
+gst_base_sink_set_flushing (GstBaseSink * basesink, GstPad * pad,
+ gboolean flushing)
+{
+ GstBaseSinkClass *bclass;
+
+ bclass = GST_BASE_SINK_GET_CLASS (basesink);
+
+ if (flushing) {
+ /* unlock any subclasses, we need to do this before grabbing the
+ * PREROLL_LOCK since we hold this lock before going into ::render. */
+ if (bclass->unlock)
+ bclass->unlock (basesink);
+ }
+
+ GST_PAD_PREROLL_LOCK (pad);
+ basesink->flushing = flushing;
+ if (flushing) {
+ /* step 1, now that we have the PREROLL lock, clear our unlock request */
+ if (bclass->unlock_stop)
+ bclass->unlock_stop (basesink);
+
+ /* set need_preroll before we unblock the clock. If the clock is unblocked
+ * before timing out, we can reuse the buffer for preroll. */
+ basesink->need_preroll = TRUE;
+
+ /* step 2, unblock clock sync (if any) or any other blocking thing */
+ if (basesink->clock_id) {
+ gst_clock_id_unschedule (basesink->clock_id);
+ }
/* flush out the data thread if it's locked in finish_preroll, this will
* also flush out the EOS state */
gst_base_sink_set_flushing (basesink, pad, FALSE);
- if (basesink->can_activate_pull && gst_pad_check_pull_range (pad)
- && gst_pad_activate_pull (pad, TRUE)) {
- GST_DEBUG_OBJECT (basesink, "Success activating pull mode");
- result = TRUE;
- } else {
- GST_DEBUG_OBJECT (basesink, "Falling back to push mode");
- if (gst_pad_activate_push (pad, TRUE)) {
- GST_DEBUG_OBJECT (basesink, "Success activating push mode");
- result = TRUE;
- }
+ /* we need to have the pull mode enabled */
+ if (!basesink->can_activate_pull) {
+ GST_DEBUG_OBJECT (basesink, "pull mode disabled");
+ goto fallback;
}
+ /* check if downstreams supports pull mode at all */
+ if (!gst_pad_check_pull_range (pad)) {
+ GST_DEBUG_OBJECT (basesink, "pull mode not supported");
+ goto fallback;
+ }
+
+ /* set the pad mode before starting the task so that it's in the
+ * correct state for the new thread. also the sink set_caps and get_caps
+ * function checks this */
+ basesink->pad_mode = GST_ACTIVATE_PULL;
+
+ /* we first try to negotiate a format so that when we try to activate
+ * downstream, it knows about our format */
+ if (!gst_base_sink_negotiate_pull (basesink)) {
+ GST_DEBUG_OBJECT (basesink, "failed to negotiate in pull mode");
+ goto fallback;
+ }
+
+ /* ok activate now */
+ if (!gst_pad_activate_pull (pad, TRUE)) {
+ /* clear any pending caps */
+ GST_OBJECT_LOCK (basesink);
+ gst_caps_replace (&basesink->priv->pull_caps, NULL);
+ GST_OBJECT_UNLOCK (basesink);
+ GST_DEBUG_OBJECT (basesink, "failed to activate in pull mode");
+ goto fallback;
+ }
+
+ GST_DEBUG_OBJECT (basesink, "Success activating pull mode");
+ result = TRUE;
+ goto done;
+
+ /* push mode fallback */
+fallback:
+ GST_DEBUG_OBJECT (basesink, "Falling back to push mode");
+ if ((result = gst_pad_activate_push (pad, TRUE))) {
+ GST_DEBUG_OBJECT (basesink, "Success activating push mode");
+ }
+
+done:
if (!result) {
GST_WARNING_OBJECT (basesink, "Could not activate pad in either mode");
gst_base_sink_set_flushing (basesink, pad, TRUE);
} else if (gst_caps_is_fixed (caps)) {
if (!gst_pad_set_caps (GST_BASE_SINK_PAD (basesink), caps))
goto could_not_set_caps;
+
+ GST_OBJECT_LOCK (basesink);
+ gst_caps_replace (&basesink->priv->pull_caps, caps);
+ GST_OBJECT_UNLOCK (basesink);
+
result = TRUE;
}
bclass = GST_BASE_SINK_GET_CLASS (basesink);
if (active) {
- if (!basesink->can_activate_pull) {
- result = FALSE;
- basesink->pad_mode = GST_ACTIVATE_NONE;
- } else {
- GstPad *peer = gst_pad_get_peer (pad);
+ GstFormat format;
+ gint64 duration;
- if (G_UNLIKELY (peer == NULL)) {
- g_warning ("Trying to activate pad in pull mode, but no peer");
- result = FALSE;
- basesink->pad_mode = GST_ACTIVATE_NONE;
- } else {
- if (gst_pad_activate_pull (peer, TRUE)) {
- /* we mark we have a newsegment here because pull based
- * mode works just fine without having a newsegment before the
- * first buffer */
- gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
- gst_segment_init (basesink->abidata.ABI.clip_segment,
- GST_FORMAT_UNDEFINED);
- GST_OBJECT_LOCK (basesink);
- basesink->have_newsegment = TRUE;
- GST_OBJECT_UNLOCK (basesink);
+ /* we mark we have a newsegment here because pull based
+ * mode works just fine without having a newsegment before the
+ * first buffer */
+ format = GST_FORMAT_BYTES;
- /* set the pad mode before starting the task so that it's in the
- correct state for the new thread. also the sink set_caps function
- checks this */
- basesink->pad_mode = GST_ACTIVATE_PULL;
- if ((result = gst_base_sink_negotiate_pull (basesink))) {
- if (bclass->activate_pull)
- result = bclass->activate_pull (basesink, TRUE);
- else
- result = FALSE;
- }
- /* but if starting the thread fails, set it back */
- if (!result)
- basesink->pad_mode = GST_ACTIVATE_NONE;
- } else {
- GST_DEBUG_OBJECT (pad, "Failed to activate peer in pull mode");
- result = FALSE;
- basesink->pad_mode = GST_ACTIVATE_NONE;
- }
- gst_object_unref (peer);
- }
+ gst_segment_init (&basesink->segment, format);
+ gst_segment_init (basesink->abidata.ABI.clip_segment, format);
+ GST_OBJECT_LOCK (basesink);
+ basesink->have_newsegment = TRUE;
+ GST_OBJECT_UNLOCK (basesink);
+
+ /* get the peer duration in bytes */
+ result = gst_pad_query_peer_duration (pad, &format, &duration);
+ if (result) {
+ GST_DEBUG_OBJECT (basesink,
+ "setting duration in bytes to %" G_GINT64_FORMAT, duration);
+ gst_segment_set_duration (basesink->abidata.ABI.clip_segment, format,
+ duration);
+ gst_segment_set_duration (&basesink->segment, format, duration);
+ } else {
+ GST_DEBUG_OBJECT (basesink, "unknown duration");
}
+
+ if (bclass->activate_pull)
+ result = bclass->activate_pull (basesink, TRUE);
+ else
+ result = FALSE;
+
+ if (!result)
+ goto activate_failed;
+
} else {
if (G_UNLIKELY (basesink->pad_mode != GST_ACTIVATE_PULL)) {
g_warning ("Internal GStreamer activation error!!!");
if (bclass->activate_pull)
result &= bclass->activate_pull (basesink, FALSE);
basesink->pad_mode = GST_ACTIVATE_NONE;
+ /* clear any pending caps */
+ GST_OBJECT_LOCK (basesink);
+ gst_caps_replace (&basesink->priv->pull_caps, NULL);
+ GST_OBJECT_UNLOCK (basesink);
}
}
-
gst_object_unref (basesink);
return result;
+
+ /* ERRORS */
+activate_failed:
+ {
+ /* reset, as starting the thread failed */
+ basesink->pad_mode = GST_ACTIVATE_NONE;
+
+ GST_ERROR_OBJECT (basesink, "subclass failed to activate in pull mode");
+ return FALSE;
+ }
}
/* send an event to our sinkpad peer. */
GstPad *pad;
GstBaseSink *basesink = GST_BASE_SINK (element);
gboolean forward, result = TRUE;
+ GstActivateMode mode;
+
+ GST_OBJECT_LOCK (element);
+ /* get the pad and the scheduling mode */
+ pad = gst_object_ref (basesink->sinkpad);
+ mode = basesink->pad_mode;
+ GST_OBJECT_UNLOCK (element);
/* only push UPSTREAM events upstream */
forward = GST_EVENT_IS_UPSTREAM (event);
+ GST_DEBUG_OBJECT (basesink, "handling event %p %" GST_PTR_FORMAT, event,
+ event);
+
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_LATENCY:
{
* it to the clock. */
GST_OBJECT_LOCK (element);
basesink->priv->latency = latency;
+ if (!basesink->priv->have_latency)
+ forward = FALSE;
GST_OBJECT_UNLOCK (element);
GST_DEBUG_OBJECT (basesink, "latency set to %" GST_TIME_FORMAT,
GST_TIME_ARGS (latency));
* when a particular piece of data will be rendered. */
break;
}
+ case GST_EVENT_SEEK:
+ /* in pull mode we will execute the seek */
+ if (mode == GST_ACTIVATE_PULL)
+ result = gst_base_sink_perform_seek (basesink, pad, event);
+ break;
+ case GST_EVENT_STEP:
+ result = gst_base_sink_perform_step (basesink, pad, event);
+ forward = FALSE;
+ break;
default:
break;
}
if (forward) {
- GST_OBJECT_LOCK (element);
- pad = gst_object_ref (basesink->sinkpad);
- GST_OBJECT_UNLOCK (element);
-
result = gst_pad_push_event (pad, event);
-
- gst_object_unref (pad);
} else {
/* not forwarded, unref the event */
gst_event_unref (event);
}
+
+ gst_object_unref (pad);
return result;
}
static gboolean
-gst_base_sink_peer_query (GstBaseSink * sink, GstQuery * query)
+gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
+ gint64 * cur, gboolean * upstream)
{
- GstPad *peer;
+ GstClock *clock = NULL;
gboolean res = FALSE;
+ GstFormat oformat, tformat;
+ GstSegment *segment;
+ GstClockTime now, latency;
+ GstClockTimeDiff base;
+ gint64 time, accum, duration;
+ gdouble rate;
+ gint64 last;
+ gboolean last_seen, with_clock, in_paused;
- if ((peer = gst_pad_get_peer (sink->sinkpad))) {
- res = gst_pad_query (peer, query);
- gst_object_unref (peer);
+ GST_OBJECT_LOCK (basesink);
+ /* we can only get the segment when we are not NULL or READY */
+ if (!basesink->have_newsegment)
+ goto wrong_state;
+
+ in_paused = FALSE;
+ /* when not in PLAYING or when we're busy with a state change, we
+ * cannot read from the clock so we report time based on the
+ * last seen timestamp. */
+ if (GST_STATE (basesink) != GST_STATE_PLAYING ||
+ GST_STATE_PENDING (basesink) != GST_STATE_VOID_PENDING) {
+ in_paused = TRUE;
}
- return res;
-}
-
-/* get the end position of the last seen object, this is used
- * for EOS and for making sure that we don't report a position we
- * have not reached yet. */
-static gboolean
-gst_base_sink_get_position_last (GstBaseSink * basesink, gint64 * cur)
-{
- /* return last observed stream time */
- *cur = basesink->priv->current_sstop;
- GST_DEBUG_OBJECT (basesink, "POSITION: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (*cur));
- return TRUE;
-}
-
-/* get the position when we are PAUSED, this is the stream time of the buffer
- * that prerolled. If no buffer is prerolled (we are still flushing), this
- * value will be -1. */
-static gboolean
-gst_base_sink_get_position_paused (GstBaseSink * basesink, gint64 * cur)
-{
- gboolean res;
- gint64 time;
- GstSegment *segment;
+ /* we don't use the clip segment in pull mode, when seeking we update the
+ * main segment directly with the new segment values without it having to be
+ * activated by the rendering after preroll */
+ if (basesink->pad_mode == GST_ACTIVATE_PUSH)
+ segment = basesink->abidata.ABI.clip_segment;
+ else
+ segment = &basesink->segment;
- *cur = basesink->priv->current_sstart;
- segment = basesink->abidata.ABI.clip_segment;
+ /* our intermediate time format */
+ tformat = GST_FORMAT_TIME;
+ /* get the format in the segment */
+ oformat = segment->format;
- time = segment->time;
+ /* report with last seen position when EOS */
+ last_seen = basesink->eos;
- if (*cur != -1) {
- *cur = MAX (*cur, time);
- GST_DEBUG_OBJECT (basesink, "POSITION as max: %" GST_TIME_FORMAT
- ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (*cur), GST_TIME_ARGS (time));
- } else {
- /* we have no buffer, use the segment times. */
- if (segment->rate >= 0.0) {
- /* forward, next position is always the time of the segment */
- *cur = time;
- GST_DEBUG_OBJECT (basesink, "POSITION as time: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (*cur));
- } else {
- /* reverse, next expected timestamp is segment->stop. We use the function
- * to get things right for negative applied_rates. */
- *cur =
- gst_segment_to_stream_time (segment, GST_FORMAT_TIME, segment->stop);
- GST_DEBUG_OBJECT (basesink, "reverse POSITION: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (*cur));
- }
- }
- res = (*cur != -1);
+ /* assume we will use the clock for getting the current position */
+ with_clock = TRUE;
+ if (basesink->sync == FALSE)
+ with_clock = FALSE;
- return res;
-}
+ /* and we need a clock */
+ if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL))
+ with_clock = FALSE;
+ else
+ gst_object_ref (clock);
-static gboolean
-gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format,
- gint64 * cur, gboolean * upstream)
-{
- GstClock *clock;
- gboolean res = FALSE;
+ /* collect all data we need holding the lock */
+ if (GST_CLOCK_TIME_IS_VALID (segment->time))
+ time = segment->time;
+ else
+ time = 0;
- switch (format) {
- /* we can answer time format */
- case GST_FORMAT_TIME:
- {
- GstClockTime now, base, latency;
- gint64 time, accum, duration;
- gdouble rate;
- gint64 last;
+ if (GST_CLOCK_TIME_IS_VALID (segment->stop))
+ duration = segment->stop - segment->start;
+ else
+ duration = 0;
- GST_OBJECT_LOCK (basesink);
+ accum = segment->accum;
+ rate = segment->rate * segment->applied_rate;
+ latency = basesink->priv->latency;
- /* can only give answer based on the clock if not EOS */
- if (G_UNLIKELY (basesink->eos))
- goto in_eos;
+ if (oformat == GST_FORMAT_TIME) {
+ gint64 start, stop;
- /* we can only get the segment when we are not NULL or READY */
- if (!basesink->have_newsegment)
- goto wrong_state;
+ start = basesink->priv->current_sstart;
+ stop = basesink->priv->current_sstop;
- /* when not in PLAYING or when we're busy with a state change, we
- * cannot read from the clock so we report time based on the
- * last seen timestamp. */
- if (GST_STATE (basesink) != GST_STATE_PLAYING ||
- GST_STATE_PENDING (basesink) != GST_STATE_VOID_PENDING)
- goto in_pause;
+ 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->last_stop);
+ }
- /* we need to sync on the clock. */
- if (basesink->sync == FALSE)
- goto no_sync;
+ if (in_paused) {
+ /* in paused, use start_time */
+ base = GST_ELEMENT_START_TIME (basesink);
+ GST_DEBUG_OBJECT (basesink, "in paused, using start time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (base));
+ } else if (with_clock) {
+ /* else use clock when needed */
+ base = GST_ELEMENT_CAST (basesink)->base_time;
+ GST_DEBUG_OBJECT (basesink, "using clock and base time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (base));
+ } else {
+ /* else, no sync or clock -> no base time */
+ GST_DEBUG_OBJECT (basesink, "no sync or no clock");
+ base = -1;
+ }
- /* and we need a clock */
- if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL))
- goto no_sync;
+ /* no base, we can't calculate running_time, use last seem timestamp to report
+ * time */
+ if (base == -1)
+ last_seen = TRUE;
- /* collect all data we need holding the lock */
- if (GST_CLOCK_TIME_IS_VALID (basesink->segment.time))
- time = basesink->segment.time;
- else
- time = 0;
+ /* 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. */
+ GST_OBJECT_UNLOCK (basesink);
- if (GST_CLOCK_TIME_IS_VALID (basesink->segment.stop))
- duration = basesink->segment.stop - basesink->segment.start;
- else
- duration = 0;
-
- base = GST_ELEMENT_CAST (basesink)->base_time;
- accum = basesink->segment.accum;
- rate = basesink->segment.rate * basesink->segment.applied_rate;
- gst_base_sink_get_position_last (basesink, &last);
- latency = basesink->priv->latency;
-
- gst_object_ref (clock);
- /* 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. */
- GST_OBJECT_UNLOCK (basesink);
+ if (last_seen) {
+ /* in EOS or when no valid stream_time, report the value of last seen
+ * timestamp */
+ if (last == -1) {
+ /* no timestamp, we need to ask upstream */
+ GST_DEBUG_OBJECT (basesink, "no last seen timestamp, asking upstream");
+ res = FALSE;
+ *upstream = TRUE;
+ goto done;
+ }
+ GST_DEBUG_OBJECT (basesink, "using last seen timestamp %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (last));
+ *cur = last;
+ } else {
+ if (oformat != tformat) {
+ /* convert accum, time and duration to time */
+ if (!gst_pad_query_convert (basesink->sinkpad, oformat, accum, &tformat,
+ &accum))
+ goto convert_failed;
+ if (!gst_pad_query_convert (basesink->sinkpad, oformat, duration,
+ &tformat, &duration))
+ goto convert_failed;
+ if (!gst_pad_query_convert (basesink->sinkpad, oformat, time, &tformat,
+ &time))
+ goto convert_failed;
+ if (!gst_pad_query_convert (basesink->sinkpad, oformat, last, &tformat,
+ &last))
+ goto convert_failed;
+
+ /* assume time format from now on */
+ oformat = tformat;
+ }
+ if (!in_paused && with_clock) {
now = gst_clock_get_time (clock);
+ } else {
+ now = base;
+ base = 0;
+ }
- /* subtract base time and accumulated time from the clock time.
- * Make sure we don't go negative. This is the current time in
- * the segment which we need to scale with the combined
- * rate and applied rate. */
- base += accum;
- base += latency;
- base = MIN (now, base);
+ /* subtract base time and accumulated time from the clock time.
+ * Make sure we don't go negative. This is the current time in
+ * the segment which we need to scale with the combined
+ * rate and applied rate. */
+ base += accum;
+ base += latency;
+ if (GST_CLOCK_DIFF (base, now) < 0)
+ base = now;
- /* for negative rates we need to count back from from the segment
- * duration. */
- if (rate < 0.0)
- time += duration;
+ /* for negative rates we need to count back from the segment
+ * duration. */
+ if (rate < 0.0)
+ time += duration;
- *cur = time + gst_guint64_to_gdouble (now - base) * rate;
+ *cur = time + gst_guint64_to_gdouble (now - base) * rate;
- /* never report more than last seen position */
+ 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)
*cur = MIN (last, *cur);
+ }
- gst_object_unref (clock);
-
- res = TRUE;
+ GST_DEBUG_OBJECT (basesink,
+ "now %" GST_TIME_FORMAT " - base %" GST_TIME_FORMAT " - accum %"
+ GST_TIME_FORMAT " + time %" GST_TIME_FORMAT " last %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (now), GST_TIME_ARGS (base), GST_TIME_ARGS (accum),
+ GST_TIME_ARGS (time), GST_TIME_ARGS (last));
+ }
- GST_DEBUG_OBJECT (basesink,
- "now %" GST_TIME_FORMAT " - base %" GST_TIME_FORMAT " - accum %"
- GST_TIME_FORMAT " + time %" GST_TIME_FORMAT,
- GST_TIME_ARGS (now), GST_TIME_ARGS (base),
- GST_TIME_ARGS (accum), GST_TIME_ARGS (time));
- break;
- }
- default:
- /* cannot answer other than TIME, ask to send the query upstream. */
- *upstream = TRUE;
- break;
+ if (oformat != format) {
+ /* convert to final format */
+ if (!gst_pad_query_convert (basesink->sinkpad, oformat, *cur, &format, cur))
+ goto convert_failed;
}
+ res = TRUE;
+
done:
GST_DEBUG_OBJECT (basesink, "res: %d, POSITION: %" GST_TIME_FORMAT,
res, GST_TIME_ARGS (*cur));
+
+ if (clock)
+ gst_object_unref (clock);
+
return res;
/* special cases */
-in_eos:
- {
- GST_DEBUG_OBJECT (basesink, "position in EOS");
- res = gst_base_sink_get_position_last (basesink, cur);
- GST_OBJECT_UNLOCK (basesink);
- goto done;
- }
-in_pause:
- {
- GST_DEBUG_OBJECT (basesink, "position in PAUSED");
- res = gst_base_sink_get_position_paused (basesink, cur);
- GST_OBJECT_UNLOCK (basesink);
- goto done;
- }
wrong_state:
{
/* in NULL or READY we always return FALSE and -1 */
GST_OBJECT_UNLOCK (basesink);
goto done;
}
-no_sync:
+convert_failed:
{
- /* report last seen timestamp if any, else ask upstream to answer */
- if ((*cur = basesink->priv->current_sstart) != -1)
- res = TRUE;
- else
- *upstream = TRUE;
+ GST_DEBUG_OBJECT (basesink, "convert failed, try upstream");
+ *upstream = TRUE;
+ res = FALSE;
+ goto done;
+ }
+}
- GST_DEBUG_OBJECT (basesink, "no sync, res %d, POSITION %" GST_TIME_FORMAT,
- res, GST_TIME_ARGS (*cur));
- GST_OBJECT_UNLOCK (basesink);
- return res;
+static gboolean
+gst_base_sink_get_duration (GstBaseSink * basesink, GstFormat format,
+ gint64 * dur, gboolean * upstream)
+{
+ gboolean res = FALSE;
+
+ if (basesink->pad_mode == GST_ACTIVATE_PULL) {
+ GstFormat uformat = GST_FORMAT_BYTES;
+ gint64 uduration;
+
+ /* get the duration in bytes, in pull mode that's all we are sure to
+ * know. We have to explicitly get this value from upstream instead of
+ * using our cached value because it might change. Duration caching
+ * should be done at a higher level. */
+ res = gst_pad_query_peer_duration (basesink->sinkpad, &uformat, &uduration);
+ if (res) {
+ gst_segment_set_duration (&basesink->segment, uformat, uduration);
+ if (format != uformat) {
+ /* convert to the requested format */
+ res = gst_pad_query_convert (basesink->sinkpad, uformat, uduration,
+ &format, dur);
+ } else {
+ *dur = uduration;
+ }
+ }
+ *upstream = FALSE;
+ } else {
+ *upstream = TRUE;
}
+
+ return res;
+}
+
+static const GstQueryType *
+gst_base_sink_get_query_types (GstElement * element)
+{
+ static const GstQueryType query_types[] = {
+ GST_QUERY_DURATION,
+ GST_QUERY_POSITION,
+ GST_QUERY_SEGMENT,
+ GST_QUERY_LATENCY,
+ 0
+ };
+
+ return query_types;
}
static gboolean
gst_query_parse_position (query, &format, NULL);
- GST_DEBUG_OBJECT (basesink, "position format %d", format);
+ GST_DEBUG_OBJECT (basesink, "position query in format %s",
+ gst_format_get_name (format));
/* first try to get the position based on the clock */
if ((res =
gst_query_set_position (query, format, cur);
} else if (upstream) {
/* fallback to peer query */
- res = gst_base_sink_peer_query (basesink, query);
+ res = gst_pad_peer_query (basesink->sinkpad, query);
+ }
+ if (!res) {
+ /* we can handle a few things if upstream failed */
+ if (format == GST_FORMAT_PERCENT) {
+ gint64 dur = 0;
+ GstFormat uformat = GST_FORMAT_TIME;
+
+ res = gst_base_sink_get_position (basesink, GST_FORMAT_TIME, &cur,
+ &upstream);
+ if (!res && upstream) {
+ res = gst_pad_query_peer_position (basesink->sinkpad, &uformat,
+ &cur);
+ }
+ if (res) {
+ res = gst_base_sink_get_duration (basesink, GST_FORMAT_TIME, &dur,
+ &upstream);
+ if (!res && upstream) {
+ res = gst_pad_query_peer_duration (basesink->sinkpad, &uformat,
+ &dur);
+ }
+ }
+ if (res) {
+ gint64 pos;
+
+ pos = gst_util_uint64_scale (100 * GST_FORMAT_PERCENT_SCALE, cur,
+ dur);
+ gst_query_set_position (query, GST_FORMAT_PERCENT, pos);
+ }
+ }
}
break;
}
case GST_QUERY_DURATION:
- GST_DEBUG_OBJECT (basesink, "duration query");
- res = gst_base_sink_peer_query (basesink, query);
+ {
+ gint64 dur = 0;
+ GstFormat format;
+ gboolean upstream = FALSE;
+
+ gst_query_parse_duration (query, &format, NULL);
+
+ GST_DEBUG_OBJECT (basesink, "duration query in format %s",
+ gst_format_get_name (format));
+
+ if ((res =
+ gst_base_sink_get_duration (basesink, format, &dur, &upstream))) {
+ gst_query_set_duration (query, format, dur);
+ } else if (upstream) {
+ /* fallback to peer query */
+ res = gst_pad_peer_query (basesink->sinkpad, query);
+ }
+ if (!res) {
+ /* we can handle a few things if upstream failed */
+ if (format == GST_FORMAT_PERCENT) {
+ gst_query_set_duration (query, GST_FORMAT_PERCENT,
+ GST_FORMAT_PERCENT_MAX);
+ res = TRUE;
+ }
+ }
break;
+ }
case GST_QUERY_LATENCY:
{
gboolean live, us_live;
break;
case GST_QUERY_SEGMENT:
{
- /* FIXME, bring start/stop to stream time */
- gst_query_set_segment (query, basesink->segment.rate,
- GST_FORMAT_TIME, basesink->segment.start, basesink->segment.stop);
+ if (basesink->pad_mode == GST_ACTIVATE_PULL) {
+ gst_query_set_segment (query, basesink->segment.rate,
+ GST_FORMAT_TIME, basesink->segment.start, basesink->segment.stop);
+ res = TRUE;
+ } else {
+ res = gst_pad_peer_query (basesink->sinkpad, query);
+ }
break;
}
case GST_QUERY_SEEKING:
case GST_QUERY_CONVERT:
case GST_QUERY_FORMATS:
default:
- res = gst_base_sink_peer_query (basesink, query);
+ res = gst_pad_peer_query (basesink->sinkpad, query);
break;
}
+ GST_DEBUG_OBJECT (basesink, "query %s returns %d",
+ GST_QUERY_TYPE_NAME (query), res);
return res;
}
GST_FORMAT_UNDEFINED);
basesink->offset = 0;
basesink->have_preroll = FALSE;
+ priv->step_unlock = FALSE;
basesink->need_preroll = TRUE;
basesink->playing_async = TRUE;
- priv->current_sstart = -1;
- priv->current_sstop = -1;
- priv->eos_rtime = -1;
+ priv->current_sstart = GST_CLOCK_TIME_NONE;
+ priv->current_sstop = GST_CLOCK_TIME_NONE;
+ priv->eos_rtime = GST_CLOCK_TIME_NONE;
priv->latency = 0;
basesink->eos = FALSE;
priv->received_eos = FALSE;
gst_base_sink_reset_qos (basesink);
priv->commited = FALSE;
+ priv->call_preroll = TRUE;
+ priv->current_step.valid = FALSE;
+ priv->pending_step.valid = FALSE;
if (priv->async_enabled) {
GST_DEBUG_OBJECT (basesink, "doing async state change");
/* when async enabled, post async-start message and return ASYNC from
basesink->playing_async = FALSE;
basesink->need_preroll = FALSE;
if (basesink->eos) {
+ GstMessage *message;
+
/* need to post EOS message here */
GST_DEBUG_OBJECT (basesink, "Now posting EOS");
- gst_element_post_message (GST_ELEMENT_CAST (basesink),
- gst_message_new_eos (GST_OBJECT_CAST (basesink)));
+ message = gst_message_new_eos (GST_OBJECT_CAST (basesink));
+ gst_message_set_seqnum (message, basesink->priv->seqnum);
+ gst_element_post_message (GST_ELEMENT_CAST (basesink), message);
} else {
GST_DEBUG_OBJECT (basesink, "signal preroll");
GST_PAD_PREROLL_SIGNAL (basesink->sinkpad);
GST_DEBUG_OBJECT (basesink, "PAUSED to PLAYING, we are not prerolled");
basesink->need_preroll = TRUE;
basesink->playing_async = TRUE;
+ priv->call_preroll = TRUE;
priv->commited = FALSE;
if (priv->async_enabled) {
GST_DEBUG_OBJECT (basesink, "doing async state change");
}
switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- /* note that this is the upward case, which doesn't follow most
- patterns */
- if (basesink->pad_mode == GST_ACTIVATE_PULL) {
- GST_DEBUG_OBJECT (basesink, "basesink activated in pull mode, "
- "returning SUCCESS directly");
- GST_PAD_PREROLL_LOCK (basesink->sinkpad);
- gst_element_post_message (GST_ELEMENT_CAST (basesink),
- gst_message_new_async_done (GST_OBJECT_CAST (basesink)));
- GST_PAD_PREROLL_UNLOCK (basesink->sinkpad);
- ret = GST_STATE_CHANGE_SUCCESS;
- }
- break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED");
/* FIXME, make sure we cannot enter _render first */
bclass->unlock (basesink);
GST_PAD_PREROLL_LOCK (basesink->sinkpad);
+ GST_DEBUG_OBJECT (basesink, "got preroll lock");
/* now that we have the PREROLL lock, clear our unlock request */
if (bclass->unlock_stop)
bclass->unlock_stop (basesink);
basesink->need_preroll = TRUE;
if (basesink->clock_id) {
+ GST_DEBUG_OBJECT (basesink, "unschedule clock");
gst_clock_id_unschedule (basesink->clock_id);
}
basesink->playing_async = FALSE;
} else {
if (GST_STATE_TARGET (GST_ELEMENT (basesink)) <= GST_STATE_READY) {
+ GST_DEBUG_OBJECT (basesink, "element is <= READY");
ret = GST_STATE_CHANGE_SUCCESS;
} else {
GST_DEBUG_OBJECT (basesink,
"PLAYING to PAUSED, we are not prerolled");
basesink->playing_async = TRUE;
priv->commited = FALSE;
+ priv->call_preroll = TRUE;
if (priv->async_enabled) {
GST_DEBUG_OBJECT (basesink, "doing async state change");
ret = GST_STATE_CHANGE_ASYNC;
* messages so that the message handlers pick this up. */
GST_OBJECT_LOCK (basesink);
basesink->have_newsegment = FALSE;
- priv->current_sstart = -1;
- priv->current_sstop = -1;
+ priv->current_sstart = GST_CLOCK_TIME_NONE;
+ priv->current_sstop = GST_CLOCK_TIME_NONE;
priv->have_latency = FALSE;
+ if (priv->cached_clock_id) {
+ gst_clock_id_unref (priv->cached_clock_id);
+ priv->cached_clock_id = NULL;
+ }
GST_OBJECT_UNLOCK (basesink);
gst_base_sink_set_last_buffer (basesink, NULL);
+ priv->call_preroll = FALSE;
if (!priv->commited) {
if (priv->async_enabled) {
}
}
gst_base_sink_set_last_buffer (basesink, NULL);
+ priv->call_preroll = FALSE;
break;
default:
break;