static GstFlowReturn gst_app_sink_preroll (GstBaseSink * psink, GstBuffer * buffer) { GstFlowReturn res = GST_FLOW_OK; GstAppSink *appsink = GST_APP_SINK_CAST (psink); GstAppSinkPrivate *priv = appsink->priv; gboolean emit; g_mutex_lock (priv->mutex); if (priv->flushing) goto flushing; GST_DEBUG_OBJECT (appsink, "setting preroll buffer %p", buffer); gst_buffer_replace (&priv->preroll, buffer); g_cond_signal (priv->cond); emit = priv->emit_signals; g_mutex_unlock (priv->mutex); if (priv->callbacks.new_preroll) res = priv->callbacks.new_preroll (appsink, priv->user_data); else if (emit) g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_PREROLL], 0); return res; flushing: { GST_DEBUG_OBJECT (appsink, "we are flushing"); g_mutex_unlock (priv->mutex); return GST_FLOW_FLUSHING; } }
static void gst_app_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstAppSink *appsink = GST_APP_SINK_CAST (object); switch (prop_id) { case PROP_CAPS: { GstCaps *caps; caps = gst_app_sink_get_caps (appsink); gst_value_set_caps (value, caps); if (caps) gst_caps_unref (caps); break; } 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; case PROP_DROP: g_value_set_boolean (value, gst_app_sink_get_drop (appsink)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void gst_app_sink_dispose (GObject * obj) { GstAppSink *appsink = GST_APP_SINK_CAST (obj); GstAppSinkPrivate *priv = appsink->priv; GstMiniObject *queue_obj; GST_OBJECT_LOCK (appsink); if (priv->caps) { gst_caps_unref (priv->caps); priv->caps = NULL; } if (priv->notify) { priv->notify (priv->user_data); } priv->user_data = NULL; priv->notify = NULL; GST_OBJECT_UNLOCK (appsink); g_mutex_lock (&priv->mutex); while ((queue_obj = g_queue_pop_head (priv->queue))) gst_mini_object_unref (queue_obj); gst_buffer_replace (&priv->preroll, NULL); gst_caps_replace (&priv->preroll_caps, NULL); gst_caps_replace (&priv->last_caps, NULL); g_mutex_unlock (&priv->mutex); G_OBJECT_CLASS (parent_class)->dispose (obj); }
static void gst_app_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstAppSink *appsink = GST_APP_SINK_CAST (object); switch (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; case PROP_DROP: gst_app_sink_set_drop (appsink, g_value_get_boolean (value)); break; case PROP_WAIT_ON_EOS: gst_app_sink_set_wait_on_eos (appsink, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void gst_app_sink_finalize (GObject * obj) { GstAppSink *appsink = GST_APP_SINK_CAST (obj); GstAppSinkPrivate *priv = appsink->priv; g_mutex_clear (&priv->mutex); g_cond_clear (&priv->cond); g_queue_free (priv->queue); G_OBJECT_CLASS (parent_class)->finalize (obj); }
static gboolean gst_app_sink_unlock_stop (GstBaseSink * bsink) { GstAppSink *appsink = GST_APP_SINK_CAST (bsink); GstAppSinkPrivate *priv = appsink->priv; g_mutex_lock (&priv->mutex); GST_DEBUG_OBJECT (appsink, "unlock stop"); priv->unlock = FALSE; g_cond_signal (&priv->cond); g_mutex_unlock (&priv->mutex); return TRUE; }
static gboolean gst_app_sink_setcaps (GstBaseSink * sink, GstCaps * caps) { GstAppSink *appsink = GST_APP_SINK_CAST (sink); GstAppSinkPrivate *priv = appsink->priv; g_mutex_lock (priv->mutex); GST_DEBUG_OBJECT (appsink, "receiving CAPS"); g_queue_push_tail (priv->queue, gst_event_new_caps (caps)); gst_caps_replace (&priv->preroll_caps, caps); g_mutex_unlock (priv->mutex); return TRUE; }
static gboolean gst_app_sink_start (GstBaseSink * psink) { GstAppSink *appsink = GST_APP_SINK_CAST (psink); GstAppSinkPrivate *priv = appsink->priv; g_mutex_lock (priv->mutex); GST_DEBUG_OBJECT (appsink, "starting"); priv->flushing = FALSE; priv->started = TRUE; gst_segment_init (&priv->last_segment, GST_FORMAT_TIME); g_mutex_unlock (priv->mutex); return TRUE; }
static gboolean gst_app_sink_stop (GstBaseSink * psink) { GstAppSink *appsink = GST_APP_SINK_CAST (psink); GstAppSinkPrivate *priv = appsink->priv; g_mutex_lock (priv->mutex); GST_DEBUG_OBJECT (appsink, "stopping"); priv->flushing = TRUE; priv->started = FALSE; gst_app_sink_flush_unlocked (appsink); gst_caps_replace (&priv->preroll_caps, NULL); gst_caps_replace (&priv->last_caps, NULL); g_mutex_unlock (priv->mutex); return TRUE; }
static GstCaps * gst_app_sink_getcaps (GstBaseSink * psink, GstCaps * filter) { GstCaps *caps; GstAppSink *appsink = GST_APP_SINK_CAST (psink); GstAppSinkPrivate *priv = appsink->priv; GST_OBJECT_LOCK (appsink); if ((caps = priv->caps)) { if (filter) caps = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); else gst_caps_ref (caps); } GST_DEBUG_OBJECT (appsink, "got caps %" GST_PTR_FORMAT, caps); GST_OBJECT_UNLOCK (appsink); return caps; }
static gboolean gst_app_sink_event (GstBaseSink * sink, GstEvent * event) { GstAppSink *appsink = GST_APP_SINK_CAST (sink); GstAppSinkPrivate *priv = appsink->priv; switch (event->type) { case GST_EVENT_SEGMENT: g_mutex_lock (priv->mutex); GST_DEBUG_OBJECT (appsink, "receiving SEGMENT"); g_queue_push_tail (priv->queue, gst_event_ref (event)); g_mutex_unlock (priv->mutex); break; case GST_EVENT_EOS: g_mutex_lock (priv->mutex); GST_DEBUG_OBJECT (appsink, "receiving EOS"); priv->is_eos = TRUE; g_cond_signal (priv->cond); g_mutex_unlock (priv->mutex); /* emit EOS now */ if (priv->callbacks.eos) priv->callbacks.eos (appsink, priv->user_data); else g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_EOS], 0); break; case GST_EVENT_FLUSH_START: /* we don't have to do anything here, the base class will call unlock * which will make sure we exit the _render method */ GST_DEBUG_OBJECT (appsink, "received FLUSH_START"); break; case GST_EVENT_FLUSH_STOP: g_mutex_lock (priv->mutex); GST_DEBUG_OBJECT (appsink, "received FLUSH_STOP"); gst_app_sink_flush_unlocked (appsink); g_mutex_unlock (priv->mutex); break; default: break; } return GST_BASE_SINK_CLASS (parent_class)->event (sink, event); }
static GstFlowReturn gst_app_sink_render (GstBaseSink * psink, GstBuffer * buffer) { GstFlowReturn ret; GstAppSink *appsink = GST_APP_SINK_CAST (psink); GstAppSinkPrivate *priv = appsink->priv; gboolean emit; restart: g_mutex_lock (&priv->mutex); if (priv->flushing) goto flushing; /* queue holding caps event might have been FLUSHed, * but caps state still present in pad caps */ if (G_UNLIKELY (!priv->last_caps && gst_pad_has_current_caps (GST_BASE_SINK_PAD (psink)))) { priv->last_caps = gst_pad_get_current_caps (GST_BASE_SINK_PAD (psink)); GST_DEBUG_OBJECT (appsink, "activating pad caps %" GST_PTR_FORMAT, priv->last_caps); } GST_DEBUG_OBJECT (appsink, "pushing render buffer %p on queue (%d)", buffer, priv->num_buffers); while (priv->max_buffers > 0 && priv->num_buffers >= priv->max_buffers) { if (priv->drop) { GstBuffer *old; /* we need to drop the oldest buffer and try again */ if ((old = dequeue_buffer (appsink))) { GST_DEBUG_OBJECT (appsink, "dropping old buffer %p", old); gst_buffer_unref (old); } } else { GST_DEBUG_OBJECT (appsink, "waiting for free space, length %d >= %d", priv->num_buffers, priv->max_buffers); if (priv->unlock) { /* we are asked to unlock, call the wait_preroll method */ g_mutex_unlock (&priv->mutex); if ((ret = gst_base_sink_wait_preroll (psink)) != GST_FLOW_OK) goto stopping; /* we are allowed to continue now */ goto restart; } /* wait for a buffer to be removed or flush */ g_cond_wait (&priv->cond, &priv->mutex); if (priv->flushing) goto flushing; } } /* we need to ref the buffer when pushing it in the queue */ g_queue_push_tail (priv->queue, gst_buffer_ref (buffer)); priv->num_buffers++; g_cond_signal (&priv->cond); emit = priv->emit_signals; g_mutex_unlock (&priv->mutex); if (priv->callbacks.new_sample) { ret = priv->callbacks.new_sample (appsink, priv->user_data); } else { ret = GST_FLOW_OK; if (emit) g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_NEW_SAMPLE], 0, &ret); } return ret; flushing: { GST_DEBUG_OBJECT (appsink, "we are flushing"); g_mutex_unlock (&priv->mutex); return GST_FLOW_FLUSHING; } stopping: { GST_DEBUG_OBJECT (appsink, "we are stopping"); return ret; } }
static gboolean gst_app_sink_event (GstBaseSink * sink, GstEvent * event) { GstAppSink *appsink = GST_APP_SINK_CAST (sink); GstAppSinkPrivate *priv = appsink->priv; switch (event->type) { case GST_EVENT_SEGMENT: g_mutex_lock (&priv->mutex); GST_DEBUG_OBJECT (appsink, "receiving SEGMENT"); g_queue_push_tail (priv->queue, gst_event_ref (event)); if (!priv->preroll) gst_event_copy_segment (event, &priv->preroll_segment); g_mutex_unlock (&priv->mutex); break; case GST_EVENT_EOS:{ gboolean emit = TRUE; g_mutex_lock (&priv->mutex); GST_DEBUG_OBJECT (appsink, "receiving EOS"); priv->is_eos = TRUE; g_cond_signal (&priv->cond); g_mutex_unlock (&priv->mutex); g_mutex_lock (&priv->mutex); /* wait until all buffers are consumed or we're flushing. * Otherwise we might signal EOS before all buffers are * consumed, which is a bit confusing for the application */ while (priv->num_buffers > 0 && !priv->flushing) g_cond_wait (&priv->cond, &priv->mutex); if (priv->flushing) emit = FALSE; g_mutex_unlock (&priv->mutex); if (emit) { /* emit EOS now */ if (priv->callbacks.eos) priv->callbacks.eos (appsink, priv->user_data); else g_signal_emit (appsink, gst_app_sink_signals[SIGNAL_EOS], 0); } break; } case GST_EVENT_FLUSH_START: /* we don't have to do anything here, the base class will call unlock * which will make sure we exit the _render method */ GST_DEBUG_OBJECT (appsink, "received FLUSH_START"); break; case GST_EVENT_FLUSH_STOP: g_mutex_lock (&priv->mutex); GST_DEBUG_OBJECT (appsink, "received FLUSH_STOP"); gst_app_sink_flush_unlocked (appsink); g_mutex_unlock (&priv->mutex); break; default: break; } return GST_BASE_SINK_CLASS (parent_class)->event (sink, event); }