From 2715cf6e28eb4112dfdc077c00c1c3bc9520731b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 30 Oct 2007 18:30:13 +0000 Subject: [PATCH] Add a new last-buffer property that contains the last buffer used in basesink for preroll or rendering. useful for ma... Original commit message from CVS: * docs/libs/gstreamer-libs-sections.txt: * libs/gst/base/gstbasesink.c: (gst_base_sink_class_init), (gst_base_sink_get_last_buffer), (gst_base_sink_set_last_buffer), (gst_base_sink_get_property), (gst_base_sink_render_object), (gst_base_sink_preroll_object), (gst_base_sink_queue_object_unlocked), (gst_base_sink_event), (gst_base_sink_change_state): * libs/gst/base/gstbasesink.h: Add a new last-buffer property that contains the last buffer used in basesink for preroll or rendering. useful for making snapshots. API: gst_base_sink_get_last_buffer() API: GstBaseSink::last-buffer --- ChangeLog | 15 +++ docs/libs/gstreamer-libs-sections.txt | 1 + libs/gst/base/gstbasesink.c | 126 +++++++++++++++++++++----- libs/gst/base/gstbasesink.h | 3 + 4 files changed, 120 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index fa19c958d8..29b91a7e6b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2007-10-30 Wim Taymans + + * docs/libs/gstreamer-libs-sections.txt: + * libs/gst/base/gstbasesink.c: (gst_base_sink_class_init), + (gst_base_sink_get_last_buffer), (gst_base_sink_set_last_buffer), + (gst_base_sink_get_property), (gst_base_sink_render_object), + (gst_base_sink_preroll_object), + (gst_base_sink_queue_object_unlocked), (gst_base_sink_event), + (gst_base_sink_change_state): + * libs/gst/base/gstbasesink.h: + Add a new last-buffer property that contains the last buffer used in + basesink for preroll or rendering. useful for making snapshots. + API: gst_base_sink_get_last_buffer() + API: GstBaseSink::last-buffer + 2007-10-29 Stefan Kost * docs/gst/running.xml: diff --git a/docs/libs/gstreamer-libs-sections.txt b/docs/libs/gstreamer-libs-sections.txt index d3abd14d13..a0f6a93bc6 100644 --- a/docs/libs/gstreamer-libs-sections.txt +++ b/docs/libs/gstreamer-libs-sections.txt @@ -271,6 +271,7 @@ gst_base_sink_set_async_enabled gst_base_sink_is_async_enabled gst_base_sink_set_ts_offset gst_base_sink_get_ts_offset +gst_base_sink_get_last_buffer GST_BASE_SINK_PAD diff --git a/libs/gst/base/gstbasesink.c b/libs/gst/base/gstbasesink.c index a926da5d56..43313b0fff 100644 --- a/libs/gst/base/gstbasesink.c +++ b/libs/gst/base/gstbasesink.c @@ -207,6 +207,9 @@ struct _GstBaseSinkPrivate /* when we are prerolled and able to report latency */ gboolean have_latency; + + /* the last buffer we prerolled or rendered. Useful for making snapshots */ + GstBuffer *last_buffer; }; #define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size)) @@ -241,7 +244,9 @@ enum PROP_MAX_LATENESS, PROP_QOS, PROP_ASYNC, - PROP_TS_OFFSET + PROP_TS_OFFSET, + PROP_LAST_BUFFER, + PROP_LAST }; static GstElementClass *parent_class = NULL; @@ -383,6 +388,19 @@ gst_base_sink_class_init (GstBaseSinkClass * klass) "Timestamp offset in nanoseconds", G_MININT64, G_MAXINT64, DEFAULT_TS_OFFSET, G_PARAM_READWRITE)); + /** + * GstBaseSink:last-buffer + * + * The last buffer that arrived in the sink and was used for preroll or for + * rendering. This property can be used to generate thumbnails. + * + * Since: 0.10.15 + */ + g_object_class_install_property (gobject_class, PROP_LAST_BUFFER, + gst_param_spec_mini_object ("last-buffer", "Last Buffer", + "The last buffer received in the sink", GST_TYPE_BUFFER, + G_PARAM_READABLE)); + gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_base_sink_change_state); gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event); @@ -783,6 +801,44 @@ gst_base_sink_get_ts_offset (GstBaseSink * sink) return res; } +/** + * gst_base_sink_get_last_buffer: + * @sink: the sink + * + * Get the last buffer that arrived in the sink and was used for preroll or for + * 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. + * + * Since: 0.10.15 + */ +GstBuffer * +gst_base_sink_get_last_buffer (GstBaseSink * sink) +{ + GstBuffer *res; + + g_return_val_if_fail (GST_IS_BASE_SINK (sink), NULL); + + GST_OBJECT_LOCK (sink); + if ((res = sink->priv->last_buffer)) + gst_buffer_ref (res); + GST_OBJECT_UNLOCK (sink); + + return res; +} + +static void +gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer) +{ + GST_OBJECT_LOCK (sink); + gst_buffer_replace (&sink->priv->last_buffer, buffer); + GST_OBJECT_UNLOCK (sink); +} + /** * gst_base_sink_get_latency: * @sink: the sink @@ -963,6 +1019,9 @@ gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value, case PROP_TS_OFFSET: g_value_set_int64 (value, gst_base_sink_get_ts_offset (sink)); break; + case PROP_LAST_BUFFER: + gst_value_take_buffer (value, gst_base_sink_get_last_buffer (sink)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1938,10 +1997,16 @@ gst_base_sink_render_object (GstBaseSink * basesink, GstPad * pad, /* and now render, event or buffer. */ if (G_LIKELY (GST_IS_BUFFER (obj))) { + GstBuffer *buf; + /* 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)) { @@ -1956,7 +2021,7 @@ gst_base_sink_render_object (GstBaseSink * basesink, GstPad * pad, if (do_qos) gst_base_sink_do_render_stats (basesink, TRUE); - ret = bclass->render (basesink, GST_BUFFER_CAST (obj)); + ret = bclass->render (basesink, buf); priv->rendered++; @@ -2045,11 +2110,15 @@ gst_base_sink_preroll_object (GstBaseSink * basesink, GstPad * pad, /* if it's a buffer, we need to call the preroll method */ if (G_LIKELY (GST_IS_BUFFER (obj))) { GstBaseSinkClass *bclass; - GstBuffer *buf = GST_BUFFER_CAST (obj); + GstBuffer *buf; + + buf = GST_BUFFER_CAST (obj); GST_DEBUG_OBJECT (basesink, "preroll buffer %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + 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) @@ -2111,7 +2180,8 @@ gst_base_sink_queue_object_unlocked (GstBaseSink * basesink, GstPad * pad, /* 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. */ + /* see if we can render now, if we can't add the object to the preroll + * queue. */ if (G_UNLIKELY (length <= basesink->preroll_queue_max_len)) goto more_preroll; } @@ -2304,6 +2374,7 @@ gst_base_sink_event (GstPad * pad, GstEvent * event) basesink->priv->have_latency = TRUE; basesink->need_preroll = FALSE; } + gst_base_sink_set_last_buffer (basesink, NULL); GST_PAD_STREAM_UNLOCK (pad); gst_event_unref (event); @@ -3122,6 +3193,9 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstBaseSink *basesink = GST_BASE_SINK (element); GstBaseSinkClass *bclass; + GstBaseSinkPrivate *priv; + + priv = basesink->priv; bclass = GST_BASE_SINK_GET_CLASS (basesink); @@ -3144,15 +3218,15 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) basesink->have_preroll = FALSE; basesink->need_preroll = TRUE; basesink->playing_async = TRUE; - basesink->priv->current_sstart = 0; - basesink->priv->current_sstop = 0; - basesink->priv->eos_rtime = -1; - basesink->priv->latency = 0; + priv->current_sstart = 0; + priv->current_sstop = 0; + priv->eos_rtime = -1; + priv->latency = 0; basesink->eos = FALSE; - basesink->priv->received_eos = FALSE; + priv->received_eos = FALSE; gst_base_sink_reset_qos (basesink); - basesink->priv->commited = FALSE; - if (basesink->priv->async_enabled) { + priv->commited = FALSE; + if (priv->async_enabled) { GST_DEBUG_OBJECT (basesink, "doing async state change"); /* when async enabled, post async-start message and return ASYNC from * the state change function */ @@ -3160,7 +3234,7 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) gst_element_post_message (GST_ELEMENT_CAST (basesink), gst_message_new_async_start (GST_OBJECT_CAST (basesink), FALSE)); } else { - basesink->priv->have_latency = TRUE; + priv->have_latency = TRUE; } GST_PAD_PREROLL_UNLOCK (basesink->sinkpad); break; @@ -3184,8 +3258,8 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) GST_DEBUG_OBJECT (basesink, "PAUSED to PLAYING, we are not prerolled"); basesink->need_preroll = TRUE; basesink->playing_async = TRUE; - basesink->priv->commited = FALSE; - if (basesink->priv->async_enabled) { + priv->commited = FALSE; + if (priv->async_enabled) { GST_DEBUG_OBJECT (basesink, "doing async state change"); ret = GST_STATE_CHANGE_ASYNC; gst_element_post_message (GST_ELEMENT_CAST (basesink), @@ -3255,8 +3329,8 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED, we are not prerolled"); basesink->playing_async = TRUE; - basesink->priv->commited = FALSE; - if (basesink->priv->async_enabled) { + priv->commited = FALSE; + if (priv->async_enabled) { GST_DEBUG_OBJECT (basesink, "doing async state change"); ret = GST_STATE_CHANGE_ASYNC; gst_element_post_message (GST_ELEMENT_CAST (basesink), @@ -3266,16 +3340,15 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) } } GST_DEBUG_OBJECT (basesink, "rendered: %" G_GUINT64_FORMAT - ", dropped: %" G_GUINT64_FORMAT, basesink->priv->rendered, - basesink->priv->dropped); + ", dropped: %" G_GUINT64_FORMAT, priv->rendered, priv->dropped); gst_base_sink_reset_qos (basesink); GST_PAD_PREROLL_UNLOCK (basesink->sinkpad); break; case GST_STATE_CHANGE_PAUSED_TO_READY: GST_PAD_PREROLL_LOCK (basesink->sinkpad); - if (!basesink->priv->commited) { - if (basesink->priv->async_enabled) { + if (!priv->commited) { + if (priv->async_enabled) { GST_DEBUG_OBJECT (basesink, "PAUSED to READY, posting async-done"); gst_element_post_message (GST_ELEMENT_CAST (basesink), @@ -3285,20 +3358,23 @@ gst_base_sink_change_state (GstElement * element, GstStateChange transition) gst_element_post_message (GST_ELEMENT_CAST (basesink), gst_message_new_async_done (GST_OBJECT_CAST (basesink))); } - basesink->priv->commited = TRUE; + priv->commited = TRUE; } else { GST_DEBUG_OBJECT (basesink, "PAUSED to READY, don't need_preroll"); } - basesink->priv->current_sstart = 0; - basesink->priv->current_sstop = 0; - basesink->priv->have_latency = FALSE; + priv->current_sstart = 0; + priv->current_sstop = 0; + priv->have_latency = FALSE; + gst_base_sink_set_last_buffer (basesink, NULL); GST_PAD_PREROLL_UNLOCK (basesink->sinkpad); break; case GST_STATE_CHANGE_READY_TO_NULL: - if (bclass->stop) + if (bclass->stop) { if (!bclass->stop (basesink)) { GST_WARNING_OBJECT (basesink, "failed to stop"); } + } + gst_base_sink_set_last_buffer (basesink, NULL); break; default: break; diff --git a/libs/gst/base/gstbasesink.h b/libs/gst/base/gstbasesink.h index cf9c4b6fba..f47cdf7efd 100644 --- a/libs/gst/base/gstbasesink.h +++ b/libs/gst/base/gstbasesink.h @@ -209,6 +209,9 @@ gboolean gst_base_sink_is_async_enabled (GstBaseSink *sink); void gst_base_sink_set_ts_offset (GstBaseSink *sink, GstClockTimeDiff offset); GstClockTimeDiff gst_base_sink_get_ts_offset (GstBaseSink *sink); +/* last buffer */ +GstBuffer * gst_base_sink_get_last_buffer (GstBaseSink *sink); + /* latency */ gboolean gst_base_sink_query_latency (GstBaseSink *sink, gboolean *live, gboolean *upstream_live, GstClockTime *min_latency, GstClockTime *max_latency);