diff options
Diffstat (limited to 'gst-libs')
-rw-r--r-- | gst-libs/gst/app/gstappsink.c | 238 | ||||
-rw-r--r-- | gst-libs/gst/app/gstappsink.h | 18 |
2 files changed, 232 insertions, 24 deletions
diff --git a/gst-libs/gst/app/gstappsink.c b/gst-libs/gst/app/gstappsink.c index d727efcfb..e073c44ff 100644 --- a/gst-libs/gst/app/gstappsink.c +++ b/gst-libs/gst/app/gstappsink.c @@ -53,11 +53,19 @@ enum LAST_SIGNAL }; +#define DEFAULT_PROP_EOS TRUE +#define DEFAULT_PROP_EMIT_SIGNALS TRUE +#define DEFAULT_PROP_MAX_BUFFERS 0 + enum { PROP_0, PROP_CAPS, - PROP_EOS + PROP_EOS, + PROP_EMIT_SIGNALS, + PROP_MAX_BUFFERS, + + PROP_LAST, }; static GstStaticPadTemplate gst_app_sink_template = @@ -146,17 +154,31 @@ gst_app_sink_class_init (GstAppSinkClass * klass) g_object_class_install_property (gobject_class, PROP_CAPS, g_param_spec_boxed ("caps", "Caps", - "The caps of the sink pad", GST_TYPE_CAPS, G_PARAM_READWRITE)); + "The caps of the sink pad", GST_TYPE_CAPS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_EOS, g_param_spec_boolean ("eos", "EOS", - "Check if the sink is EOS", TRUE, G_PARAM_READABLE)); + "Check if the sink is EOS or not started", DEFAULT_PROP_EOS, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_EMIT_SIGNALS, + g_param_spec_boolean ("emit-signals", "Emit signals", + "Emit new-preroll and new-buffer signals", DEFAULT_PROP_EMIT_SIGNALS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_MAX_BUFFERS, + g_param_spec_uint ("max-buffers", "Max Buffers", + "Control the maximum buffers to queue internally (0 = unlimited)", + 0, G_MAXUINT, DEFAULT_PROP_MAX_BUFFERS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** * GstAppSink::eos: * @appsink: the appsink element that emited the signal * - * Signal that the end-of-stream has been reached. + * Signal that the end-of-stream has been reached. This signal is emited from + * the steaming thread. */ gst_app_sink_signals[SIGNAL_EOS] = g_signal_new ("eos", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -165,49 +187,95 @@ gst_app_sink_class_init (GstAppSinkClass * klass) /** * GstAppSink::new-preroll: * @appsink: the appsink element that emited the signal - * @buffer: the buffer that caused the preroll * - * Signal that a new preroll buffer is available. + * Signal that a new preroll buffer is available. + * + * This signal is emited from the steaming thread and only when the + * "emit-signals" property is %TRUE. + * + * The new preroll buffer can be retrieved with the "pull-preroll" action + * signal or gst_app_sink_pull_preroll() either from this signal callback + * or from any other thread. + * + * Note that this signal is only emited when the "emit-signals" property is + * set to %TRUE, which it is not by default for performance reasons. */ gst_app_sink_signals[SIGNAL_NEW_PREROLL] = g_signal_new ("new-preroll", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstAppSinkClass, new_preroll), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 0, - GST_TYPE_BUFFER); + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); /** * GstAppSink::new-buffer: * @appsink: the appsink element that emited the signal - * @buffer: the buffer that is available * * Signal that a new buffer is available. + * + * This signal is emited from the steaming thread and only when the + * "emit-signals" property is %TRUE. + * + * The new preroll buffer can be retrieved with the "pull-buffer" action + * signal or gst_app_sink_pull_buffer() either from this signal callback + * or from any other thread. + * + * Note that this signal is only emited when the "emit-signals" property is + * set to %TRUE, which it is not by default for performance reasons. */ gst_app_sink_signals[SIGNAL_NEW_BUFFER] = g_signal_new ("new-buffer", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstAppSinkClass, new_buffer), - NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 0, - GST_TYPE_BUFFER); + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE); /** * GstAppSink::pull-preroll: * @appsink: the appsink element to emit this signal on * - * Get the last preroll buffer on @appsink. + * Get the last preroll buffer in @appsink. This was the buffer that caused the + * appsink to preroll in the PAUSED state. This buffer can be pulled many times + * and remains available to the application even after EOS. + * + * This function is typically used when dealing with a pipeline in the PAUSED + * state. Calling this function after doing a seek will give the buffer right + * after the seek position. + * + * Note that the preroll buffer will also be returned as the first buffer + * when calling gst_app_sink_pull_buffer() or the "pull-buffer" action signal. + * + * If an EOS event was received before any buffers, this function returns + * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition. + * + * This function blocks until a preroll buffer or EOS is received or the appsink + * element is set to the READY/NULL state. + * + * Returns: a #GstBuffer or NULL when the appsink is stopped or EOS. */ gst_app_sink_signals[SIGNAL_PULL_PREROLL] = g_signal_new ("pull-preroll", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstAppSinkClass, pull_preroll), NULL, - NULL, gst_app_marshal_OBJECT__VOID, GST_TYPE_BUFFER, 0, G_TYPE_NONE); + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass, + pull_preroll), NULL, NULL, gst_app_marshal_OBJECT__VOID, + GST_TYPE_BUFFER, 0, G_TYPE_NONE); /** * GstAppSink::pull-buffer: * @appsink: the appsink element to emit this signal on * - * Get the next buffer buffer on @appsink. + * This function blocks until a buffer or EOS becomes available or the appsink + * element is set to the READY/NULL state. + * + * This function will only return buffers when the appsink is in the PLAYING + * state. All rendered buffers will be put in a queue so that the application + * can pull buffers at its own rate. Note that when the application does not + * pull buffers fast enough, the queued buffers could consume a lot of memory, + * especially when dealing with raw video frames. + * + * If an EOS event was received before any buffers, this function returns + * %NULL. Use gst_app_sink_is_eos () to check for the EOS condition. + * + * Returns: a #GstBuffer or NULL when the appsink is stopped or EOS. */ gst_app_sink_signals[SIGNAL_PULL_PREROLL] = - g_signal_new ("pull-buffer", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstAppSinkClass, pull_buffer), - NULL, NULL, gst_app_marshal_OBJECT__VOID, GST_TYPE_BUFFER, 0, - G_TYPE_NONE); + g_signal_new ("pull-buffer", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstAppSinkClass, + pull_buffer), NULL, NULL, gst_app_marshal_OBJECT__VOID, + GST_TYPE_BUFFER, 0, G_TYPE_NONE); basesink_class->start = gst_app_sink_start; basesink_class->stop = gst_app_sink_stop; @@ -226,6 +294,9 @@ gst_app_sink_init (GstAppSink * appsink, GstAppSinkClass * klass) appsink->mutex = g_mutex_new (); appsink->cond = g_cond_new (); appsink->queue = g_queue_new (); + + appsink->emit_signals = DEFAULT_PROP_EMIT_SIGNALS; + appsink->max_buffers = DEFAULT_PROP_MAX_BUFFERS; } static void @@ -272,6 +343,12 @@ gst_app_sink_set_property (GObject * object, guint prop_id, case PROP_CAPS: gst_app_sink_set_caps (appsink, gst_value_get_caps (value)); break; + case PROP_EMIT_SIGNALS: + gst_app_sink_set_emit_signals (appsink, g_value_get_boolean (value)); + break; + case PROP_MAX_BUFFERS: + gst_app_sink_set_max_buffers (appsink, g_value_get_uint (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -298,6 +375,12 @@ gst_app_sink_get_property (GObject * object, guint prop_id, GValue * value, case PROP_EOS: g_value_set_boolean (value, gst_app_sink_is_eos (appsink)); break; + case PROP_EMIT_SIGNALS: + g_value_set_boolean (value, gst_app_sink_get_emit_signals (appsink)); + break; + case PROP_MAX_BUFFERS: + g_value_set_uint (value, gst_app_sink_get_max_buffers (appsink)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -352,11 +435,15 @@ gst_app_sink_event (GstBaseSink * sink, GstEvent * event) switch (event->type) { case GST_EVENT_EOS: + g_mutex_lock (appsink->mutex); GST_DEBUG_OBJECT (appsink, "receiving EOS"); appsink->is_eos = TRUE; g_cond_signal (appsink->cond); g_mutex_unlock (appsink->mutex); + + /* emit EOS now */ + g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_EOS], 0); break; case GST_EVENT_FLUSH_START: break; @@ -376,13 +463,18 @@ static GstFlowReturn gst_app_sink_preroll (GstBaseSink * psink, GstBuffer * buffer) { GstAppSink *appsink = GST_APP_SINK (psink); + gboolean emit; g_mutex_lock (appsink->mutex); GST_DEBUG_OBJECT (appsink, "setting preroll buffer %p", buffer); gst_buffer_replace (&appsink->preroll, buffer); g_cond_signal (appsink->cond); + emit = appsink->emit_signals; g_mutex_unlock (appsink->mutex); + if (emit) + g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_PREROLL], 0); + return GST_FLOW_OK; } @@ -390,14 +482,34 @@ static GstFlowReturn gst_app_sink_render (GstBaseSink * psink, GstBuffer * buffer) { GstAppSink *appsink = GST_APP_SINK (psink); + gboolean emit; g_mutex_lock (appsink->mutex); GST_DEBUG_OBJECT (appsink, "pushing render buffer %p on queue", buffer); + + while (appsink->max_buffers > 0 && + appsink->queue->length >= appsink->max_buffers) { + /* FIXME, do proper unlocking when flushing */ + g_cond_wait (appsink->cond, appsink->mutex); + if (!appsink->started) + goto not_started; + } g_queue_push_tail (appsink->queue, gst_buffer_ref (buffer)); g_cond_signal (appsink->cond); + emit = appsink->emit_signals; g_mutex_unlock (appsink->mutex); + if (emit) + g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_BUFFER], 0); + return GST_FLOW_OK; + +not_started: + { + GST_DEBUG_OBJECT (appsink, "we stopped"); + g_mutex_unlock (appsink->mutex); + return GST_FLOW_WRONG_STATE; + } } static GstCaps * @@ -517,6 +629,93 @@ not_started: } /** + * gst_app_sink_set_emit_signals: + * @appsink: a #GstAppSink + * @emit: the new state + * + * Make appsink emit the "new-preroll" and "new-buffer" signals. This option is + * by default disabled because signal emission is expensive and unneeded when + * the application prefers to operate in pull mode. + */ +void +gst_app_sink_set_emit_signals (GstAppSink * appsink, gboolean emit) +{ + g_return_if_fail (GST_IS_APP_SINK (appsink)); + + g_mutex_lock (appsink->mutex); + appsink->emit_signals = emit; + g_mutex_unlock (appsink->mutex); +} + +/** + * gst_app_sink_get_emit_signals: + * @appsink: a #GstAppSink + * + * Check if appsink will emit the "new-preroll" and "new-buffer" signals. + * + * Returns: %TRUE if @appsink is emiting the "new-preroll" and "new-buffer" + * signals. + */ +gboolean +gst_app_sink_get_emit_signals (GstAppSink * appsink) +{ + gboolean result; + + g_return_val_if_fail (GST_IS_APP_SINK (appsink), FALSE); + + g_mutex_lock (appsink->mutex); + result = appsink->emit_signals; + g_mutex_unlock (appsink->mutex); + + return result; +} + +/** + * gst_app_sink_set_max_buffers: + * @appsink: a #GstAppSink + * @max: the maximum number of buffers to queue + * + * Set the maximum amount of buffers that can be queued in @appsink. After this + * amount of buffers are queued in appsink, any more buffers will block upstream + * elements until a buffer is pulled from @appsink. + */ +void +gst_app_sink_set_max_buffers (GstAppSink * appsink, guint max) +{ + g_return_if_fail (GST_IS_APP_SINK (appsink)); + + g_mutex_lock (appsink->mutex); + if (max != appsink->max_buffers) { + appsink->max_buffers = max; + /* signal the change */ + g_cond_signal (appsink->cond); + } + g_mutex_unlock (appsink->mutex); +} + +/** + * gst_app_sink_get_max_buffers: + * @appsink: a #GstAppSink + * + * Get the maximum amount of buffers that can be queued in @appsink. + * + * Returns: The maximum amount of buffers that can be queued. + */ +guint +gst_app_sink_get_max_buffers (GstAppSink * appsink) +{ + guint result; + + g_return_val_if_fail (GST_IS_APP_SINK (appsink), 0); + + g_mutex_lock (appsink->mutex); + result = appsink->max_buffers; + g_mutex_unlock (appsink->mutex); + + return result; +} + +/** * gst_app_sink_pull_preroll: * @appsink: a #GstAppSink * @@ -630,6 +829,7 @@ gst_app_sink_pull_buffer (GstAppSink * appsink) } buf = g_queue_pop_head (appsink->queue); GST_DEBUG_OBJECT (appsink, "we have a buffer %p", buf); + g_cond_signal (appsink->cond); g_mutex_unlock (appsink->mutex); return buf; diff --git a/gst-libs/gst/app/gstappsink.h b/gst-libs/gst/app/gstappsink.h index 9ac0dc84d..2614fdb5f 100644 --- a/gst-libs/gst/app/gstappsink.h +++ b/gst-libs/gst/app/gstappsink.h @@ -45,6 +45,8 @@ struct _GstAppSink /*< private >*/ GstCaps *caps; + gboolean emit_signals; + guint max_buffers; GCond *cond; GMutex *mutex; @@ -72,13 +74,19 @@ GType gst_app_sink_get_type(void); GST_DEBUG_CATEGORY_EXTERN (app_sink_debug); -void gst_app_sink_set_caps (GstAppSink *appsink, const GstCaps *caps); -GstCaps * gst_app_sink_get_caps (GstAppSink *appsink); +void gst_app_sink_set_caps (GstAppSink *appsink, const GstCaps *caps); +GstCaps * gst_app_sink_get_caps (GstAppSink *appsink); -gboolean gst_app_sink_is_eos (GstAppSink *appsink); +gboolean gst_app_sink_is_eos (GstAppSink *appsink); -GstBuffer * gst_app_sink_pull_preroll (GstAppSink *appsink); -GstBuffer * gst_app_sink_pull_buffer (GstAppSink *appsink); +void gst_app_sink_set_emit_signals (GstAppSink *appsink, gboolean emit); +gboolean gst_app_sink_get_emit_signals (GstAppSink *appsink); + +void gst_app_sink_set_max_buffers (GstAppSink *appsink, guint max); +guint gst_app_sink_get_max_buffers (GstAppSink *appsink); + +GstBuffer * gst_app_sink_pull_preroll (GstAppSink *appsink); +GstBuffer * gst_app_sink_pull_buffer (GstAppSink *appsink); G_END_DECLS |