#include <gst/gst_private.h>
#include "gstbasesink.h"
-#include <gst/gstmarshal.h>
#include <gst/gst-i18n-lib.h>
GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug);
/* for throttling and QoS */
GstClockTime earliest_in_time;
GstClockTime throttle_time;
-
- gboolean reset_time;
};
#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_CAN_ACTIVATE_PULL FALSE /* fixme: enable me */
GstEvent * event);
static gboolean default_element_query (GstElement * element, GstQuery * query);
-static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink, GstCaps * caps);
-static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
-static void gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
- GstClockTime * start, GstClockTime * end);
+static GstCaps *gst_base_sink_default_get_caps (GstBaseSink * sink,
+ GstCaps * caps);
+static gboolean gst_base_sink_default_set_caps (GstBaseSink * sink,
+ GstCaps * caps);
+static void gst_base_sink_default_get_times (GstBaseSink * basesink,
+ GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
static gboolean gst_base_sink_set_flushing (GstBaseSink * basesink,
GstPad * pad, gboolean flushing);
static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink,
static gboolean gst_base_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
-static gboolean default_sink_query (GstBaseSink * sink, GstQuery * query);
+static gboolean gst_base_sink_default_query (GstBaseSink * sink,
+ GstQuery * query);
static gboolean gst_base_sink_negotiate_pull (GstBaseSink * basesink);
-static void gst_base_sink_default_fixate (GstBaseSink * bsink, GstCaps * caps);
-static void gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps);
+static GstCaps *gst_base_sink_default_fixate (GstBaseSink * bsink,
+ GstCaps * caps);
+static GstCaps *gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps);
/* check if an object was too late */
static gboolean gst_base_sink_is_too_late (GstBaseSink * basesink,
GstMiniObject * obj, GstClockTime rstart, GstClockTime rstop,
GstClockReturn status, GstClockTimeDiff jitter);
-static GstFlowReturn gst_base_sink_preroll_object (GstBaseSink * basesink,
- guint8 obj_type, GstMiniObject * obj);
static void
gst_base_sink_class_init (GstBaseSinkClass * klass)
*/
g_object_class_install_property (gobject_class, PROP_THROTTLE_TIME,
g_param_spec_uint64 ("throttle-time", "Throttle time",
- "The time to keep between rendered buffers (unused)", 0, G_MAXUINT64,
+ "The time to keep between rendered buffers", 0, G_MAXUINT64,
DEFAULT_THROTTLE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state =
gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event);
gstelement_class->query = GST_DEBUG_FUNCPTR (default_element_query);
- klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_get_caps);
- klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps);
+ klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_default_get_caps);
+ klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_default_set_caps);
klass->fixate = GST_DEBUG_FUNCPTR (gst_base_sink_default_fixate);
klass->activate_pull =
GST_DEBUG_FUNCPTR (gst_base_sink_default_activate_pull);
- klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_get_times);
- klass->query = GST_DEBUG_FUNCPTR (default_sink_query);
+ klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_default_get_times);
+ klass->query = GST_DEBUG_FUNCPTR (gst_base_sink_default_query);
klass->event = GST_DEBUG_FUNCPTR (gst_base_sink_default_event);
klass->wait_eos = GST_DEBUG_FUNCPTR (gst_base_sink_default_wait_eos);
return caps;
}
-static void
+static GstCaps *
gst_base_sink_default_fixate (GstBaseSink * bsink, GstCaps * caps)
{
GST_DEBUG_OBJECT (bsink, "using default caps fixate function");
- gst_caps_fixate (caps);
+ return gst_caps_fixate (caps);
}
-static void
+static GstCaps *
gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps)
{
GstBaseSinkClass *bclass;
bclass = GST_BASE_SINK_GET_CLASS (bsink);
if (bclass->fixate)
- bclass->fixate (bsink, caps);
+ caps = bclass->fixate (bsink, caps);
+
+ return caps;
}
static void
gst_element_add_pad (GST_ELEMENT_CAST (basesink), basesink->sinkpad);
basesink->pad_mode = GST_PAD_MODE_NONE;
- basesink->preroll_lock = g_mutex_new ();
- basesink->preroll_cond = g_cond_new ();
+ g_mutex_init (&basesink->preroll_lock);
+ g_cond_init (&basesink->preroll_cond);
priv->have_latency = FALSE;
basesink->can_activate_push = DEFAULT_CAN_ACTIVATE_PUSH;
basesink = GST_BASE_SINK (object);
- g_mutex_free (basesink->preroll_lock);
- g_cond_free (basesink->preroll_cond);
+ g_mutex_clear (&basesink->preroll_lock);
+ g_cond_clear (&basesink->preroll_cond);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstCaps *
-gst_base_sink_get_caps (GstBaseSink * sink, GstCaps * filter)
+gst_base_sink_default_get_caps (GstBaseSink * sink, GstCaps * filter)
{
return NULL;
}
static gboolean
-gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
+gst_base_sink_default_set_caps (GstBaseSink * sink, GstCaps * caps)
{
return TRUE;
}
gboolean post_paused = FALSE;
gboolean post_async_done = FALSE;
gboolean post_playing = FALSE;
- gboolean reset_time;
/* 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;
- reset_time = basesink->priv->reset_time;
- basesink->priv->reset_time = FALSE;
switch (pending) {
case GST_STATE_PLAYING:
if (post_async_done) {
GST_DEBUG_OBJECT (basesink, "posting async-done message");
gst_element_post_message (GST_ELEMENT_CAST (basesink),
- gst_message_new_async_done (GST_OBJECT_CAST (basesink), reset_time));
+ gst_message_new_async_done (GST_OBJECT_CAST (basesink), FALSE));
}
if (post_playing) {
GST_DEBUG_OBJECT (basesink, "posting PLAYING state change message");
gst_base_sink_get_sync_times (GstBaseSink * basesink, GstMiniObject * obj,
GstClockTime * rsstart, GstClockTime * rsstop,
GstClockTime * rrstart, GstClockTime * rrstop, gboolean * do_sync,
- gboolean * stepped, GstStepInfo * step,
- gboolean * step_end, guint8 obj_type)
+ gboolean * stepped, GstStepInfo * step, gboolean * step_end)
{
GstBaseSinkClass *bclass;
GstBuffer *buffer;
/* start with nothing */
start = stop = GST_CLOCK_TIME_NONE;
- if (G_UNLIKELY (OBJ_IS_EVENT (obj_type))) {
+ if (G_UNLIKELY (GST_IS_EVENT (obj))) {
GstEvent *event = GST_EVENT_CAST (obj);
switch (GST_EVENT_TYPE (event)) {
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);
+ gst_base_sink_default_get_times (basesink, buffer, &start, &stop);
*do_sync = FALSE;
} else {
*do_sync = TRUE;
* This function will block until a state change to PLAYING happens (in which
* case this function returns #GST_FLOW_OK) or the processing must be stopped due
* to a state change to READY or a FLUSH event (in which case this function
- * returns #GST_FLOW_WRONG_STATE).
+ * returns #GST_FLOW_FLUSHING).
*
* This function should only be called with the PREROLL_LOCK held, like in the
* render function.
stopping:
{
GST_DEBUG_OBJECT (sink, "preroll interrupted because of flush");
- return GST_FLOW_WRONG_STATE;
+ return GST_FLOW_FLUSHING;
}
step_unlocked:
{
}
}
-static inline guint8
-get_object_type (GstMiniObject * obj)
-{
- guint8 obj_type;
-
- 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;
- else
- obj_type = _PR_IS_NOTHING;
-
- return obj_type;
-}
-
/**
* gst_base_sink_do_preroll:
* @sink: the sink
GstFlowReturn ret;
while (G_UNLIKELY (sink->need_preroll)) {
- guint8 obj_type;
GST_DEBUG_OBJECT (sink, "prerolling object %p", obj);
- obj_type = get_object_type (obj);
+ /* if it's a buffer, we need to call the preroll method */
+ if (sink->priv->call_preroll) {
+ GstBaseSinkClass *bclass;
+ GstBuffer *buf;
- ret = gst_base_sink_preroll_object (sink, obj_type, obj);
- if (ret != GST_FLOW_OK)
- goto preroll_failed;
+ if (GST_IS_BUFFER_LIST (obj)) {
+ buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
+ g_assert (NULL != buf);
+ } else if (GST_IS_BUFFER (obj)) {
+ buf = GST_BUFFER_CAST (obj);
+ /* For buffer lists do not set last buffer for now */
+ gst_base_sink_set_last_buffer (sink, buf);
+ } else
+ buf = NULL;
+
+ if (buf) {
+ GST_DEBUG_OBJECT (sink, "preroll buffer %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+
+ bclass = GST_BASE_SINK_GET_CLASS (sink);
+
+ if (bclass->prepare)
+ if ((ret = bclass->prepare (sink, buf)) != GST_FLOW_OK)
+ goto prepare_canceled;
+
+ if (bclass->preroll)
+ if ((ret = bclass->preroll (sink, buf)) != GST_FLOW_OK)
+ goto preroll_canceled;
+
+ sink->priv->call_preroll = FALSE;
+ }
+ }
+
+ /* commit state */
+ if (G_LIKELY (sink->playing_async)) {
+ if (G_UNLIKELY (!gst_base_sink_commit_state (sink)))
+ goto stopping;
+ }
/* need to recheck here because the commit state could have
* made us not need the preroll anymore */
return GST_FLOW_OK;
/* ERRORS */
+prepare_canceled:
+ {
+ GST_DEBUG_OBJECT (sink, "prepare failed, abort state");
+ gst_element_abort_state (GST_ELEMENT_CAST (sink));
+ return ret;
+ }
+preroll_canceled:
+ {
+ GST_DEBUG_OBJECT (sink, "preroll failed, abort state");
+ gst_element_abort_state (GST_ELEMENT_CAST (sink));
+ return ret;
+ }
+stopping:
+ {
+ GST_DEBUG_OBJECT (sink, "stopping while commiting state");
+ return GST_FLOW_FLUSHING;
+ }
preroll_failed:
{
GST_DEBUG_OBJECT (sink, "preroll failed: %s", gst_flow_get_name (ret));
GST_DEBUG_OBJECT (sink, "possibly waiting for clock to reach %"
GST_TIME_FORMAT, GST_TIME_ARGS (time));
- /* compensate for latency and ts_offset. We don't adjust for render delay
- * because we don't interact with the device on EOS normally. */
+ /* compensate for latency, ts_offset and render delay */
stime = gst_base_sink_adjust_time (sink, time);
/* wait for the clock, this can be interrupted because we got shut down or
flushing:
{
GST_DEBUG_OBJECT (sink, "we are flushing");
- return GST_FLOW_WRONG_STATE;
+ return GST_FLOW_FLUSHING;
}
}
*/
static GstFlowReturn
gst_base_sink_do_sync (GstBaseSink * basesink,
- GstMiniObject * obj, gboolean * late, gboolean * step_end, guint8 obj_type)
+ GstMiniObject * obj, gboolean * late, gboolean * step_end)
{
GstClockTimeDiff jitter = 0;
gboolean syncable;
/* get timing information for this object against the render segment */
syncable = gst_base_sink_get_sync_times (basesink, obj,
- &sstart, &sstop, &rstart, &rstop, &do_sync, &stepped,
- current, step_end, obj_type);
+ &sstart, &sstop, &rstart, &rstop, &do_sync, &stepped, current, step_end);
if (G_UNLIKELY (stepped))
goto step_skipped;
/* adjust for latency */
stime = gst_base_sink_adjust_time (basesink, rstart);
- /* adjust for render-delay, avoid underflows */
- if (GST_CLOCK_TIME_IS_VALID (stime)) {
- if (stime > priv->render_delay)
- stime -= priv->render_delay;
- else
- stime = 0;
- }
-
/* preroll done, we can sync since we are in PLAYING now. */
GST_DEBUG_OBJECT (basesink, "possibly waiting for clock to reach %"
GST_TIME_FORMAT ", adjusted %" GST_TIME_FORMAT,
flushing:
{
GST_DEBUG_OBJECT (basesink, "we are flushing");
- return GST_FLOW_WRONG_STATE;
+ return GST_FLOW_FLUSHING;
}
preroll_failed:
{
}
}
-/* with STREAM_LOCK, PREROLL_LOCK,
- *
- * Synchronize the object on the clock and then render it.
- *
- * takes ownership of obj.
- */
-static GstFlowReturn
-gst_base_sink_render_object (GstBaseSink * basesink,
- guint8 obj_type, gpointer obj)
-{
- GstFlowReturn ret;
- GstBaseSinkClass *bclass;
- 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);
- g_assert (NULL != sync_obj);
- } else {
- sync_obj = obj;
- }
-
-again:
- late = FALSE;
- step_end = FALSE;
-
- /* synchronize this object, non syncable objects return OK
- * immediately. */
- ret = gst_base_sink_do_sync (basesink, sync_obj, &late, &step_end, obj_type);
- if (G_UNLIKELY (ret != GST_FLOW_OK))
- goto sync_failed;
-
- /* 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;
-
- bclass = GST_BASE_SINK_GET_CLASS (basesink);
-
- 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 object %p", obj);
-
- /* record rendering time for QoS and stats */
- if (do_qos)
- gst_base_sink_do_render_stats (basesink, TRUE);
-
- if (!OBJ_IS_BUFFERLIST (obj_type)) {
- GstBuffer *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 */
- 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 {
- 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 (GST_MINI_OBJECT_CAST (obj));
- return ret;
-
- /* ERRORS */
-sync_failed:
- {
- GST_DEBUG_OBJECT (basesink, "do_sync returned %s", gst_flow_get_name (ret));
- goto done;
- }
-dropped:
- {
- 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:
- {
- GST_DEBUG_OBJECT (basesink, "we are flushing, ignore object");
- gst_mini_object_unref (obj);
- return GST_FLOW_WRONG_STATE;
- }
-}
-
-/* with STREAM_LOCK, PREROLL_LOCK
- *
- * 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, guint8 obj_type,
- GstMiniObject * obj)
-{
- GstFlowReturn ret;
-
- GST_DEBUG_OBJECT (basesink, "prerolling object %p", obj);
-
- /* if it's a buffer, we need to call the preroll method */
- if (G_LIKELY (OBJ_IS_BUFFERFULL (obj_type) && basesink->priv->call_preroll)) {
- GstBaseSinkClass *bclass;
- GstBuffer *buf;
-
- if (OBJ_IS_BUFFERLIST (obj_type)) {
- buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
- g_assert (NULL != buf);
- } else {
- buf = GST_BUFFER_CAST (obj);
- }
-
- GST_DEBUG_OBJECT (basesink, "preroll buffer %" GST_TIME_FORMAT,
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (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 */
- if (G_LIKELY (basesink->playing_async)) {
- if (G_UNLIKELY (!gst_base_sink_commit_state (basesink)))
- goto stopping;
- }
-
- return GST_FLOW_OK;
-
- /* ERRORS */
-preroll_failed:
- {
- GST_DEBUG_OBJECT (basesink, "preroll failed, abort state");
- gst_element_abort_state (GST_ELEMENT_CAST (basesink));
- return ret;
- }
-stopping:
- {
- GST_DEBUG_OBJECT (basesink, "stopping while commiting state");
- return GST_FLOW_WRONG_STATE;
- }
-}
-
static void
gst_base_sink_flush_start (GstBaseSink * basesink, GstPad * pad)
{
gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED);
}
}
- basesink->priv->reset_time = reset_time;
GST_OBJECT_UNLOCK (basesink);
+
+ if (reset_time) {
+ GST_DEBUG_OBJECT (basesink, "posting reset-time message");
+ gst_element_post_message (GST_ELEMENT_CAST (basesink),
+ gst_message_new_reset_time (GST_OBJECT_CAST (basesink), 0));
+ }
}
static GstFlowReturn
gboolean late, step_end;
ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (event),
- &late, &step_end, _PR_IS_EVENT);
+ &late, &step_end);
return ret;
}
* timestamps on a buffer, subclasses can override
*/
static void
-gst_base_sink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
+gst_base_sink_default_get_times (GstBaseSink * basesink, GstBuffer * buffer,
GstClockTime * start, GstClockTime * end)
{
GstClockTime timestamp, duration;
- timestamp = GST_BUFFER_TIMESTAMP (buffer);
- if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+ /* first sync on DTS, else use PTS */
+ timestamp = GST_BUFFER_DTS (buffer);
+ if (!GST_CLOCK_TIME_IS_VALID (timestamp))
+ timestamp = GST_BUFFER_PTS (buffer);
+ if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
/* get duration to calculate end time */
duration = GST_BUFFER_DURATION (buffer);
if (GST_CLOCK_TIME_IS_VALID (duration)) {
*
* Takes a buffer and compare the timestamps with the last segment.
* If the buffer falls outside of the segment boundaries, drop it.
- * Else queue the buffer for preroll and rendering.
+ * Else send the buffer for preroll and rendering.
*
* This function takes ownership of the buffer.
*/
static GstFlowReturn
gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad,
- guint8 obj_type, gpointer obj)
+ gpointer obj, gboolean is_list)
{
GstBaseSinkClass *bclass;
- GstFlowReturn result;
+ GstBaseSinkPrivate *priv = basesink->priv;
+ GstFlowReturn ret;
GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE;
GstSegment *segment;
- GstBuffer *time_buf;
+ GstBuffer *sync_buf;
+ gint do_qos;
+ gboolean late, step_end;
if (G_UNLIKELY (basesink->flushing))
goto flushing;
- if (G_UNLIKELY (basesink->priv->received_eos))
+ if (G_UNLIKELY (priv->received_eos))
goto was_eos;
- if (OBJ_IS_BUFFERLIST (obj_type)) {
- time_buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
- g_assert (NULL != time_buf);
+ if (is_list) {
+ sync_buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0);
+ g_assert (NULL != sync_buf);
} else {
- time_buf = GST_BUFFER_CAST (obj);
+ sync_buf = GST_BUFFER_CAST (obj);
}
/* for code clarity */
/* 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, time_buf, &start, &end);
+ bclass->get_times (basesink, sync_buf, &start, &end);
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, time_buf, &start, &end);
+ gst_base_sink_default_get_times (basesink, sync_buf, &start, &end);
}
GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT
goto out_of_segment;
}
- /* now we can process the buffer in the queue, this function takes ownership
- * of the buffer */
- result = gst_base_sink_render_object (basesink, obj_type, obj);
- return result;
+ if (!is_list) {
+ if (bclass->prepare) {
+ ret = bclass->prepare (basesink, GST_BUFFER_CAST (obj));
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto prepare_failed;
+ }
+ } else {
+ if (bclass->prepare_list) {
+ ret = bclass->prepare_list (basesink, GST_BUFFER_LIST_CAST (obj));
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto prepare_failed;
+ }
+ }
+
+again:
+ late = FALSE;
+ step_end = FALSE;
+
+ /* synchronize this object, non syncable objects return OK
+ * immediately. */
+ ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (sync_buf),
+ &late, &step_end);
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto sync_failed;
+
+ /* drop late buffers unconditionally, let's hope it's unlikely */
+ if (G_UNLIKELY (late))
+ goto dropped;
+
+ /* read once, to get same value before and after */
+ do_qos = g_atomic_int_get (&priv->qos_enabled);
+
+ GST_DEBUG_OBJECT (basesink, "rendering object %p", obj);
+
+ /* record rendering time for QoS and stats */
+ if (do_qos)
+ gst_base_sink_do_render_stats (basesink, TRUE);
+
+ if (!is_list) {
+ /* For buffer lists do not set last buffer for now. */
+ gst_base_sink_set_last_buffer (basesink, GST_BUFFER_CAST (obj));
+
+ if (bclass->render)
+ ret = bclass->render (basesink, GST_BUFFER_CAST (obj));
+ } else {
+ if (bclass->render_list)
+ ret = bclass->render_list (basesink, GST_BUFFER_LIST_CAST (obj));
+ }
+
+ 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++;
+
+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 (GST_MINI_OBJECT_CAST (obj));
+
+ return ret;
/* ERRORS */
flushing:
{
GST_DEBUG_OBJECT (basesink, "sink is flushing");
gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
- return GST_FLOW_WRONG_STATE;
+ return GST_FLOW_FLUSHING;
}
was_eos:
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
return GST_FLOW_OK;
}
+prepare_failed:
+ {
+ GST_DEBUG_OBJECT (basesink, "prepare buffer failed %s",
+ gst_flow_get_name (ret));
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj));
+ return ret;
+ }
+sync_failed:
+ {
+ GST_DEBUG_OBJECT (basesink, "do_sync returned %s", gst_flow_get_name (ret));
+ goto done;
+ }
+dropped:
+ {
+ 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_buf));
+ duration = GST_BUFFER_DURATION (GST_BUFFER_CAST (sync_buf));
+
+ 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;
+ }
}
/* with STREAM_LOCK
*/
static GstFlowReturn
-gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad,
- guint8 obj_type, gpointer obj)
+gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad, gpointer obj,
+ gboolean is_list)
{
GstFlowReturn result;
goto wrong_mode;
GST_BASE_SINK_PREROLL_LOCK (basesink);
- result = gst_base_sink_chain_unlocked (basesink, pad, obj_type, obj);
+ result = gst_base_sink_chain_unlocked (basesink, pad, obj, is_list);
GST_BASE_SINK_PREROLL_UNLOCK (basesink);
done:
basesink = GST_BASE_SINK (parent);
- return gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER, buf);
+ return gst_base_sink_chain_main (basesink, pad, buf, FALSE);
}
static GstFlowReturn
bclass = GST_BASE_SINK_GET_CLASS (basesink);
if (G_LIKELY (bclass->render_list)) {
- result = gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFERLIST, list);
+ result = gst_base_sink_chain_main (basesink, pad, list, TRUE);
} else {
guint i, len;
GstBuffer *buffer;
result = GST_FLOW_OK;
for (i = 0; i < len; i++) {
- buffer = gst_buffer_list_get (list, 0);
- result = gst_base_sink_chain_main (basesink, pad, _PR_IS_BUFFER,
- gst_buffer_ref (buffer));
+ buffer = gst_buffer_list_get (list, i);
+ result = gst_base_sink_chain_main (basesink, pad,
+ gst_buffer_ref (buffer), FALSE);
if (result != GST_FLOW_OK)
break;
}
basesink->segment.position = offset;
GST_BASE_SINK_PREROLL_LOCK (basesink);
- result = gst_base_sink_chain_unlocked (basesink, pad, _PR_IS_BUFFER, buf);
+ result = gst_base_sink_chain_unlocked (basesink, pad, buf, FALSE);
GST_BASE_SINK_PREROLL_UNLOCK (basesink);
if (G_UNLIKELY (result != GST_FLOW_OK))
goto paused;
pull() without setcaps() */
result = TRUE;
} else {
- caps = gst_caps_make_writable (caps);
/* try to fixate */
- gst_base_sink_fixate (basesink, caps);
+ caps = gst_base_sink_fixate (basesink, caps);
GST_DEBUG_OBJECT (basesink, "fixated to: %" GST_PTR_FORMAT, caps);
if (gst_caps_is_fixed (caps)) {
- if (!gst_pad_send_event (GST_BASE_SINK_PAD (basesink),
- gst_event_new_caps (caps)))
+ if (!gst_pad_set_caps (GST_BASE_SINK_PAD (basesink), caps))
goto could_not_set_caps;
result = TRUE;
static gboolean
-default_sink_query (GstBaseSink * basesink, GstQuery * query)
+gst_base_sink_default_query (GstBaseSink * basesink, GstQuery * query)
{
gboolean res;
GstBaseSinkClass *bclass;
res = TRUE;
break;
}
+ case GST_QUERY_ACCEPT_CAPS:
+ {
+ GstCaps *caps, *allowed;
+ gboolean subset;
+
+ /* slightly faster than the default implementation */
+ gst_query_parse_accept_caps (query, &caps);
+ allowed = gst_base_sink_query_caps (basesink, basesink->sinkpad, NULL);
+ subset = gst_caps_is_subset (caps, allowed);
+ gst_caps_unref (allowed);
+ gst_query_set_accept_caps_result (query, subset);
+ res = TRUE;
+ break;
+ }
+ case GST_QUERY_DRAIN:
+ res = TRUE;
+ break;
default:
res =
gst_pad_query_default (basesink->sinkpad, GST_OBJECT_CAST (basesink),
priv->step_unlock = FALSE;
basesink->need_preroll = TRUE;
basesink->playing_async = TRUE;
- basesink->priv->reset_time = FALSE;
priv->current_sstart = GST_CLOCK_TIME_NONE;
priv->current_sstop = GST_CLOCK_TIME_NONE;
priv->eos_rtime = GST_CLOCK_TIME_NONE;