diff options
Diffstat (limited to 'plugins/nle/nlesource.c')
-rw-r--r-- | plugins/nle/nlesource.c | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/plugins/nle/nlesource.c b/plugins/nle/nlesource.c new file mode 100644 index 00000000..fcb98506 --- /dev/null +++ b/plugins/nle/nlesource.c @@ -0,0 +1,505 @@ +/* Gnonlin + * Copyright (C) <2001> Wim Taymans <wim.taymans@gmail.com> + * <2004-2008> Edward Hervey <bilboed@bilboed.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "nle.h" + +/** + * SECTION:element-nlesource + * + * The NleSource encapsulates a pipeline which produces data for processing + * in a #NleComposition. + */ + +static GstStaticPadTemplate nle_source_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +GST_DEBUG_CATEGORY_STATIC (nlesource); +#define GST_CAT_DEFAULT nlesource + +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (nlesource, "nlesource", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Source Element"); +#define nle_source_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (NleSource, nle_source, NLE_TYPE_OBJECT, _do_init); + +struct _NleSourcePrivate +{ + gboolean dispose_has_run; + + gboolean dynamicpads; /* TRUE if the controlled element has dynamic pads */ + + gulong padremovedid; /* signal handler for element pad-removed signal */ + gulong padaddedid; /* signal handler for element pad-added signal */ + + gboolean pendingblock; /* We have a pending pad_block */ + gboolean areblocked; /* We already got blocked */ + GstPad *ghostedpad; /* Pad (to be) ghosted */ + GstPad *staticpad; /* The only pad. We keep an extra ref */ + + GstEvent *seek_event; + gulong probeid; +}; + +static gboolean nle_source_prepare (NleObject * object); +static gboolean nle_source_send_event (GstElement * element, GstEvent * event); +static gboolean nle_source_add_element (GstBin * bin, GstElement * element); +static gboolean nle_source_remove_element (GstBin * bin, GstElement * element); +static void nle_source_dispose (GObject * object); + +static gboolean +nle_source_control_element_func (NleSource * source, GstElement * element); + +static void +nle_source_class_init (NleSourceClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBinClass *gstbin_class; + NleObjectClass *nleobject_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbin_class = (GstBinClass *) klass; + nleobject_class = (NleObjectClass *) klass; + + g_type_class_add_private (klass, sizeof (NleSourcePrivate)); + + gst_element_class_set_static_metadata (gstelement_class, "GNonLin Source", + "Filter/Editor", + "Manages source elements", + "Wim Taymans <wim.taymans@gmail.com>, Edward Hervey <bilboed@bilboed.com>"); + + gstelement_class->send_event = GST_DEBUG_FUNCPTR (nle_source_send_event); + + parent_class = g_type_class_ref (NLE_TYPE_OBJECT); + + klass->control_element = GST_DEBUG_FUNCPTR (nle_source_control_element_func); + + nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_source_prepare); + + gstbin_class->add_element = GST_DEBUG_FUNCPTR (nle_source_add_element); + gstbin_class->remove_element = GST_DEBUG_FUNCPTR (nle_source_remove_element); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_source_dispose); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&nle_source_src_template)); + +} + + +static void +nle_source_init (NleSource * source) +{ + GST_OBJECT_FLAG_SET (source, NLE_OBJECT_SOURCE); + source->element = NULL; + source->priv = + G_TYPE_INSTANCE_GET_PRIVATE (source, NLE_TYPE_SOURCE, NleSourcePrivate); + + GST_DEBUG_OBJECT (source, "Setting GstBin async-handling to TRUE"); + g_object_set (G_OBJECT (source), "async-handling", TRUE, NULL); +} + +static void +nle_source_dispose (GObject * object) +{ + NleObject *nleobject = (NleObject *) object; + NleSource *source = (NleSource *) object; + NleSourcePrivate *priv = source->priv; + + GST_DEBUG_OBJECT (object, "dispose"); + + if (priv->dispose_has_run) + return; + + GST_OBJECT_LOCK (object); + if (priv->probeid) { + GST_DEBUG_OBJECT (source, "Removing blocking probe! %lu", priv->probeid); + priv->areblocked = FALSE; + gst_pad_remove_probe (priv->ghostedpad, priv->probeid); + priv->probeid = 0; + } + GST_OBJECT_UNLOCK (object); + + + if (source->element) { + gst_object_unref (source->element); + source->element = NULL; + } + + priv->dispose_has_run = TRUE; + if (priv->ghostedpad) + nle_object_ghost_pad_set_target (nleobject, nleobject->srcpad, NULL); + + if (priv->staticpad) { + gst_object_unref (priv->staticpad); + priv->staticpad = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +element_pad_added_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad, + NleSource * source) +{ + GstCaps *srccaps; + NleSourcePrivate *priv = source->priv; + NleObject *nleobject = (NleObject *) source; + + GST_DEBUG_OBJECT (source, "pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + if (priv->ghostedpad) { + GST_DEBUG_OBJECT (source, + "We already have a target, not doing anything with %s:%s", + GST_DEBUG_PAD_NAME (pad)); + + return; + } + + /* FIXME: pass filter caps to query_caps directly */ + srccaps = gst_pad_query_caps (pad, NULL); + if (nleobject->caps && !gst_caps_can_intersect (srccaps, nleobject->caps)) { + gst_caps_unref (srccaps); + GST_DEBUG_OBJECT (source, "Pad doesn't have valid caps, ignoring"); + return; + } + gst_caps_unref (srccaps); + + priv->ghostedpad = pad; + GST_DEBUG_OBJECT (nleobject, "SET target %" GST_PTR_FORMAT, pad); + nle_object_ghost_pad_set_target (nleobject, nleobject->srcpad, pad); + + GST_DEBUG_OBJECT (source, "Using pad pad %s:%s as a target now!", + GST_DEBUG_PAD_NAME (pad)); +} + +static void +element_pad_removed_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad, + NleSource * source) +{ + NleSourcePrivate *priv = source->priv; + NleObject *nleobject = (NleObject *) source; + + GST_DEBUG_OBJECT (source, "pad %s:%s (controlled pad %s:%s)", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (priv->ghostedpad)); + + if (pad == priv->ghostedpad) { + GST_DEBUG_OBJECT (source, + "The removed pad is the controlled pad, clearing up"); + + GST_DEBUG_OBJECT (source, "Clearing up ghostpad"); + + nle_object_ghost_pad_set_target (NLE_OBJECT (source), nleobject->srcpad, + NULL); + priv->ghostedpad = NULL; + } else { + GST_DEBUG_OBJECT (source, "The removed pad is NOT our controlled pad"); + } +} + +static gint +compare_src_pad (GValue * item, GstCaps * caps) +{ + gint ret = 1; + GstPad *pad = g_value_get_object (item); + GstCaps *padcaps; + + GST_DEBUG_OBJECT (pad, "Trying pad for caps %" GST_PTR_FORMAT, caps); + + /* FIXME: can pass the filter caps right away.. */ + padcaps = gst_pad_query_caps (pad, NULL); + + if (gst_caps_can_intersect (padcaps, caps)) + ret = 0; + + gst_caps_unref (padcaps); + + return ret; +} + +/* + get_valid_src_pad + + Returns True if there's a src pad compatible with the NleObject caps in the + given element. Fills in pad if so. The returned pad has an incremented refcount +*/ + +static gboolean +get_valid_src_pad (NleSource * source, GstElement * element, GstPad ** pad) +{ + gboolean res = FALSE; + GstIterator *srcpads; + GValue item = { 0, }; + + g_return_val_if_fail (pad, FALSE); + + srcpads = gst_element_iterate_src_pads (element); + if (gst_iterator_find_custom (srcpads, (GCompareFunc) compare_src_pad, &item, + NLE_OBJECT (source)->caps)) { + *pad = g_value_get_object (&item); + gst_object_ref (*pad); + g_value_reset (&item); + res = TRUE; + } + gst_iterator_free (srcpads); + + return res; +} + +/* + * has_dynamic_pads + * Returns TRUE if the element has only dynamic pads. + */ + +static gboolean +has_dynamic_srcpads (GstElement * element) +{ + gboolean ret = TRUE; + GList *templates; + GstPadTemplate *template; + + templates = + gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS (element)); + + while (templates) { + template = (GstPadTemplate *) templates->data; + + if ((GST_PAD_TEMPLATE_DIRECTION (template) == GST_PAD_SRC) + && (GST_PAD_TEMPLATE_PRESENCE (template) == GST_PAD_ALWAYS)) { + ret = FALSE; + break; + } + + templates = templates->next; + } + + return ret; +} + +static gboolean +nle_source_control_element_func (NleSource * source, GstElement * element) +{ + NleSourcePrivate *priv = source->priv; + GstPad *pad = NULL; + + g_return_val_if_fail (source->element == NULL, FALSE); + + GST_DEBUG_OBJECT (source, "element:%s, source->element:%p", + GST_ELEMENT_NAME (element), source->element); + + source->element = element; + gst_object_ref (element); + + if (get_valid_src_pad (source, source->element, &pad)) { + priv->staticpad = pad; + nle_object_ghost_pad_set_target (NLE_OBJECT (source), + NLE_OBJECT_SRC (source), pad); + priv->dynamicpads = FALSE; + } else { + priv->dynamicpads = has_dynamic_srcpads (element); + GST_DEBUG_OBJECT (source, "No valid source pad yet, dynamicpads:%d", + priv->dynamicpads); + if (priv->dynamicpads) { + /* connect to pad-added/removed signals */ + priv->padremovedid = g_signal_connect + (G_OBJECT (element), "pad-removed", + G_CALLBACK (element_pad_removed_cb), source); + priv->padaddedid = + g_signal_connect (G_OBJECT (element), "pad-added", + G_CALLBACK (element_pad_added_cb), source); + } + } + + return TRUE; +} + +static gboolean +nle_source_add_element (GstBin * bin, GstElement * element) +{ + NleSource *source = (NleSource *) bin; + gboolean pret; + + GST_DEBUG_OBJECT (source, "Adding element %s", GST_ELEMENT_NAME (element)); + + if (source->element) { + GST_WARNING_OBJECT (bin, "NleSource can only handle one element at a time"); + return FALSE; + } + + /* call parent add_element */ + pret = GST_BIN_CLASS (parent_class)->add_element (bin, element); + + if (pret) { + nle_source_control_element_func (source, element); + } + return pret; +} + +static gboolean +nle_source_remove_element (GstBin * bin, GstElement * element) +{ + NleSource *source = (NleSource *) bin; + NleObject *nleobject = (NleObject *) element; + NleSourcePrivate *priv = source->priv; + gboolean pret; + + GST_DEBUG_OBJECT (source, "Removing element %s", GST_ELEMENT_NAME (element)); + + /* try to remove it */ + pret = GST_BIN_CLASS (parent_class)->remove_element (bin, element); + + if ((!source->element) || (source->element != element)) { + return TRUE; + } + + if (pret) { + nle_object_ghost_pad_set_target (NLE_OBJECT (source), nleobject->srcpad, + NULL); + + /* remove signal handlers */ + if (priv->padremovedid) { + g_signal_handler_disconnect (source->element, priv->padremovedid); + priv->padremovedid = 0; + } + if (priv->padaddedid) { + g_signal_handler_disconnect (source->element, priv->padaddedid); + priv->padaddedid = 0; + } + + priv->dynamicpads = FALSE; + gst_object_unref (element); + source->element = NULL; + } + return pret; +} + +static gboolean +nle_source_send_event (GstElement * element, GstEvent * event) +{ + gboolean res = TRUE; + NleSource *source = (NleSource *) element; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + source->priv->seek_event = event; + break; + default: + res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event); + break; + } + + return res; +} + +static gpointer +ghost_seek_pad (NleSource * source) +{ + NleSourcePrivate *priv = source->priv; + + if (priv->seek_event) { + GstEvent *seek_event = priv->seek_event; + priv->seek_event = NULL; + + if (!(gst_pad_send_event (priv->ghostedpad, seek_event))) + GST_ELEMENT_ERROR (source, RESOURCE, SEEK, + (NULL), ("Sending initial seek to upstream element failed")); + } + + GST_OBJECT_LOCK (source); + if (priv->probeid) { + GST_DEBUG_OBJECT (source, "Removing blocking probe! %lu", priv->probeid); + priv->areblocked = FALSE; + gst_pad_remove_probe (priv->ghostedpad, priv->probeid); + priv->probeid = 0; + } + GST_OBJECT_UNLOCK (source); + + return NULL; +} + +static GstPadProbeReturn +pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, NleSource * source) +{ + GThread *lthread; + + if (!source->priv->areblocked) { + GST_INFO_OBJECT (pad, "Blocked now, launching seek"); + source->priv->areblocked = TRUE; + lthread = + g_thread_new ("gnlsourceseek", (GThreadFunc) ghost_seek_pad, source); + g_thread_unref (lthread); + } + + return GST_PAD_PROBE_OK; +} + +static gboolean +nle_source_prepare (NleObject * object) +{ + GstPad *pad; + NleSource *source = NLE_SOURCE (object); + NleSourcePrivate *priv = source->priv; + GstElement *parent = + (GstElement *) gst_element_get_parent ((GstElement *) object); + + if (!source->element) { + GST_WARNING_OBJECT (source, + "NleSource doesn't have an element to control !"); + if (parent) + gst_object_unref (parent); + return FALSE; + } + + if (object->in_composition == FALSE) { + gst_element_send_event (GST_ELEMENT_CAST (parent), + gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, object->start, GST_SEEK_TYPE_SET, object->stop)); + } + + GST_LOG_OBJECT (source, "srcpad:%p, dynamicpads:%d", + object->srcpad, priv->dynamicpads); + + if (!priv->staticpad && !(get_valid_src_pad (source, source->element, &pad))) { + GST_DEBUG_OBJECT (source, "Couldn't find a valid source pad"); + } else { + if (priv->staticpad) + pad = gst_object_ref (priv->staticpad); + priv->ghostedpad = pad; + GST_OBJECT_LOCK (source); + priv->probeid = gst_pad_add_probe (pad, + GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, + (GstPadProbeCallback) pad_blocked_cb, source, NULL); + GST_OBJECT_UNLOCK (source); + gst_object_unref (pad); + } + + gst_object_unref (parent); + + return TRUE; +} |