From edc5c27ffbcc527d6bc8121463228eaa015e9cdd Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Wed, 19 Aug 2015 11:24:11 +0200 Subject: Move NLE to a dedicated GstPlugin Summary: Allowing external user to directly use it Reviewers: Mathieu_Du Differential Revision: https://phabricator.freedesktop.org/D231 --- .arcconfig | 2 +- Makefile.am | 2 +- configure.ac | 8 + ges/Makefile.am | 15 - ges/ges-internal.h | 1 + ges/ges-track.c | 5 +- ges/ges-utils.c | 10 + ges/ges.c | 24 - ges/nle/.gitignore | 8 - ges/nle/gnlmarshal.list | 2 - ges/nle/nle.h | 36 - ges/nle/nlecomposition.c | 3069 ------------------------------------------ ges/nle/nlecomposition.h | 66 - ges/nle/nleghostpad.c | 803 ----------- ges/nle/nleghostpad.h | 48 - ges/nle/nleobject.c | 738 ---------- ges/nle/nleobject.h | 173 --- ges/nle/nleoperation.c | 843 ------------ ges/nle/nleoperation.h | 90 -- ges/nle/nlesource.c | 505 ------- ges/nle/nlesource.h | 66 - ges/nle/nletypes.h | 42 - ges/nle/nleurisource.c | 166 --- ges/nle/nleurisource.h | 57 - plugins/Makefile.am | 1 + plugins/nle/.gitignore | 8 + plugins/nle/Makefile.am | 30 + plugins/nle/gnlmarshal.list | 2 + plugins/nle/gstnle.c | 66 + plugins/nle/nle.h | 36 + plugins/nle/nlecomposition.c | 3069 ++++++++++++++++++++++++++++++++++++++++++ plugins/nle/nlecomposition.h | 66 + plugins/nle/nleghostpad.c | 803 +++++++++++ plugins/nle/nleghostpad.h | 48 + plugins/nle/nleobject.c | 738 ++++++++++ plugins/nle/nleobject.h | 173 +++ plugins/nle/nleoperation.c | 843 ++++++++++++ plugins/nle/nleoperation.h | 90 ++ plugins/nle/nlesource.c | 505 +++++++ plugins/nle/nlesource.h | 66 + plugins/nle/nletypes.h | 42 + plugins/nle/nleurisource.c | 166 +++ plugins/nle/nleurisource.h | 57 + 43 files changed, 6832 insertions(+), 6756 deletions(-) delete mode 100644 ges/nle/.gitignore delete mode 100644 ges/nle/gnlmarshal.list delete mode 100644 ges/nle/nle.h delete mode 100644 ges/nle/nlecomposition.c delete mode 100644 ges/nle/nlecomposition.h delete mode 100644 ges/nle/nleghostpad.c delete mode 100644 ges/nle/nleghostpad.h delete mode 100644 ges/nle/nleobject.c delete mode 100644 ges/nle/nleobject.h delete mode 100644 ges/nle/nleoperation.c delete mode 100644 ges/nle/nleoperation.h delete mode 100644 ges/nle/nlesource.c delete mode 100644 ges/nle/nlesource.h delete mode 100644 ges/nle/nletypes.h delete mode 100644 ges/nle/nleurisource.c delete mode 100644 ges/nle/nleurisource.h create mode 100644 plugins/Makefile.am create mode 100644 plugins/nle/.gitignore create mode 100644 plugins/nle/Makefile.am create mode 100644 plugins/nle/gnlmarshal.list create mode 100644 plugins/nle/gstnle.c create mode 100644 plugins/nle/nle.h create mode 100644 plugins/nle/nlecomposition.c create mode 100644 plugins/nle/nlecomposition.h create mode 100644 plugins/nle/nleghostpad.c create mode 100644 plugins/nle/nleghostpad.h create mode 100644 plugins/nle/nleobject.c create mode 100644 plugins/nle/nleobject.h create mode 100644 plugins/nle/nleoperation.c create mode 100644 plugins/nle/nleoperation.h create mode 100644 plugins/nle/nlesource.c create mode 100644 plugins/nle/nlesource.h create mode 100644 plugins/nle/nletypes.h create mode 100644 plugins/nle/nleurisource.c create mode 100644 plugins/nle/nleurisource.h diff --git a/.arcconfig b/.arcconfig index fc70fd3d..728aa7a4 100644 --- a/.arcconfig +++ b/.arcconfig @@ -1,3 +1,3 @@ { - "phabricator.uri" : "http://phabricator.freedesktop.org/" + "phabricator.uri" : "https:\/\/phabricator.freedesktop.org\/api\/" } diff --git a/Makefile.am b/Makefile.am index 127de2d4..c9c83a38 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ DISTCHECK_CONFIGURE_FLAGS=--enable-gtk-doc -SUBDIRS = ges tests tools common m4 pkgconfig docs bindings +SUBDIRS = ges tests tools common m4 pkgconfig docs bindings plugins DIST_SUBDIRS = $(SUBDIRS) diff --git a/configure.ac b/configure.ac index 3a20a231..65ed31a0 100644 --- a/configure.ac +++ b/configure.ac @@ -235,6 +235,12 @@ AG_GST_CHECK_GST_CONTROLLER($GST_API_VERSION, [$GST_REQ], yes) AG_GST_CHECK_GST_PLUGINS_BASE($GST_API_VERSION, [$GSTPB_REQ], yes) AM_CONDITIONAL(HAVE_GST_CHECK, test "x$HAVE_GST_CHECK" = "xyes") +AG_GST_ARG_WITH_PLUGINS +AG_GST_CHECK_PLUGIN(plugins) + +dnl set location of plugin directory +AG_GST_SET_PLUGINDIR + GSTPB_PLUGINS_DIR=`$PKG_CONFIG gstreamer-plugins-base-$GST_API_VERSION --variable pluginsdir` AC_SUBST(GSTPB_PLUGINS_DIR) AC_MSG_NOTICE(Using GStreamer Base Plugins in $GSTPB_PLUGINS_DIR) @@ -420,6 +426,8 @@ docs/libs/Makefile pkgconfig/Makefile pkgconfig/gst-editing-services.pc pkgconfig/gst-editing-services-uninstalled.pc +plugins/Makefile +plugins/nle/Makefile bindings/Makefile bindings/python/Makefile bindings/python/gi/Makefile diff --git a/ges/Makefile.am b/ges/Makefile.am index 0c300832..8f459242 100644 --- a/ges/Makefile.am +++ b/ges/Makefile.am @@ -15,12 +15,6 @@ nodist_libges_@GST_API_VERSION@_la_SOURCES = lex.priv_ges_parse_yy.c parse_lex.h libges_@GST_API_VERSION@_la_SOURCES = \ $(built_source_make) \ - nle/nleobject.c \ - nle/nlecomposition.c \ - nle/nleghostpad.c \ - nle/nleoperation.c \ - nle/nlesource.c \ - nle/nleurisource.c \ ges.c \ ges-enums.c \ ges-meta-container.c \ @@ -151,15 +145,6 @@ libges_@GST_API_VERSION@include_HEADERS = \ ges-version.h noinst_HEADERS = \ - nle/nle.h \ - nle/nleobject.h \ - nle/nlecomposition.h \ - nle/nletypes.h \ - nle/nleghostpad.h \ - nle/nleoperation.h \ - nle/nlesource.h \ - nle/nletypes.h \ - nle/nleurisource.h \ ges-internal.h \ ges-auto-transition.h \ ges-structured-interface.h \ diff --git a/ges/ges-internal.h b/ges/ges-internal.h index 53a775da..cf3852c3 100644 --- a/ges/ges-internal.h +++ b/ges/ges-internal.h @@ -382,5 +382,6 @@ G_GNUC_INTERNAL GESMultiFileURI * ges_multi_file_uri_new (const gchar * uri); G_GNUC_INTERNAL gboolean nle_composition_add_object (GstElement *comp, GstElement *object); G_GNUC_INTERNAL gboolean nle_composition_remove_object (GstElement *comp, GstElement *object); +G_GNUC_INTERNAL gboolean nle_object_commit (GstElement * nlesource, gboolean recurse); #endif /* __GES_INTERNAL_H__ */ diff --git a/ges/ges-track.c b/ges/ges-track.c index e573f85a..8d663422 100644 --- a/ges/ges-track.c +++ b/ges/ges-track.c @@ -33,7 +33,6 @@ #include "ges-meta-container.h" #include "ges-video-track.h" #include "ges-audio-track.h" -#include "nle/nleobject.h" G_DEFINE_TYPE_WITH_CODE (GESTrack, ges_track, GST_TYPE_BIN, G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER, NULL)); @@ -453,7 +452,7 @@ ges_track_dispose (GObject * object) (GFunc) dispose_trackelements_foreach, track); g_sequence_free (priv->trackelements_by_start); g_list_free_full (priv->gaps, (GDestroyNotify) free_gap); - nle_object_commit (NLE_OBJECT (track->priv->composition), TRUE); + nle_object_commit (track->priv->composition, TRUE); if (priv->mixing_operation) gst_object_unref (priv->mixing_operation); @@ -1096,7 +1095,7 @@ ges_track_commit (GESTrack * track) track_resort_and_fill_gaps (track); - return nle_object_commit (NLE_OBJECT (track->priv->composition), TRUE); + return nle_object_commit (track->priv->composition, TRUE); } diff --git a/ges/ges-utils.c b/ges/ges-utils.c index 21eb5dc9..4be6be4f 100644 --- a/ges/ges-utils.c +++ b/ges/ges-utils.c @@ -173,3 +173,13 @@ nle_composition_remove_object (GstElement * comp, GstElement * object) { return gst_bin_remove (GST_BIN (comp), object); } + +gboolean +nle_object_commit (GstElement * nlesource, gboolean recurse) +{ + gboolean ret; + + g_signal_emit_by_name (nlesource, "commit", recurse, &ret); + + return ret; +} diff --git a/ges/ges.c b/ges/ges.c index f1697295..5cc39f43 100644 --- a/ges/ges.c +++ b/ges/ges.c @@ -34,7 +34,6 @@ #include #include "ges/gstframepositionner.h" #include "ges-internal.h" -#include "ges/nle/nle.h" #define GES_GNONLIN_VERSION_NEEDED_MAJOR 1 #define GES_GNONLIN_VERSION_NEEDED_MINOR 2 @@ -44,20 +43,6 @@ GST_DEBUG_CATEGORY (_ges_debug); static gboolean ges_initialized = FALSE; -struct _elements_entry -{ - const gchar *name; - GType (*type) (void); -}; - -static struct _elements_entry _elements[] = { - {"nlesource", nle_source_get_type}, - {"nlecomposition", nle_composition_get_type}, - {"nleoperation", nle_operation_get_type}, - {"nleurisource", nle_urisource_get_type}, - {NULL, 0} -}; - /** * ges_init: * @@ -69,8 +54,6 @@ static struct _elements_entry _elements[] = { gboolean ges_init (void) { - gint i = 0; - /* initialize debugging category */ GST_DEBUG_CATEGORY_INIT (_ges_debug, "ges", GST_DEBUG_FG_YELLOW, "GStreamer Editing Services"); @@ -108,13 +91,6 @@ ges_init (void) GST_TYPE_FRAME_POSITIONNER); gst_element_register (NULL, "gespipeline", 0, GES_TYPE_PIPELINE); - for (; _elements[i].name; i++) - if (!(gst_element_register (NULL, - _elements[i].name, GST_RANK_NONE, (_elements[i].type) ()))) - return FALSE; - - nle_init_ghostpad_category (); - /* TODO: user-defined types? */ ges_initialized = TRUE; diff --git a/ges/nle/.gitignore b/ges/nle/.gitignore deleted file mode 100644 index 3ac440bf..00000000 --- a/ges/nle/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -Makefile -Makefile.in -*.o -*.lo -*.la -.deps -.libs -nleversion.h diff --git a/ges/nle/gnlmarshal.list b/ges/nle/gnlmarshal.list deleted file mode 100644 index 67c63946..00000000 --- a/ges/nle/gnlmarshal.list +++ /dev/null @@ -1,2 +0,0 @@ -VOID:OBJECT,UINT - diff --git a/ges/nle/nle.h b/ges/nle/nle.h deleted file mode 100644 index 6e00eae4..00000000 --- a/ges/nle/nle.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Gnonlin - * Copyright (C) <2001> Wim Taymans - * <2004-2008> Edward Hervey - * - * 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. - */ - -#ifndef __NLE_H__ -#define __NLE_H__ - -#include - -#include "nletypes.h" - -#include "nleobject.h" -#include "nleghostpad.h" -#include "nlesource.h" -#include "nlecomposition.h" -#include "nleoperation.h" - -#include "nleurisource.h" - -#endif /* __GST_H__ */ diff --git a/ges/nle/nlecomposition.c b/ges/nle/nlecomposition.c deleted file mode 100644 index 7b69589b..00000000 --- a/ges/nle/nlecomposition.c +++ /dev/null @@ -1,3069 +0,0 @@ -/* GStreamer - * Copyright (C) 2001 Wim Taymans - * 2004-2008 Edward Hervey - * 2014 Mathieu Duponchelle - * 2014 Thibault Saunier - * - * 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-nlecomposition - * - * A NleComposition contains NleObjects such as NleSources and NleOperations, - * and connects them dynamically to create a composition timeline. - */ - -static GstStaticPadTemplate nle_composition_src_template = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -GST_DEBUG_CATEGORY_STATIC (nlecomposition_debug); -#define GST_CAT_DEFAULT nlecomposition_debug - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (nlecomposition_debug,"nlecomposition", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Composition"); -#define nle_composition_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (NleComposition, nle_composition, NLE_TYPE_OBJECT, - _do_init); - -enum -{ - PROP_0, - PROP_DEACTIVATED_ELEMENTS_STATE, - PROP_LAST, -}; - -/* Properties from NleObject */ -enum -{ - NLEOBJECT_PROP_START, - NLEOBJECT_PROP_STOP, - NLEOBJECT_PROP_DURATION, - NLEOBJECT_PROP_LAST -}; - -enum -{ - COMMIT_SIGNAL, - COMMITED_SIGNAL, - QUERY_POSITION_SIGNAL, - LAST_SIGNAL -}; - -typedef enum -{ - COMP_UPDATE_STACK_INITIALIZE, - COMP_UPDATE_STACK_ON_COMMIT, - COMP_UPDATE_STACK_ON_EOS, - COMP_UPDATE_STACK_ON_SEEK, - COMP_UPDATE_STACK_NONE -} NleUpdateStackReason; - -static const char *UPDATE_PIPELINE_REASONS[] = { - "Initialize", "Commit", "EOS", "Seek" -}; - -typedef struct -{ - NleComposition *comp; - GstEvent *event; -} SeekData; - -typedef struct -{ - NleComposition *comp; - NleObject *object; -} ChildIOData; - -typedef struct -{ - NleComposition *comp; - gint32 seqnum; - - NleUpdateStackReason reason; -} UpdateCompositionData; - -struct _NleCompositionPrivate -{ - gboolean dispose_has_run; - - /* - Sorted List of NleObjects , ThreadSafe - objects_start : sorted by start-time then priority - objects_stop : sorted by stop-time then priority - objects_hash : contains all controlled objects - - Those list should be manipulated exclusively in the main context - or while the task is totally stopped. - */ - GList *objects_start; - GList *objects_stop; - GHashTable *objects_hash; - - /* List of NleObject to be inserted or removed from the composition on the - * next commit */ - GHashTable *pending_io; - - gulong ghosteventprobe; - - /* current stack, list of NleObject* */ - GNode *current; - - /* List of NleObject whose start/duration will be the same as the composition */ - GList *expandables; - - /* - current segment seek start/stop time. - Reconstruct pipeline ONLY if seeking outside of those values - FIXME : segment_start isn't always the earliest time before which the - timeline doesn't need to be modified - */ - GstClockTime segment_start; - GstClockTime segment_stop; - - /* Seek segment handler */ - GstSegment *segment; - GstSegment *outside_segment; - - /* Next running base_time to set on outgoing segment */ - guint64 next_base_time; - - /* - OUR sync_handler on the child_bus - We are called before nle_object_sync_handler - */ - GstPadEventFunction nle_event_pad_func; - gboolean send_stream_start; - - /* Protect the actions list */ - GMutex actions_lock; - GCond actions_cond; - GList *actions; - - gboolean running; - gboolean initialized; - - GstElement *current_bin; - - gboolean seeking_itself; - gint real_eos_seqnum; - gint next_eos_seqnum; - gint flush_seqnum; - - /* 0 means that we already received the right caps or segment */ - gint seqnum_to_restart_task; - gboolean waiting_for_buffer; - - gboolean tearing_down_stack; - - NleUpdateStackReason updating_reason; -}; - -typedef struct _Action -{ - GCClosure closure; - gint priority; -} Action; - -#define ACTION_CALLBACK(__action) (((GCClosure*) (__action))->callback) - -static guint _signals[LAST_SIGNAL] = { 0 }; - -static GParamSpec *nleobject_properties[NLEOBJECT_PROP_LAST]; - -#define OBJECT_IN_ACTIVE_SEGMENT(comp,element) \ - ((NLE_OBJECT_START(element) < comp->priv->segment_stop) && \ - (NLE_OBJECT_STOP(element) >= comp->priv->segment_start)) - -static void nle_composition_dispose (GObject * object); -static void nle_composition_finalize (GObject * object); -static void nle_composition_reset (NleComposition * comp); - -static gboolean nle_composition_add_object (GstBin * bin, GstElement * element); - -static gboolean -nle_composition_remove_object (GstBin * bin, GstElement * element); - -static GstStateChangeReturn -nle_composition_change_state (GstElement * element, GstStateChange transition); - -static inline void nle_composition_reset_target_pad (NleComposition * comp); - -static gboolean -seek_handling (NleComposition * comp, gint32 seqnum, - NleUpdateStackReason update_stack_reason); -static gint objects_start_compare (NleObject * a, NleObject * b); -static gint objects_stop_compare (NleObject * a, NleObject * b); -static GstClockTime get_current_position (NleComposition * comp); - -static gboolean update_pipeline (NleComposition * comp, - GstClockTime currenttime, gint32 seqnum, - NleUpdateStackReason update_stack_reason); -static gboolean nle_composition_commit_func (NleObject * object, - gboolean recurse); -static void update_start_stop_duration (NleComposition * comp); - -static gboolean -nle_composition_event_handler (GstPad * ghostpad, GstObject * parent, - GstEvent * event); -static void _relink_single_node (NleComposition * comp, GNode * node, - GstEvent * toplevel_seek); -static void _update_pipeline_func (NleComposition * comp, - UpdateCompositionData * ucompo); -static void _commit_func (NleComposition * comp, - UpdateCompositionData * ucompo); -static GstEvent *get_new_seek_event (NleComposition * comp, gboolean initial, - gboolean updatestoponly); -static gboolean _nle_composition_add_object (NleComposition * comp, - NleObject * object); -static gboolean _nle_composition_remove_object (NleComposition * comp, - NleObject * object); -static void _deactivate_stack (NleComposition * comp, - gboolean flush_downstream); -static gboolean _set_real_eos_seqnum_from_seek (NleComposition * comp, - GstEvent * event); -static void _emit_commited_signal_func (NleComposition * comp, gpointer udata); -static void _restart_task (NleComposition * comp); -static void -_add_action (NleComposition * comp, GCallback func, gpointer data, - gint priority); -static gboolean -_is_ready_to_restart_task (NleComposition * comp, GstEvent * event); - - -/* COMP_REAL_START: actual position to start current playback at. */ -#define COMP_REAL_START(comp) \ - (MAX (comp->priv->segment->start, NLE_OBJECT_START (comp))) - -#define COMP_REAL_STOP(comp) \ - (GST_CLOCK_TIME_IS_VALID (comp->priv->segment->stop) ? \ - (MIN (comp->priv->segment->stop, NLE_OBJECT_STOP (comp))) : \ - NLE_OBJECT_STOP (comp)) - -#define ACTIONS_LOCK(comp) G_STMT_START { \ - GST_LOG_OBJECT (comp, "Getting ACTIONS_LOCK in thread %p", \ - g_thread_self()); \ - g_mutex_lock(&((NleComposition*)comp)->priv->actions_lock); \ - GST_LOG_OBJECT (comp, "Got ACTIONS_LOCK in thread %p", \ - g_thread_self()); \ -} G_STMT_END - -#define ACTIONS_UNLOCK(comp) G_STMT_START { \ - g_mutex_unlock(&((NleComposition*)comp)->priv->actions_lock); \ - GST_LOG_OBJECT (comp, "Unlocked ACTIONS_LOCK in thread %p", \ - g_thread_self()); \ -} G_STMT_END - -#define WAIT_FOR_AN_ACTION(comp) G_STMT_START { \ - GST_LOG_OBJECT (comp, "Waiting for an action in thread %p", \ - g_thread_self()); \ - g_cond_wait(&((NleComposition*)comp)->priv->actions_cond, \ - &((NleComposition*)comp)->priv->actions_lock); \ - GST_LOG_OBJECT (comp, "Done WAITING for an action in thread %p", \ - g_thread_self()); \ -} G_STMT_END - -#define SIGNAL_NEW_ACTION(comp) G_STMT_START { \ - GST_LOG_OBJECT (comp, "Signalling new action from thread %p", \ - g_thread_self()); \ - g_cond_signal(&((NleComposition*)comp)->priv->actions_cond); \ -} G_STMT_END - -#define GET_TASK_LOCK(comp) (&(NLE_COMPOSITION(comp)->task_rec_lock)) - -static inline gboolean -_have_to_flush_downstream (NleUpdateStackReason update_reason) -{ - if (update_reason == COMP_UPDATE_STACK_ON_COMMIT || - update_reason == COMP_UPDATE_STACK_ON_SEEK || - update_reason == COMP_UPDATE_STACK_INITIALIZE) - return TRUE; - - return FALSE; -} - -static void -_assert_proper_thread (NleComposition * comp) -{ - if (comp->task && gst_task_get_state (comp->task) != GST_TASK_STOPPED && - g_thread_self () != comp->task->thread) { - g_warning ("Trying to touch children in a thread different from" - " its dedicated thread!"); - } -} - -static void -_remove_actions_for_type (NleComposition * comp, GCallback callback) -{ - GList *tmp; - - ACTIONS_LOCK (comp); - for (tmp = comp->priv->actions; tmp; tmp = tmp->next) { - Action *act = tmp->data; - - if (ACTION_CALLBACK (act) == callback) { - g_closure_unref ((GClosure *) act); - comp->priv->actions = g_list_delete_link (comp->priv->actions, tmp); - } - } - ACTIONS_UNLOCK (comp); - -} - -static void -_execute_actions (NleComposition * comp) -{ - NleCompositionPrivate *priv = comp->priv; - - ACTIONS_LOCK (comp); - if (priv->running == FALSE) { - GST_DEBUG_OBJECT (comp, "Not running anymore"); - - ACTIONS_UNLOCK (comp); - return; - } - - if (priv->actions == NULL) - WAIT_FOR_AN_ACTION (comp); - - if (comp->priv->running == FALSE) { - GST_INFO_OBJECT (comp, "Done waiting but not running anymore"); - - ACTIONS_UNLOCK (comp); - return; - } - - if (priv->actions) { - GValue params[1] = { G_VALUE_INIT }; - GList *lact; - - g_value_init (¶ms[0], G_TYPE_OBJECT); - g_value_set_object (¶ms[0], comp); - - lact = priv->actions; - priv->actions = priv->actions->next; - ACTIONS_UNLOCK (comp); - - GST_INFO_OBJECT (comp, "Invoking %p:%s", - lact->data, GST_DEBUG_FUNCPTR_NAME ((ACTION_CALLBACK (lact->data)))); - g_closure_invoke (lact->data, NULL, 1, params, NULL); - g_value_unset (¶ms[0]); - } else { - ACTIONS_UNLOCK (comp); - } -} - -static void -_start_task (NleComposition * comp) -{ - GstTask *task; - - ACTIONS_LOCK (comp); - comp->priv->running = TRUE; - ACTIONS_UNLOCK (comp); - - GST_OBJECT_LOCK (comp); - - task = comp->task; - if (task == NULL) { - gchar *taskname = - g_strdup_printf ("%s_update_management", GST_OBJECT_NAME (comp)); - - task = gst_task_new ((GstTaskFunction) _execute_actions, comp, NULL); - gst_object_set_name (GST_OBJECT_CAST (task), taskname); - gst_task_set_lock (task, GET_TASK_LOCK (comp)); - GST_DEBUG_OBJECT (comp, "created task %p", task); - comp->task = task; - } - - gst_task_set_state (task, GST_TASK_STARTED); - GST_OBJECT_UNLOCK (comp); -} - -static gboolean -_stop_task (NleComposition * comp) -{ - gboolean res = TRUE; - GstTask *task; - - GST_INFO_OBJECT (comp, "Stoping children management task"); - - ACTIONS_LOCK (comp); - comp->priv->running = FALSE; - - /* Make sure we do not stay blocked trying to execute an action */ - SIGNAL_NEW_ACTION (comp); - ACTIONS_UNLOCK (comp); - - GST_DEBUG_OBJECT (comp, "stop task"); - - GST_OBJECT_LOCK (comp); - task = comp->task; - if (task == NULL) - goto no_task; - comp->task = NULL; - res = gst_task_set_state (task, GST_TASK_STOPPED); - GST_OBJECT_UNLOCK (comp); - - if (!gst_task_join (task)) - goto join_failed; - - gst_object_unref (task); - - return res; - -no_task: - { - GST_OBJECT_UNLOCK (comp); - - /* this is not an error */ - return TRUE; - } -join_failed: - { - /* this is bad, possibly the application tried to join the task from - * the task's thread. We install the task again so that it will be stopped - * again from the right thread next time hopefully. */ - GST_OBJECT_LOCK (comp); - GST_DEBUG_OBJECT (comp, "join failed"); - /* we can only install this task if there was no other task */ - if (comp->task == NULL) - comp->task = task; - GST_OBJECT_UNLOCK (comp); - - return FALSE; - } - - return res; -} - -static void -_post_start_composition_update (NleComposition * comp, - gint32 seqnum, NleUpdateStackReason reason) -{ - GstMessage *msg; - - msg = gst_message_new_element (GST_OBJECT (comp), - gst_structure_new ("NleCompositionStartUpdate", - "reason", G_TYPE_STRING, UPDATE_PIPELINE_REASONS[reason], NULL)); - - gst_message_set_seqnum (msg, seqnum); - gst_element_post_message (GST_ELEMENT (comp), msg); -} - -static void -_post_start_composition_update_done (NleComposition * comp, - gint32 seqnum, NleUpdateStackReason reason) -{ - GstMessage *msg = gst_message_new_element (GST_OBJECT (comp), - gst_structure_new ("NleCompositionUpdateDone", - "reason", G_TYPE_STRING, UPDATE_PIPELINE_REASONS[reason], - NULL)); - - gst_message_set_seqnum (msg, seqnum); - gst_element_post_message (GST_ELEMENT (comp), msg); -} - -static void -_seek_pipeline_func (NleComposition * comp, SeekData * seekd) -{ - gdouble rate; - GstFormat format; - GstSeekFlags flags; - GstSeekType cur_type, stop_type; - gint64 cur, stop; - NleCompositionPrivate *priv = comp->priv; - - gst_event_parse_seek (seekd->event, &rate, &format, &flags, - &cur_type, &cur, &stop_type, &stop); - - GST_DEBUG_OBJECT (seekd->comp, - "start:%" GST_TIME_FORMAT " -- stop:%" GST_TIME_FORMAT " flags:%d", - GST_TIME_ARGS (cur), GST_TIME_ARGS (stop), flags); - - gst_segment_do_seek (priv->segment, - rate, format, flags, cur_type, cur, stop_type, stop, NULL); - gst_segment_do_seek (priv->outside_segment, - rate, format, flags, cur_type, cur, stop_type, stop, NULL); - - GST_DEBUG_OBJECT (seekd->comp, "Segment now has flags:%d", - priv->segment->flags); - - if (priv->segment->start >= NLE_OBJECT_STOP (seekd->comp)) { - GST_INFO_OBJECT (seekd->comp, - "Start %" GST_TIME_FORMAT " > comp->stop: %" GST_TIME_FORMAT - " Not seeking", GST_TIME_ARGS (priv->segment->start), - GST_TIME_ARGS (NLE_OBJECT_STOP (seekd->comp))); - GST_FIXME_OBJECT (seekd->comp, "HANDLE error async!"); - return; - } - - _post_start_composition_update (seekd->comp, - gst_event_get_seqnum (seekd->event), COMP_UPDATE_STACK_ON_SEEK); - - /* crop the segment start/stop values */ - /* Only crop segment start value if we don't have a default object */ - if (priv->expandables == NULL) - priv->segment->start = - MAX (priv->segment->start, NLE_OBJECT_START (seekd->comp)); - priv->segment->stop = - MIN (priv->segment->stop, NLE_OBJECT_STOP (seekd->comp)); - - priv->next_base_time = 0; - - seek_handling (seekd->comp, gst_event_get_seqnum (seekd->event), - COMP_UPDATE_STACK_ON_SEEK); - - _post_start_composition_update_done (seekd->comp, - gst_event_get_seqnum (seekd->event), COMP_UPDATE_STACK_ON_SEEK); -} - -/* Must be called with OBJECTS_LOCK taken */ -static void -_process_pending_entries (NleComposition * comp) -{ - NleObject *object; - GHashTableIter iter; - gboolean deactivated_stack = FALSE; - - NleCompositionPrivate *priv = comp->priv; - - g_hash_table_iter_init (&iter, priv->pending_io); - while (g_hash_table_iter_next (&iter, (gpointer *) & object, NULL)) { - if (g_hash_table_contains (priv->objects_hash, object)) { - - if (GST_OBJECT_PARENT (object) == GST_OBJECT_CAST (priv->current_bin) && - deactivated_stack == FALSE) { - deactivated_stack = TRUE; - - _deactivate_stack (comp, TRUE); - } - - _nle_composition_remove_object (comp, object); - } else { - _nle_composition_add_object (comp, object); - } - } - - g_hash_table_remove_all (priv->pending_io); -} - - -static inline gboolean -_commit_values (NleComposition * comp) -{ - GList *tmp; - gboolean commited = FALSE; - NleCompositionPrivate *priv = comp->priv; - - for (tmp = priv->objects_start; tmp; tmp = tmp->next) { - if (nle_object_commit (tmp->data, TRUE)) - commited = TRUE; - } - - GST_DEBUG_OBJECT (comp, "Linking up commit vmethod"); - commited |= NLE_OBJECT_CLASS (parent_class)->commit (NLE_OBJECT (comp), TRUE); - - return commited; -} - -static gboolean -_commit_all_values (NleComposition * comp) -{ - NleCompositionPrivate *priv = comp->priv; - - priv->next_base_time = 0; - - _process_pending_entries (comp); - - if (_commit_values (comp) == FALSE) { - - return FALSE;; - } - - /* The topology of the composition might have changed, update the lists */ - priv->objects_start = g_list_sort - (priv->objects_start, (GCompareFunc) objects_start_compare); - priv->objects_stop = g_list_sort - (priv->objects_stop, (GCompareFunc) objects_stop_compare); - - return TRUE; -} - -static gboolean -_initialize_stack_func (NleComposition * comp, UpdateCompositionData * ucompo) -{ - NleCompositionPrivate *priv = comp->priv; - - - _post_start_composition_update (comp, ucompo->seqnum, ucompo->reason); - - _commit_all_values (comp); - update_start_stop_duration (comp); - comp->priv->next_base_time = 0; - /* set ghostpad target */ - if (!(update_pipeline (comp, COMP_REAL_START (comp), - ucompo->seqnum, COMP_UPDATE_STACK_INITIALIZE))) { - GST_FIXME_OBJECT (comp, "PLEASE signal state change failure ASYNC"); - } - - _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason); - priv->initialized = TRUE; - - return G_SOURCE_REMOVE; -} - -static void -_remove_object_func (NleComposition * comp, ChildIOData * childio) -{ - NleObject *object = childio->object; - - NleCompositionPrivate *priv = comp->priv; - NleObject *in_pending_io; - - in_pending_io = g_hash_table_lookup (priv->pending_io, object); - - if (!g_hash_table_contains (priv->objects_hash, object)) { - if (in_pending_io) { - GST_INFO_OBJECT (comp, "Object %" GST_PTR_FORMAT " was marked" - " for addition, removing it from the addition list", object); - - g_hash_table_remove (priv->pending_io, object); - return; - } - - GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is " - " not in the composition", object); - - return; - } - - if (in_pending_io) { - GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked" - " for removal", object); - - return; - } - - g_hash_table_add (priv->pending_io, object); - - return; -} - -static void -_add_remove_object_action (NleComposition * comp, NleObject * object) -{ - ChildIOData *childio = g_slice_new0 (ChildIOData); - - GST_DEBUG_OBJECT (comp, "Adding Action"); - - childio->comp = comp; - childio->object = object; - - _add_action (comp, G_CALLBACK (_remove_object_func), - childio, G_PRIORITY_DEFAULT); -} - -static void -_add_object_func (NleComposition * comp, ChildIOData * childio) -{ - NleObject *object = childio->object; - NleCompositionPrivate *priv = comp->priv; - NleObject *in_pending_io; - - in_pending_io = g_hash_table_lookup (priv->pending_io, object); - - if (g_hash_table_contains (priv->objects_hash, object)) { - - if (in_pending_io) { - GST_INFO_OBJECT (comp, "Object already in but marked in pendings" - " removing from pendings"); - g_hash_table_remove (priv->pending_io, object); - - return; - } - GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is " - " already in the composition", object); - - return; - } - - if (in_pending_io) { - GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked" - " for addition", object); - - return; - } - - - g_hash_table_add (priv->pending_io, object); -} - -static void -_add_add_object_action (NleComposition * comp, NleObject * object) -{ - ChildIOData *childio = g_slice_new0 (ChildIOData); - - GST_DEBUG_OBJECT (comp, "Adding Action"); - - childio->comp = comp; - childio->object = object; - - _add_action (comp, G_CALLBACK (_add_object_func), childio, - G_PRIORITY_DEFAULT); -} - -static void -_free_action (Action * action, gpointer udata) -{ - if (ACTION_CALLBACK (action) == _seek_pipeline_func) { - SeekData *seekd = (SeekData *) udata; - - gst_event_unref (seekd->event); - g_slice_free (SeekData, seekd); - } else if (ACTION_CALLBACK (action) == _remove_object_func || - ACTION_CALLBACK (action) == _add_object_func) { - g_slice_free (ChildIOData, udata); - } else if (ACTION_CALLBACK (action) == _update_pipeline_func || - ACTION_CALLBACK (action) == _commit_func || - ACTION_CALLBACK (action) == _initialize_stack_func) { - g_slice_free (UpdateCompositionData, udata); - } -} - -static void -_add_action (NleComposition * comp, GCallback func, - gpointer data, gint priority) -{ - Action *action; - NleCompositionPrivate *priv = comp->priv; - - - action = (Action *) g_closure_new_simple (sizeof (Action), data); - g_closure_add_finalize_notifier ((GClosure *) action, data, - (GClosureNotify) _free_action); - ACTION_CALLBACK (action) = func; - action->priority = priority; - g_closure_set_marshal ((GClosure *) action, g_cclosure_marshal_VOID__VOID); - - ACTIONS_LOCK (comp); - GST_INFO_OBJECT (comp, "Adding Action for function: %p:%s", - action, GST_DEBUG_FUNCPTR_NAME (func)); - - if (func == G_CALLBACK (_emit_commited_signal_func)) - priv->actions = g_list_prepend (priv->actions, action); - else - priv->actions = g_list_append (priv->actions, action); - - SIGNAL_NEW_ACTION (comp); - ACTIONS_UNLOCK (comp); -} - -static void -_add_seek_action (NleComposition * comp, GstEvent * event) -{ - SeekData *seekd = g_slice_new0 (SeekData); - - GST_DEBUG_OBJECT (comp, "Adding Action"); - - seekd->comp = comp; - seekd->event = event; - - comp->priv->next_eos_seqnum = 0; - comp->priv->real_eos_seqnum = 0; - _add_action (comp, G_CALLBACK (_seek_pipeline_func), seekd, - G_PRIORITY_DEFAULT); -} - -static void -_remove_update_actions (NleComposition * comp) -{ - _remove_actions_for_type (comp, G_CALLBACK (_update_pipeline_func)); -} - -static void -_remove_seek_actions (NleComposition * comp) -{ - _remove_actions_for_type (comp, G_CALLBACK (_seek_pipeline_func)); -} - -static void -_add_update_compo_action (NleComposition * comp, - GCallback callback, NleUpdateStackReason reason) -{ - UpdateCompositionData *ucompo = g_slice_new0 (UpdateCompositionData); - - ucompo->comp = comp; - ucompo->reason = reason; - ucompo->seqnum = gst_util_seqnum_next (); - - GST_INFO_OBJECT (comp, "Updating because: %s -- Setting seqnum: %i", - UPDATE_PIPELINE_REASONS[reason], ucompo->seqnum); - - _add_action (comp, callback, ucompo, G_PRIORITY_DEFAULT); -} - -static void -nle_composition_handle_message (GstBin * bin, GstMessage * message) -{ - NleComposition *comp = (NleComposition *) bin; - - if (comp->priv->tearing_down_stack) { - if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) { - GST_FIXME_OBJECT (comp, "Dropping %" GST_PTR_FORMAT " message from " - " %" GST_PTR_FORMAT " being teared down to READY", - message, GST_MESSAGE_SRC (message)); - } - - GST_DEBUG_OBJECT (comp, "Dropping message %" GST_PTR_FORMAT " from " - "object being teared down to READY!", message); - gst_message_unref (message); - - return; - } - - GST_BIN_CLASS (parent_class)->handle_message (bin, message); -} - -static void -nle_composition_class_init (NleCompositionClass * 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 (NleCompositionPrivate)); - - gst_element_class_set_static_metadata (gstelement_class, - "GNonLin Composition", "Filter/Editor", "Combines NLE objects", - "Wim Taymans , Edward Hervey ," - " Mathieu Duponchelle ," - " Thibault Saunier "); - - gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_composition_dispose); - gobject_class->finalize = GST_DEBUG_FUNCPTR (nle_composition_finalize); - - gstelement_class->change_state = nle_composition_change_state; - - gstbin_class->add_element = GST_DEBUG_FUNCPTR (nle_composition_add_object); - gstbin_class->remove_element = - GST_DEBUG_FUNCPTR (nle_composition_remove_object); - gstbin_class->handle_message = - GST_DEBUG_FUNCPTR (nle_composition_handle_message); - - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&nle_composition_src_template)); - - /* Get the paramspec of the NleObject klass so we can do - * fast notifies */ - nleobject_properties[NLEOBJECT_PROP_START] = - g_object_class_find_property (gobject_class, "start"); - nleobject_properties[NLEOBJECT_PROP_STOP] = - g_object_class_find_property (gobject_class, "stop"); - nleobject_properties[NLEOBJECT_PROP_DURATION] = - g_object_class_find_property (gobject_class, "duration"); - - _signals[COMMITED_SIGNAL] = - g_signal_new ("commited", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, - G_TYPE_BOOLEAN); - - /** - * NleComposition::query-position - * @comp: a #NleComposition - * - * A signal that *has* to be connected and which should return the current - * position of the pipeline. - * - * This signal is used in order to know the current position of the whole - * pipeline so it is user's responsability to give that answer as there - * is no other way to precisely know the position in the whole pipeline. - */ - _signals[QUERY_POSITION_SIGNAL] = - g_signal_new ("query-position", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, - 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_UINT64, 0, NULL); - - GST_DEBUG_REGISTER_FUNCPTR (_seek_pipeline_func); - GST_DEBUG_REGISTER_FUNCPTR (_remove_object_func); - GST_DEBUG_REGISTER_FUNCPTR (_add_object_func); - GST_DEBUG_REGISTER_FUNCPTR (_update_pipeline_func); - GST_DEBUG_REGISTER_FUNCPTR (_commit_func); - GST_DEBUG_REGISTER_FUNCPTR (_emit_commited_signal_func); - GST_DEBUG_REGISTER_FUNCPTR (_initialize_stack_func); - - /* Just be useless, so the compiler does not warn us - * about our uselessness */ - nleobject_class->commit = nle_composition_commit_func; - -} - -static void -nle_composition_init (NleComposition * comp) -{ - NleCompositionPrivate *priv; - - GST_OBJECT_FLAG_SET (comp, NLE_OBJECT_SOURCE); - GST_OBJECT_FLAG_SET (comp, NLE_OBJECT_COMPOSITION); - - priv = G_TYPE_INSTANCE_GET_PRIVATE (comp, NLE_TYPE_COMPOSITION, - NleCompositionPrivate); - priv->objects_start = NULL; - priv->objects_stop = NULL; - - priv->segment = gst_segment_new (); - priv->outside_segment = gst_segment_new (); - - g_rec_mutex_init (&comp->task_rec_lock); - - priv->objects_hash = g_hash_table_new (g_direct_hash, g_direct_equal); - - g_mutex_init (&priv->actions_lock); - g_cond_init (&priv->actions_cond); - - priv->pending_io = g_hash_table_new (g_direct_hash, g_direct_equal); - - comp->priv = priv; - - priv->current_bin = gst_bin_new ("current-bin"); - gst_bin_add (GST_BIN (comp), priv->current_bin); - - nle_composition_reset (comp); - - priv->nle_event_pad_func = GST_PAD_EVENTFUNC (NLE_OBJECT_SRC (comp)); - gst_pad_set_event_function (NLE_OBJECT_SRC (comp), - GST_DEBUG_FUNCPTR (nle_composition_event_handler)); -} - -static void -nle_composition_dispose (GObject * object) -{ - GList *iter; - NleComposition *comp = NLE_COMPOSITION (object); - NleCompositionPrivate *priv = comp->priv; - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - iter = priv->objects_start; - while (iter) { - GList *next = iter->next; - - _nle_composition_remove_object (comp, iter->data); - iter = next; - } - - g_list_free (priv->objects_stop); - - if (priv->expandables) { - GList *iter; - - iter = priv->expandables; - - while (iter) { - GList *next = iter->next; - - _nle_composition_remove_object (comp, iter->data); - iter = next; - } - - priv->expandables = NULL; - } - - nle_composition_reset_target_pad (comp); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -nle_composition_finalize (GObject * object) -{ - NleComposition *comp = NLE_COMPOSITION (object); - NleCompositionPrivate *priv = comp->priv; - - _assert_proper_thread (comp); - - if (priv->current) { - g_node_destroy (priv->current); - priv->current = NULL; - } - - g_hash_table_destroy (priv->objects_hash); - - gst_segment_free (priv->segment); - gst_segment_free (priv->outside_segment); - - g_rec_mutex_clear (&comp->task_rec_lock); - - G_OBJECT_CLASS (parent_class)->finalize (object); - - g_mutex_clear (&priv->actions_lock); - g_cond_clear (&priv->actions_cond); -} - -/* signal_duration_change - * Creates a new GST_MESSAGE_DURATION_CHANGED with the currently configured - * composition duration and sends that on the bus. - */ -static inline void -signal_duration_change (NleComposition * comp) -{ - gst_element_post_message (GST_ELEMENT_CAST (comp), - gst_message_new_duration_changed (GST_OBJECT_CAST (comp))); -} - -static gboolean -_remove_child (GValue * item, GValue * ret G_GNUC_UNUSED, GstBin * bin) -{ - GstElement *child = g_value_get_object (item); - - if (NLE_IS_OPERATION (child)) - nle_operation_hard_cleanup (NLE_OPERATION (child)); - - - gst_bin_remove (bin, child); - - return TRUE; -} - -static void -_empty_bin (GstBin * bin) -{ - GstIterator *children; - - children = gst_bin_iterate_elements (bin); - - while (G_UNLIKELY (gst_iterator_fold (children, - (GstIteratorFoldFunction) _remove_child, NULL, - bin) == GST_ITERATOR_RESYNC)) { - gst_iterator_resync (children); - } - - gst_iterator_free (children); -} - -static void -nle_composition_reset (NleComposition * comp) -{ - NleCompositionPrivate *priv = comp->priv; - - GST_DEBUG_OBJECT (comp, "resetting"); - - _assert_proper_thread (comp); - - priv->segment_start = GST_CLOCK_TIME_NONE; - priv->segment_stop = GST_CLOCK_TIME_NONE; - priv->next_base_time = 0; - - gst_segment_init (priv->segment, GST_FORMAT_TIME); - gst_segment_init (priv->outside_segment, GST_FORMAT_TIME); - - if (priv->current) - g_node_destroy (priv->current); - priv->current = NULL; - - nle_composition_reset_target_pad (comp); - - priv->initialized = FALSE; - priv->send_stream_start = TRUE; - priv->real_eos_seqnum = 0; - priv->next_eos_seqnum = 0; - priv->flush_seqnum = 0; - - _empty_bin (GST_BIN_CAST (priv->current_bin)); - - GST_DEBUG_OBJECT (comp, "Composition now resetted"); -} - -static GstPadProbeReturn -ghost_event_probe_handler (GstPad * ghostpad G_GNUC_UNUSED, - GstPadProbeInfo * info, NleComposition * comp) -{ - GstPadProbeReturn retval = GST_PAD_PROBE_OK; - NleCompositionPrivate *priv = comp->priv; - GstEvent *event; - - if (GST_IS_BUFFER (info->data)) { - if (priv->waiting_for_buffer) { - GST_INFO_OBJECT (comp, "update_pipeline DONE"); - _restart_task (comp); - } - - return GST_PAD_PROBE_OK; - } - - event = GST_PAD_PROBE_INFO_EVENT (info); - - GST_DEBUG_OBJECT (comp, "event: %s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_STOP: - if (_is_ready_to_restart_task (comp, event)) - _restart_task (comp); - - if (gst_event_get_seqnum (event) != comp->priv->flush_seqnum) { - GST_INFO_OBJECT (comp, "Dropping flush stop %d -- %d", - gst_event_get_seqnum (event), priv->seqnum_to_restart_task); - retval = GST_PAD_PROBE_DROP; - } else { - GST_INFO_OBJECT (comp, "Forwarding our flush stop with seqnum %i", - comp->priv->flush_seqnum); - gst_event_unref (event); - event = gst_event_new_flush_stop (TRUE); - GST_PAD_PROBE_INFO_DATA (info) = event; - gst_event_set_seqnum (event, comp->priv->flush_seqnum); - comp->priv->flush_seqnum = 0; - } - break; - case GST_EVENT_FLUSH_START: - if (gst_event_get_seqnum (event) != comp->priv->flush_seqnum) { - GST_INFO_OBJECT (comp, "Dropping flush start"); - retval = GST_PAD_PROBE_DROP; - } else { - GST_INFO_OBJECT (comp, "Forwarding our flush start with seqnum %i", - comp->priv->flush_seqnum); - } - break; - case GST_EVENT_STREAM_START: - if (g_atomic_int_compare_and_exchange (&priv->send_stream_start, TRUE, - FALSE)) { - /* FIXME: Do we want to create a new stream ID here? */ - GST_DEBUG_OBJECT (comp, "forward stream-start %p", event); - } else { - GST_DEBUG_OBJECT (comp, "dropping stream-start %p", event); - retval = GST_PAD_PROBE_DROP; - } - break; - case GST_EVENT_SEGMENT: - { - guint64 rstart, rstop; - const GstSegment *segment; - GstSegment copy; - GstEvent *event2; - /* next_base_time */ - - if (_is_ready_to_restart_task (comp, event)) - _restart_task (comp); - - gst_event_parse_segment (event, &segment); - gst_segment_copy_into (segment, ©); - - rstart = - gst_segment_to_running_time (segment, GST_FORMAT_TIME, - segment->start); - rstop = - gst_segment_to_running_time (segment, GST_FORMAT_TIME, segment->stop); - copy.base = comp->priv->next_base_time; - GST_DEBUG_OBJECT (comp, - "Updating base time to %" GST_TIME_FORMAT ", next:%" GST_TIME_FORMAT, - GST_TIME_ARGS (comp->priv->next_base_time), - GST_TIME_ARGS (comp->priv->next_base_time + rstop - rstart)); - comp->priv->next_base_time += rstop - rstart; - - event2 = gst_event_new_segment (©); - GST_EVENT_SEQNUM (event2) = GST_EVENT_SEQNUM (event); - - GST_PAD_PROBE_INFO_DATA (info) = event2; - gst_event_unref (event); - } - break; - case GST_EVENT_EOS: - { - gint seqnum = gst_event_get_seqnum (event); - - GST_INFO_OBJECT (comp, "Got EOS, last EOS seqnum id : %i current " - "seq num is: %i", comp->priv->real_eos_seqnum, seqnum); - - if (_is_ready_to_restart_task (comp, event)) { - GST_INFO_OBJECT (comp, "We got an EOS right after seeing the right" - " segment, restarting task"); - - _restart_task (comp); - } - - if (g_atomic_int_compare_and_exchange (&comp->priv->real_eos_seqnum, - seqnum, 1)) { - - GST_INFO_OBJECT (comp, "Got EOS for real, seq ID is %i, fowarding it", - seqnum); - - return GST_PAD_PROBE_OK; - } - - if (priv->next_eos_seqnum == seqnum) - _add_update_compo_action (comp, G_CALLBACK (_update_pipeline_func), - COMP_UPDATE_STACK_ON_EOS); - else - GST_INFO_OBJECT (comp, - "Got an EOS but it seqnum %i != next eos seqnum %i", seqnum, - priv->next_eos_seqnum); - - retval = GST_PAD_PROBE_DROP; - } - break; - default: - break; - } - - return retval; -} - -static gint -priority_comp (NleObject * a, NleObject * b) -{ - if (a->priority < b->priority) - return -1; - - if (a->priority > b->priority) - return 1; - - return 0; -} - -static inline gboolean -have_to_update_pipeline (NleComposition * comp, - NleUpdateStackReason update_stack_reason) -{ - NleCompositionPrivate *priv = comp->priv; - - if (update_stack_reason == COMP_UPDATE_STACK_ON_EOS) - return TRUE; - - GST_DEBUG_OBJECT (comp, - "segment[%" GST_TIME_FORMAT "--%" GST_TIME_FORMAT "] current[%" - GST_TIME_FORMAT "--%" GST_TIME_FORMAT "]", - GST_TIME_ARGS (priv->segment->start), - GST_TIME_ARGS (priv->segment->stop), - GST_TIME_ARGS (priv->segment_start), GST_TIME_ARGS (priv->segment_stop)); - - if (priv->segment->start < priv->segment_start) - return TRUE; - - if (priv->segment->start >= priv->segment_stop) - return TRUE; - - return FALSE; -} - -static gboolean -nle_composition_commit_func (NleObject * object, gboolean recurse) -{ - _add_update_compo_action (NLE_COMPOSITION (object), - G_CALLBACK (_commit_func), COMP_UPDATE_STACK_ON_COMMIT); - - return TRUE; -} - -/* - * get_new_seek_event: - * - * Returns a seek event for the currently configured segment - * and start/stop values - * - * The GstSegment and segment_start|stop must have been configured - * before calling this function. - */ -static GstEvent * -get_new_seek_event (NleComposition * comp, gboolean initial, - gboolean updatestoponly) -{ - GstSeekFlags flags = GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH; - gint64 start, stop; - GstSeekType starttype = GST_SEEK_TYPE_SET; - NleCompositionPrivate *priv = comp->priv; - - GST_DEBUG_OBJECT (comp, "initial:%d", initial); - /* remove the seek flag */ - if (!initial) - flags |= (GstSeekFlags) priv->segment->flags; - - GST_DEBUG_OBJECT (comp, - "private->segment->start:%" GST_TIME_FORMAT " segment_start%" - GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->start), - GST_TIME_ARGS (priv->segment_start)); - - GST_DEBUG_OBJECT (comp, - "private->segment->stop:%" GST_TIME_FORMAT " segment_stop%" - GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->stop), - GST_TIME_ARGS (priv->segment_stop)); - - start = GST_CLOCK_TIME_IS_VALID (priv->segment->start) - ? MAX (priv->segment->start, priv->segment_start) - : priv->segment_start; - stop = GST_CLOCK_TIME_IS_VALID (priv->segment->stop) - ? MIN (priv->segment->stop, priv->segment_stop) - : priv->segment_stop; - - if (updatestoponly) { - starttype = GST_SEEK_TYPE_NONE; - start = GST_CLOCK_TIME_NONE; - } - - GST_DEBUG_OBJECT (comp, - "Created new seek event. Flags:%d, start:%" GST_TIME_FORMAT ", stop:%" - GST_TIME_FORMAT ", rate:%lf", flags, GST_TIME_ARGS (start), - GST_TIME_ARGS (stop), priv->segment->rate); - - return gst_event_new_seek (priv->segment->rate, - priv->segment->format, flags, starttype, start, GST_SEEK_TYPE_SET, stop); -} - -/* OBJECTS LOCK must be taken when calling this ! */ -static GstClockTime -get_current_position (NleComposition * comp) -{ - GstPad *pad; - NleObject *obj; - NleCompositionPrivate *priv = comp->priv; - gboolean res; - gint64 value = GST_CLOCK_TIME_NONE; - - GstPad *peer; - - g_signal_emit (comp, _signals[QUERY_POSITION_SIGNAL], 0, &value); - - if (value >= 0) { - GST_DEBUG_OBJECT (comp, "Got position %" GST_TIME_FORMAT, - GST_TIME_ARGS (value)); - - return value; - } - - /* Try querying position downstream */ - peer = gst_pad_get_peer (NLE_OBJECT (comp)->srcpad); - if (peer) { - res = gst_pad_query_position (peer, GST_FORMAT_TIME, &value); - gst_object_unref (peer); - - if (res) { - GST_DEBUG_OBJECT (comp, - "Successfully got downstream position %" GST_TIME_FORMAT, - GST_TIME_ARGS ((guint64) value)); - goto beach; - } - } - - GST_DEBUG_OBJECT (comp, "Downstream position query failed"); - - /* resetting format/value */ - value = GST_CLOCK_TIME_NONE; - - /* If downstream fails , try within the current stack */ - if (!priv->current) { - GST_DEBUG_OBJECT (comp, "No current stack, can't send query"); - goto beach; - } - - obj = (NleObject *) priv->current->data; - - pad = NLE_OBJECT_SRC (obj); - res = gst_pad_query_position (pad, GST_FORMAT_TIME, &value); - - if (G_UNLIKELY (res == FALSE)) { - GST_WARNING_OBJECT (comp, "query position failed"); - value = GST_CLOCK_TIME_NONE; - } else { - GST_LOG_OBJECT (comp, "Query returned %" GST_TIME_FORMAT, - GST_TIME_ARGS ((guint64) value)); - } - -beach: - - if (!GST_CLOCK_TIME_IS_VALID (value)) { - if (GST_CLOCK_TIME_IS_VALID (comp->priv->segment_start)) { - value = comp->priv->segment_start; - } else { - GST_INFO_OBJECT (comp, "Current position is unknown, " "setting it to 0"); - - value = 0; - } - } - - return (guint64) value; -} - -/* WITH OBJECTS LOCK TAKEN */ -static gboolean -update_base_time (GNode * node, GstClockTime * timestamp) -{ - if (NLE_IS_OPERATION (node->data)) - nle_operation_update_base_time (NLE_OPERATION (node->data), *timestamp); - - return FALSE; -} - -/* WITH OBJECTS LOCK TAKEN */ -static void -update_operations_base_time (NleComposition * comp, gboolean reverse) -{ - GstClockTime timestamp; - - if (reverse) - timestamp = comp->priv->segment->stop; - else - timestamp = comp->priv->segment->start; - - g_node_traverse (comp->priv->current, G_IN_ORDER, G_TRAVERSE_ALL, -1, - (GNodeTraverseFunc) update_base_time, ×tamp); -} - -/* WITH OBJECTS LOCK TAKEN */ -static gboolean -_seek_current_stack (NleComposition * comp, GstEvent * event, - gboolean flush_downstream) -{ - gboolean res; - NleCompositionPrivate *priv = comp->priv; - GstPad *peer = gst_pad_get_peer (NLE_OBJECT_SRC (comp)); - - GST_INFO_OBJECT (comp, "Seeking itself %" GST_PTR_FORMAT, event); - - if (flush_downstream) { - priv->flush_seqnum = gst_event_get_seqnum (event); - GST_INFO_OBJECT (comp, "sending flushes downstream with seqnum %d", - priv->flush_seqnum); - } - - priv->seeking_itself = TRUE; - res = gst_pad_push_event (peer, event); - priv->seeking_itself = FALSE; - gst_object_unref (peer); - - GST_DEBUG_OBJECT (comp, "Done seeking"); - - return res; -} - -/* - Figures out if pipeline needs updating. - Updates it and sends the seek event. - Sends flush events downstream if needed. - can be called by user_seek or segment_done - - update_stack_reason: The reason for which we need to handle 'seek' -*/ - -static gboolean -seek_handling (NleComposition * comp, gint32 seqnum, - NleUpdateStackReason update_stack_reason) -{ - GST_DEBUG_OBJECT (comp, "Seek hanlding update pipeline reason: %s", - UPDATE_PIPELINE_REASONS[update_stack_reason]); - - if (have_to_update_pipeline (comp, update_stack_reason)) { - if (comp->priv->segment->rate >= 0.0) - update_pipeline (comp, comp->priv->segment->start, seqnum, - update_stack_reason); - else - update_pipeline (comp, comp->priv->segment->stop, seqnum, - update_stack_reason); - } else { - GstEvent *toplevel_seek = get_new_seek_event (comp, FALSE, FALSE); - - gst_event_set_seqnum (toplevel_seek, seqnum); - _set_real_eos_seqnum_from_seek (comp, toplevel_seek); - - _remove_update_actions (comp); - update_operations_base_time (comp, !(comp->priv->segment->rate >= 0.0)); - _seek_current_stack (comp, toplevel_seek, - _have_to_flush_downstream (update_stack_reason)); - } - - return TRUE; -} - -static gboolean -nle_composition_event_handler (GstPad * ghostpad, GstObject * parent, - GstEvent * event) -{ - NleComposition *comp = (NleComposition *) parent; - NleCompositionPrivate *priv = comp->priv; - gboolean res = TRUE; - - GST_DEBUG_OBJECT (comp, "event type:%s", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - { - if (!priv->seeking_itself) { - _add_seek_action (comp, event); - event = NULL; - GST_FIXME_OBJECT (comp, "HANDLE seeking errors!"); - - return TRUE; - } - break; - } - case GST_EVENT_QOS: - { - gdouble prop; - GstQOSType qostype; - GstClockTimeDiff diff; - GstClockTime timestamp; - - gst_event_parse_qos (event, &qostype, &prop, &diff, ×tamp); - - GST_DEBUG_OBJECT (comp, - "timestamp:%" GST_TIME_FORMAT " segment.start:%" GST_TIME_FORMAT - " segment.stop:%" GST_TIME_FORMAT " segment_start%" GST_TIME_FORMAT - " segment_stop:%" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp), - GST_TIME_ARGS (priv->outside_segment->start), - GST_TIME_ARGS (priv->outside_segment->stop), - GST_TIME_ARGS (priv->segment_start), - GST_TIME_ARGS (priv->segment_stop)); - - /* The problem with QoS events is the following: - * At each new internal segment (i.e. when we re-arrange our internal - * elements) we send flushing seeks to those elements (to properly - * configure their playback range) but don't let the FLUSH events get - * downstream. - * - * The problem is that the QoS running timestamps we receive from - * downstream will not have taken into account those flush. - * - * What we need to do is to translate to our internal running timestamps - * which for each configured segment starts at 0 for those elements. - * - * The generic algorithm for the incoming running timestamp translation - * is therefore: - * (original_seek_time : original seek position received from usptream) - * (current_segment_start : Start position of the currently configured - * timeline segment) - * - * difference = original_seek_time - current_segment_start - * new_qos_position = upstream_qos_position - difference - * - * The new_qos_position is only valid when: - * * it applies to the current segment (difference > 0) - * * The QoS difference + timestamp is greater than the difference - * - */ - - if (GST_CLOCK_TIME_IS_VALID (priv->outside_segment->start)) { - GstClockTimeDiff curdiff; - - /* We'll either create a new event or discard it */ - gst_event_unref (event); - - if (priv->segment->rate < 0.0) - curdiff = priv->outside_segment->stop - priv->segment_stop; - else - curdiff = priv->segment_start - priv->outside_segment->start; - GST_DEBUG ("curdiff %" GST_TIME_FORMAT, GST_TIME_ARGS (curdiff)); - if ((curdiff != 0) && ((timestamp < curdiff) - || (curdiff > timestamp + diff))) { - GST_DEBUG_OBJECT (comp, - "QoS event outside of current segment, discarding"); - /* The QoS timestamp is before the currently set-up pipeline */ - goto beach; - } - - /* Substract the amount of running time we've already outputted - * until the currently configured pipeline from the QoS timestamp.*/ - timestamp -= curdiff; - GST_DEBUG_OBJECT (comp, - "Creating new QoS event with timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (timestamp)); - event = gst_event_new_qos (qostype, prop, diff, timestamp); - } - break; - } - default: - break; - } - - if (res) { - GST_DEBUG_OBJECT (comp, "About to call nle_event_pad_func: %p", - priv->nle_event_pad_func); - res = priv->nle_event_pad_func (NLE_OBJECT (comp)->srcpad, parent, event); - GST_DEBUG_OBJECT (comp, "Done calling nle_event_pad_func() %d", res); - } - -beach: - return res; -} - -static inline void -nle_composition_reset_target_pad (NleComposition * comp) -{ - NleCompositionPrivate *priv = comp->priv; - - GST_DEBUG_OBJECT (comp, "Removing ghostpad"); - - if (priv->ghosteventprobe) { - GstPad *target; - - target = gst_ghost_pad_get_target ((GstGhostPad *) NLE_OBJECT_SRC (comp)); - if (target) - gst_pad_remove_probe (target, priv->ghosteventprobe); - priv->ghosteventprobe = 0; - } - - nle_object_ghost_pad_set_target (NLE_OBJECT (comp), - NLE_OBJECT_SRC (comp), NULL); - priv->send_stream_start = TRUE; -} - -/* nle_composition_ghost_pad_set_target: - * target: The target #GstPad. The refcount will be decremented (given to the ghostpad). - */ -static void -nle_composition_ghost_pad_set_target (NleComposition * comp, GstPad * target) -{ - GstPad *ptarget; - NleCompositionPrivate *priv = comp->priv; - - if (target) - GST_DEBUG_OBJECT (comp, "target:%s:%s", GST_DEBUG_PAD_NAME (target)); - else - GST_DEBUG_OBJECT (comp, "Removing target"); - - - ptarget = - gst_ghost_pad_get_target (GST_GHOST_PAD (NLE_OBJECT (comp)->srcpad)); - if (ptarget) { - gst_object_unref (ptarget); - - if (ptarget == target) { - GST_DEBUG_OBJECT (comp, - "Target of srcpad is the same as existing one, not changing"); - return; - } - } - - /* Actually set the target */ - nle_object_ghost_pad_set_target ((NleObject *) comp, - NLE_OBJECT (comp)->srcpad, target); - - if (target && (priv->ghosteventprobe == 0)) { - priv->ghosteventprobe = - gst_pad_add_probe (target, - GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_EVENT_FLUSH | - GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, - (GstPadProbeCallback) ghost_event_probe_handler, comp, NULL); - GST_DEBUG_OBJECT (comp, "added event probe %lu", priv->ghosteventprobe); - } -} - -static void -refine_start_stop_in_region_above_priority (NleComposition * composition, - GstClockTime timestamp, GstClockTime start, - GstClockTime stop, - GstClockTime * rstart, GstClockTime * rstop, guint32 priority) -{ - GList *tmp; - NleObject *object; - GstClockTime nstart = start, nstop = stop; - - GST_DEBUG_OBJECT (composition, - "timestamp:%" GST_TIME_FORMAT " start: %" GST_TIME_FORMAT " stop: %" - GST_TIME_FORMAT " priority:%u", GST_TIME_ARGS (timestamp), - GST_TIME_ARGS (start), GST_TIME_ARGS (stop), priority); - - for (tmp = composition->priv->objects_start; tmp; tmp = tmp->next) { - object = (NleObject *) tmp->data; - - GST_LOG_OBJECT (object, "START %" GST_TIME_FORMAT "--%" GST_TIME_FORMAT, - GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop)); - - if ((object->priority >= priority) || (!NLE_OBJECT_ACTIVE (object))) - continue; - - if (object->start <= timestamp) - continue; - - if (object->start >= nstop) - continue; - - nstop = object->start; - - GST_DEBUG_OBJECT (composition, - "START Found %s [prio:%u] at %" GST_TIME_FORMAT, - GST_OBJECT_NAME (object), object->priority, - GST_TIME_ARGS (object->start)); - - break; - } - - for (tmp = composition->priv->objects_stop; tmp; tmp = tmp->next) { - object = (NleObject *) tmp->data; - - GST_LOG_OBJECT (object, "STOP %" GST_TIME_FORMAT "--%" GST_TIME_FORMAT, - GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop)); - - if ((object->priority >= priority) || (!NLE_OBJECT_ACTIVE (object))) - continue; - - if (object->stop >= timestamp) - continue; - - if (object->stop <= nstart) - continue; - - nstart = object->stop; - - GST_DEBUG_OBJECT (composition, - "STOP Found %s [prio:%u] at %" GST_TIME_FORMAT, - GST_OBJECT_NAME (object), object->priority, - GST_TIME_ARGS (object->start)); - - break; - } - - if (*rstart) - *rstart = nstart; - - if (*rstop) - *rstop = nstop; -} - - -/* - * Converts a sorted list to a tree - * Recursive - * - * stack will be set to the next item to use in the parent. - * If operations number of sinks is limited, it will only use that number. - */ - -static GNode * -convert_list_to_tree (GList ** stack, GstClockTime * start, - GstClockTime * stop, guint32 * highprio) -{ - GNode *ret; - guint nbsinks; - gboolean limit; - GList *tmp; - NleObject *object; - - if (!stack || !*stack) - return NULL; - - object = (NleObject *) (*stack)->data; - - GST_DEBUG ("object:%s , *start:%" GST_TIME_FORMAT ", *stop:%" - GST_TIME_FORMAT " highprio:%d", - GST_ELEMENT_NAME (object), GST_TIME_ARGS (*start), - GST_TIME_ARGS (*stop), *highprio); - - /* update earliest stop */ - if (GST_CLOCK_TIME_IS_VALID (*stop)) { - if (GST_CLOCK_TIME_IS_VALID (object->stop) && (*stop > object->stop)) - *stop = object->stop; - } else { - *stop = object->stop; - } - - if (GST_CLOCK_TIME_IS_VALID (*start)) { - if (GST_CLOCK_TIME_IS_VALID (object->start) && (*start < object->start)) - *start = object->start; - } else { - *start = object->start; - } - - if (NLE_OBJECT_IS_SOURCE (object)) { - *stack = g_list_next (*stack); - - /* update highest priority. - * We do this here, since it's only used with sources (leafs of the tree) */ - if (object->priority > *highprio) - *highprio = object->priority; - - ret = g_node_new (object); - - goto beach; - } else { - /* NleOperation */ - NleOperation *oper = (NleOperation *) object; - - GST_LOG_OBJECT (oper, "operation, num_sinks:%d", oper->num_sinks); - - ret = g_node_new (object); - limit = (oper->dynamicsinks == FALSE); - nbsinks = oper->num_sinks; - - /* FIXME : if num_sinks == -1 : request the proper number of pads */ - for (tmp = g_list_next (*stack); tmp && (!limit || nbsinks);) { - g_node_append (ret, convert_list_to_tree (&tmp, start, stop, highprio)); - if (limit) - nbsinks--; - } - - *stack = tmp; - } - -beach: - GST_DEBUG_OBJECT (object, - "*start:%" GST_TIME_FORMAT " *stop:%" GST_TIME_FORMAT - " priority:%u", GST_TIME_ARGS (*start), GST_TIME_ARGS (*stop), *highprio); - - return ret; -} - -/* - * get_stack_list: - * @comp: The #NleComposition - * @timestamp: The #GstClockTime to look at - * @priority: The priority level to start looking from - * @activeonly: Only look for active elements if TRUE - * @start: The biggest start time of the objects in the stack - * @stop: The smallest stop time of the objects in the stack - * @highprio: The highest priority in the stack - * - * Not MT-safe, you should take the objects lock before calling it. - * Returns: A tree of #GNode sorted in priority order, corresponding - * to the given search arguments. The returned value can be #NULL. - * - * WITH OBJECTS LOCK TAKEN - */ -static GNode * -get_stack_list (NleComposition * comp, GstClockTime timestamp, - guint32 priority, gboolean activeonly, GstClockTime * start, - GstClockTime * stop, guint * highprio) -{ - GList *tmp; - GList *stack = NULL; - GNode *ret = NULL; - GstClockTime nstart = GST_CLOCK_TIME_NONE; - GstClockTime nstop = GST_CLOCK_TIME_NONE; - GstClockTime first_out_of_stack = GST_CLOCK_TIME_NONE; - guint32 highest = 0; - gboolean reverse = (comp->priv->segment->rate < 0.0); - - GST_DEBUG_OBJECT (comp, - "timestamp:%" GST_TIME_FORMAT ", priority:%u, activeonly:%d", - GST_TIME_ARGS (timestamp), priority, activeonly); - - GST_LOG ("objects_start:%p objects_stop:%p", comp->priv->objects_start, - comp->priv->objects_stop); - - if (reverse) { - for (tmp = comp->priv->objects_stop; tmp; tmp = g_list_next (tmp)) { - NleObject *object = (NleObject *) tmp->data; - - GST_LOG_OBJECT (object, - "start: %" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT " , duration:%" - GST_TIME_FORMAT ", priority:%u, active:%d", - GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop), - GST_TIME_ARGS (object->duration), object->priority, object->active); - - if (object->stop >= timestamp) { - if ((object->start < timestamp) && - (object->priority >= priority) && - ((!activeonly) || (NLE_OBJECT_ACTIVE (object)))) { - GST_LOG_OBJECT (comp, "adding %s: sorted to the stack", - GST_OBJECT_NAME (object)); - stack = g_list_insert_sorted (stack, object, - (GCompareFunc) priority_comp); - if (NLE_IS_OPERATION (object)) - nle_operation_update_base_time (NLE_OPERATION (object), timestamp); - } - } else { - GST_LOG_OBJECT (comp, "too far, stopping iteration"); - first_out_of_stack = object->stop; - break; - } - } - } else { - for (tmp = comp->priv->objects_start; tmp; tmp = g_list_next (tmp)) { - NleObject *object = (NleObject *) tmp->data; - - GST_LOG_OBJECT (object, - "start: %" GST_TIME_FORMAT " , stop:%" GST_TIME_FORMAT " , duration:%" - GST_TIME_FORMAT ", priority:%u", GST_TIME_ARGS (object->start), - GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->duration), - object->priority); - - if (object->start <= timestamp) { - if ((object->stop > timestamp) && - (object->priority >= priority) && - ((!activeonly) || (NLE_OBJECT_ACTIVE (object)))) { - GST_LOG_OBJECT (comp, "adding %s: sorted to the stack", - GST_OBJECT_NAME (object)); - stack = g_list_insert_sorted (stack, object, - (GCompareFunc) priority_comp); - if (NLE_IS_OPERATION (object)) - nle_operation_update_base_time (NLE_OPERATION (object), timestamp); - } - } else { - GST_LOG_OBJECT (comp, "too far, stopping iteration"); - first_out_of_stack = object->start; - break; - } - } - } - - /* Insert the expandables */ - if (G_LIKELY (timestamp < NLE_OBJECT_STOP (comp))) - for (tmp = comp->priv->expandables; tmp; tmp = tmp->next) { - GST_DEBUG_OBJECT (comp, "Adding expandable %s sorted to the list", - GST_OBJECT_NAME (tmp->data)); - stack = g_list_insert_sorted (stack, tmp->data, - (GCompareFunc) priority_comp); - if (NLE_IS_OPERATION (tmp->data)) - nle_operation_update_base_time (NLE_OPERATION (tmp->data), timestamp); - } - - /* convert that list to a stack */ - tmp = stack; - ret = convert_list_to_tree (&tmp, &nstart, &nstop, &highest); - if (GST_CLOCK_TIME_IS_VALID (first_out_of_stack)) { - if (reverse && nstart < first_out_of_stack) - nstart = first_out_of_stack; - else if (!reverse && nstop > first_out_of_stack) - nstop = first_out_of_stack; - } - - GST_DEBUG ("nstart:%" GST_TIME_FORMAT ", nstop:%" GST_TIME_FORMAT, - GST_TIME_ARGS (nstart), GST_TIME_ARGS (nstop)); - - if (*stop) - *stop = nstop; - if (*start) - *start = nstart; - if (highprio) - *highprio = highest; - - g_list_free (stack); - - return ret; -} - -/* - * get_clean_toplevel_stack: - * @comp: The #NleComposition - * @timestamp: The #GstClockTime to look at - * @stop_time: Pointer to a #GstClockTime for min stop time of returned stack - * @start_time: Pointer to a #GstClockTime for greatest start time of returned stack - * - * Returns: The new current stack for the given #NleComposition and @timestamp. - * - * WITH OBJECTS LOCK TAKEN - */ -static GNode * -get_clean_toplevel_stack (NleComposition * comp, GstClockTime * timestamp, - GstClockTime * start_time, GstClockTime * stop_time) -{ - GNode *stack = NULL; - GstClockTime start = G_MAXUINT64; - GstClockTime stop = G_MAXUINT64; - guint highprio; - gboolean reverse = (comp->priv->segment->rate < 0.0); - - GST_DEBUG_OBJECT (comp, "timestamp:%" GST_TIME_FORMAT, - GST_TIME_ARGS (*timestamp)); - GST_DEBUG ("start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT, - GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); - - stack = get_stack_list (comp, *timestamp, 0, TRUE, &start, &stop, &highprio); - - if (!stack && - ((reverse && (*timestamp > COMP_REAL_START (comp))) || - (!reverse && (*timestamp < COMP_REAL_STOP (comp))))) { - GST_ELEMENT_ERROR (comp, STREAM, WRONG_TYPE, - ("Gaps ( at %" GST_TIME_FORMAT - ") in the stream is not supported, the application is responsible" - " for filling them", GST_TIME_ARGS (*timestamp)), - ("Gap in the composition this should never" - "append, make sure to fill them")); - - return NULL; - } - - GST_DEBUG ("start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT, - GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); - - if (stack) { - guint32 top_priority = NLE_OBJECT_PRIORITY (stack->data); - - /* Figure out if there's anything blocking us with smaller priority */ - refine_start_stop_in_region_above_priority (comp, *timestamp, start, - stop, &start, &stop, (highprio == 0) ? top_priority : highprio); - } - - if (*stop_time) { - if (stack) - *stop_time = stop; - else - *stop_time = 0; - } - - if (*start_time) { - if (stack) - *start_time = start; - else - *start_time = 0; - } - - GST_DEBUG_OBJECT (comp, - "Returning timestamp:%" GST_TIME_FORMAT " , start_time:%" - GST_TIME_FORMAT " , stop_time:%" GST_TIME_FORMAT, - GST_TIME_ARGS (*timestamp), GST_TIME_ARGS (*start_time), - GST_TIME_ARGS (*stop_time)); - - return stack; -} - -static GstPadProbeReturn -_drop_all_cb (GstPad * pad G_GNUC_UNUSED, - GstPadProbeInfo * info, NleComposition * comp) -{ - return GST_PAD_PROBE_DROP; -} - -/* Must be called with OBJECTS_LOCK taken */ -static void -_set_current_bin_to_ready (NleComposition * comp, gboolean flush_downstream) -{ - gint probe_id = -1; - GstPad *ptarget = NULL; - NleCompositionPrivate *priv = comp->priv; - GstEvent *flush_event; - - comp->priv->tearing_down_stack = TRUE; - if (flush_downstream) { - ptarget = gst_ghost_pad_get_target (GST_GHOST_PAD (NLE_OBJECT_SRC (comp))); - if (ptarget) { - - /* Make sure that between the flush_start/flush_stop - * and the time we set the current_bin to READY, no - * buffer can ever get prerolled which would lead to - * a deadlock */ - probe_id = gst_pad_add_probe (ptarget, - GST_PAD_PROBE_TYPE_DATA_BOTH | GST_PAD_PROBE_TYPE_EVENT_BOTH, - (GstPadProbeCallback) _drop_all_cb, comp, NULL); - - GST_DEBUG_OBJECT (comp, "added event probe %lu", priv->ghosteventprobe); - - flush_event = gst_event_new_flush_start (); - priv->flush_seqnum = gst_event_get_seqnum (flush_event); - GST_INFO_OBJECT (comp, "sending flushes downstream with seqnum %d", - priv->flush_seqnum); - gst_pad_push_event (ptarget, flush_event); - - } - - } - - gst_element_set_locked_state (priv->current_bin, TRUE); - gst_element_set_state (priv->current_bin, GST_STATE_READY); - - if (ptarget) { - if (flush_downstream) { - flush_event = gst_event_new_flush_stop (TRUE); - gst_event_set_seqnum (flush_event, priv->flush_seqnum); - - /* Force ad activation so that the event can actually travel. - * Not doing that would lead to the event being discarded. - */ - gst_pad_set_active (ptarget, TRUE); - gst_pad_push_event (ptarget, flush_event); - gst_pad_set_active (ptarget, FALSE); - } - - gst_pad_remove_probe (ptarget, probe_id); - gst_object_unref (ptarget); - } - - comp->priv->tearing_down_stack = FALSE; -} - -static void -_emit_commited_signal_func (NleComposition * comp, gpointer udata) -{ - GST_INFO_OBJECT (comp, "Emiting COMMITED now that the stack " "is ready"); - - g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE); -} - -static void -_restart_task (NleComposition * comp) -{ - GST_INFO_OBJECT (comp, "Restarting task! after %s DONE", - UPDATE_PIPELINE_REASONS[comp->priv->updating_reason]); - - if (comp->priv->updating_reason == COMP_UPDATE_STACK_ON_COMMIT) - _add_action (comp, G_CALLBACK (_emit_commited_signal_func), comp, - G_PRIORITY_HIGH); - - comp->priv->seqnum_to_restart_task = 0; - comp->priv->waiting_for_buffer = FALSE; - - comp->priv->updating_reason = COMP_UPDATE_STACK_NONE; - GST_OBJECT_LOCK (comp); - if (comp->task) - gst_task_start (comp->task); - GST_OBJECT_UNLOCK (comp); -} - -static gboolean -_is_ready_to_restart_task (NleComposition * comp, GstEvent * event) -{ - NleCompositionPrivate *priv = comp->priv; - gint seqnum = gst_event_get_seqnum (event); - - - if (comp->priv->seqnum_to_restart_task == seqnum) { - gchar *name = g_strdup_printf ("%s-new-stack__%" GST_TIME_FORMAT "--%" - GST_TIME_FORMAT "", GST_OBJECT_NAME (comp), - GST_TIME_ARGS (comp->priv->segment_start), - GST_TIME_ARGS (comp->priv->segment_stop)); - - GST_INFO_OBJECT (comp, "Got %s with proper seqnum" - " done with stack reconfiguration %" GST_PTR_FORMAT, - GST_EVENT_TYPE_NAME (event), event); - - GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (comp), - GST_DEBUG_GRAPH_SHOW_ALL, name); - g_free (name); - - if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { - GST_INFO_OBJECT (comp, "update_pipeline DONE"); - return TRUE; - } - - priv->waiting_for_buffer = TRUE; - return FALSE; - - } else if (comp->priv->seqnum_to_restart_task) { - GST_INFO_OBJECT (comp, "WARNING: %s seqnum %i != wanted %i", - GST_EVENT_TYPE_NAME (event), seqnum, - comp->priv->seqnum_to_restart_task); - } - - return FALSE; -} - -static void -_commit_func (NleComposition * comp, UpdateCompositionData * ucompo) -{ - GstClockTime curpos; - NleCompositionPrivate *priv = comp->priv; - - _post_start_composition_update (comp, ucompo->seqnum, ucompo->reason); - - /* Get current so that it represent the duration it was - * before commiting children */ - curpos = get_current_position (comp); - - if (!_commit_all_values (comp)) { - GST_DEBUG_OBJECT (comp, "Nothing to commit, leaving"); - - g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, FALSE); - _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason); - - return; - } - - if (priv->initialized == FALSE) { - GST_DEBUG_OBJECT (comp, "Not initialized yet, just updating values"); - - update_start_stop_duration (comp); - - g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE); - - } else { - /* And update the pipeline at current position if needed */ - - update_start_stop_duration (comp); - update_pipeline (comp, curpos, ucompo->seqnum, COMP_UPDATE_STACK_ON_COMMIT); - - if (!priv->current) { - GST_INFO_OBJECT (comp, "No new stack set, we can go and keep acting on" - " our children"); - - g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE); - } - } - - _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason); -} - -static void -_update_pipeline_func (NleComposition * comp, UpdateCompositionData * ucompo) -{ - gboolean reverse; - NleCompositionPrivate *priv = comp->priv; - - _post_start_composition_update (comp, ucompo->seqnum, ucompo->reason); - - /* Set up a non-initial seek on segment_stop */ - reverse = (priv->segment->rate < 0.0); - if (!reverse) { - GST_DEBUG_OBJECT (comp, - "Setting segment->start to segment_stop:%" GST_TIME_FORMAT, - GST_TIME_ARGS (priv->segment_stop)); - priv->segment->start = priv->segment_stop; - } else { - GST_DEBUG_OBJECT (comp, - "Setting segment->stop to segment_start:%" GST_TIME_FORMAT, - GST_TIME_ARGS (priv->segment_start)); - priv->segment->stop = priv->segment_start; - } - - seek_handling (comp, ucompo->seqnum, COMP_UPDATE_STACK_ON_EOS); - - /* Post segment done if last seek was a segment seek */ - if (!priv->current && (priv->segment->flags & GST_SEEK_FLAG_SEGMENT)) { - gint64 epos; - - if (GST_CLOCK_TIME_IS_VALID (priv->segment->stop)) - epos = (MIN (priv->segment->stop, NLE_OBJECT_STOP (comp))); - else - epos = NLE_OBJECT_STOP (comp); - - GST_LOG_OBJECT (comp, "Emitting segment done pos %" GST_TIME_FORMAT, - GST_TIME_ARGS (epos)); - gst_element_post_message (GST_ELEMENT_CAST (comp), - gst_message_new_segment_done (GST_OBJECT (comp), - priv->segment->format, epos)); - gst_pad_push_event (NLE_OBJECT (comp)->srcpad, - gst_event_new_segment_done (priv->segment->format, epos)); - } - - _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason); -} - -static void -_set_all_children_state (NleComposition * comp, GstState state) -{ - GList *tmp; - - GST_DEBUG_OBJECT (comp, "Setting all children state to %s", - gst_element_state_get_name (state)); - - comp->priv->tearing_down_stack = TRUE; - gst_element_set_state (comp->priv->current_bin, state); - for (tmp = comp->priv->objects_start; tmp; tmp = tmp->next) - gst_element_set_state (tmp->data, state); - - for (tmp = comp->priv->expandables; tmp; tmp = tmp->next) - gst_element_set_state (tmp->data, state); - comp->priv->tearing_down_stack = FALSE; -} - -static GstStateChangeReturn -nle_composition_change_state (GstElement * element, GstStateChange transition) -{ - NleComposition *comp = (NleComposition *) element; - - GST_DEBUG_OBJECT (comp, "%s => %s", - gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)), - gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition))); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - _start_task (comp); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - /* state-lock all elements */ - GST_DEBUG_OBJECT (comp, - "Setting all children to READY and locking their state"); - - _add_update_compo_action (comp, G_CALLBACK (_initialize_stack_func), - COMP_UPDATE_STACK_INITIALIZE); - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - _stop_task (comp); - - _remove_update_actions (comp); - _remove_seek_actions (comp); - _set_all_children_state (comp, GST_STATE_READY); - nle_composition_reset (comp); - - _start_task (comp); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - _stop_task (comp); - - _remove_update_actions (comp); - _remove_seek_actions (comp); - _set_all_children_state (comp, GST_STATE_NULL); - break; - default: - break; - } - - return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); -} - -static gint -objects_start_compare (NleObject * a, NleObject * b) -{ - if (a->start == b->start) { - if (a->priority < b->priority) - return -1; - if (a->priority > b->priority) - return 1; - return 0; - } - if (a->start < b->start) - return -1; - if (a->start > b->start) - return 1; - return 0; -} - -static gint -objects_stop_compare (NleObject * a, NleObject * b) -{ - if (a->stop == b->stop) { - if (a->priority < b->priority) - return -1; - if (a->priority > b->priority) - return 1; - return 0; - } - if (b->stop < a->stop) - return -1; - if (b->stop > a->stop) - return 1; - return 0; -} - -/* WITH OBJECTS LOCK TAKEN */ -static void -update_start_stop_duration (NleComposition * comp) -{ - NleObject *obj; - NleObject *cobj = (NleObject *) comp; - NleCompositionPrivate *priv = comp->priv; - - _assert_proper_thread (comp); - - if (!priv->objects_start) { - GST_INFO_OBJECT (comp, "no objects, resetting everything to 0"); - - if (cobj->start) { - cobj->start = cobj->pending_start = 0; - g_object_notify_by_pspec (G_OBJECT (cobj), - nleobject_properties[NLEOBJECT_PROP_START]); - } - - if (cobj->duration) { - cobj->pending_duration = cobj->duration = 0; - g_object_notify_by_pspec (G_OBJECT (cobj), - nleobject_properties[NLEOBJECT_PROP_DURATION]); - signal_duration_change (comp); - } - - if (cobj->stop) { - cobj->stop = 0; - g_object_notify_by_pspec (G_OBJECT (cobj), - nleobject_properties[NLEOBJECT_PROP_STOP]); - } - - return; - } - - /* If we have a default object, the start position is 0 */ - if (priv->expandables) { - GST_INFO_OBJECT (cobj, - "Setting start to 0 because we have a default object"); - - if (cobj->start != 0) { - cobj->pending_start = cobj->start = 0; - g_object_notify_by_pspec (G_OBJECT (cobj), - nleobject_properties[NLEOBJECT_PROP_START]); - } - - } else { - - /* Else it's the first object's start value */ - obj = (NleObject *) priv->objects_start->data; - - if (obj->start != cobj->start) { - GST_INFO_OBJECT (obj, "setting start from %s to %" GST_TIME_FORMAT, - GST_OBJECT_NAME (obj), GST_TIME_ARGS (obj->start)); - cobj->pending_start = cobj->start = obj->start; - g_object_notify_by_pspec (G_OBJECT (cobj), - nleobject_properties[NLEOBJECT_PROP_START]); - } - - } - - obj = (NleObject *) priv->objects_stop->data; - - if (obj->stop != cobj->stop) { - GST_INFO_OBJECT (obj, "setting stop from %s to %" GST_TIME_FORMAT, - GST_OBJECT_NAME (obj), GST_TIME_ARGS (obj->stop)); - - if (priv->expandables) { - GList *tmp; - - GST_INFO_OBJECT (comp, "RE-setting all expandables duration and commit"); - for (tmp = priv->expandables; tmp; tmp = tmp->next) { - g_object_set (tmp->data, "duration", obj->stop, NULL); - nle_object_commit (NLE_OBJECT (tmp->data), FALSE); - } - } - - priv->segment->stop = obj->stop; - cobj->stop = obj->stop; - g_object_notify_by_pspec (G_OBJECT (cobj), - nleobject_properties[NLEOBJECT_PROP_STOP]); - } - - if ((cobj->stop - cobj->start) != cobj->duration) { - cobj->pending_duration = cobj->duration = cobj->stop - cobj->start; - g_object_notify_by_pspec (G_OBJECT (cobj), - nleobject_properties[NLEOBJECT_PROP_DURATION]); - signal_duration_change (comp); - } - - GST_INFO_OBJECT (comp, - "start:%" GST_TIME_FORMAT - " stop:%" GST_TIME_FORMAT - " duration:%" GST_TIME_FORMAT, - GST_TIME_ARGS (cobj->start), - GST_TIME_ARGS (cobj->stop), GST_TIME_ARGS (cobj->duration)); -} - -static void -_link_to_parent (NleComposition * comp, NleObject * newobj, - NleObject * newparent) -{ - GstPad *sinkpad; - - /* relink to new parent in required order */ - GST_LOG_OBJECT (comp, "Linking %s and %s", - GST_ELEMENT_NAME (GST_ELEMENT (newobj)), - GST_ELEMENT_NAME (GST_ELEMENT (newparent))); - - sinkpad = get_unlinked_sink_ghost_pad ((NleOperation *) newparent); - - if (G_UNLIKELY (sinkpad == NULL)) { - GST_WARNING_OBJECT (comp, - "Couldn't find an unlinked sinkpad from %s", - GST_ELEMENT_NAME (newparent)); - } else { - if (G_UNLIKELY (gst_pad_link_full (NLE_OBJECT_SRC (newobj), sinkpad, - GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK)) { - GST_WARNING_OBJECT (comp, "Failed to link pads %s:%s - %s:%s", - GST_DEBUG_PAD_NAME (NLE_OBJECT_SRC (newobj)), - GST_DEBUG_PAD_NAME (sinkpad)); - } - gst_object_unref (sinkpad); - } -} - -static void -_relink_children_recursively (NleComposition * comp, - NleObject * newobj, GNode * node, GstEvent * toplevel_seek) -{ - GNode *child; - guint nbchildren = g_node_n_children (node); - NleOperation *oper = (NleOperation *) newobj; - - GST_INFO_OBJECT (newobj, "is a %s operation, analyzing the %d children", - oper->dynamicsinks ? "dynamic" : "regular", nbchildren); - /* Update the operation's number of sinks, that will make it have the proper - * number of sink pads to connect the children to. */ - if (oper->dynamicsinks) - g_object_set (G_OBJECT (newobj), "sinks", nbchildren, NULL); - - for (child = node->children; child; child = child->next) - _relink_single_node (comp, child, toplevel_seek); - - if (G_UNLIKELY (nbchildren < oper->num_sinks)) - GST_ERROR_OBJECT (comp, - "%" GST_PTR_FORMAT - " Not enough sinkpads to link all objects to the operation ! " - "%d / %d", oper, oper->num_sinks, nbchildren); - - if (G_UNLIKELY (nbchildren == 0)) { - GST_ERROR_OBJECT (comp, - "Operation %" GST_PTR_FORMAT - " has no child objects to be connected to !!!", oper); - } - /* Make sure we have enough sinkpads */ -} - -/* - * recursive depth-first relink stack function on new stack - * - * _ relink nodes with changed parent/order - * _ links new nodes with parents - * _ unblocks available source pads (except for toplevel) - * - * WITH OBJECTS LOCK TAKEN - */ -static void -_relink_single_node (NleComposition * comp, GNode * node, - GstEvent * toplevel_seek) -{ - NleObject *newobj; - NleObject *newparent; - GstPad *srcpad = NULL, *sinkpad = NULL; - GstEvent *translated_seek; - - if (G_UNLIKELY (!node)) - return; - - newparent = G_NODE_IS_ROOT (node) ? NULL : (NleObject *) node->parent->data; - newobj = (NleObject *) node->data; - - GST_DEBUG_OBJECT (comp, "newobj:%s", - GST_ELEMENT_NAME ((GstElement *) newobj)); - - srcpad = NLE_OBJECT_SRC (newobj); - - gst_bin_add (GST_BIN (comp->priv->current_bin), GST_ELEMENT (newobj)); - gst_element_sync_state_with_parent (GST_ELEMENT_CAST (newobj)); - - translated_seek = nle_object_translate_incoming_seek (newobj, toplevel_seek); - - gst_element_send_event (GST_ELEMENT (newobj), translated_seek); - - /* link to parent if needed. */ - if (newparent) { - _link_to_parent (comp, newobj, newparent); - - /* If there's an operation, inform it about priority changes */ - sinkpad = gst_pad_get_peer (srcpad); - nle_operation_signal_input_priority_changed ((NleOperation *) - newparent, sinkpad, newobj->priority); - gst_object_unref (sinkpad); - } - - /* Handle children */ - if (NLE_IS_OPERATION (newobj)) - _relink_children_recursively (comp, newobj, node, toplevel_seek); - - GST_LOG_OBJECT (comp, "done with object %s", - GST_ELEMENT_NAME (GST_ELEMENT (newobj))); -} - - - -/* - * compare_relink_stack: - * @comp: The #NleComposition - * @stack: The new stack - * @modify: TRUE if the timeline has changed and needs downstream flushes. - * - * Compares the given stack to the current one and relinks it if needed. - * - * WITH OBJECTS LOCK TAKEN - * - * Returns: The #GList of #NleObject no longer used - */ - -static void -_deactivate_stack (NleComposition * comp, gboolean flush_downstream) -{ - GstPad *ptarget; - - GST_INFO_OBJECT (comp, "Deactivating current stack (flushing downstream: %d", - flush_downstream); - _set_current_bin_to_ready (comp, flush_downstream); - - ptarget = gst_ghost_pad_get_target (GST_GHOST_PAD (NLE_OBJECT_SRC (comp))); - _empty_bin (GST_BIN_CAST (comp->priv->current_bin)); - - if (comp->priv->ghosteventprobe) { - GST_INFO_OBJECT (comp, "Removing old ghost pad probe"); - - gst_pad_remove_probe (ptarget, comp->priv->ghosteventprobe); - comp->priv->ghosteventprobe = 0; - } - - if (ptarget) - gst_object_unref (ptarget); - - GST_INFO_OBJECT (comp, "Stack desctivated"); - -/* priv->current = NULL; - */ -} - -static void -_relink_new_stack (NleComposition * comp, GNode * stack, - GstEvent * toplevel_seek) -{ - _relink_single_node (comp, stack, toplevel_seek); - - gst_event_unref (toplevel_seek); -} - -/* static void - * unlock_activate_stack (NleComposition * comp, GNode * node, GstState state) - * { - * GNode *child; - * - * GST_LOG_OBJECT (comp, "object:%s", - * GST_ELEMENT_NAME ((GstElement *) (node->data))); - * - * gst_element_set_locked_state ((GstElement *) (node->data), FALSE); - * gst_element_set_state (GST_ELEMENT (node->data), state); - * - * for (child = node->children; child; child = child->next) - * unlock_activate_stack (comp, child, state); - * } - */ - -static gboolean -are_same_stacks (GNode * stack1, GNode * stack2) -{ - gboolean res = FALSE; - - /* TODO : FIXME : we should also compare start/inpoint */ - /* stacks are not equal if one of them is NULL but not the other */ - if ((!stack1 && stack2) || (stack1 && !stack2)) - goto beach; - - if (stack1 && stack2) { - GNode *child1, *child2; - - /* if they don't contain the same source, not equal */ - if (!(stack1->data == stack2->data)) - goto beach; - - /* if they don't have the same number of children, not equal */ - if (!(g_node_n_children (stack1) == g_node_n_children (stack2))) - goto beach; - - child1 = stack1->children; - child2 = stack2->children; - while (child1 && child2) { - if (!(are_same_stacks (child1, child2))) - goto beach; - child1 = g_node_next_sibling (child1); - child2 = g_node_next_sibling (child2); - } - - /* if there's a difference in child number, stacks are not equal */ - if (child1 || child2) - goto beach; - } - - /* if stack1 AND stack2 are NULL, then they're equal (both empty) */ - res = TRUE; - -beach: - GST_LOG ("Stacks are equal : %d", res); - - return res; -} - -static inline gboolean -_activate_new_stack (NleComposition * comp) -{ - GstPad *pad; - GstElement *topelement; - - NleCompositionPrivate *priv = comp->priv; - - if (!priv->current) { - if ((!priv->objects_start)) { - nle_composition_reset_target_pad (comp); - priv->segment_start = 0; - priv->segment_stop = GST_CLOCK_TIME_NONE; - } - - GST_DEBUG_OBJECT (comp, "Nothing else in the composition" - ", update 'worked'"); - goto resync_state; - } - - /* The stack is entirely ready, send seek out synchronously */ - topelement = GST_ELEMENT (priv->current->data); - /* Get toplevel object source pad */ - pad = NLE_OBJECT_SRC (topelement); - - GST_INFO_OBJECT (comp, - "We have a valid toplevel element pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - nle_composition_ghost_pad_set_target (comp, pad); - - GST_DEBUG_OBJECT (comp, "New stack activated!"); - -resync_state: - gst_element_set_locked_state (priv->current_bin, FALSE); - GST_DEBUG ("going back to parent state"); - gst_element_sync_state_with_parent (priv->current_bin); - GST_DEBUG ("gone back to parent state"); - - return TRUE; -} - -/* WITH OBJECTS LOCK TAKEN */ -static gboolean -_set_real_eos_seqnum_from_seek (NleComposition * comp, GstEvent * event) -{ - GList *tmp; - - gboolean should_check_objects = FALSE; - NleCompositionPrivate *priv = comp->priv; - gboolean reverse = (priv->segment->rate < 0); - gint stack_seqnum = gst_event_get_seqnum (event); - - if (reverse && GST_CLOCK_TIME_IS_VALID (priv->segment_start)) - should_check_objects = TRUE; - else if (!reverse && GST_CLOCK_TIME_IS_VALID (priv->segment_stop)) - should_check_objects = TRUE; - - if (should_check_objects) { - for (tmp = priv->objects_stop; tmp; tmp = g_list_next (tmp)) { - NleObject *object = (NleObject *) tmp->data; - - if (!NLE_IS_SOURCE (object)) - continue; - - if ((!reverse && priv->segment_stop < object->stop) || - (reverse && priv->segment_start > object->start)) { - priv->next_eos_seqnum = stack_seqnum; - g_atomic_int_set (&priv->real_eos_seqnum, 0); - return FALSE; - } - } - } - - priv->next_eos_seqnum = stack_seqnum; - g_atomic_int_set (&priv->real_eos_seqnum, stack_seqnum); - - return TRUE; -} - -/* - * update_pipeline: - * @comp: The #NleComposition - * @currenttime: The #GstClockTime to update at, can be GST_CLOCK_TIME_NONE. - * @update_reason: Reason why we are updating the pipeline - * - * Updates the internal pipeline and properties. If @currenttime is - * GST_CLOCK_TIME_NONE, it will not modify the current pipeline - * - * Returns: FALSE if there was an error updating the pipeline. - * - * WITH OBJECTS LOCK TAKEN - */ -static gboolean -update_pipeline (NleComposition * comp, GstClockTime currenttime, gint32 seqnum, - NleUpdateStackReason update_reason) -{ - - GstEvent *toplevel_seek; - - GNode *stack = NULL; - gboolean samestack = FALSE; - gboolean updatestoponly = FALSE; - GstState state = GST_STATE (comp); - NleCompositionPrivate *priv = comp->priv; - GstClockTime new_stop = GST_CLOCK_TIME_NONE; - GstClockTime new_start = GST_CLOCK_TIME_NONE; - GstClockTime duration = NLE_OBJECT (comp)->duration - 1; - - GstState nextstate = (GST_STATE_NEXT (comp) == GST_STATE_VOID_PENDING) ? - GST_STATE (comp) : GST_STATE_NEXT (comp); - - _assert_proper_thread (comp); - - if (currenttime >= duration) { - currenttime = duration; - priv->segment->start = GST_CLOCK_TIME_NONE; - priv->segment->stop = GST_CLOCK_TIME_NONE; - } - - GST_INFO_OBJECT (comp, - "currenttime:%" GST_TIME_FORMAT - " Reason: %s, Seqnum: %i", GST_TIME_ARGS (currenttime), - UPDATE_PIPELINE_REASONS[update_reason], seqnum); - - if (!GST_CLOCK_TIME_IS_VALID (currenttime)) - return FALSE; - - if (state == GST_STATE_NULL && nextstate == GST_STATE_NULL) { - GST_DEBUG_OBJECT (comp, "STATE_NULL: not updating pipeline"); - return FALSE; - } - - GST_DEBUG_OBJECT (comp, - "now really updating the pipeline, current-state:%s", - gst_element_state_get_name (state)); - - /* Get new stack and compare it to current one */ - stack = get_clean_toplevel_stack (comp, ¤ttime, &new_start, &new_stop); - samestack = are_same_stacks (priv->current, stack); - - /* set new segment_start/stop (the current zone over which the new stack - * is valid) */ - if (priv->segment->rate >= 0.0) { - priv->segment_start = currenttime; - priv->segment_stop = new_stop; - } else { - priv->segment_start = new_start; - priv->segment_stop = currenttime; - } - -# if 0 - /* FIXME -- We should be ablt to use updatestoponly in that case, - * but it simply does not work! Not using it leads to same - * behaviour, but less optimized */ - - gboolean startchanged, stopchanged; - - if (priv->segment->rate >= 0.0) { - startchanged = priv->segment_start != currenttime; - stopchanged = priv->segment_stop != new_stop; - } else { - startchanged = priv->segment_start != new_start; - stopchanged = priv->segment_stop != currenttime; - } - - if (samestack) { - if (startchanged || stopchanged) { - /* Update seek events need to be flushing if not in PLAYING, - * else we will encounter deadlocks. */ - updatestoponly = (state == GST_STATE_PLAYING) ? FALSE : TRUE; - } - } -#endif - - toplevel_seek = get_new_seek_event (comp, TRUE, updatestoponly); - gst_event_set_seqnum (toplevel_seek, seqnum); - _set_real_eos_seqnum_from_seek (comp, toplevel_seek); - - _remove_update_actions (comp); - - /* If stacks are different, unlink/relink objects */ - if (!samestack) { - _deactivate_stack (comp, _have_to_flush_downstream (update_reason)); - _relink_new_stack (comp, stack, toplevel_seek); - } - - /* Unlock all elements in new stack */ - GST_INFO_OBJECT (comp, "Setting current stack [%" GST_TIME_FORMAT " - %" - GST_TIME_FORMAT "]", GST_TIME_ARGS (priv->segment_start), - GST_TIME_ARGS (priv->segment_stop)); - priv->current = stack; - - if (priv->current) { - UpdateCompositionData *ucompo = g_slice_new0 (UpdateCompositionData); - - GST_INFO_OBJECT (comp, "New stack set and ready to run, probing src pad" - " and stopping children thread until we are actually ready with" - " that new stack"); - - ucompo->comp = comp; - ucompo->reason = update_reason; - ucompo->seqnum = seqnum; - - comp->priv->updating_reason = update_reason; - comp->priv->seqnum_to_restart_task = seqnum; - - GST_OBJECT_LOCK (comp); - if (comp->task == NULL) { - GST_INFO_OBJECT (comp, - "No task set, it must have been stopped, returning"); - GST_OBJECT_UNLOCK (comp); - return FALSE; - } - - gst_task_pause (comp->task); - GST_OBJECT_UNLOCK (comp); - } - - /* Activate stack */ - if (!samestack) - return _activate_new_stack (comp); - else - return _seek_current_stack (comp, toplevel_seek, - _have_to_flush_downstream (update_reason)); -} - -static gboolean -nle_composition_add_object (GstBin * bin, GstElement * element) -{ - NleObject *object; - NleComposition *comp = (NleComposition *) bin; - - if (element == comp->priv->current_bin) { - GST_INFO_OBJECT (comp, "Adding internal bin"); - return GST_BIN_CLASS (parent_class)->add_element (bin, element); - } - - g_return_val_if_fail (NLE_IS_OBJECT (element), FALSE); - - object = NLE_OBJECT (element); - gst_object_ref_sink (object); - - object->in_composition = TRUE; - _add_add_object_action (comp, object); - - return TRUE; -} - -static gboolean -_nle_composition_add_object (NleComposition * comp, NleObject * object) -{ - gboolean ret = TRUE; - NleCompositionPrivate *priv = comp->priv; - - GST_DEBUG_OBJECT (comp, "element %s", GST_OBJECT_NAME (object)); - GST_DEBUG_OBJECT (object, "%" GST_TIME_FORMAT "--%" GST_TIME_FORMAT, - GST_TIME_ARGS (NLE_OBJECT_START (object)), - GST_TIME_ARGS (NLE_OBJECT_STOP (object))); - - if ((NLE_OBJECT_IS_EXPANDABLE (object)) && - g_list_find (priv->expandables, object)) { - GST_WARNING_OBJECT (comp, - "We already have an expandable, remove it before adding new one"); - ret = FALSE; - - goto chiringuito; - } - - nle_object_set_caps (object, NLE_OBJECT (comp)->caps); - nle_object_set_commit_needed (NLE_OBJECT (comp)); - - if (!ret) { - GST_WARNING_OBJECT (comp, "couldn't add object"); - goto chiringuito; - } - - /* lock state of child ! */ - GST_LOG_OBJECT (comp, "Locking state of %s", GST_ELEMENT_NAME (object)); - - if (NLE_OBJECT_IS_EXPANDABLE (object)) { - /* Only react on non-default objects properties */ - g_object_set (object, - "start", (GstClockTime) 0, - "inpoint", (GstClockTime) 0, - "duration", (GstClockTimeDiff) NLE_OBJECT_STOP (comp), NULL); - - GST_INFO_OBJECT (object, "Used as expandable, commiting now"); - nle_object_commit (NLE_OBJECT (object), FALSE); - } - - /* ...and add it to the hash table */ - g_hash_table_add (priv->objects_hash, object); - - /* Set the caps of the composition on the NleObject it handles */ - if (G_UNLIKELY (!gst_caps_is_any (((NleObject *) comp)->caps))) - nle_object_set_caps ((NleObject *) object, ((NleObject *) comp)->caps); - - /* Special case for default source. */ - if (NLE_OBJECT_IS_EXPANDABLE (object)) { - /* It doesn't get added to objects_start and objects_stop. */ - priv->expandables = g_list_prepend (priv->expandables, object); - goto beach; - } - - /* add it sorted to the objects list */ - priv->objects_start = g_list_insert_sorted - (priv->objects_start, object, (GCompareFunc) objects_start_compare); - - if (priv->objects_start) - GST_LOG_OBJECT (comp, - "Head of objects_start is now %s [%" GST_TIME_FORMAT "--%" - GST_TIME_FORMAT "]", - GST_OBJECT_NAME (priv->objects_start->data), - GST_TIME_ARGS (NLE_OBJECT_START (priv->objects_start->data)), - GST_TIME_ARGS (NLE_OBJECT_STOP (priv->objects_start->data))); - - priv->objects_stop = g_list_insert_sorted - (priv->objects_stop, object, (GCompareFunc) objects_stop_compare); - - /* Now the object is ready to be commited and then used */ - -beach: - return ret; - -chiringuito: - { - update_start_stop_duration (comp); - goto beach; - } -} - -static gboolean -nle_composition_remove_object (GstBin * bin, GstElement * element) -{ - NleObject *object; - NleComposition *comp = (NleComposition *) bin; - - if (element == comp->priv->current_bin) { - GST_INFO_OBJECT (comp, "Adding internal bin"); - return GST_BIN_CLASS (parent_class)->remove_element (bin, element); - } - - g_return_val_if_fail (NLE_IS_OBJECT (element), FALSE); - - object = NLE_OBJECT (element); - - object->in_composition = FALSE; - _add_remove_object_action (comp, object); - - return TRUE; -} - -static gboolean -_nle_composition_remove_object (NleComposition * comp, NleObject * object) -{ - NleCompositionPrivate *priv = comp->priv; - - GST_DEBUG_OBJECT (comp, "removing object %s", GST_OBJECT_NAME (object)); - - if (!g_hash_table_contains (priv->objects_hash, object)) { - GST_INFO_OBJECT (comp, "object was not in composition"); - return FALSE; - } - - gst_element_set_locked_state (GST_ELEMENT (object), FALSE); - gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL); - - /* handle default source */ - if (NLE_OBJECT_IS_EXPANDABLE (object)) { - /* Find it in the list */ - priv->expandables = g_list_remove (priv->expandables, object); - } else { - /* remove it from the objects list and resort the lists */ - priv->objects_start = g_list_remove (priv->objects_start, object); - priv->objects_stop = g_list_remove (priv->objects_stop, object); - GST_LOG_OBJECT (object, "Removed from the objects start/stop list"); - } - - if (priv->current && NLE_OBJECT (priv->current->data) == NLE_OBJECT (object)) - nle_composition_reset_target_pad (comp); - - g_hash_table_remove (priv->objects_hash, object); - - GST_LOG_OBJECT (object, "Done removing from the composition, now updating"); - - /* Make it possible to reuse the same object later */ - nle_object_reset (NLE_OBJECT (object)); - gst_object_unref (object); - - return TRUE; -} diff --git a/ges/nle/nlecomposition.h b/ges/nle/nlecomposition.h deleted file mode 100644 index 9f5da3d8..00000000 --- a/ges/nle/nlecomposition.h +++ /dev/null @@ -1,66 +0,0 @@ -/* GStreamer - * Copyright (C) 2001 Wim Taymans - * 2004-2008 Edward Hervey - * - * nlecomposition.h: Header for base NleComposition - * - * 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. - */ - - -#ifndef __NLE_COMPOSITION_H__ -#define __NLE_COMPOSITION_H__ - -#include -#include "nleobject.h" - -G_BEGIN_DECLS -#define NLE_TYPE_COMPOSITION \ - (nle_composition_get_type()) -#define NLE_COMPOSITION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_COMPOSITION,NleComposition)) -#define NLE_COMPOSITION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_COMPOSITION,NleCompositionClass)) -#define NLE_COMPOSITION_GET_CLASS(obj) \ - (NLE_COMPOSITION_CLASS (G_OBJECT_GET_CLASS (obj))) -#define NLE_IS_COMPOSITION(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_COMPOSITION)) -#define NLE_IS_COMPOSITION_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_COMPOSITION)) - -typedef struct _NleCompositionPrivate NleCompositionPrivate; - -struct _NleComposition -{ - NleObject parent; - - GstTask * task; - GRecMutex task_rec_lock; - - /*< private >*/ - NleCompositionPrivate * priv; - -}; - -struct _NleCompositionClass -{ - NleObjectClass parent_class; -}; - -GType nle_composition_get_type (void) G_GNUC_INTERNAL; - -G_END_DECLS -#endif /* __NLE_COMPOSITION_H__ */ diff --git a/ges/nle/nleghostpad.c b/ges/nle/nleghostpad.c deleted file mode 100644 index c98364ae..00000000 --- a/ges/nle/nleghostpad.c +++ /dev/null @@ -1,803 +0,0 @@ -/* Gnonlin - * Copyright (C) <2009> Edward Hervey - * - * 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" - -GST_DEBUG_CATEGORY_STATIC (nleghostpad); -#define GST_CAT_DEFAULT nleghostpad - -typedef struct _NlePadPrivate NlePadPrivate; - -struct _NlePadPrivate -{ - NleObject *object; - NlePadPrivate *ghostpriv; - GstPadDirection dir; - GstPadEventFunction eventfunc; - GstPadQueryFunction queryfunc; - - GstEvent *pending_seek; -}; - -GstEvent * -nle_object_translate_incoming_seek (NleObject * object, GstEvent * event) -{ - GstEvent *event2; - GstFormat format; - gdouble rate; - GstSeekFlags flags; - GstSeekType curtype, stoptype; - GstSeekType ncurtype; - gint64 cur; - guint64 ncur; - gint64 stop; - guint64 nstop; - guint32 seqnum = GST_EVENT_SEQNUM (event); - - gst_event_parse_seek (event, &rate, &format, &flags, - &curtype, &cur, &stoptype, &stop); - - GST_DEBUG_OBJECT (object, - "GOT SEEK rate:%f, format:%d, flags:%d, curtype:%d, stoptype:%d, %" - GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, format, flags, curtype, - stoptype, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop)); - - if (G_UNLIKELY (format != GST_FORMAT_TIME)) - goto invalid_format; - - /* convert cur */ - ncurtype = GST_SEEK_TYPE_SET; - if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET) - && (nle_object_to_media_time (object, cur, &ncur)))) { - /* cur is TYPE_SET and value is valid */ - if (ncur > G_MAXINT64) - GST_WARNING_OBJECT (object, "return value too big..."); - GST_LOG_OBJECT (object, "Setting cur to %" GST_TIME_FORMAT, - GST_TIME_ARGS (ncur)); - } else if ((curtype != GST_SEEK_TYPE_NONE)) { - GST_DEBUG_OBJECT (object, "Limiting seek start to inpoint"); - ncur = object->inpoint; - } else { - GST_DEBUG_OBJECT (object, "leaving GST_SEEK_TYPE_NONE"); - ncur = cur; - ncurtype = GST_SEEK_TYPE_NONE; - } - - /* convert stop, we also need to limit it to object->stop */ - if (G_LIKELY ((stoptype == GST_SEEK_TYPE_SET) - && (nle_object_to_media_time (object, stop, &nstop)))) { - if (nstop > G_MAXINT64) - GST_WARNING_OBJECT (object, "return value too big..."); - GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT, - GST_TIME_ARGS (nstop)); - } else { - GST_DEBUG_OBJECT (object, "Limiting end of seek to media_stop"); - nle_object_to_media_time (object, object->stop, &nstop); - if (nstop > G_MAXINT64) - GST_WARNING_OBJECT (object, "return value too big..."); - GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT, - GST_TIME_ARGS (nstop)); - } - - - /* add accurate seekflags */ - if (G_UNLIKELY (!(flags & GST_SEEK_FLAG_ACCURATE))) { - GST_DEBUG_OBJECT (object, "Adding GST_SEEK_FLAG_ACCURATE"); - flags |= GST_SEEK_FLAG_ACCURATE; - } else { - GST_DEBUG_OBJECT (object, - "event already has GST_SEEK_FLAG_ACCURATE : %d", flags); - } - - - - GST_DEBUG_OBJECT (object, - "SENDING SEEK rate:%f, format:TIME, flags:%d, curtype:%d, stoptype:SET, %" - GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, flags, ncurtype, - GST_TIME_ARGS (ncur), GST_TIME_ARGS (nstop)); - - event2 = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, - ncurtype, (gint64) ncur, GST_SEEK_TYPE_SET, (gint64) nstop); - GST_EVENT_SEQNUM (event2) = seqnum; - - return event2; - - /* ERRORS */ -invalid_format: - { - GST_WARNING ("GNonLin time shifting only works with GST_FORMAT_TIME"); - return event; - } -} - -static GstEvent * -translate_outgoing_seek (NleObject * object, GstEvent * event) -{ - GstEvent *event2; - GstFormat format; - gdouble rate; - GstSeekFlags flags; - GstSeekType curtype, stoptype; - GstSeekType ncurtype; - gint64 cur; - guint64 ncur; - gint64 stop; - guint64 nstop; - guint32 seqnum = GST_EVENT_SEQNUM (event); - - gst_event_parse_seek (event, &rate, &format, &flags, - &curtype, &cur, &stoptype, &stop); - - GST_DEBUG_OBJECT (object, - "GOT SEEK rate:%f, format:%d, flags:%d, curtype:%d, stoptype:%d, %" - GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, format, flags, curtype, - stoptype, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop)); - - if (G_UNLIKELY (format != GST_FORMAT_TIME)) - goto invalid_format; - - /* convert cur */ - ncurtype = GST_SEEK_TYPE_SET; - if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET) - && (nle_media_to_object_time (object, cur, &ncur)))) { - /* cur is TYPE_SET and value is valid */ - if (ncur > G_MAXINT64) - GST_WARNING_OBJECT (object, "return value too big..."); - GST_LOG_OBJECT (object, "Setting cur to %" GST_TIME_FORMAT, - GST_TIME_ARGS (ncur)); - } else if ((curtype != GST_SEEK_TYPE_NONE)) { - GST_DEBUG_OBJECT (object, "Limiting seek start to start"); - ncur = object->start; - } else { - GST_DEBUG_OBJECT (object, "leaving GST_SEEK_TYPE_NONE"); - ncur = cur; - ncurtype = GST_SEEK_TYPE_NONE; - } - - /* convert stop, we also need to limit it to object->stop */ - if (G_LIKELY ((stoptype == GST_SEEK_TYPE_SET) - && (nle_media_to_object_time (object, stop, &nstop)))) { - if (nstop > G_MAXINT64) - GST_WARNING_OBJECT (object, "return value too big..."); - GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT, - GST_TIME_ARGS (nstop)); - } else { - GST_DEBUG_OBJECT (object, "Limiting end of seek to stop"); - nstop = object->stop; - if (nstop > G_MAXINT64) - GST_WARNING_OBJECT (object, "return value too big..."); - GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT, - GST_TIME_ARGS (nstop)); - } - - GST_DEBUG_OBJECT (object, - "SENDING SEEK rate:%f, format:TIME, flags:%d, curtype:%d, stoptype:SET, %" - GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, flags, ncurtype, - GST_TIME_ARGS (ncur), GST_TIME_ARGS (nstop)); - - event2 = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, - ncurtype, (gint64) ncur, GST_SEEK_TYPE_SET, (gint64) nstop); - GST_EVENT_SEQNUM (event2) = seqnum; - - gst_event_unref (event); - - return event2; - - /* ERRORS */ -invalid_format: - { - GST_WARNING ("GNonLin time shifting only works with GST_FORMAT_TIME"); - return event; - } -} - -static GstEvent * -translate_outgoing_segment (NleObject * object, GstEvent * event) -{ - const GstSegment *orig; - GstSegment segment; - GstEvent *event2; - guint32 seqnum = GST_EVENT_SEQNUM (event); - - /* only modify the streamtime */ - gst_event_parse_segment (event, &orig); - - GST_DEBUG_OBJECT (object, - "Got SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %" - GST_TIME_FORMAT, GST_TIME_ARGS (orig->start), GST_TIME_ARGS (orig->stop), - GST_TIME_ARGS (orig->time)); - - if (G_UNLIKELY (orig->format != GST_FORMAT_TIME)) { - GST_WARNING_OBJECT (object, - "Can't translate segments with format != GST_FORMAT_TIME"); - return event; - } - - gst_segment_copy_into (orig, &segment); - - nle_media_to_object_time (object, orig->time, &segment.time); - - if (G_UNLIKELY (segment.time > G_MAXINT64)) - GST_WARNING_OBJECT (object, "Return value too big..."); - - GST_DEBUG_OBJECT (object, - "Sending SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %" - GST_TIME_FORMAT, GST_TIME_ARGS (segment.start), - GST_TIME_ARGS (segment.stop), GST_TIME_ARGS (segment.time)); - - event2 = gst_event_new_segment (&segment); - GST_EVENT_SEQNUM (event2) = seqnum; - gst_event_unref (event); - - return event2; -} - -static GstEvent * -translate_incoming_segment (NleObject * object, GstEvent * event) -{ - GstEvent *event2; - const GstSegment *orig; - GstSegment segment; - guint32 seqnum = GST_EVENT_SEQNUM (event); - - /* only modify the streamtime */ - gst_event_parse_segment (event, &orig); - - GST_DEBUG_OBJECT (object, - "Got SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %" - GST_TIME_FORMAT, GST_TIME_ARGS (orig->start), GST_TIME_ARGS (orig->stop), - GST_TIME_ARGS (orig->time)); - - if (G_UNLIKELY (orig->format != GST_FORMAT_TIME)) { - GST_WARNING_OBJECT (object, - "Can't translate segments with format != GST_FORMAT_TIME"); - return event; - } - - gst_segment_copy_into (orig, &segment); - - if (!nle_object_to_media_time (object, orig->time, &segment.time)) { - GST_DEBUG ("Can't convert media_time, using 0"); - segment.time = 0; - }; - - if (NLE_IS_OPERATION (object)) { - segment.base = NLE_OPERATION (object)->next_base_time; - GST_INFO_OBJECT (object, "Using operation base time %" GST_TIME_FORMAT, - GST_TIME_ARGS (NLE_OPERATION (object)->next_base_time)); - } - - if (G_UNLIKELY (segment.time > G_MAXINT64)) - GST_WARNING_OBJECT (object, "Return value too big..."); - - GST_DEBUG_OBJECT (object, - "Sending SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %" - GST_TIME_FORMAT, GST_TIME_ARGS (segment.start), - GST_TIME_ARGS (segment.stop), GST_TIME_ARGS (segment.time)); - - event2 = gst_event_new_segment (&segment); - GST_EVENT_SEQNUM (event2) = seqnum; - gst_event_unref (event); - - return event2; -} - -static gboolean -internalpad_event_function (GstPad * internal, GstObject * parent, - GstEvent * event) -{ - NlePadPrivate *priv = gst_pad_get_element_private (internal); - NleObject *object = priv->object; - gboolean res; - - GST_DEBUG_OBJECT (internal, "event:%s (seqnum::%d)", - GST_EVENT_TYPE_NAME (event), GST_EVENT_SEQNUM (event)); - - if (G_UNLIKELY (!(priv->eventfunc))) { - GST_WARNING_OBJECT (internal, - "priv->eventfunc == NULL !! What is going on ?"); - return FALSE; - } - - switch (priv->dir) { - case GST_PAD_SRC:{ - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEGMENT: - event = translate_outgoing_segment (object, event); - break; - case GST_EVENT_EOS: - break; - default: - break; - } - - break; - } - case GST_PAD_SINK:{ - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - event = translate_outgoing_seek (object, event); - break; - default: - break; - } - break; - } - default: - break; - } - GST_DEBUG_OBJECT (internal, "Calling priv->eventfunc %p", priv->eventfunc); - res = priv->eventfunc (internal, parent, event); - - return res; -} - -/* - translate_outgoing_position_query - - Should only be called: - _ if the query is a GST_QUERY_POSITION - _ after the query was sent upstream - _ if the upstream query returned TRUE -*/ - -static gboolean -translate_incoming_position_query (NleObject * object, GstQuery * query) -{ - GstFormat format; - gint64 cur, cur2; - - gst_query_parse_position (query, &format, &cur); - if (G_UNLIKELY (format != GST_FORMAT_TIME)) { - GST_WARNING_OBJECT (object, - "position query is in a format different from time, returning without modifying values"); - goto beach; - } - - nle_media_to_object_time (object, (guint64) cur, (guint64 *) & cur2); - - GST_DEBUG_OBJECT (object, - "Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, - GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2)); - gst_query_set_position (query, GST_FORMAT_TIME, cur2); - -beach: - return TRUE; -} - -static gboolean -translate_outgoing_position_query (NleObject * object, GstQuery * query) -{ - GstFormat format; - gint64 cur, cur2; - - gst_query_parse_position (query, &format, &cur); - if (G_UNLIKELY (format != GST_FORMAT_TIME)) { - GST_WARNING_OBJECT (object, - "position query is in a format different from time, returning without modifying values"); - goto beach; - } - - if (G_UNLIKELY (!(nle_object_to_media_time (object, (guint64) cur, - (guint64 *) & cur2)))) { - GST_WARNING_OBJECT (object, - "Couldn't get media time for %" GST_TIME_FORMAT, GST_TIME_ARGS (cur)); - goto beach; - } - - GST_DEBUG_OBJECT (object, - "Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, - GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2)); - gst_query_set_position (query, GST_FORMAT_TIME, cur2); - -beach: - return TRUE; -} - -static gboolean -translate_incoming_duration_query (NleObject * object, GstQuery * query) -{ - GstFormat format; - gint64 cur; - - gst_query_parse_duration (query, &format, &cur); - if (G_UNLIKELY (format != GST_FORMAT_TIME)) { - GST_WARNING_OBJECT (object, - "We can only handle duration queries in GST_FORMAT_TIME"); - return FALSE; - } - - gst_query_set_duration (query, GST_FORMAT_TIME, object->duration); - - return TRUE; -} - -static gboolean -internalpad_query_function (GstPad * internal, GstObject * parent, - GstQuery * query) -{ - NlePadPrivate *priv = gst_pad_get_element_private (internal); - NleObject *object = priv->object; - gboolean ret; - - GST_DEBUG_OBJECT (internal, "querytype:%s", - gst_query_type_get_name (GST_QUERY_TYPE (query))); - - if (!(priv->queryfunc)) { - GST_WARNING_OBJECT (internal, - "priv->queryfunc == NULL !! What is going on ?"); - return FALSE; - } - - if ((ret = priv->queryfunc (internal, parent, query))) { - - switch (priv->dir) { - case GST_PAD_SRC: - break; - case GST_PAD_SINK: - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - ret = translate_outgoing_position_query (object, query); - break; - default: - break; - } - break; - default: - break; - } - } - return ret; -} - -static gboolean -ghostpad_event_function (GstPad * ghostpad, GstObject * parent, - GstEvent * event) -{ - NlePadPrivate *priv; - NleObject *object; - gboolean ret = FALSE; - - priv = gst_pad_get_element_private (ghostpad); - object = priv->object; - - GST_DEBUG_OBJECT (ghostpad, "event:%s", GST_EVENT_TYPE_NAME (event)); - - if (G_UNLIKELY (priv->eventfunc == NULL)) - goto no_function; - - switch (priv->dir) { - case GST_PAD_SRC: - { - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - { - GstPad *target; - - event = nle_object_translate_incoming_seek (object, event); - if (!(target = gst_ghost_pad_get_target (GST_GHOST_PAD (ghostpad)))) { - g_assert ("Seeked a pad with not target SHOULD NOT HAPPEND"); - ret = FALSE; - event = NULL; - } else { - gst_object_unref (target); - } - } - break; - default: - break; - } - } - break; - case GST_PAD_SINK:{ - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEGMENT: - event = translate_incoming_segment (object, event); - break; - default: - break; - } - } - break; - default: - break; - } - - if (event) { - GST_DEBUG_OBJECT (ghostpad, "Calling priv->eventfunc"); - ret = priv->eventfunc (ghostpad, parent, event); - GST_DEBUG_OBJECT (ghostpad, "Returned from calling priv->eventfunc : %d", - ret); - } - - return ret; - - /* ERRORS */ -no_function: - { - GST_WARNING_OBJECT (ghostpad, - "priv->eventfunc == NULL !! What's going on ?"); - return FALSE; - } -} - -static gboolean -ghostpad_query_function (GstPad * ghostpad, GstObject * parent, - GstQuery * query) -{ - NlePadPrivate *priv = gst_pad_get_element_private (ghostpad); - NleObject *object = NLE_OBJECT (parent); - gboolean pret = TRUE; - - GST_DEBUG_OBJECT (ghostpad, "querytype:%s", GST_QUERY_TYPE_NAME (query)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_DURATION: - /* skip duration upstream query, we'll fill it in ourselves */ - break; - default: - pret = priv->queryfunc (ghostpad, parent, query); - } - - if (pret) { - /* translate result */ - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - pret = translate_incoming_position_query (object, query); - break; - case GST_QUERY_DURATION: - pret = translate_incoming_duration_query (object, query); - break; - default: - break; - } - } - - return pret; -} - -/* internal pad going away */ -static void -internal_pad_finalizing (NlePadPrivate * priv, GObject * pad G_GNUC_UNUSED) -{ - g_slice_free (NlePadPrivate, priv); -} - -static inline GstPad * -get_proxy_pad (GstPad * ghostpad) -{ - GValue item = { 0, }; - GstIterator *it; - GstPad *ret = NULL; - - it = gst_pad_iterate_internal_links (ghostpad); - g_assert (it); - gst_iterator_next (it, &item); - ret = g_value_dup_object (&item); - g_value_unset (&item); - g_assert (ret); - gst_iterator_free (it); - - return ret; -} - -static void -control_internal_pad (GstPad * ghostpad, NleObject * object) -{ - NlePadPrivate *priv; - NlePadPrivate *privghost; - GstPad *internal; - - if (!ghostpad) { - GST_DEBUG_OBJECT (object, "We don't have a valid ghostpad !"); - return; - } - privghost = gst_pad_get_element_private (ghostpad); - - GST_LOG_OBJECT (ghostpad, "overriding ghostpad's internal pad function"); - - internal = get_proxy_pad (ghostpad); - - if (G_UNLIKELY (!(priv = gst_pad_get_element_private (internal)))) { - GST_DEBUG_OBJECT (internal, - "Creating a NlePadPrivate to put in element_private"); - priv = g_slice_new0 (NlePadPrivate); - - /* Remember existing pad functions */ - priv->eventfunc = GST_PAD_EVENTFUNC (internal); - priv->queryfunc = GST_PAD_QUERYFUNC (internal); - gst_pad_set_element_private (internal, priv); - - g_object_weak_ref ((GObject *) internal, - (GWeakNotify) internal_pad_finalizing, priv); - - /* add query/event function overrides on internal pad */ - gst_pad_set_event_function (internal, - GST_DEBUG_FUNCPTR (internalpad_event_function)); - gst_pad_set_query_function (internal, - GST_DEBUG_FUNCPTR (internalpad_query_function)); - } - - priv->object = object; - priv->ghostpriv = privghost; - priv->dir = GST_PAD_DIRECTION (ghostpad); - gst_object_unref (internal); - - GST_DEBUG_OBJECT (ghostpad, "Done with pad %s:%s", - GST_DEBUG_PAD_NAME (ghostpad)); -} - - -/** - * nle_object_ghost_pad: - * @object: #NleObject to add the ghostpad to - * @name: Name for the new pad - * @target: Target #GstPad to ghost - * - * Adds a #GstGhostPad overridding the correct pad [query|event]_function so - * that time shifting is done correctly - * The #GstGhostPad is added to the #NleObject - * - * /!\ This function doesn't check if the existing [src|sink] pad was removed - * first, so you might end up with more pads than wanted - * - * Returns: The #GstPad if everything went correctly, else NULL. - */ -GstPad * -nle_object_ghost_pad (NleObject * object, const gchar * name, GstPad * target) -{ - GstPadDirection dir = GST_PAD_DIRECTION (target); - GstPad *ghost; - - GST_DEBUG_OBJECT (object, "name:%s, target:%p", name, target); - - g_return_val_if_fail (target, FALSE); - g_return_val_if_fail ((dir != GST_PAD_UNKNOWN), FALSE); - - ghost = nle_object_ghost_pad_no_target (object, name, dir, NULL); - if (!ghost) { - GST_WARNING_OBJECT (object, "Couldn't create ghostpad"); - return NULL; - } - - if (!(nle_object_ghost_pad_set_target (object, ghost, target))) { - GST_WARNING_OBJECT (object, - "Couldn't set the target pad... removing ghostpad"); - gst_object_unref (ghost); - return NULL; - } - - GST_DEBUG_OBJECT (object, "activating ghostpad"); - /* activate pad */ - gst_pad_set_active (ghost, TRUE); - /* add it to element */ - if (!(gst_element_add_pad (GST_ELEMENT (object), ghost))) { - GST_WARNING ("couldn't add newly created ghostpad"); - return NULL; - } - - return ghost; -} - -/* - * nle_object_ghost_pad_no_target: - * /!\ Doesn't add the pad to the NleObject.... - */ -GstPad * -nle_object_ghost_pad_no_target (NleObject * object, const gchar * name, - GstPadDirection dir, GstPadTemplate * template) -{ - GstPad *ghost; - NlePadPrivate *priv; - - /* create a no_target ghostpad */ - if (template) - ghost = gst_ghost_pad_new_no_target_from_template (name, template); - else - ghost = gst_ghost_pad_new_no_target (name, dir); - if (!ghost) - return NULL; - - - /* remember the existing ghostpad event/query/link/unlink functions */ - priv = g_slice_new0 (NlePadPrivate); - priv->dir = dir; - priv->object = object; - - /* grab/replace event/query functions */ - GST_DEBUG_OBJECT (ghost, "Setting priv->eventfunc to %p", - GST_PAD_EVENTFUNC (ghost)); - priv->eventfunc = GST_PAD_EVENTFUNC (ghost); - priv->queryfunc = GST_PAD_QUERYFUNC (ghost); - - gst_pad_set_event_function (ghost, - GST_DEBUG_FUNCPTR (ghostpad_event_function)); - gst_pad_set_query_function (ghost, - GST_DEBUG_FUNCPTR (ghostpad_query_function)); - - gst_pad_set_element_private (ghost, priv); - control_internal_pad (ghost, object); - - return ghost; -} - - - -void -nle_object_remove_ghost_pad (NleObject * object, GstPad * ghost) -{ - NlePadPrivate *priv; - - GST_DEBUG_OBJECT (object, "ghostpad %s:%s", GST_DEBUG_PAD_NAME (ghost)); - - priv = gst_pad_get_element_private (ghost); - gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), NULL); - gst_element_remove_pad (GST_ELEMENT (object), ghost); - if (priv) - g_slice_free (NlePadPrivate, priv); -} - -gboolean -nle_object_ghost_pad_set_target (NleObject * object, GstPad * ghost, - GstPad * target) -{ - NlePadPrivate *priv = gst_pad_get_element_private (ghost); - - g_return_val_if_fail (priv, FALSE); - g_return_val_if_fail (GST_IS_PAD (ghost), FALSE); - - if (target) { - GST_DEBUG_OBJECT (object, "setting target %s:%s on %s:%s", - GST_DEBUG_PAD_NAME (target), GST_DEBUG_PAD_NAME (ghost)); - } else { - GST_DEBUG_OBJECT (object, "removing target from ghostpad"); - priv->pending_seek = NULL; - } - - /* set target */ - if (!(gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), target))) { - GST_WARNING_OBJECT (priv->object, "Could not set ghost %s:%s " - "target to: %s:%s", GST_DEBUG_PAD_NAME (ghost), - GST_DEBUG_PAD_NAME (target)); - return FALSE; - } - - if (target && priv->pending_seek) { - gboolean res = gst_pad_send_event (ghost, priv->pending_seek); - - GST_INFO_OBJECT (object, "Sending our pending seek event: %" GST_PTR_FORMAT - " -- Result is %i", priv->pending_seek, res); - - priv->pending_seek = NULL; - } - - return TRUE; -} - -void -nle_init_ghostpad_category (void) -{ - GST_DEBUG_CATEGORY_INIT (nleghostpad, "nleghostpad", - GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin GhostPad"); - -} diff --git a/ges/nle/nleghostpad.h b/ges/nle/nleghostpad.h deleted file mode 100644 index 25fd3626..00000000 --- a/ges/nle/nleghostpad.h +++ /dev/null @@ -1,48 +0,0 @@ -/* GStreamer - * Copyright (C) 2009 Edward Hervey - * - * nleghostpad.h: Header for helper ghostpad - * - * 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. - */ - - -#ifndef __NLE_GHOSTPAD_H__ -#define __NLE_GHOSTPAD_H__ - -#include - -#include "nletypes.h" - -G_BEGIN_DECLS - -GstPad *nle_object_ghost_pad (NleObject * object, - const gchar * name, GstPad * target) G_GNUC_INTERNAL; - -GstPad *nle_object_ghost_pad_no_target (NleObject * object, - const gchar * name, GstPadDirection dir, GstPadTemplate *templ) G_GNUC_INTERNAL; - -gboolean nle_object_ghost_pad_set_target (NleObject * object, - GstPad * ghost, GstPad * target) G_GNUC_INTERNAL; - -void nle_object_remove_ghost_pad (NleObject * object, GstPad * ghost) G_GNUC_INTERNAL; -GstEvent * nle_object_translate_incoming_seek (NleObject * object, GstEvent * event) G_GNUC_INTERNAL; - -void nle_init_ghostpad_category (void) G_GNUC_INTERNAL; - -G_END_DECLS - -#endif /* __NLE_GHOSTPAD_H__ */ diff --git a/ges/nle/nleobject.c b/ges/nle/nleobject.c deleted file mode 100644 index 94be16d2..00000000 --- a/ges/nle/nleobject.c +++ /dev/null @@ -1,738 +0,0 @@ -/* Gnonlin - * Copyright (C) <2001> Wim Taymans - * <2004-2008> Edward Hervey - * - * 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 -#include "nle.h" - -/** - * SECTION:nleobject - * @short_description: Base class for GNonLin elements - * - * - * - * NleObject encapsulates default behaviour and implements standard - * properties provided by all the GNonLin elements. - * - * - * - */ - - -GST_DEBUG_CATEGORY_STATIC (nleobject_debug); -#define GST_CAT_DEFAULT nleobject_debug - -static GObjectClass *parent_class = NULL; - -/**************************************************** - * Helper macros * - ****************************************************/ -#define CHECK_AND_SET(PROPERTY, property, prop_str, print_format) \ -{ \ -if (object->pending_##property != object->property) { \ - object->property = object->pending_##property; \ - GST_DEBUG_OBJECT(object, "Setting " prop_str " to %" \ - print_format, object->property); \ -} else \ - GST_DEBUG_OBJECT(object, "Nothing to do for " prop_str); \ -} - -#define SET_PENDING_VALUE(property, property_str, type, print_format) \ -nleobject->pending_##property = g_value_get_##type (value); \ -if (nleobject->property != nleobject->pending_##property) { \ - GST_DEBUG_OBJECT(object, "Setting pending " property_str " to %" \ - print_format, nleobject->pending_##property); \ - nle_object_set_commit_needed (nleobject); \ -} else \ - GST_DEBUG_OBJECT(object, "Pending " property_str " did not change"); - -enum -{ - PROP_0, - PROP_START, - PROP_DURATION, - PROP_STOP, - PROP_INPOINT, - PROP_PRIORITY, - PROP_ACTIVE, - PROP_CAPS, - PROP_EXPANDABLE, - PROP_LAST -}; - -enum -{ - COMMIT_SIGNAL, - LAST_SIGNAL -}; - -static guint _signals[LAST_SIGNAL] = { 0 }; - -static GParamSpec *properties[PROP_LAST]; - -static void nle_object_dispose (GObject * object); - -static void nle_object_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void nle_object_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void nle_object_constructed (GObject * object); - -static GstStateChangeReturn nle_object_change_state (GstElement * element, - GstStateChange transition); - -static gboolean nle_object_prepare_func (NleObject * object); -static gboolean nle_object_cleanup_func (NleObject * object); -static gboolean nle_object_commit_func (NleObject * object, gboolean recurse); - -static GstStateChangeReturn nle_object_prepare (NleObject * object); - -static void -nle_object_class_init (NleObjectClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - NleObjectClass *nleobject_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - nleobject_class = (NleObjectClass *) klass; - GST_DEBUG_CATEGORY_INIT (nleobject_debug, "nleobject", - GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin object"); - parent_class = g_type_class_ref (GST_TYPE_BIN); - - gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_object_set_property); - gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_object_get_property); - gobject_class->constructed = GST_DEBUG_FUNCPTR (nle_object_constructed); - gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_object_dispose); - - gstelement_class->change_state = GST_DEBUG_FUNCPTR (nle_object_change_state); - - nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_object_prepare_func); - nleobject_class->cleanup = GST_DEBUG_FUNCPTR (nle_object_cleanup_func); - nleobject_class->commit_signal_handler = - GST_DEBUG_FUNCPTR (nle_object_commit); - nleobject_class->commit = GST_DEBUG_FUNCPTR (nle_object_commit_func); - - /** - * NleObject:start - * - * The start position relative to the parent in nanoseconds. - */ - properties[PROP_START] = g_param_spec_uint64 ("start", "Start", - "The start position relative to the parent (in nanoseconds)", - 0, G_MAXUINT64, 0, G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_START, - properties[PROP_START]); - - /** - * NleObject:duration - * - * The outgoing duration in nanoseconds. - */ - properties[PROP_DURATION] = g_param_spec_int64 ("duration", "Duration", - "Outgoing duration (in nanoseconds)", 0, G_MAXINT64, 0, - G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_DURATION, - properties[PROP_DURATION]); - - /** - * NleObject:stop - * - * The stop position relative to the parent in nanoseconds. - * - * This value is computed based on the values of start and duration. - */ - properties[PROP_STOP] = g_param_spec_uint64 ("stop", "Stop", - "The stop position relative to the parent (in nanoseconds)", - 0, G_MAXUINT64, 0, G_PARAM_READABLE); - g_object_class_install_property (gobject_class, PROP_STOP, - properties[PROP_STOP]); - - /** - * NleObject:inpoint - * - * The media start position in nanoseconds. - * - * Also called 'in-point' in video-editing, this corresponds to - * what position in the 'contained' object we should start outputting from. - */ - properties[PROP_INPOINT] = - g_param_spec_uint64 ("inpoint", "Media start", - "The media start position (in nanoseconds)", 0, G_MAXUINT64, - GST_CLOCK_TIME_NONE, G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_INPOINT, - properties[PROP_INPOINT]); - - /** - * NleObject:priority - * - * The priority of the object in the container. - * - * The highest priority is 0, meaning this object will be selected over - * any other between start and stop. - * - * The lowest priority is G_MAXUINT32. - * - * Objects whose priority is (-1) will be considered as 'default' objects - * in NleComposition and their start/stop values will be modified as to - * fit the whole duration of the composition. - */ - properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority", - "The priority of the object (0 = highest priority)", 0, G_MAXUINT, 0, - G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_PRIORITY, - properties[PROP_PRIORITY]); - - /** - * NleObject:active - * - * Indicates whether this object should be used by its container. - * - * Set to #TRUE to temporarily disable this object in a #NleComposition. - */ - properties[PROP_ACTIVE] = g_param_spec_boolean ("active", "Active", - "Use this object in the NleComposition", TRUE, G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_ACTIVE, - properties[PROP_ACTIVE]); - - /** - * NleObject:caps - * - * Caps used to filter/choose the output stream. - * - * If the controlled object produces several stream, you can set this - * property to choose a specific stream. - * - * If nothing is specified then a source pad will be chosen at random. - */ - properties[PROP_CAPS] = g_param_spec_boxed ("caps", "Caps", - "Caps used to filter/choose the output stream", - GST_TYPE_CAPS, G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_CAPS, - properties[PROP_CAPS]); - - /** - * NleObject:expandable - * - * Indicates whether this object should expand to the full duration of its - * container #NleComposition. - */ - properties[PROP_EXPANDABLE] = - g_param_spec_boolean ("expandable", "Expandable", - "Expand to the full duration of the container composition", FALSE, - G_PARAM_READWRITE); - g_object_class_install_property (gobject_class, PROP_EXPANDABLE, - properties[PROP_EXPANDABLE]); - - /** - * NleObject::commit - * @object: a #NleObject - * @recurse: Whether to commit recursiverly into (NleComposition) children of - * @object. This is used in case we have composition inside - * a nlesource composition, telling it to commit the included - * composition state. - * - * Action signal to commit all the pending changes of the composition and - * its children timing properties - * - * Returns: %TRUE if changes have been commited, %FALSE if nothing had to - * be commited - */ - _signals[COMMIT_SIGNAL] = g_signal_new ("commit", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (NleObjectClass, commit_signal_handler), NULL, NULL, NULL, - G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); -} - -static void -nle_object_init (NleObject * object, NleObjectClass * klass) -{ - object->start = object->pending_start = 0; - object->duration = object->pending_duration = 0; - object->stop = 0; - - object->inpoint = object->pending_inpoint = GST_CLOCK_TIME_NONE; - object->priority = object->pending_priority = 0; - object->active = object->pending_active = TRUE; - - object->caps = gst_caps_new_any (); - - object->segment_rate = 1.0; - object->segment_start = -1; - object->segment_stop = -1; - - object->srcpad = nle_object_ghost_pad_no_target (object, - "src", GST_PAD_SRC, - gst_element_class_get_pad_template ((GstElementClass *) klass, "src")); - - gst_element_add_pad (GST_ELEMENT (object), object->srcpad); -} - -static void -nle_object_dispose (GObject * object) -{ - NleObject *nle = (NleObject *) object; - - if (nle->caps) { - gst_caps_unref (nle->caps); - nle->caps = NULL; - } - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -/** - * nle_object_to_media_time: - * @object: a #NleObject - * @objecttime: The #GstClockTime we want to convert - * @mediatime: A pointer on a #GstClockTime to fill - * - * Converts a #GstClockTime from the object (container) context to the media context - * - * Returns: TRUE if @objecttime was within the limits of the @object start/stop time, - * FALSE otherwise - */ -gboolean -nle_object_to_media_time (NleObject * object, GstClockTime otime, - GstClockTime * mtime) -{ - g_return_val_if_fail (mtime, FALSE); - - GST_DEBUG_OBJECT (object, "ObjectTime : %" GST_TIME_FORMAT, - GST_TIME_ARGS (otime)); - - GST_DEBUG_OBJECT (object, - "Start/Stop:[%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "] " - "Media start: %" GST_TIME_FORMAT, GST_TIME_ARGS (object->start), - GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->inpoint)); - - /* limit check */ - if (G_UNLIKELY ((otime < object->start))) { - GST_DEBUG_OBJECT (object, "ObjectTime is before start"); - *mtime = (object->inpoint == GST_CLOCK_TIME_NONE) ? 0 : object->inpoint; - return FALSE; - } - - if (G_UNLIKELY ((otime >= object->stop))) { - GST_DEBUG_OBJECT (object, "ObjectTime is after stop"); - if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint))) - *mtime = object->inpoint + object->duration; - else - *mtime = object->stop - object->start; - return FALSE; - } - - if (G_UNLIKELY (object->inpoint == GST_CLOCK_TIME_NONE)) { - /* no time shifting, for live sources ? */ - *mtime = otime - object->start; - } else { - *mtime = otime - object->start + object->inpoint; - } - - GST_DEBUG_OBJECT (object, "Returning MediaTime : %" GST_TIME_FORMAT, - GST_TIME_ARGS (*mtime)); - - return TRUE; -} - -/** - * nle_media_to_object_time: - * @object: The #NleObject - * @mediatime: The #GstClockTime we want to convert - * @objecttime: A pointer on a #GstClockTime to fill - * - * Converts a #GstClockTime from the media context to the object (container) context - * - * Returns: TRUE if @objecttime was within the limits of the @object media start/stop time, - * FALSE otherwise - */ - -gboolean -nle_media_to_object_time (NleObject * object, GstClockTime mtime, - GstClockTime * otime) -{ - g_return_val_if_fail (otime, FALSE); - - GST_DEBUG_OBJECT (object, "MediaTime : %" GST_TIME_FORMAT, - GST_TIME_ARGS (mtime)); - - GST_DEBUG_OBJECT (object, - "Start/Stop:[%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "] " - "inpoint %" GST_TIME_FORMAT, GST_TIME_ARGS (object->start), - GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->inpoint)); - - - /* limit check */ - if (G_UNLIKELY ((object->inpoint != GST_CLOCK_TIME_NONE) - && (mtime < object->inpoint))) { - GST_DEBUG_OBJECT (object, "media time is before inpoint, forcing to start"); - *otime = object->start; - return FALSE; - } - - if (G_LIKELY (object->inpoint != GST_CLOCK_TIME_NONE)) { - *otime = mtime - object->inpoint + object->start; - } else - *otime = mtime + object->start; - - GST_DEBUG_OBJECT (object, "Returning ObjectTime : %" GST_TIME_FORMAT, - GST_TIME_ARGS (*otime)); - return TRUE; -} - -static gboolean -nle_object_prepare_func (NleObject * object) -{ - GST_DEBUG_OBJECT (object, "default prepare function, returning TRUE"); - - return TRUE; -} - -static GstStateChangeReturn -nle_object_prepare (NleObject * object) -{ - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - - GST_DEBUG_OBJECT (object, "preparing"); - - if (!(NLE_OBJECT_GET_CLASS (object)->prepare (object))) - ret = GST_STATE_CHANGE_FAILURE; - - GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret); - - return ret; -} - -static gboolean -nle_object_cleanup_func (NleObject * object) -{ - GST_DEBUG_OBJECT (object, "default cleanup function, returning TRUE"); - - return TRUE; -} - -GstStateChangeReturn -nle_object_cleanup (NleObject * object) -{ - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - - GST_DEBUG_OBJECT (object, "cleaning-up"); - - if (!(NLE_OBJECT_GET_CLASS (object)->cleanup (object))) - ret = GST_STATE_CHANGE_FAILURE; - - GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret); - - return ret; -} - -void -nle_object_set_caps (NleObject * object, const GstCaps * caps) -{ - if (object->caps) - gst_caps_unref (object->caps); - - object->caps = gst_caps_copy (caps); -} - -static inline void -_update_stop (NleObject * nleobject) -{ - /* check if start/duration has changed */ - - if ((nleobject->pending_start + nleobject->pending_duration) != - nleobject->stop) { - nleobject->stop = nleobject->pending_start + nleobject->pending_duration; - - GST_LOG_OBJECT (nleobject, - "Updating stop value : %" GST_TIME_FORMAT " [start:%" GST_TIME_FORMAT - ", duration:%" GST_TIME_FORMAT "]", GST_TIME_ARGS (nleobject->stop), - GST_TIME_ARGS (nleobject->pending_start), - GST_TIME_ARGS (nleobject->pending_duration)); - g_object_notify_by_pspec (G_OBJECT (nleobject), properties[PROP_STOP]); - } -} - -static void -update_values (NleObject * object) -{ - CHECK_AND_SET (START, start, "start", G_GUINT64_FORMAT); - CHECK_AND_SET (INPOINT, inpoint, "inpoint", G_GUINT64_FORMAT); - CHECK_AND_SET (DURATION, duration, "duration", G_GINT64_FORMAT); - CHECK_AND_SET (PRIORITY, priority, "priority", G_GUINT32_FORMAT); - CHECK_AND_SET (ACTIVE, active, "active", G_GUINT32_FORMAT); - - _update_stop (object); -} - -static gboolean -nle_object_commit_func (NleObject * object, gboolean recurse) -{ - GST_DEBUG_OBJECT (object, "Commiting object changed"); - - if (object->commit_needed == FALSE) { - GST_INFO_OBJECT (object, "No changes to commit"); - - return FALSE; - } - - update_values (object); - - GST_DEBUG_OBJECT (object, "Done commiting"); - - return TRUE; -} - -static void -nle_object_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - NleObject *nleobject = (NleObject *) object; - - g_return_if_fail (NLE_IS_OBJECT (object)); - - GST_OBJECT_LOCK (object); - switch (prop_id) { - case PROP_START: - SET_PENDING_VALUE (start, "start", uint64, G_GUINT64_FORMAT); - break; - case PROP_DURATION: - SET_PENDING_VALUE (duration, "duration", int64, G_GINT64_FORMAT); - break; - case PROP_INPOINT: - SET_PENDING_VALUE (inpoint, "inpoint", uint64, G_GUINT64_FORMAT); - break; - case PROP_PRIORITY: - SET_PENDING_VALUE (priority, "priority", uint, G_GUINT32_FORMAT); - break; - case PROP_ACTIVE: - SET_PENDING_VALUE (active, "active", boolean, G_GUINT32_FORMAT); - break; - case PROP_CAPS: - nle_object_set_caps (nleobject, gst_value_get_caps (value)); - break; - case PROP_EXPANDABLE: - if (g_value_get_boolean (value)) - GST_OBJECT_FLAG_SET (nleobject, NLE_OBJECT_EXPANDABLE); - else - GST_OBJECT_FLAG_UNSET (nleobject, NLE_OBJECT_EXPANDABLE); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (object); -} - -static void -nle_object_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - NleObject *nleobject = (NleObject *) object; - - switch (prop_id) { - case PROP_START: - g_value_set_uint64 (value, nleobject->pending_start); - break; - case PROP_DURATION: - g_value_set_int64 (value, nleobject->pending_duration); - break; - case PROP_STOP: - g_value_set_uint64 (value, nleobject->stop); - break; - case PROP_INPOINT: - g_value_set_uint64 (value, nleobject->pending_inpoint); - break; - case PROP_PRIORITY: - g_value_set_uint (value, nleobject->pending_priority); - break; - case PROP_ACTIVE: - g_value_set_boolean (value, nleobject->pending_active); - break; - case PROP_CAPS: - gst_value_set_caps (value, nleobject->caps); - break; - case PROP_EXPANDABLE: - g_value_set_boolean (value, NLE_OBJECT_IS_EXPANDABLE (object)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -nle_object_constructed (GObject * object) -{ - NleObject *nleobject = (NleObject *) object; - - _update_stop (nleobject); -} - -static GstStateChangeReturn -nle_object_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - { - GstObject *parent = gst_object_get_parent (GST_OBJECT (element)); - - /* Going to READY and if we are not in a composition, we need to make - * sure that the object positioning state is properly commited */ - if (parent) { - if (g_strcmp0 (gst_element_get_name (GST_ELEMENT (parent)), - "current-bin") - && !NLE_OBJECT_IS_COMPOSITION (NLE_OBJECT (element))) { - GST_INFO ("Adding nleobject to something that is not a composition," - " commiting ourself"); - nle_object_commit (NLE_OBJECT (element), FALSE); - } - - gst_object_unref (parent); - } - } - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - if (nle_object_prepare (NLE_OBJECT (element)) == GST_STATE_CHANGE_FAILURE) { - ret = GST_STATE_CHANGE_FAILURE; - goto beach; - } - break; - default: - break; - } - - GST_DEBUG_OBJECT (element, "Calling parent change_state"); - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - GST_DEBUG_OBJECT (element, "Return from parent change_state was %d", ret); - - if (ret == GST_STATE_CHANGE_FAILURE) - goto beach; - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - /* cleanup nleobject */ - if (nle_object_cleanup (NLE_OBJECT (element)) == GST_STATE_CHANGE_FAILURE) - ret = GST_STATE_CHANGE_FAILURE; - break; - default: - break; - } - -beach: - return ret; -} - -void -nle_object_set_commit_needed (NleObject * object) -{ - if (G_UNLIKELY (object->commiting)) { - GST_WARNING_OBJECT (object, - "Trying to set 'commit-needed' while commiting"); - - return; - } - - GST_DEBUG_OBJECT (object, "Setting 'commit_needed'"); - object->commit_needed = TRUE; -} - -gboolean -nle_object_commit (NleObject * object, gboolean recurse) -{ - gboolean ret; - - GST_DEBUG_OBJECT (object, "Commiting object state"); - - object->commiting = TRUE; - ret = NLE_OBJECT_GET_CLASS (object)->commit (object, recurse); - object->commiting = FALSE; - - return ret; - -} - -static void -_send_seek_event (const GValue * item, gpointer seek_event) -{ - GstElement *child = g_value_get_object (item); - - gst_element_send_event (child, gst_event_ref (seek_event)); -} - -void -nle_object_seek_all_children (NleObject * object, GstEvent * seek_event) -{ - GstIterator *it = gst_bin_iterate_recurse (GST_BIN (object)); - - while (gst_iterator_foreach (it, _send_seek_event, - seek_event) == GST_ITERATOR_RESYNC) - gst_iterator_resync (it); - - gst_iterator_free (it); - gst_event_unref (seek_event); -} - -void -nle_object_reset (NleObject * object) -{ - GST_INFO_OBJECT (object, "Resetting child timing values to default"); - - object->start = 0; - object->duration = 0; - object->stop = 0; - object->inpoint = GST_CLOCK_TIME_NONE; - object->priority = 0; - object->active = TRUE; -} - -GType -nle_object_get_type (void) -{ - static volatile gsize type = 0; - - if (g_once_init_enter (&type)) { - GType _type; - static const GTypeInfo info = { - sizeof (NleObjectClass), - NULL, - NULL, - (GClassInitFunc) nle_object_class_init, - NULL, - NULL, - sizeof (NleObject), - 0, - (GInstanceInitFunc) nle_object_init, - }; - - _type = g_type_register_static (GST_TYPE_BIN, - "NleObject", &info, G_TYPE_FLAG_ABSTRACT); - g_once_init_leave (&type, _type); - } - return type; -} diff --git a/ges/nle/nleobject.h b/ges/nle/nleobject.h deleted file mode 100644 index 1e0272e4..00000000 --- a/ges/nle/nleobject.h +++ /dev/null @@ -1,173 +0,0 @@ -/* GStreamer - * Copyright (C) 2001 Wim Taymans - * 2004-2008 Edward Hervey - * - * nleobject.h: Header for base NleObject - * - * 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. - */ - - -#ifndef __NLE_OBJECT_H__ -#define __NLE_OBJECT_H__ - -#include - -#include "nletypes.h" - -G_BEGIN_DECLS -#define NLE_TYPE_OBJECT \ - (nle_object_get_type()) -#define NLE_OBJECT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_OBJECT,NleObject)) -#define NLE_OBJECT_CAST(obj) ((NleObject*) (obj)) -#define NLE_OBJECT_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_OBJECT,NleObjectClass)) -#define NLE_OBJECT_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), NLE_TYPE_OBJECT, NleObjectClass)) -#define NLE_IS_OBJECT(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_OBJECT)) -#define NLE_IS_OBJECT_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_OBJECT)) - -#define NLE_OBJECT_SRC(obj) (((NleObject *) obj)->srcpad) - -/** - * NleObjectFlags: - * @NLE_OBJECT_IS_SOURCE: - * @NLE_OBJECT_IS_OPERATION: - * @NLE_OBJECT_IS_EXPANDABLE: The #NleObject start/stop will extend accross the full composition. - * @NLE_OBJECT_LAST_FLAG: -*/ - -typedef enum -{ - NLE_OBJECT_SOURCE = (GST_BIN_FLAG_LAST << 0), - NLE_OBJECT_OPERATION = (GST_BIN_FLAG_LAST << 1), - NLE_OBJECT_EXPANDABLE = (GST_BIN_FLAG_LAST << 2), - NLE_OBJECT_COMPOSITION = (GST_BIN_FLAG_LAST << 3), - /* padding */ - NLE_OBJECT_LAST_FLAG = (GST_BIN_FLAG_LAST << 5) -} NleObjectFlags; - - -#define NLE_OBJECT_IS_SOURCE(obj) \ - (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_SOURCE)) -#define NLE_OBJECT_IS_OPERATION(obj) \ - (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_OPERATION)) -#define NLE_OBJECT_IS_EXPANDABLE(obj) \ - (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_EXPANDABLE)) -#define NLE_OBJECT_IS_COMPOSITION(obj) \ - (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_COMPOSITION)) - -/* For internal usage only */ -#define NLE_OBJECT_START(obj) (NLE_OBJECT_CAST (obj)->start) -#define NLE_OBJECT_STOP(obj) (NLE_OBJECT_CAST (obj)->stop) -#define NLE_OBJECT_DURATION(obj) (NLE_OBJECT_CAST (obj)->duration) -#define NLE_OBJECT_INPOINT(obj) (NLE_OBJECT_CAST (obj)->inpoint) -#define NLE_OBJECT_PRIORITY(obj) (NLE_OBJECT_CAST (obj)->priority) -#define NLE_OBJECT_ACTIVE(obj) (NLE_OBJECT_DURATION(obj) > 0 && NLE_OBJECT_CAST (obj)->active) - -#define NLE_OBJECT_IS_COMMITING(obj) (NLE_OBJECT_CAST (obj)->commiting) - -struct _NleObject -{ - GstBin parent; - - GstPad *srcpad; - - /* Time positionning */ - GstClockTime start; - GstClockTime inpoint; - GstClockTimeDiff duration; - - /* Pending time positionning - * Should be == GST_CLOCK_TIME_NONE when nothing to do - */ - GstClockTime pending_start; - GstClockTime pending_inpoint; - GstClockTimeDiff pending_duration; - guint32 pending_priority; - gboolean pending_active; - - gboolean commit_needed; - gboolean commiting; /* Set to TRUE during the commiting time only */ - - gboolean expandable; - - /* read-only */ - GstClockTime stop; - - /* priority in parent */ - guint32 priority; - - /* active in parent */ - gboolean active; - - /* Filtering caps */ - GstCaps *caps; - - /* current segment seek */ - gdouble segment_rate; - GstSeekFlags segment_flags; - gint64 segment_start; - gint64 segment_stop; - - gboolean in_composition; -}; - -struct _NleObjectClass -{ - GstBinClass parent_class; - - /* Signal method handler */ - gboolean (*commit_signal_handler) (NleObject * object, gboolean recurse); - - /* virtual methods for subclasses */ - gboolean (*prepare) (NleObject * object); - gboolean (*cleanup) (NleObject * object); - gboolean (*commit) (NleObject * object, gboolean recurse); -}; - -GType nle_object_get_type (void)G_GNUC_INTERNAL; - -gboolean -nle_object_to_media_time (NleObject * object, GstClockTime otime, - GstClockTime * mtime) G_GNUC_INTERNAL; - -gboolean -nle_media_to_object_time (NleObject * object, GstClockTime mtime, - GstClockTime * otime) G_GNUC_INTERNAL; - -void -nle_object_set_caps (NleObject * object, const GstCaps * caps) G_GNUC_INTERNAL; - -void -nle_object_set_commit_needed (NleObject *object) G_GNUC_INTERNAL; - -gboolean -nle_object_commit (NleObject *object, gboolean recurse) G_GNUC_INTERNAL; - -void -nle_object_reset (NleObject *object) G_GNUC_INTERNAL; - -GstStateChangeReturn -nle_object_cleanup (NleObject * object) G_GNUC_INTERNAL; - -void nle_object_seek_all_children (NleObject *object, GstEvent *seek_event) G_GNUC_INTERNAL; - -G_END_DECLS -#endif /* __NLE_OBJECT_H__ */ diff --git a/ges/nle/nleoperation.c b/ges/nle/nleoperation.c deleted file mode 100644 index fa59734c..00000000 --- a/ges/nle/nleoperation.c +++ /dev/null @@ -1,843 +0,0 @@ -/* GStreamer - * Copyright (C) 2001 Wim Taymans - * 2004-2008 Edward Hervey - * - * 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-nleoperation - * - * - * - * A NleOperation performs a transformation or mixing operation on the - * data from one or more #NleSources, which is used to implement filters or - * effects. - * - * - */ - -static GstStaticPadTemplate nle_operation_src_template = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate nle_operation_sink_template = -GST_STATIC_PAD_TEMPLATE ("sink%d", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); - -GST_DEBUG_CATEGORY_STATIC (nleoperation); -#define GST_CAT_DEFAULT nleoperation - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (nleoperation, "nleoperation", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Operation element"); -#define nle_operation_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (NleOperation, nle_operation, NLE_TYPE_OBJECT, - _do_init); - -enum -{ - ARG_0, - ARG_SINKS, -}; - -enum -{ - INPUT_PRIORITY_CHANGED, - LAST_SIGNAL -}; - -static guint nle_operation_signals[LAST_SIGNAL] = { 0 }; - -static void nle_operation_dispose (GObject * object); - -static void nle_operation_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void nle_operation_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static gboolean nle_operation_prepare (NleObject * object); -static gboolean nle_operation_cleanup (NleObject * object); - -static gboolean nle_operation_add_element (GstBin * bin, GstElement * element); -static gboolean nle_operation_remove_element (GstBin * bin, - GstElement * element); - -static GstPad *nle_operation_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name, const GstCaps * caps); -static void nle_operation_release_pad (GstElement * element, GstPad * pad); - -static void synchronize_sinks (NleOperation * operation); -static gboolean remove_sink_pad (NleOperation * operation, GstPad * sinkpad); - - -static gboolean -nle_operation_send_event (GstElement * element, GstEvent * event) -{ - gboolean res = TRUE; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - nle_object_seek_all_children (NLE_OBJECT (element), event); - break; - default: - res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event); - break; - } - - return res; -} - -static void -nle_operation_class_init (NleOperationClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstBinClass *gstbin_class = (GstBinClass *) klass; - - GstElementClass *gstelement_class = (GstElementClass *) klass; - NleObjectClass *nleobject_class = (NleObjectClass *) klass; - - gst_element_class_set_static_metadata (gstelement_class, "GNonLin Operation", - "Filter/Editor", - "Encapsulates filters/effects for use with NLE Objects", - "Wim Taymans , Edward Hervey "); - - gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_operation_dispose); - - gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_operation_set_property); - gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_operation_get_property); - - /** - * NleOperation:sinks: - * - * Specifies the number of sink pads the operation should provide. - * If the sinks property is -1 (the default) pads are only created as - * demanded via get_request_pad() calls on the element. - */ - g_object_class_install_property (gobject_class, ARG_SINKS, - g_param_spec_int ("sinks", "Sinks", - "Number of input sinks (-1 for automatic handling)", -1, G_MAXINT, -1, - G_PARAM_READWRITE)); - - /** - * NleOperation:input-priority-changed: - * @pad: The operation's input pad whose priority changed. - * @priority: The new priority - * - * Signals that the @priority of the stream being fed to the given @pad - * might have changed. - */ - nle_operation_signals[INPUT_PRIORITY_CHANGED] = - g_signal_new ("input-priority-changed", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NleOperationClass, - input_priority_changed), NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 2, GST_TYPE_PAD, G_TYPE_UINT); - - gstelement_class->request_new_pad = - GST_DEBUG_FUNCPTR (nle_operation_request_new_pad); - gstelement_class->release_pad = GST_DEBUG_FUNCPTR (nle_operation_release_pad); - gstelement_class->send_event = GST_DEBUG_FUNCPTR (nle_operation_send_event); - - gstbin_class->add_element = GST_DEBUG_FUNCPTR (nle_operation_add_element); - gstbin_class->remove_element = - GST_DEBUG_FUNCPTR (nle_operation_remove_element); - - nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_operation_prepare); - nleobject_class->cleanup = GST_DEBUG_FUNCPTR (nle_operation_cleanup); - - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&nle_operation_src_template)); - - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&nle_operation_sink_template)); - -} - -static void -nle_operation_dispose (GObject * object) -{ - NleOperation *oper = (NleOperation *) object; - - GST_DEBUG_OBJECT (object, "Disposing of source pad"); - - nle_object_ghost_pad_set_target (NLE_OBJECT (object), - NLE_OBJECT (object)->srcpad, NULL); - - GST_DEBUG_OBJECT (object, "Disposing of sink pad(s)"); - while (oper->sinks) { - GstPad *ghost = (GstPad *) oper->sinks->data; - remove_sink_pad (oper, ghost); - } - - GST_DEBUG_OBJECT (object, "Done, calling parent class ::dispose()"); - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -nle_operation_reset (NleOperation * operation) -{ - operation->num_sinks = 1; - operation->realsinks = 0; - operation->next_base_time = 0; -} - -static void -nle_operation_init (NleOperation * operation) -{ - nle_operation_reset (operation); - operation->element = NULL; -} - -static gboolean -element_is_valid_filter (GstElement * element, gboolean * isdynamic) -{ - gboolean havesink = FALSE; - gboolean havesrc = FALSE; - gboolean done = FALSE; - GstIterator *pads; - GValue item = { 0, }; - - if (isdynamic) - *isdynamic = FALSE; - - pads = gst_element_iterate_pads (element); - - while (!done) { - switch (gst_iterator_next (pads, &item)) { - case GST_ITERATOR_OK: - { - GstPad *pad = g_value_get_object (&item); - - if (gst_pad_get_direction (pad) == GST_PAD_SRC) - havesrc = TRUE; - else if (gst_pad_get_direction (pad) == GST_PAD_SINK) - havesink = TRUE; - - g_value_reset (&item); - break; - } - case GST_ITERATOR_RESYNC: - gst_iterator_resync (pads); - havesrc = FALSE; - havesink = FALSE; - break; - default: - /* ERROR and DONE */ - done = TRUE; - break; - } - } - - g_value_unset (&item); - gst_iterator_free (pads); - - /* just look at the element's class, not the factory, since there might - * not be a factory (in case of python elements) or the factory is the - * wrong one (in case of a GstBin sub-class) and doesn't have complete - * information. */ - { - GList *tmp = - gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS - (element)); - - while (tmp) { - GstPadTemplate *template = (GstPadTemplate *) tmp->data; - - if (template->direction == GST_PAD_SRC) - havesrc = TRUE; - else if (template->direction == GST_PAD_SINK) { - if (!havesink && (template->presence == GST_PAD_REQUEST) && isdynamic) - *isdynamic = TRUE; - havesink = TRUE; - } - tmp = tmp->next; - } - } - return (havesink && havesrc); -} - -/* - * get_src_pad: - * element: a #GstElement - * - * Returns: The src pad for the given element. A reference was added to the - * returned pad, remove it when you don't need that pad anymore. - * Returns NULL if there's no source pad. - */ - -static GstPad * -get_src_pad (GstElement * element) -{ - GstIterator *it; - GstIteratorResult itres; - GValue item = { 0, }; - GstPad *srcpad = NULL; - - it = gst_element_iterate_src_pads (element); - itres = gst_iterator_next (it, &item); - if (itres != GST_ITERATOR_OK) { - GST_DEBUG ("%s doesn't have a src pad !", GST_ELEMENT_NAME (element)); - } else { - srcpad = g_value_get_object (&item); - gst_object_ref (srcpad); - } - g_value_reset (&item); - gst_iterator_free (it); - - return srcpad; -} - -/* get_nb_static_sinks: - * - * Returns : The number of static sink pads of the controlled element. - */ -static guint -get_nb_static_sinks (NleOperation * oper) -{ - GstIterator *sinkpads; - gboolean done = FALSE; - guint nbsinks = 0; - GValue item = { 0, }; - - sinkpads = gst_element_iterate_sink_pads (oper->element); - - while (!done) { - switch (gst_iterator_next (sinkpads, &item)) { - case GST_ITERATOR_OK:{ - nbsinks++; - g_value_unset (&item); - } - break; - case GST_ITERATOR_RESYNC: - nbsinks = 0; - gst_iterator_resync (sinkpads); - break; - default: - /* ERROR and DONE */ - done = TRUE; - break; - } - } - - g_value_reset (&item); - gst_iterator_free (sinkpads); - - GST_DEBUG ("We found %d static sinks", nbsinks); - - return nbsinks; -} - -static gboolean -nle_operation_add_element (GstBin * bin, GstElement * element) -{ - NleOperation *operation = (NleOperation *) bin; - gboolean res = FALSE; - gboolean isdynamic; - - GST_DEBUG_OBJECT (bin, "element:%s", GST_ELEMENT_NAME (element)); - - if (operation->element) { - GST_WARNING_OBJECT (operation, - "We already control an element : %s , remove it first", - GST_OBJECT_NAME (operation->element)); - } else { - if (!element_is_valid_filter (element, &isdynamic)) { - GST_WARNING_OBJECT (operation, - "Element %s is not a valid filter element", - GST_ELEMENT_NAME (element)); - } else { - if ((res = GST_BIN_CLASS (parent_class)->add_element (bin, element))) { - GstPad *srcpad; - - srcpad = get_src_pad (element); - if (!srcpad) - return FALSE; - - operation->element = element; - operation->dynamicsinks = isdynamic; - - nle_object_ghost_pad_set_target (NLE_OBJECT (operation), - NLE_OBJECT (operation)->srcpad, srcpad); - - /* Remove the reference get_src_pad gave us */ - gst_object_unref (srcpad); - - /* Figure out number of static sink pads */ - operation->num_sinks = get_nb_static_sinks (operation); - - /* Finally sync the ghostpads with the real pads */ - synchronize_sinks (operation); - } - } - } - - return res; -} - -static gboolean -nle_operation_remove_element (GstBin * bin, GstElement * element) -{ - NleOperation *operation = (NleOperation *) bin; - gboolean res = FALSE; - - if (operation->element) { - if ((res = GST_BIN_CLASS (parent_class)->remove_element (bin, element))) - operation->element = NULL; - } else { - GST_WARNING_OBJECT (bin, - "Element %s is not the one controlled by this operation", - GST_ELEMENT_NAME (element)); - } - return res; -} - -static void -nle_operation_set_sinks (NleOperation * operation, guint sinks) -{ - /* FIXME : Check if sinkpad of element is on-demand .... */ - - operation->num_sinks = sinks; - synchronize_sinks (operation); -} - -static void -nle_operation_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - NleOperation *operation = (NleOperation *) object; - - switch (prop_id) { - case ARG_SINKS: - nle_operation_set_sinks (operation, g_value_get_int (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -nle_operation_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - NleOperation *operation = (NleOperation *) object; - - switch (prop_id) { - case ARG_SINKS: - g_value_set_int (value, operation->num_sinks); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -/* - * Returns the first unused sink pad of the controlled element. - * Only use with static element. Unref after usage. - * Returns NULL if there's no more unused sink pads. - */ -static GstPad * -get_unused_static_sink_pad (NleOperation * operation) -{ - GstIterator *pads; - gboolean done = FALSE; - GValue item = { 0, }; - GstPad *ret = NULL; - - if (!operation->element) - return NULL; - - pads = gst_element_iterate_pads (operation->element); - - while (!done) { - switch (gst_iterator_next (pads, &item)) { - case GST_ITERATOR_OK: - { - GstPad *pad = g_value_get_object (&item); - - if (gst_pad_get_direction (pad) == GST_PAD_SINK) { - GList *tmp; - gboolean istaken = FALSE; - - /* 1. figure out if one of our sink ghostpads has this pad as target */ - for (tmp = operation->sinks; tmp; tmp = tmp->next) { - GstGhostPad *gpad = (GstGhostPad *) tmp->data; - GstPad *target = gst_ghost_pad_get_target (gpad); - - GST_LOG ("found ghostpad with target %s:%s", - GST_DEBUG_PAD_NAME (target)); - - if (target) { - if (target == pad) - istaken = TRUE; - gst_object_unref (target); - } - } - - /* 2. if not taken, return that pad */ - if (!istaken) { - gst_object_ref (pad); - ret = pad; - done = TRUE; - } - } - g_value_reset (&item); - break; - } - case GST_ITERATOR_RESYNC: - gst_iterator_resync (pads); - break; - default: - /* ERROR and DONE */ - done = TRUE; - break; - } - } - - g_value_unset (&item); - gst_iterator_free (pads); - - if (ret) - GST_DEBUG_OBJECT (operation, "found free sink pad %s:%s", - GST_DEBUG_PAD_NAME (ret)); - else - GST_DEBUG_OBJECT (operation, "Couldn't find an unused sink pad"); - - return ret; -} - -GstPad * -get_unlinked_sink_ghost_pad (NleOperation * operation) -{ - GstIterator *pads; - gboolean done = FALSE; - GValue item = { 0, }; - GstPad *ret = NULL; - - if (!operation->element) - return NULL; - - pads = gst_element_iterate_sink_pads ((GstElement *) operation); - - while (!done) { - switch (gst_iterator_next (pads, &item)) { - case GST_ITERATOR_OK: - { - GstPad *pad = g_value_get_object (&item); - GstPad *peer = gst_pad_get_peer (pad); - - if (peer == NULL) { - ret = pad; - gst_object_ref (ret); - done = TRUE; - } else { - gst_object_unref (peer); - } - g_value_reset (&item); - break; - } - case GST_ITERATOR_RESYNC: - gst_iterator_resync (pads); - break; - default: - /* ERROR and DONE */ - done = TRUE; - break; - } - } - - g_value_unset (&item); - gst_iterator_free (pads); - - if (ret) - GST_DEBUG_OBJECT (operation, "found unlinked ghost sink pad %s:%s", - GST_DEBUG_PAD_NAME (ret)); - else - GST_DEBUG_OBJECT (operation, "Couldn't find an unlinked ghost sink pad"); - - return ret; - -} - -static GstPad * -get_request_sink_pad (NleOperation * operation) -{ - GstPad *pad = NULL; - GList *templates; - - if (!operation->element) - return NULL; - - templates = gst_element_class_get_pad_template_list - (GST_ELEMENT_GET_CLASS (operation->element)); - - for (; templates; templates = templates->next) { - GstPadTemplate *templ = (GstPadTemplate *) templates->data; - - GST_LOG_OBJECT (operation->element, "Trying template %s", - GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); - - if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SINK) && - (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_REQUEST)) { - pad = - gst_element_get_request_pad (operation->element, - GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); - if (pad) - break; - } - } - - return pad; -} - -static GstPad * -add_sink_pad (NleOperation * operation) -{ - GstPad *gpad = NULL; - GstPad *ret = NULL; - - if (!operation->element) - return NULL; - - /* FIXME : implement */ - GST_LOG_OBJECT (operation, "element:%s , dynamicsinks:%d", - GST_ELEMENT_NAME (operation->element), operation->dynamicsinks); - - - if (!operation->dynamicsinks) { - /* static sink pads */ - ret = get_unused_static_sink_pad (operation); - if (ret) { - gpad = nle_object_ghost_pad ((NleObject *) operation, GST_PAD_NAME (ret), - ret); - gst_object_unref (ret); - } - } - - if (!gpad) { - /* request sink pads */ - ret = get_request_sink_pad (operation); - if (ret) { - gpad = nle_object_ghost_pad ((NleObject *) operation, GST_PAD_NAME (ret), - ret); - gst_object_unref (ret); - } - } - - if (gpad) { - operation->sinks = g_list_append (operation->sinks, gpad); - operation->realsinks++; - GST_DEBUG ("Created new pad %s:%s ghosting %s:%s", - GST_DEBUG_PAD_NAME (gpad), GST_DEBUG_PAD_NAME (ret)); - } else { - GST_WARNING ("Couldn't find a usable sink pad!"); - } - - return gpad; -} - -static gboolean -remove_sink_pad (NleOperation * operation, GstPad * sinkpad) -{ - gboolean ret = TRUE; - - GST_DEBUG ("sinkpad %s:%s", GST_DEBUG_PAD_NAME (sinkpad)); - - /* - We can't remove any random pad. - We should remove an unused pad ... which is hard to figure out in a - thread-safe way. - */ - - if ((sinkpad == NULL) && operation->dynamicsinks) { - /* Find an unlinked sinkpad */ - if ((sinkpad = get_unlinked_sink_ghost_pad (operation)) == NULL) { - ret = FALSE; - goto beach; - } - } - - if (sinkpad) { - GstPad *target = gst_ghost_pad_get_target ((GstGhostPad *) sinkpad); - - if (target) { - /* release the target pad */ - nle_object_ghost_pad_set_target ((NleObject *) operation, sinkpad, NULL); - if (operation->dynamicsinks) - gst_element_release_request_pad (operation->element, target); - gst_object_unref (target); - } - operation->sinks = g_list_remove (operation->sinks, sinkpad); - nle_object_remove_ghost_pad ((NleObject *) operation, sinkpad); - operation->realsinks--; - } - -beach: - return ret; -} - -static void -synchronize_sinks (NleOperation * operation) -{ - - GST_DEBUG_OBJECT (operation, "num_sinks:%d , realsinks:%d, dynamicsinks:%d", - operation->num_sinks, operation->realsinks, operation->dynamicsinks); - - if (operation->num_sinks == operation->realsinks) - return; - - if (operation->num_sinks > operation->realsinks) { - while (operation->num_sinks > operation->realsinks) /* Add pad */ - if (!(add_sink_pad (operation))) { - break; - } - } else { - /* Remove pad */ - /* FIXME, which one do we remove ? :) */ - while (operation->num_sinks < operation->realsinks) - if (!remove_sink_pad (operation, NULL)) - break; - } -} - -static gboolean -nle_operation_prepare (NleObject * object) -{ - /* Prepare the pads */ - synchronize_sinks ((NleOperation *) object); - - return TRUE; -} - -static gboolean -nle_operation_cleanup (NleObject * object) -{ - NleOperation *oper = (NleOperation *) object; - - if (oper->dynamicsinks) { - GST_DEBUG ("Resetting dynamic sinks"); - nle_operation_set_sinks (oper, 0); - } - - return TRUE; -} - -void -nle_operation_hard_cleanup (NleOperation * operation) -{ - gboolean done = FALSE; - - GValue item = { 0, }; - GstIterator *pads; - - GST_INFO_OBJECT (operation, "Hard reset of the operation"); - - pads = gst_element_iterate_sink_pads (GST_ELEMENT (operation)); - while (!done) { - switch (gst_iterator_next (pads, &item)) { - case GST_ITERATOR_OK: - { - GstPad *sinkpad = g_value_get_object (&item); - GstPad *srcpad = gst_pad_get_peer (sinkpad); - - if (srcpad) { - GST_ERROR ("Unlinking %" GST_PTR_FORMAT " and %" - GST_PTR_FORMAT, srcpad, sinkpad); - gst_pad_unlink (srcpad, sinkpad); - } - - g_value_reset (&item); - break; - } - case GST_ITERATOR_RESYNC: - gst_iterator_resync (pads); - break; - default: - /* ERROR and DONE */ - done = TRUE; - break; - } - } - nle_object_cleanup (NLE_OBJECT (operation)); -} - - -static GstPad * -nle_operation_request_new_pad (GstElement * element, GstPadTemplate * templ, - const gchar * name, const GstCaps * caps) -{ - NleOperation *operation = (NleOperation *) element; - GstPad *ret; - - GST_DEBUG ("template:%s name:%s", templ->name_template, name); - - if (operation->num_sinks == operation->realsinks) { - GST_WARNING_OBJECT (element, - "We already have the maximum number of pads : %d", - operation->num_sinks); - return NULL; - } - - ret = add_sink_pad ((NleOperation *) element); - - return ret; -} - -static void -nle_operation_release_pad (GstElement * element, GstPad * pad) -{ - GST_DEBUG ("pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - remove_sink_pad ((NleOperation *) element, pad); -} - -void -nle_operation_signal_input_priority_changed (NleOperation * operation, - GstPad * pad, guint32 priority) -{ - GST_DEBUG_OBJECT (operation, "pad:%s:%s, priority:%d", - GST_DEBUG_PAD_NAME (pad), priority); - g_signal_emit (operation, nle_operation_signals[INPUT_PRIORITY_CHANGED], - 0, pad, priority); -} - -void -nle_operation_update_base_time (NleOperation * operation, - GstClockTime timestamp) -{ - if (!nle_object_to_media_time (NLE_OBJECT (operation), - timestamp, &operation->next_base_time)) { - GST_WARNING_OBJECT (operation, "Trying to set a basetime outside of " - "ourself"); - - return; - } - - GST_INFO_OBJECT (operation, "Setting next_basetime to %" - GST_TIME_FORMAT, GST_TIME_ARGS (operation->next_base_time)); -} diff --git a/ges/nle/nleoperation.h b/ges/nle/nleoperation.h deleted file mode 100644 index 3f68f5d8..00000000 --- a/ges/nle/nleoperation.h +++ /dev/null @@ -1,90 +0,0 @@ -/* GStreamer - * Copyright (C) 2001 Wim Taymans - * 2004 Edward Hervey - * - * nleoperation.h: Header for base NleOperation - * - * 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. - */ - - -#ifndef __NLE_OPERATION_H__ -#define __NLE_OPERATION_H__ - -#include -#include "nleobject.h" - -G_BEGIN_DECLS -#define NLE_TYPE_OPERATION \ - (nle_operation_get_type()) -#define NLE_OPERATION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_OPERATION,NleOperation)) -#define NLE_OPERATION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_OPERATION,NleOperationClass)) -#define NLE_IS_OPERATION(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_OPERATION)) -#define NLE_IS_OPERATION_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_OPERATION)) - struct _NleOperation -{ - NleObject parent; - - /* */ - - /* num_sinks: - * Number of sink inputs of the controlled element. - * -1 if the sink pads are dynamic */ - gint num_sinks; - - /* TRUE if element has request pads */ - gboolean dynamicsinks; - - /* realsinks: - * Number of sink pads currently used on the contolled element. */ - gint realsinks; - - /* FIXME : We might need to use a lock to access this list */ - GList * sinks; /* The sink ghostpads */ - - GstElement *element; /* controlled element */ - - GstClockTime next_base_time; -}; - -struct _NleOperationClass -{ - NleObjectClass parent_class; - - void (*input_priority_changed) (NleOperation * operation, GstPad *pad, guint32 priority); -}; - -GstPad * get_unlinked_sink_ghost_pad (NleOperation * operation) G_GNUC_INTERNAL; - -void -nle_operation_signal_input_priority_changed(NleOperation * operation, GstPad *pad, - guint32 priority) G_GNUC_INTERNAL; - -void nle_operation_update_base_time (NleOperation *operation, - GstClockTime timestamp) G_GNUC_INTERNAL; - -void nle_operation_hard_cleanup (NleOperation *operation) G_GNUC_INTERNAL; - - -/* normal GOperation stuff */ -GType nle_operation_get_type (void) G_GNUC_INTERNAL; - -G_END_DECLS -#endif /* __NLE_OPERATION_H__ */ diff --git a/ges/nle/nlesource.c b/ges/nle/nlesource.c deleted file mode 100644 index fcb98506..00000000 --- a/ges/nle/nlesource.c +++ /dev/null @@ -1,505 +0,0 @@ -/* Gnonlin - * Copyright (C) <2001> Wim Taymans - * <2004-2008> Edward Hervey - * - * 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 , Edward Hervey "); - - 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; -} diff --git a/ges/nle/nlesource.h b/ges/nle/nlesource.h deleted file mode 100644 index 6fcb1af4..00000000 --- a/ges/nle/nlesource.h +++ /dev/null @@ -1,66 +0,0 @@ -/* GStreamer - * Copyright (C) 2001 Wim Taymans - * 2004-2008 Edward Hervey - * - * nlesource.h: Header for base NleSource - * - * 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. - */ - - -#ifndef __NLE_SOURCE_H__ -#define __NLE_SOURCE_H__ - -#include -#include "nleobject.h" - -G_BEGIN_DECLS -#define NLE_TYPE_SOURCE \ - (nle_source_get_type()) -#define NLE_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_SOURCE,NleSource)) -#define NLE_SOURCE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_SOURCE,NleSourceClass)) -#define NLE_SOURCE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), NLE_TYPE_SOURCE, NleSourceClass)) -#define NLE_IS_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_SOURCE)) -#define NLE_IS_SOURCE_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_SOURCE)) -typedef struct _NleSourcePrivate NleSourcePrivate; - -struct _NleSource -{ - NleObject parent; - - /* controlled source element, acces with gst_bin_[add|remove]_element */ - GstElement *element; - - NleSourcePrivate *priv; -}; - -struct _NleSourceClass -{ - NleObjectClass parent_class; - - /* control_element() takes care of controlling the given element */ - gboolean (*control_element) (NleSource * source, GstElement * element); -}; - -GType nle_source_get_type (void) G_GNUC_INTERNAL; - -G_END_DECLS -#endif /* __NLE_SOURCE_H__ */ diff --git a/ges/nle/nletypes.h b/ges/nle/nletypes.h deleted file mode 100644 index f84cb23b..00000000 --- a/ges/nle/nletypes.h +++ /dev/null @@ -1,42 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Edward Hervey - * - * nletypes.h: Header for class definition - * - * 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. - */ - -#ifndef __NLE_TYPES_H__ -#define __NLE_TYPES_H__ - -#include - -typedef struct _NleObject NleObject; -typedef struct _NleObjectClass NleObjectClass; - -typedef struct _NleComposition NleComposition; -typedef struct _NleCompositionClass NleCompositionClass; - -typedef struct _NleOperation NleOperation; -typedef struct _NleOperationClass NleOperationClass; - -typedef struct _NleSource NleSource; -typedef struct _NleSourceClass NleSourceClass; - -typedef struct _NleURISource NleURISource; -typedef struct _NleURISourceClass NleURISourceClass; - -#endif diff --git a/ges/nle/nleurisource.c b/ges/nle/nleurisource.c deleted file mode 100644 index 8fd2950a..00000000 --- a/ges/nle/nleurisource.c +++ /dev/null @@ -1,166 +0,0 @@ -/* Gnonlin - * Copyright (C) <2005-2008> Edward Hervey - * - * 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" -#include "nleurisource.h" - -/** - * SECTION:element-nleurisource - * - * NleURISource is a #NleSource which reads and decodes the contents - * of a given file. The data in the file is decoded using any available - * GStreamer plugins. - */ - -static GstStaticPadTemplate nle_urisource_src_template = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS_ANY); - -GST_DEBUG_CATEGORY_STATIC (nleurisource); -#define GST_CAT_DEFAULT nleurisource - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (nleurisource, "nleurisource", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin URI Source Element"); -#define nle_urisource_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (NleURISource, nle_urisource, NLE_TYPE_SOURCE, - _do_init); - -enum -{ - ARG_0, - ARG_URI, -}; - -static gboolean nle_urisource_prepare (NleObject * object); - -static void -nle_urisource_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); - -static void -nle_urisource_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static void -nle_urisource_class_init (NleURISourceClass * klass) -{ - GObjectClass *gobject_class; - NleObjectClass *nleobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - nleobject_class = (NleObjectClass *) klass; - parent_class = g_type_class_ref (NLE_TYPE_SOURCE); - - gst_element_class_set_static_metadata (gstelement_class, "GNonLin URI Source", - "Filter/Editor", - "High-level URI Source element", "Edward Hervey "); - - gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_urisource_set_property); - gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_urisource_get_property); - - g_object_class_install_property (gobject_class, ARG_URI, - g_param_spec_string ("uri", "Uri", - "Uri of the file to use", NULL, G_PARAM_READWRITE)); - - gst_element_class_add_pad_template (gstelement_class, - gst_static_pad_template_get (&nle_urisource_src_template)); - - nleobject_class->prepare = nle_urisource_prepare; -} - -static void -nle_urisource_init (NleURISource * urisource) -{ - GstElement *decodebin = NULL; - - GST_OBJECT_FLAG_SET (urisource, NLE_OBJECT_SOURCE); - - /* We create a bin with source and decodebin within */ - decodebin = - gst_element_factory_make ("uridecodebin", "internal-uridecodebin"); - g_object_set (decodebin, "expose-all-streams", FALSE, NULL); - - gst_bin_add (GST_BIN (urisource), decodebin); -} - -static inline void -nle_urisource_set_uri (NleURISource * fs, const gchar * uri) -{ - g_object_set (NLE_SOURCE (fs)->element, "uri", uri, NULL); -} - -static void -nle_urisource_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - NleURISource *fs = (NleURISource *) object; - - switch (prop_id) { - case ARG_URI: - nle_urisource_set_uri (fs, g_value_get_string (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -nle_urisource_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - NleURISource *fs = (NleURISource *) object; - - switch (prop_id) { - case ARG_URI: - g_object_get_property ((GObject *) NLE_SOURCE (fs)->element, "uri", - value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - -} - -static gboolean -nle_urisource_prepare (NleObject * object) -{ - NleSource *fs = (NleSource *) object; - - GST_DEBUG ("prepare"); - - /* Set the caps on uridecodebin */ - if (!gst_caps_is_any (object->caps)) { - GST_DEBUG_OBJECT (object, "Setting uridecodebin caps to %" GST_PTR_FORMAT, - object->caps); - g_object_set (fs->element, "caps", object->caps, NULL); - } - - return NLE_OBJECT_CLASS (parent_class)->prepare (object); -} diff --git a/ges/nle/nleurisource.h b/ges/nle/nleurisource.h deleted file mode 100644 index a2750e59..00000000 --- a/ges/nle/nleurisource.h +++ /dev/null @@ -1,57 +0,0 @@ -/* GStreamer - * Copyright (C) 2001 Wim Taymans - * 2004-2008 Edward Hervey - * - * nleurisource.h: Header for NleURISource - * - * 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. - */ - - -#ifndef __NLE_URI_SOURCE_H__ -#define __NLE_URI_SOURCE_H__ - -#include -#include "nlesource.h" - -G_BEGIN_DECLS -#define NLE_TYPE_URI_SOURCE \ - (nle_urisource_get_type()) -#define NLE_URI_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_URI_SOURCE,NleURIsource)) -#define NLE_URI_SOURCE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_URI_SOURCE,NleURIsourceClass)) -#define NLE_IS_URI_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_URI_SOURCE)) -#define NLE_IS_URI_SOURCE_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_URI_SOURCE)) - -struct _NleURISource -{ - NleSource parent; - - gchar *uri; -}; - -struct _NleURISourceClass -{ - NleSourceClass parent_class; -}; - -GType nle_urisource_get_type (void) G_GNUC_INTERNAL; - -G_END_DECLS -#endif /* __NLE_URI_SOURCE_H__ */ diff --git a/plugins/Makefile.am b/plugins/Makefile.am new file mode 100644 index 00000000..7f0a0065 --- /dev/null +++ b/plugins/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = nle diff --git a/plugins/nle/.gitignore b/plugins/nle/.gitignore new file mode 100644 index 00000000..3ac440bf --- /dev/null +++ b/plugins/nle/.gitignore @@ -0,0 +1,8 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs +nleversion.h diff --git a/plugins/nle/Makefile.am b/plugins/nle/Makefile.am new file mode 100644 index 00000000..2e8fa57d --- /dev/null +++ b/plugins/nle/Makefile.am @@ -0,0 +1,30 @@ +plugin_LTLIBRARIES = libgstnle.la + +libgstnle_la_SOURCES = gstnle.c \ + nleobject.c \ + nlecomposition.c \ + nleghostpad.c \ + nleoperation.c \ + nlesource.c \ + nleurisource.c + +libgstnle_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CFLAGS) \ + -I$(top_srcdir) + +libgstnle_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + $(GST_BASE_LIBS) $(GST_LIBS) + +libgstnle_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstnle_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = \ + nle.h \ + nleobject.h \ + nlecomposition.h \ + nletypes.h \ + nleghostpad.h \ + nleoperation.h \ + nlesource.h \ + nletypes.h \ + nleurisource.h diff --git a/plugins/nle/gnlmarshal.list b/plugins/nle/gnlmarshal.list new file mode 100644 index 00000000..67c63946 --- /dev/null +++ b/plugins/nle/gnlmarshal.list @@ -0,0 +1,2 @@ +VOID:OBJECT,UINT + diff --git a/plugins/nle/gstnle.c b/plugins/nle/gstnle.c new file mode 100644 index 00000000..df32330a --- /dev/null +++ b/plugins/nle/gstnle.c @@ -0,0 +1,66 @@ +/* Non Linear Engine plugin + * + * Copyright (C) 2015 Thibault Saunier + * + * gstnle.c + * + * 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 +#include "nle.h" + +struct _elements_entry +{ + const gchar *name; + GType (*type) (void); +}; + +static struct _elements_entry _elements[] = { + {"nlesource", nle_source_get_type}, + {"nlecomposition", nle_composition_get_type}, + {"nleoperation", nle_operation_get_type}, + {"nleurisource", nle_urisource_get_type}, + {NULL, 0} +}; + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gint i = 0; + + for (; _elements[i].name; i++) + if (!(gst_element_register (plugin, + _elements[i].name, GST_RANK_NONE, (_elements[i].type) ()))) + return FALSE; + + nle_init_ghostpad_category (); + + return TRUE; +} + +/* plugin export resolution */ +GST_PLUGIN_DEFINE + (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + nle, + "GStreamer Non Linear Engine", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/plugins/nle/nle.h b/plugins/nle/nle.h new file mode 100644 index 00000000..6e00eae4 --- /dev/null +++ b/plugins/nle/nle.h @@ -0,0 +1,36 @@ +/* Gnonlin + * Copyright (C) <2001> Wim Taymans + * <2004-2008> Edward Hervey + * + * 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. + */ + +#ifndef __NLE_H__ +#define __NLE_H__ + +#include + +#include "nletypes.h" + +#include "nleobject.h" +#include "nleghostpad.h" +#include "nlesource.h" +#include "nlecomposition.h" +#include "nleoperation.h" + +#include "nleurisource.h" + +#endif /* __GST_H__ */ diff --git a/plugins/nle/nlecomposition.c b/plugins/nle/nlecomposition.c new file mode 100644 index 00000000..7b69589b --- /dev/null +++ b/plugins/nle/nlecomposition.c @@ -0,0 +1,3069 @@ +/* GStreamer + * Copyright (C) 2001 Wim Taymans + * 2004-2008 Edward Hervey + * 2014 Mathieu Duponchelle + * 2014 Thibault Saunier + * + * 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-nlecomposition + * + * A NleComposition contains NleObjects such as NleSources and NleOperations, + * and connects them dynamically to create a composition timeline. + */ + +static GstStaticPadTemplate nle_composition_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +GST_DEBUG_CATEGORY_STATIC (nlecomposition_debug); +#define GST_CAT_DEFAULT nlecomposition_debug + +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (nlecomposition_debug,"nlecomposition", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Composition"); +#define nle_composition_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (NleComposition, nle_composition, NLE_TYPE_OBJECT, + _do_init); + +enum +{ + PROP_0, + PROP_DEACTIVATED_ELEMENTS_STATE, + PROP_LAST, +}; + +/* Properties from NleObject */ +enum +{ + NLEOBJECT_PROP_START, + NLEOBJECT_PROP_STOP, + NLEOBJECT_PROP_DURATION, + NLEOBJECT_PROP_LAST +}; + +enum +{ + COMMIT_SIGNAL, + COMMITED_SIGNAL, + QUERY_POSITION_SIGNAL, + LAST_SIGNAL +}; + +typedef enum +{ + COMP_UPDATE_STACK_INITIALIZE, + COMP_UPDATE_STACK_ON_COMMIT, + COMP_UPDATE_STACK_ON_EOS, + COMP_UPDATE_STACK_ON_SEEK, + COMP_UPDATE_STACK_NONE +} NleUpdateStackReason; + +static const char *UPDATE_PIPELINE_REASONS[] = { + "Initialize", "Commit", "EOS", "Seek" +}; + +typedef struct +{ + NleComposition *comp; + GstEvent *event; +} SeekData; + +typedef struct +{ + NleComposition *comp; + NleObject *object; +} ChildIOData; + +typedef struct +{ + NleComposition *comp; + gint32 seqnum; + + NleUpdateStackReason reason; +} UpdateCompositionData; + +struct _NleCompositionPrivate +{ + gboolean dispose_has_run; + + /* + Sorted List of NleObjects , ThreadSafe + objects_start : sorted by start-time then priority + objects_stop : sorted by stop-time then priority + objects_hash : contains all controlled objects + + Those list should be manipulated exclusively in the main context + or while the task is totally stopped. + */ + GList *objects_start; + GList *objects_stop; + GHashTable *objects_hash; + + /* List of NleObject to be inserted or removed from the composition on the + * next commit */ + GHashTable *pending_io; + + gulong ghosteventprobe; + + /* current stack, list of NleObject* */ + GNode *current; + + /* List of NleObject whose start/duration will be the same as the composition */ + GList *expandables; + + /* + current segment seek start/stop time. + Reconstruct pipeline ONLY if seeking outside of those values + FIXME : segment_start isn't always the earliest time before which the + timeline doesn't need to be modified + */ + GstClockTime segment_start; + GstClockTime segment_stop; + + /* Seek segment handler */ + GstSegment *segment; + GstSegment *outside_segment; + + /* Next running base_time to set on outgoing segment */ + guint64 next_base_time; + + /* + OUR sync_handler on the child_bus + We are called before nle_object_sync_handler + */ + GstPadEventFunction nle_event_pad_func; + gboolean send_stream_start; + + /* Protect the actions list */ + GMutex actions_lock; + GCond actions_cond; + GList *actions; + + gboolean running; + gboolean initialized; + + GstElement *current_bin; + + gboolean seeking_itself; + gint real_eos_seqnum; + gint next_eos_seqnum; + gint flush_seqnum; + + /* 0 means that we already received the right caps or segment */ + gint seqnum_to_restart_task; + gboolean waiting_for_buffer; + + gboolean tearing_down_stack; + + NleUpdateStackReason updating_reason; +}; + +typedef struct _Action +{ + GCClosure closure; + gint priority; +} Action; + +#define ACTION_CALLBACK(__action) (((GCClosure*) (__action))->callback) + +static guint _signals[LAST_SIGNAL] = { 0 }; + +static GParamSpec *nleobject_properties[NLEOBJECT_PROP_LAST]; + +#define OBJECT_IN_ACTIVE_SEGMENT(comp,element) \ + ((NLE_OBJECT_START(element) < comp->priv->segment_stop) && \ + (NLE_OBJECT_STOP(element) >= comp->priv->segment_start)) + +static void nle_composition_dispose (GObject * object); +static void nle_composition_finalize (GObject * object); +static void nle_composition_reset (NleComposition * comp); + +static gboolean nle_composition_add_object (GstBin * bin, GstElement * element); + +static gboolean +nle_composition_remove_object (GstBin * bin, GstElement * element); + +static GstStateChangeReturn +nle_composition_change_state (GstElement * element, GstStateChange transition); + +static inline void nle_composition_reset_target_pad (NleComposition * comp); + +static gboolean +seek_handling (NleComposition * comp, gint32 seqnum, + NleUpdateStackReason update_stack_reason); +static gint objects_start_compare (NleObject * a, NleObject * b); +static gint objects_stop_compare (NleObject * a, NleObject * b); +static GstClockTime get_current_position (NleComposition * comp); + +static gboolean update_pipeline (NleComposition * comp, + GstClockTime currenttime, gint32 seqnum, + NleUpdateStackReason update_stack_reason); +static gboolean nle_composition_commit_func (NleObject * object, + gboolean recurse); +static void update_start_stop_duration (NleComposition * comp); + +static gboolean +nle_composition_event_handler (GstPad * ghostpad, GstObject * parent, + GstEvent * event); +static void _relink_single_node (NleComposition * comp, GNode * node, + GstEvent * toplevel_seek); +static void _update_pipeline_func (NleComposition * comp, + UpdateCompositionData * ucompo); +static void _commit_func (NleComposition * comp, + UpdateCompositionData * ucompo); +static GstEvent *get_new_seek_event (NleComposition * comp, gboolean initial, + gboolean updatestoponly); +static gboolean _nle_composition_add_object (NleComposition * comp, + NleObject * object); +static gboolean _nle_composition_remove_object (NleComposition * comp, + NleObject * object); +static void _deactivate_stack (NleComposition * comp, + gboolean flush_downstream); +static gboolean _set_real_eos_seqnum_from_seek (NleComposition * comp, + GstEvent * event); +static void _emit_commited_signal_func (NleComposition * comp, gpointer udata); +static void _restart_task (NleComposition * comp); +static void +_add_action (NleComposition * comp, GCallback func, gpointer data, + gint priority); +static gboolean +_is_ready_to_restart_task (NleComposition * comp, GstEvent * event); + + +/* COMP_REAL_START: actual position to start current playback at. */ +#define COMP_REAL_START(comp) \ + (MAX (comp->priv->segment->start, NLE_OBJECT_START (comp))) + +#define COMP_REAL_STOP(comp) \ + (GST_CLOCK_TIME_IS_VALID (comp->priv->segment->stop) ? \ + (MIN (comp->priv->segment->stop, NLE_OBJECT_STOP (comp))) : \ + NLE_OBJECT_STOP (comp)) + +#define ACTIONS_LOCK(comp) G_STMT_START { \ + GST_LOG_OBJECT (comp, "Getting ACTIONS_LOCK in thread %p", \ + g_thread_self()); \ + g_mutex_lock(&((NleComposition*)comp)->priv->actions_lock); \ + GST_LOG_OBJECT (comp, "Got ACTIONS_LOCK in thread %p", \ + g_thread_self()); \ +} G_STMT_END + +#define ACTIONS_UNLOCK(comp) G_STMT_START { \ + g_mutex_unlock(&((NleComposition*)comp)->priv->actions_lock); \ + GST_LOG_OBJECT (comp, "Unlocked ACTIONS_LOCK in thread %p", \ + g_thread_self()); \ +} G_STMT_END + +#define WAIT_FOR_AN_ACTION(comp) G_STMT_START { \ + GST_LOG_OBJECT (comp, "Waiting for an action in thread %p", \ + g_thread_self()); \ + g_cond_wait(&((NleComposition*)comp)->priv->actions_cond, \ + &((NleComposition*)comp)->priv->actions_lock); \ + GST_LOG_OBJECT (comp, "Done WAITING for an action in thread %p", \ + g_thread_self()); \ +} G_STMT_END + +#define SIGNAL_NEW_ACTION(comp) G_STMT_START { \ + GST_LOG_OBJECT (comp, "Signalling new action from thread %p", \ + g_thread_self()); \ + g_cond_signal(&((NleComposition*)comp)->priv->actions_cond); \ +} G_STMT_END + +#define GET_TASK_LOCK(comp) (&(NLE_COMPOSITION(comp)->task_rec_lock)) + +static inline gboolean +_have_to_flush_downstream (NleUpdateStackReason update_reason) +{ + if (update_reason == COMP_UPDATE_STACK_ON_COMMIT || + update_reason == COMP_UPDATE_STACK_ON_SEEK || + update_reason == COMP_UPDATE_STACK_INITIALIZE) + return TRUE; + + return FALSE; +} + +static void +_assert_proper_thread (NleComposition * comp) +{ + if (comp->task && gst_task_get_state (comp->task) != GST_TASK_STOPPED && + g_thread_self () != comp->task->thread) { + g_warning ("Trying to touch children in a thread different from" + " its dedicated thread!"); + } +} + +static void +_remove_actions_for_type (NleComposition * comp, GCallback callback) +{ + GList *tmp; + + ACTIONS_LOCK (comp); + for (tmp = comp->priv->actions; tmp; tmp = tmp->next) { + Action *act = tmp->data; + + if (ACTION_CALLBACK (act) == callback) { + g_closure_unref ((GClosure *) act); + comp->priv->actions = g_list_delete_link (comp->priv->actions, tmp); + } + } + ACTIONS_UNLOCK (comp); + +} + +static void +_execute_actions (NleComposition * comp) +{ + NleCompositionPrivate *priv = comp->priv; + + ACTIONS_LOCK (comp); + if (priv->running == FALSE) { + GST_DEBUG_OBJECT (comp, "Not running anymore"); + + ACTIONS_UNLOCK (comp); + return; + } + + if (priv->actions == NULL) + WAIT_FOR_AN_ACTION (comp); + + if (comp->priv->running == FALSE) { + GST_INFO_OBJECT (comp, "Done waiting but not running anymore"); + + ACTIONS_UNLOCK (comp); + return; + } + + if (priv->actions) { + GValue params[1] = { G_VALUE_INIT }; + GList *lact; + + g_value_init (¶ms[0], G_TYPE_OBJECT); + g_value_set_object (¶ms[0], comp); + + lact = priv->actions; + priv->actions = priv->actions->next; + ACTIONS_UNLOCK (comp); + + GST_INFO_OBJECT (comp, "Invoking %p:%s", + lact->data, GST_DEBUG_FUNCPTR_NAME ((ACTION_CALLBACK (lact->data)))); + g_closure_invoke (lact->data, NULL, 1, params, NULL); + g_value_unset (¶ms[0]); + } else { + ACTIONS_UNLOCK (comp); + } +} + +static void +_start_task (NleComposition * comp) +{ + GstTask *task; + + ACTIONS_LOCK (comp); + comp->priv->running = TRUE; + ACTIONS_UNLOCK (comp); + + GST_OBJECT_LOCK (comp); + + task = comp->task; + if (task == NULL) { + gchar *taskname = + g_strdup_printf ("%s_update_management", GST_OBJECT_NAME (comp)); + + task = gst_task_new ((GstTaskFunction) _execute_actions, comp, NULL); + gst_object_set_name (GST_OBJECT_CAST (task), taskname); + gst_task_set_lock (task, GET_TASK_LOCK (comp)); + GST_DEBUG_OBJECT (comp, "created task %p", task); + comp->task = task; + } + + gst_task_set_state (task, GST_TASK_STARTED); + GST_OBJECT_UNLOCK (comp); +} + +static gboolean +_stop_task (NleComposition * comp) +{ + gboolean res = TRUE; + GstTask *task; + + GST_INFO_OBJECT (comp, "Stoping children management task"); + + ACTIONS_LOCK (comp); + comp->priv->running = FALSE; + + /* Make sure we do not stay blocked trying to execute an action */ + SIGNAL_NEW_ACTION (comp); + ACTIONS_UNLOCK (comp); + + GST_DEBUG_OBJECT (comp, "stop task"); + + GST_OBJECT_LOCK (comp); + task = comp->task; + if (task == NULL) + goto no_task; + comp->task = NULL; + res = gst_task_set_state (task, GST_TASK_STOPPED); + GST_OBJECT_UNLOCK (comp); + + if (!gst_task_join (task)) + goto join_failed; + + gst_object_unref (task); + + return res; + +no_task: + { + GST_OBJECT_UNLOCK (comp); + + /* this is not an error */ + return TRUE; + } +join_failed: + { + /* this is bad, possibly the application tried to join the task from + * the task's thread. We install the task again so that it will be stopped + * again from the right thread next time hopefully. */ + GST_OBJECT_LOCK (comp); + GST_DEBUG_OBJECT (comp, "join failed"); + /* we can only install this task if there was no other task */ + if (comp->task == NULL) + comp->task = task; + GST_OBJECT_UNLOCK (comp); + + return FALSE; + } + + return res; +} + +static void +_post_start_composition_update (NleComposition * comp, + gint32 seqnum, NleUpdateStackReason reason) +{ + GstMessage *msg; + + msg = gst_message_new_element (GST_OBJECT (comp), + gst_structure_new ("NleCompositionStartUpdate", + "reason", G_TYPE_STRING, UPDATE_PIPELINE_REASONS[reason], NULL)); + + gst_message_set_seqnum (msg, seqnum); + gst_element_post_message (GST_ELEMENT (comp), msg); +} + +static void +_post_start_composition_update_done (NleComposition * comp, + gint32 seqnum, NleUpdateStackReason reason) +{ + GstMessage *msg = gst_message_new_element (GST_OBJECT (comp), + gst_structure_new ("NleCompositionUpdateDone", + "reason", G_TYPE_STRING, UPDATE_PIPELINE_REASONS[reason], + NULL)); + + gst_message_set_seqnum (msg, seqnum); + gst_element_post_message (GST_ELEMENT (comp), msg); +} + +static void +_seek_pipeline_func (NleComposition * comp, SeekData * seekd) +{ + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType cur_type, stop_type; + gint64 cur, stop; + NleCompositionPrivate *priv = comp->priv; + + gst_event_parse_seek (seekd->event, &rate, &format, &flags, + &cur_type, &cur, &stop_type, &stop); + + GST_DEBUG_OBJECT (seekd->comp, + "start:%" GST_TIME_FORMAT " -- stop:%" GST_TIME_FORMAT " flags:%d", + GST_TIME_ARGS (cur), GST_TIME_ARGS (stop), flags); + + gst_segment_do_seek (priv->segment, + rate, format, flags, cur_type, cur, stop_type, stop, NULL); + gst_segment_do_seek (priv->outside_segment, + rate, format, flags, cur_type, cur, stop_type, stop, NULL); + + GST_DEBUG_OBJECT (seekd->comp, "Segment now has flags:%d", + priv->segment->flags); + + if (priv->segment->start >= NLE_OBJECT_STOP (seekd->comp)) { + GST_INFO_OBJECT (seekd->comp, + "Start %" GST_TIME_FORMAT " > comp->stop: %" GST_TIME_FORMAT + " Not seeking", GST_TIME_ARGS (priv->segment->start), + GST_TIME_ARGS (NLE_OBJECT_STOP (seekd->comp))); + GST_FIXME_OBJECT (seekd->comp, "HANDLE error async!"); + return; + } + + _post_start_composition_update (seekd->comp, + gst_event_get_seqnum (seekd->event), COMP_UPDATE_STACK_ON_SEEK); + + /* crop the segment start/stop values */ + /* Only crop segment start value if we don't have a default object */ + if (priv->expandables == NULL) + priv->segment->start = + MAX (priv->segment->start, NLE_OBJECT_START (seekd->comp)); + priv->segment->stop = + MIN (priv->segment->stop, NLE_OBJECT_STOP (seekd->comp)); + + priv->next_base_time = 0; + + seek_handling (seekd->comp, gst_event_get_seqnum (seekd->event), + COMP_UPDATE_STACK_ON_SEEK); + + _post_start_composition_update_done (seekd->comp, + gst_event_get_seqnum (seekd->event), COMP_UPDATE_STACK_ON_SEEK); +} + +/* Must be called with OBJECTS_LOCK taken */ +static void +_process_pending_entries (NleComposition * comp) +{ + NleObject *object; + GHashTableIter iter; + gboolean deactivated_stack = FALSE; + + NleCompositionPrivate *priv = comp->priv; + + g_hash_table_iter_init (&iter, priv->pending_io); + while (g_hash_table_iter_next (&iter, (gpointer *) & object, NULL)) { + if (g_hash_table_contains (priv->objects_hash, object)) { + + if (GST_OBJECT_PARENT (object) == GST_OBJECT_CAST (priv->current_bin) && + deactivated_stack == FALSE) { + deactivated_stack = TRUE; + + _deactivate_stack (comp, TRUE); + } + + _nle_composition_remove_object (comp, object); + } else { + _nle_composition_add_object (comp, object); + } + } + + g_hash_table_remove_all (priv->pending_io); +} + + +static inline gboolean +_commit_values (NleComposition * comp) +{ + GList *tmp; + gboolean commited = FALSE; + NleCompositionPrivate *priv = comp->priv; + + for (tmp = priv->objects_start; tmp; tmp = tmp->next) { + if (nle_object_commit (tmp->data, TRUE)) + commited = TRUE; + } + + GST_DEBUG_OBJECT (comp, "Linking up commit vmethod"); + commited |= NLE_OBJECT_CLASS (parent_class)->commit (NLE_OBJECT (comp), TRUE); + + return commited; +} + +static gboolean +_commit_all_values (NleComposition * comp) +{ + NleCompositionPrivate *priv = comp->priv; + + priv->next_base_time = 0; + + _process_pending_entries (comp); + + if (_commit_values (comp) == FALSE) { + + return FALSE;; + } + + /* The topology of the composition might have changed, update the lists */ + priv->objects_start = g_list_sort + (priv->objects_start, (GCompareFunc) objects_start_compare); + priv->objects_stop = g_list_sort + (priv->objects_stop, (GCompareFunc) objects_stop_compare); + + return TRUE; +} + +static gboolean +_initialize_stack_func (NleComposition * comp, UpdateCompositionData * ucompo) +{ + NleCompositionPrivate *priv = comp->priv; + + + _post_start_composition_update (comp, ucompo->seqnum, ucompo->reason); + + _commit_all_values (comp); + update_start_stop_duration (comp); + comp->priv->next_base_time = 0; + /* set ghostpad target */ + if (!(update_pipeline (comp, COMP_REAL_START (comp), + ucompo->seqnum, COMP_UPDATE_STACK_INITIALIZE))) { + GST_FIXME_OBJECT (comp, "PLEASE signal state change failure ASYNC"); + } + + _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason); + priv->initialized = TRUE; + + return G_SOURCE_REMOVE; +} + +static void +_remove_object_func (NleComposition * comp, ChildIOData * childio) +{ + NleObject *object = childio->object; + + NleCompositionPrivate *priv = comp->priv; + NleObject *in_pending_io; + + in_pending_io = g_hash_table_lookup (priv->pending_io, object); + + if (!g_hash_table_contains (priv->objects_hash, object)) { + if (in_pending_io) { + GST_INFO_OBJECT (comp, "Object %" GST_PTR_FORMAT " was marked" + " for addition, removing it from the addition list", object); + + g_hash_table_remove (priv->pending_io, object); + return; + } + + GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is " + " not in the composition", object); + + return; + } + + if (in_pending_io) { + GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked" + " for removal", object); + + return; + } + + g_hash_table_add (priv->pending_io, object); + + return; +} + +static void +_add_remove_object_action (NleComposition * comp, NleObject * object) +{ + ChildIOData *childio = g_slice_new0 (ChildIOData); + + GST_DEBUG_OBJECT (comp, "Adding Action"); + + childio->comp = comp; + childio->object = object; + + _add_action (comp, G_CALLBACK (_remove_object_func), + childio, G_PRIORITY_DEFAULT); +} + +static void +_add_object_func (NleComposition * comp, ChildIOData * childio) +{ + NleObject *object = childio->object; + NleCompositionPrivate *priv = comp->priv; + NleObject *in_pending_io; + + in_pending_io = g_hash_table_lookup (priv->pending_io, object); + + if (g_hash_table_contains (priv->objects_hash, object)) { + + if (in_pending_io) { + GST_INFO_OBJECT (comp, "Object already in but marked in pendings" + " removing from pendings"); + g_hash_table_remove (priv->pending_io, object); + + return; + } + GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is " + " already in the composition", object); + + return; + } + + if (in_pending_io) { + GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked" + " for addition", object); + + return; + } + + + g_hash_table_add (priv->pending_io, object); +} + +static void +_add_add_object_action (NleComposition * comp, NleObject * object) +{ + ChildIOData *childio = g_slice_new0 (ChildIOData); + + GST_DEBUG_OBJECT (comp, "Adding Action"); + + childio->comp = comp; + childio->object = object; + + _add_action (comp, G_CALLBACK (_add_object_func), childio, + G_PRIORITY_DEFAULT); +} + +static void +_free_action (Action * action, gpointer udata) +{ + if (ACTION_CALLBACK (action) == _seek_pipeline_func) { + SeekData *seekd = (SeekData *) udata; + + gst_event_unref (seekd->event); + g_slice_free (SeekData, seekd); + } else if (ACTION_CALLBACK (action) == _remove_object_func || + ACTION_CALLBACK (action) == _add_object_func) { + g_slice_free (ChildIOData, udata); + } else if (ACTION_CALLBACK (action) == _update_pipeline_func || + ACTION_CALLBACK (action) == _commit_func || + ACTION_CALLBACK (action) == _initialize_stack_func) { + g_slice_free (UpdateCompositionData, udata); + } +} + +static void +_add_action (NleComposition * comp, GCallback func, + gpointer data, gint priority) +{ + Action *action; + NleCompositionPrivate *priv = comp->priv; + + + action = (Action *) g_closure_new_simple (sizeof (Action), data); + g_closure_add_finalize_notifier ((GClosure *) action, data, + (GClosureNotify) _free_action); + ACTION_CALLBACK (action) = func; + action->priority = priority; + g_closure_set_marshal ((GClosure *) action, g_cclosure_marshal_VOID__VOID); + + ACTIONS_LOCK (comp); + GST_INFO_OBJECT (comp, "Adding Action for function: %p:%s", + action, GST_DEBUG_FUNCPTR_NAME (func)); + + if (func == G_CALLBACK (_emit_commited_signal_func)) + priv->actions = g_list_prepend (priv->actions, action); + else + priv->actions = g_list_append (priv->actions, action); + + SIGNAL_NEW_ACTION (comp); + ACTIONS_UNLOCK (comp); +} + +static void +_add_seek_action (NleComposition * comp, GstEvent * event) +{ + SeekData *seekd = g_slice_new0 (SeekData); + + GST_DEBUG_OBJECT (comp, "Adding Action"); + + seekd->comp = comp; + seekd->event = event; + + comp->priv->next_eos_seqnum = 0; + comp->priv->real_eos_seqnum = 0; + _add_action (comp, G_CALLBACK (_seek_pipeline_func), seekd, + G_PRIORITY_DEFAULT); +} + +static void +_remove_update_actions (NleComposition * comp) +{ + _remove_actions_for_type (comp, G_CALLBACK (_update_pipeline_func)); +} + +static void +_remove_seek_actions (NleComposition * comp) +{ + _remove_actions_for_type (comp, G_CALLBACK (_seek_pipeline_func)); +} + +static void +_add_update_compo_action (NleComposition * comp, + GCallback callback, NleUpdateStackReason reason) +{ + UpdateCompositionData *ucompo = g_slice_new0 (UpdateCompositionData); + + ucompo->comp = comp; + ucompo->reason = reason; + ucompo->seqnum = gst_util_seqnum_next (); + + GST_INFO_OBJECT (comp, "Updating because: %s -- Setting seqnum: %i", + UPDATE_PIPELINE_REASONS[reason], ucompo->seqnum); + + _add_action (comp, callback, ucompo, G_PRIORITY_DEFAULT); +} + +static void +nle_composition_handle_message (GstBin * bin, GstMessage * message) +{ + NleComposition *comp = (NleComposition *) bin; + + if (comp->priv->tearing_down_stack) { + if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) { + GST_FIXME_OBJECT (comp, "Dropping %" GST_PTR_FORMAT " message from " + " %" GST_PTR_FORMAT " being teared down to READY", + message, GST_MESSAGE_SRC (message)); + } + + GST_DEBUG_OBJECT (comp, "Dropping message %" GST_PTR_FORMAT " from " + "object being teared down to READY!", message); + gst_message_unref (message); + + return; + } + + GST_BIN_CLASS (parent_class)->handle_message (bin, message); +} + +static void +nle_composition_class_init (NleCompositionClass * 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 (NleCompositionPrivate)); + + gst_element_class_set_static_metadata (gstelement_class, + "GNonLin Composition", "Filter/Editor", "Combines NLE objects", + "Wim Taymans , Edward Hervey ," + " Mathieu Duponchelle ," + " Thibault Saunier "); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_composition_dispose); + gobject_class->finalize = GST_DEBUG_FUNCPTR (nle_composition_finalize); + + gstelement_class->change_state = nle_composition_change_state; + + gstbin_class->add_element = GST_DEBUG_FUNCPTR (nle_composition_add_object); + gstbin_class->remove_element = + GST_DEBUG_FUNCPTR (nle_composition_remove_object); + gstbin_class->handle_message = + GST_DEBUG_FUNCPTR (nle_composition_handle_message); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&nle_composition_src_template)); + + /* Get the paramspec of the NleObject klass so we can do + * fast notifies */ + nleobject_properties[NLEOBJECT_PROP_START] = + g_object_class_find_property (gobject_class, "start"); + nleobject_properties[NLEOBJECT_PROP_STOP] = + g_object_class_find_property (gobject_class, "stop"); + nleobject_properties[NLEOBJECT_PROP_DURATION] = + g_object_class_find_property (gobject_class, "duration"); + + _signals[COMMITED_SIGNAL] = + g_signal_new ("commited", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, + G_TYPE_BOOLEAN); + + /** + * NleComposition::query-position + * @comp: a #NleComposition + * + * A signal that *has* to be connected and which should return the current + * position of the pipeline. + * + * This signal is used in order to know the current position of the whole + * pipeline so it is user's responsability to give that answer as there + * is no other way to precisely know the position in the whole pipeline. + */ + _signals[QUERY_POSITION_SIGNAL] = + g_signal_new ("query-position", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, + 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_UINT64, 0, NULL); + + GST_DEBUG_REGISTER_FUNCPTR (_seek_pipeline_func); + GST_DEBUG_REGISTER_FUNCPTR (_remove_object_func); + GST_DEBUG_REGISTER_FUNCPTR (_add_object_func); + GST_DEBUG_REGISTER_FUNCPTR (_update_pipeline_func); + GST_DEBUG_REGISTER_FUNCPTR (_commit_func); + GST_DEBUG_REGISTER_FUNCPTR (_emit_commited_signal_func); + GST_DEBUG_REGISTER_FUNCPTR (_initialize_stack_func); + + /* Just be useless, so the compiler does not warn us + * about our uselessness */ + nleobject_class->commit = nle_composition_commit_func; + +} + +static void +nle_composition_init (NleComposition * comp) +{ + NleCompositionPrivate *priv; + + GST_OBJECT_FLAG_SET (comp, NLE_OBJECT_SOURCE); + GST_OBJECT_FLAG_SET (comp, NLE_OBJECT_COMPOSITION); + + priv = G_TYPE_INSTANCE_GET_PRIVATE (comp, NLE_TYPE_COMPOSITION, + NleCompositionPrivate); + priv->objects_start = NULL; + priv->objects_stop = NULL; + + priv->segment = gst_segment_new (); + priv->outside_segment = gst_segment_new (); + + g_rec_mutex_init (&comp->task_rec_lock); + + priv->objects_hash = g_hash_table_new (g_direct_hash, g_direct_equal); + + g_mutex_init (&priv->actions_lock); + g_cond_init (&priv->actions_cond); + + priv->pending_io = g_hash_table_new (g_direct_hash, g_direct_equal); + + comp->priv = priv; + + priv->current_bin = gst_bin_new ("current-bin"); + gst_bin_add (GST_BIN (comp), priv->current_bin); + + nle_composition_reset (comp); + + priv->nle_event_pad_func = GST_PAD_EVENTFUNC (NLE_OBJECT_SRC (comp)); + gst_pad_set_event_function (NLE_OBJECT_SRC (comp), + GST_DEBUG_FUNCPTR (nle_composition_event_handler)); +} + +static void +nle_composition_dispose (GObject * object) +{ + GList *iter; + NleComposition *comp = NLE_COMPOSITION (object); + NleCompositionPrivate *priv = comp->priv; + + if (priv->dispose_has_run) + return; + + priv->dispose_has_run = TRUE; + + iter = priv->objects_start; + while (iter) { + GList *next = iter->next; + + _nle_composition_remove_object (comp, iter->data); + iter = next; + } + + g_list_free (priv->objects_stop); + + if (priv->expandables) { + GList *iter; + + iter = priv->expandables; + + while (iter) { + GList *next = iter->next; + + _nle_composition_remove_object (comp, iter->data); + iter = next; + } + + priv->expandables = NULL; + } + + nle_composition_reset_target_pad (comp); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +nle_composition_finalize (GObject * object) +{ + NleComposition *comp = NLE_COMPOSITION (object); + NleCompositionPrivate *priv = comp->priv; + + _assert_proper_thread (comp); + + if (priv->current) { + g_node_destroy (priv->current); + priv->current = NULL; + } + + g_hash_table_destroy (priv->objects_hash); + + gst_segment_free (priv->segment); + gst_segment_free (priv->outside_segment); + + g_rec_mutex_clear (&comp->task_rec_lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); + + g_mutex_clear (&priv->actions_lock); + g_cond_clear (&priv->actions_cond); +} + +/* signal_duration_change + * Creates a new GST_MESSAGE_DURATION_CHANGED with the currently configured + * composition duration and sends that on the bus. + */ +static inline void +signal_duration_change (NleComposition * comp) +{ + gst_element_post_message (GST_ELEMENT_CAST (comp), + gst_message_new_duration_changed (GST_OBJECT_CAST (comp))); +} + +static gboolean +_remove_child (GValue * item, GValue * ret G_GNUC_UNUSED, GstBin * bin) +{ + GstElement *child = g_value_get_object (item); + + if (NLE_IS_OPERATION (child)) + nle_operation_hard_cleanup (NLE_OPERATION (child)); + + + gst_bin_remove (bin, child); + + return TRUE; +} + +static void +_empty_bin (GstBin * bin) +{ + GstIterator *children; + + children = gst_bin_iterate_elements (bin); + + while (G_UNLIKELY (gst_iterator_fold (children, + (GstIteratorFoldFunction) _remove_child, NULL, + bin) == GST_ITERATOR_RESYNC)) { + gst_iterator_resync (children); + } + + gst_iterator_free (children); +} + +static void +nle_composition_reset (NleComposition * comp) +{ + NleCompositionPrivate *priv = comp->priv; + + GST_DEBUG_OBJECT (comp, "resetting"); + + _assert_proper_thread (comp); + + priv->segment_start = GST_CLOCK_TIME_NONE; + priv->segment_stop = GST_CLOCK_TIME_NONE; + priv->next_base_time = 0; + + gst_segment_init (priv->segment, GST_FORMAT_TIME); + gst_segment_init (priv->outside_segment, GST_FORMAT_TIME); + + if (priv->current) + g_node_destroy (priv->current); + priv->current = NULL; + + nle_composition_reset_target_pad (comp); + + priv->initialized = FALSE; + priv->send_stream_start = TRUE; + priv->real_eos_seqnum = 0; + priv->next_eos_seqnum = 0; + priv->flush_seqnum = 0; + + _empty_bin (GST_BIN_CAST (priv->current_bin)); + + GST_DEBUG_OBJECT (comp, "Composition now resetted"); +} + +static GstPadProbeReturn +ghost_event_probe_handler (GstPad * ghostpad G_GNUC_UNUSED, + GstPadProbeInfo * info, NleComposition * comp) +{ + GstPadProbeReturn retval = GST_PAD_PROBE_OK; + NleCompositionPrivate *priv = comp->priv; + GstEvent *event; + + if (GST_IS_BUFFER (info->data)) { + if (priv->waiting_for_buffer) { + GST_INFO_OBJECT (comp, "update_pipeline DONE"); + _restart_task (comp); + } + + return GST_PAD_PROBE_OK; + } + + event = GST_PAD_PROBE_INFO_EVENT (info); + + GST_DEBUG_OBJECT (comp, "event: %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + if (_is_ready_to_restart_task (comp, event)) + _restart_task (comp); + + if (gst_event_get_seqnum (event) != comp->priv->flush_seqnum) { + GST_INFO_OBJECT (comp, "Dropping flush stop %d -- %d", + gst_event_get_seqnum (event), priv->seqnum_to_restart_task); + retval = GST_PAD_PROBE_DROP; + } else { + GST_INFO_OBJECT (comp, "Forwarding our flush stop with seqnum %i", + comp->priv->flush_seqnum); + gst_event_unref (event); + event = gst_event_new_flush_stop (TRUE); + GST_PAD_PROBE_INFO_DATA (info) = event; + gst_event_set_seqnum (event, comp->priv->flush_seqnum); + comp->priv->flush_seqnum = 0; + } + break; + case GST_EVENT_FLUSH_START: + if (gst_event_get_seqnum (event) != comp->priv->flush_seqnum) { + GST_INFO_OBJECT (comp, "Dropping flush start"); + retval = GST_PAD_PROBE_DROP; + } else { + GST_INFO_OBJECT (comp, "Forwarding our flush start with seqnum %i", + comp->priv->flush_seqnum); + } + break; + case GST_EVENT_STREAM_START: + if (g_atomic_int_compare_and_exchange (&priv->send_stream_start, TRUE, + FALSE)) { + /* FIXME: Do we want to create a new stream ID here? */ + GST_DEBUG_OBJECT (comp, "forward stream-start %p", event); + } else { + GST_DEBUG_OBJECT (comp, "dropping stream-start %p", event); + retval = GST_PAD_PROBE_DROP; + } + break; + case GST_EVENT_SEGMENT: + { + guint64 rstart, rstop; + const GstSegment *segment; + GstSegment copy; + GstEvent *event2; + /* next_base_time */ + + if (_is_ready_to_restart_task (comp, event)) + _restart_task (comp); + + gst_event_parse_segment (event, &segment); + gst_segment_copy_into (segment, ©); + + rstart = + gst_segment_to_running_time (segment, GST_FORMAT_TIME, + segment->start); + rstop = + gst_segment_to_running_time (segment, GST_FORMAT_TIME, segment->stop); + copy.base = comp->priv->next_base_time; + GST_DEBUG_OBJECT (comp, + "Updating base time to %" GST_TIME_FORMAT ", next:%" GST_TIME_FORMAT, + GST_TIME_ARGS (comp->priv->next_base_time), + GST_TIME_ARGS (comp->priv->next_base_time + rstop - rstart)); + comp->priv->next_base_time += rstop - rstart; + + event2 = gst_event_new_segment (©); + GST_EVENT_SEQNUM (event2) = GST_EVENT_SEQNUM (event); + + GST_PAD_PROBE_INFO_DATA (info) = event2; + gst_event_unref (event); + } + break; + case GST_EVENT_EOS: + { + gint seqnum = gst_event_get_seqnum (event); + + GST_INFO_OBJECT (comp, "Got EOS, last EOS seqnum id : %i current " + "seq num is: %i", comp->priv->real_eos_seqnum, seqnum); + + if (_is_ready_to_restart_task (comp, event)) { + GST_INFO_OBJECT (comp, "We got an EOS right after seeing the right" + " segment, restarting task"); + + _restart_task (comp); + } + + if (g_atomic_int_compare_and_exchange (&comp->priv->real_eos_seqnum, + seqnum, 1)) { + + GST_INFO_OBJECT (comp, "Got EOS for real, seq ID is %i, fowarding it", + seqnum); + + return GST_PAD_PROBE_OK; + } + + if (priv->next_eos_seqnum == seqnum) + _add_update_compo_action (comp, G_CALLBACK (_update_pipeline_func), + COMP_UPDATE_STACK_ON_EOS); + else + GST_INFO_OBJECT (comp, + "Got an EOS but it seqnum %i != next eos seqnum %i", seqnum, + priv->next_eos_seqnum); + + retval = GST_PAD_PROBE_DROP; + } + break; + default: + break; + } + + return retval; +} + +static gint +priority_comp (NleObject * a, NleObject * b) +{ + if (a->priority < b->priority) + return -1; + + if (a->priority > b->priority) + return 1; + + return 0; +} + +static inline gboolean +have_to_update_pipeline (NleComposition * comp, + NleUpdateStackReason update_stack_reason) +{ + NleCompositionPrivate *priv = comp->priv; + + if (update_stack_reason == COMP_UPDATE_STACK_ON_EOS) + return TRUE; + + GST_DEBUG_OBJECT (comp, + "segment[%" GST_TIME_FORMAT "--%" GST_TIME_FORMAT "] current[%" + GST_TIME_FORMAT "--%" GST_TIME_FORMAT "]", + GST_TIME_ARGS (priv->segment->start), + GST_TIME_ARGS (priv->segment->stop), + GST_TIME_ARGS (priv->segment_start), GST_TIME_ARGS (priv->segment_stop)); + + if (priv->segment->start < priv->segment_start) + return TRUE; + + if (priv->segment->start >= priv->segment_stop) + return TRUE; + + return FALSE; +} + +static gboolean +nle_composition_commit_func (NleObject * object, gboolean recurse) +{ + _add_update_compo_action (NLE_COMPOSITION (object), + G_CALLBACK (_commit_func), COMP_UPDATE_STACK_ON_COMMIT); + + return TRUE; +} + +/* + * get_new_seek_event: + * + * Returns a seek event for the currently configured segment + * and start/stop values + * + * The GstSegment and segment_start|stop must have been configured + * before calling this function. + */ +static GstEvent * +get_new_seek_event (NleComposition * comp, gboolean initial, + gboolean updatestoponly) +{ + GstSeekFlags flags = GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH; + gint64 start, stop; + GstSeekType starttype = GST_SEEK_TYPE_SET; + NleCompositionPrivate *priv = comp->priv; + + GST_DEBUG_OBJECT (comp, "initial:%d", initial); + /* remove the seek flag */ + if (!initial) + flags |= (GstSeekFlags) priv->segment->flags; + + GST_DEBUG_OBJECT (comp, + "private->segment->start:%" GST_TIME_FORMAT " segment_start%" + GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->start), + GST_TIME_ARGS (priv->segment_start)); + + GST_DEBUG_OBJECT (comp, + "private->segment->stop:%" GST_TIME_FORMAT " segment_stop%" + GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->stop), + GST_TIME_ARGS (priv->segment_stop)); + + start = GST_CLOCK_TIME_IS_VALID (priv->segment->start) + ? MAX (priv->segment->start, priv->segment_start) + : priv->segment_start; + stop = GST_CLOCK_TIME_IS_VALID (priv->segment->stop) + ? MIN (priv->segment->stop, priv->segment_stop) + : priv->segment_stop; + + if (updatestoponly) { + starttype = GST_SEEK_TYPE_NONE; + start = GST_CLOCK_TIME_NONE; + } + + GST_DEBUG_OBJECT (comp, + "Created new seek event. Flags:%d, start:%" GST_TIME_FORMAT ", stop:%" + GST_TIME_FORMAT ", rate:%lf", flags, GST_TIME_ARGS (start), + GST_TIME_ARGS (stop), priv->segment->rate); + + return gst_event_new_seek (priv->segment->rate, + priv->segment->format, flags, starttype, start, GST_SEEK_TYPE_SET, stop); +} + +/* OBJECTS LOCK must be taken when calling this ! */ +static GstClockTime +get_current_position (NleComposition * comp) +{ + GstPad *pad; + NleObject *obj; + NleCompositionPrivate *priv = comp->priv; + gboolean res; + gint64 value = GST_CLOCK_TIME_NONE; + + GstPad *peer; + + g_signal_emit (comp, _signals[QUERY_POSITION_SIGNAL], 0, &value); + + if (value >= 0) { + GST_DEBUG_OBJECT (comp, "Got position %" GST_TIME_FORMAT, + GST_TIME_ARGS (value)); + + return value; + } + + /* Try querying position downstream */ + peer = gst_pad_get_peer (NLE_OBJECT (comp)->srcpad); + if (peer) { + res = gst_pad_query_position (peer, GST_FORMAT_TIME, &value); + gst_object_unref (peer); + + if (res) { + GST_DEBUG_OBJECT (comp, + "Successfully got downstream position %" GST_TIME_FORMAT, + GST_TIME_ARGS ((guint64) value)); + goto beach; + } + } + + GST_DEBUG_OBJECT (comp, "Downstream position query failed"); + + /* resetting format/value */ + value = GST_CLOCK_TIME_NONE; + + /* If downstream fails , try within the current stack */ + if (!priv->current) { + GST_DEBUG_OBJECT (comp, "No current stack, can't send query"); + goto beach; + } + + obj = (NleObject *) priv->current->data; + + pad = NLE_OBJECT_SRC (obj); + res = gst_pad_query_position (pad, GST_FORMAT_TIME, &value); + + if (G_UNLIKELY (res == FALSE)) { + GST_WARNING_OBJECT (comp, "query position failed"); + value = GST_CLOCK_TIME_NONE; + } else { + GST_LOG_OBJECT (comp, "Query returned %" GST_TIME_FORMAT, + GST_TIME_ARGS ((guint64) value)); + } + +beach: + + if (!GST_CLOCK_TIME_IS_VALID (value)) { + if (GST_CLOCK_TIME_IS_VALID (comp->priv->segment_start)) { + value = comp->priv->segment_start; + } else { + GST_INFO_OBJECT (comp, "Current position is unknown, " "setting it to 0"); + + value = 0; + } + } + + return (guint64) value; +} + +/* WITH OBJECTS LOCK TAKEN */ +static gboolean +update_base_time (GNode * node, GstClockTime * timestamp) +{ + if (NLE_IS_OPERATION (node->data)) + nle_operation_update_base_time (NLE_OPERATION (node->data), *timestamp); + + return FALSE; +} + +/* WITH OBJECTS LOCK TAKEN */ +static void +update_operations_base_time (NleComposition * comp, gboolean reverse) +{ + GstClockTime timestamp; + + if (reverse) + timestamp = comp->priv->segment->stop; + else + timestamp = comp->priv->segment->start; + + g_node_traverse (comp->priv->current, G_IN_ORDER, G_TRAVERSE_ALL, -1, + (GNodeTraverseFunc) update_base_time, ×tamp); +} + +/* WITH OBJECTS LOCK TAKEN */ +static gboolean +_seek_current_stack (NleComposition * comp, GstEvent * event, + gboolean flush_downstream) +{ + gboolean res; + NleCompositionPrivate *priv = comp->priv; + GstPad *peer = gst_pad_get_peer (NLE_OBJECT_SRC (comp)); + + GST_INFO_OBJECT (comp, "Seeking itself %" GST_PTR_FORMAT, event); + + if (flush_downstream) { + priv->flush_seqnum = gst_event_get_seqnum (event); + GST_INFO_OBJECT (comp, "sending flushes downstream with seqnum %d", + priv->flush_seqnum); + } + + priv->seeking_itself = TRUE; + res = gst_pad_push_event (peer, event); + priv->seeking_itself = FALSE; + gst_object_unref (peer); + + GST_DEBUG_OBJECT (comp, "Done seeking"); + + return res; +} + +/* + Figures out if pipeline needs updating. + Updates it and sends the seek event. + Sends flush events downstream if needed. + can be called by user_seek or segment_done + + update_stack_reason: The reason for which we need to handle 'seek' +*/ + +static gboolean +seek_handling (NleComposition * comp, gint32 seqnum, + NleUpdateStackReason update_stack_reason) +{ + GST_DEBUG_OBJECT (comp, "Seek hanlding update pipeline reason: %s", + UPDATE_PIPELINE_REASONS[update_stack_reason]); + + if (have_to_update_pipeline (comp, update_stack_reason)) { + if (comp->priv->segment->rate >= 0.0) + update_pipeline (comp, comp->priv->segment->start, seqnum, + update_stack_reason); + else + update_pipeline (comp, comp->priv->segment->stop, seqnum, + update_stack_reason); + } else { + GstEvent *toplevel_seek = get_new_seek_event (comp, FALSE, FALSE); + + gst_event_set_seqnum (toplevel_seek, seqnum); + _set_real_eos_seqnum_from_seek (comp, toplevel_seek); + + _remove_update_actions (comp); + update_operations_base_time (comp, !(comp->priv->segment->rate >= 0.0)); + _seek_current_stack (comp, toplevel_seek, + _have_to_flush_downstream (update_stack_reason)); + } + + return TRUE; +} + +static gboolean +nle_composition_event_handler (GstPad * ghostpad, GstObject * parent, + GstEvent * event) +{ + NleComposition *comp = (NleComposition *) parent; + NleCompositionPrivate *priv = comp->priv; + gboolean res = TRUE; + + GST_DEBUG_OBJECT (comp, "event type:%s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + if (!priv->seeking_itself) { + _add_seek_action (comp, event); + event = NULL; + GST_FIXME_OBJECT (comp, "HANDLE seeking errors!"); + + return TRUE; + } + break; + } + case GST_EVENT_QOS: + { + gdouble prop; + GstQOSType qostype; + GstClockTimeDiff diff; + GstClockTime timestamp; + + gst_event_parse_qos (event, &qostype, &prop, &diff, ×tamp); + + GST_DEBUG_OBJECT (comp, + "timestamp:%" GST_TIME_FORMAT " segment.start:%" GST_TIME_FORMAT + " segment.stop:%" GST_TIME_FORMAT " segment_start%" GST_TIME_FORMAT + " segment_stop:%" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp), + GST_TIME_ARGS (priv->outside_segment->start), + GST_TIME_ARGS (priv->outside_segment->stop), + GST_TIME_ARGS (priv->segment_start), + GST_TIME_ARGS (priv->segment_stop)); + + /* The problem with QoS events is the following: + * At each new internal segment (i.e. when we re-arrange our internal + * elements) we send flushing seeks to those elements (to properly + * configure their playback range) but don't let the FLUSH events get + * downstream. + * + * The problem is that the QoS running timestamps we receive from + * downstream will not have taken into account those flush. + * + * What we need to do is to translate to our internal running timestamps + * which for each configured segment starts at 0 for those elements. + * + * The generic algorithm for the incoming running timestamp translation + * is therefore: + * (original_seek_time : original seek position received from usptream) + * (current_segment_start : Start position of the currently configured + * timeline segment) + * + * difference = original_seek_time - current_segment_start + * new_qos_position = upstream_qos_position - difference + * + * The new_qos_position is only valid when: + * * it applies to the current segment (difference > 0) + * * The QoS difference + timestamp is greater than the difference + * + */ + + if (GST_CLOCK_TIME_IS_VALID (priv->outside_segment->start)) { + GstClockTimeDiff curdiff; + + /* We'll either create a new event or discard it */ + gst_event_unref (event); + + if (priv->segment->rate < 0.0) + curdiff = priv->outside_segment->stop - priv->segment_stop; + else + curdiff = priv->segment_start - priv->outside_segment->start; + GST_DEBUG ("curdiff %" GST_TIME_FORMAT, GST_TIME_ARGS (curdiff)); + if ((curdiff != 0) && ((timestamp < curdiff) + || (curdiff > timestamp + diff))) { + GST_DEBUG_OBJECT (comp, + "QoS event outside of current segment, discarding"); + /* The QoS timestamp is before the currently set-up pipeline */ + goto beach; + } + + /* Substract the amount of running time we've already outputted + * until the currently configured pipeline from the QoS timestamp.*/ + timestamp -= curdiff; + GST_DEBUG_OBJECT (comp, + "Creating new QoS event with timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp)); + event = gst_event_new_qos (qostype, prop, diff, timestamp); + } + break; + } + default: + break; + } + + if (res) { + GST_DEBUG_OBJECT (comp, "About to call nle_event_pad_func: %p", + priv->nle_event_pad_func); + res = priv->nle_event_pad_func (NLE_OBJECT (comp)->srcpad, parent, event); + GST_DEBUG_OBJECT (comp, "Done calling nle_event_pad_func() %d", res); + } + +beach: + return res; +} + +static inline void +nle_composition_reset_target_pad (NleComposition * comp) +{ + NleCompositionPrivate *priv = comp->priv; + + GST_DEBUG_OBJECT (comp, "Removing ghostpad"); + + if (priv->ghosteventprobe) { + GstPad *target; + + target = gst_ghost_pad_get_target ((GstGhostPad *) NLE_OBJECT_SRC (comp)); + if (target) + gst_pad_remove_probe (target, priv->ghosteventprobe); + priv->ghosteventprobe = 0; + } + + nle_object_ghost_pad_set_target (NLE_OBJECT (comp), + NLE_OBJECT_SRC (comp), NULL); + priv->send_stream_start = TRUE; +} + +/* nle_composition_ghost_pad_set_target: + * target: The target #GstPad. The refcount will be decremented (given to the ghostpad). + */ +static void +nle_composition_ghost_pad_set_target (NleComposition * comp, GstPad * target) +{ + GstPad *ptarget; + NleCompositionPrivate *priv = comp->priv; + + if (target) + GST_DEBUG_OBJECT (comp, "target:%s:%s", GST_DEBUG_PAD_NAME (target)); + else + GST_DEBUG_OBJECT (comp, "Removing target"); + + + ptarget = + gst_ghost_pad_get_target (GST_GHOST_PAD (NLE_OBJECT (comp)->srcpad)); + if (ptarget) { + gst_object_unref (ptarget); + + if (ptarget == target) { + GST_DEBUG_OBJECT (comp, + "Target of srcpad is the same as existing one, not changing"); + return; + } + } + + /* Actually set the target */ + nle_object_ghost_pad_set_target ((NleObject *) comp, + NLE_OBJECT (comp)->srcpad, target); + + if (target && (priv->ghosteventprobe == 0)) { + priv->ghosteventprobe = + gst_pad_add_probe (target, + GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_EVENT_FLUSH | + GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, + (GstPadProbeCallback) ghost_event_probe_handler, comp, NULL); + GST_DEBUG_OBJECT (comp, "added event probe %lu", priv->ghosteventprobe); + } +} + +static void +refine_start_stop_in_region_above_priority (NleComposition * composition, + GstClockTime timestamp, GstClockTime start, + GstClockTime stop, + GstClockTime * rstart, GstClockTime * rstop, guint32 priority) +{ + GList *tmp; + NleObject *object; + GstClockTime nstart = start, nstop = stop; + + GST_DEBUG_OBJECT (composition, + "timestamp:%" GST_TIME_FORMAT " start: %" GST_TIME_FORMAT " stop: %" + GST_TIME_FORMAT " priority:%u", GST_TIME_ARGS (timestamp), + GST_TIME_ARGS (start), GST_TIME_ARGS (stop), priority); + + for (tmp = composition->priv->objects_start; tmp; tmp = tmp->next) { + object = (NleObject *) tmp->data; + + GST_LOG_OBJECT (object, "START %" GST_TIME_FORMAT "--%" GST_TIME_FORMAT, + GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop)); + + if ((object->priority >= priority) || (!NLE_OBJECT_ACTIVE (object))) + continue; + + if (object->start <= timestamp) + continue; + + if (object->start >= nstop) + continue; + + nstop = object->start; + + GST_DEBUG_OBJECT (composition, + "START Found %s [prio:%u] at %" GST_TIME_FORMAT, + GST_OBJECT_NAME (object), object->priority, + GST_TIME_ARGS (object->start)); + + break; + } + + for (tmp = composition->priv->objects_stop; tmp; tmp = tmp->next) { + object = (NleObject *) tmp->data; + + GST_LOG_OBJECT (object, "STOP %" GST_TIME_FORMAT "--%" GST_TIME_FORMAT, + GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop)); + + if ((object->priority >= priority) || (!NLE_OBJECT_ACTIVE (object))) + continue; + + if (object->stop >= timestamp) + continue; + + if (object->stop <= nstart) + continue; + + nstart = object->stop; + + GST_DEBUG_OBJECT (composition, + "STOP Found %s [prio:%u] at %" GST_TIME_FORMAT, + GST_OBJECT_NAME (object), object->priority, + GST_TIME_ARGS (object->start)); + + break; + } + + if (*rstart) + *rstart = nstart; + + if (*rstop) + *rstop = nstop; +} + + +/* + * Converts a sorted list to a tree + * Recursive + * + * stack will be set to the next item to use in the parent. + * If operations number of sinks is limited, it will only use that number. + */ + +static GNode * +convert_list_to_tree (GList ** stack, GstClockTime * start, + GstClockTime * stop, guint32 * highprio) +{ + GNode *ret; + guint nbsinks; + gboolean limit; + GList *tmp; + NleObject *object; + + if (!stack || !*stack) + return NULL; + + object = (NleObject *) (*stack)->data; + + GST_DEBUG ("object:%s , *start:%" GST_TIME_FORMAT ", *stop:%" + GST_TIME_FORMAT " highprio:%d", + GST_ELEMENT_NAME (object), GST_TIME_ARGS (*start), + GST_TIME_ARGS (*stop), *highprio); + + /* update earliest stop */ + if (GST_CLOCK_TIME_IS_VALID (*stop)) { + if (GST_CLOCK_TIME_IS_VALID (object->stop) && (*stop > object->stop)) + *stop = object->stop; + } else { + *stop = object->stop; + } + + if (GST_CLOCK_TIME_IS_VALID (*start)) { + if (GST_CLOCK_TIME_IS_VALID (object->start) && (*start < object->start)) + *start = object->start; + } else { + *start = object->start; + } + + if (NLE_OBJECT_IS_SOURCE (object)) { + *stack = g_list_next (*stack); + + /* update highest priority. + * We do this here, since it's only used with sources (leafs of the tree) */ + if (object->priority > *highprio) + *highprio = object->priority; + + ret = g_node_new (object); + + goto beach; + } else { + /* NleOperation */ + NleOperation *oper = (NleOperation *) object; + + GST_LOG_OBJECT (oper, "operation, num_sinks:%d", oper->num_sinks); + + ret = g_node_new (object); + limit = (oper->dynamicsinks == FALSE); + nbsinks = oper->num_sinks; + + /* FIXME : if num_sinks == -1 : request the proper number of pads */ + for (tmp = g_list_next (*stack); tmp && (!limit || nbsinks);) { + g_node_append (ret, convert_list_to_tree (&tmp, start, stop, highprio)); + if (limit) + nbsinks--; + } + + *stack = tmp; + } + +beach: + GST_DEBUG_OBJECT (object, + "*start:%" GST_TIME_FORMAT " *stop:%" GST_TIME_FORMAT + " priority:%u", GST_TIME_ARGS (*start), GST_TIME_ARGS (*stop), *highprio); + + return ret; +} + +/* + * get_stack_list: + * @comp: The #NleComposition + * @timestamp: The #GstClockTime to look at + * @priority: The priority level to start looking from + * @activeonly: Only look for active elements if TRUE + * @start: The biggest start time of the objects in the stack + * @stop: The smallest stop time of the objects in the stack + * @highprio: The highest priority in the stack + * + * Not MT-safe, you should take the objects lock before calling it. + * Returns: A tree of #GNode sorted in priority order, corresponding + * to the given search arguments. The returned value can be #NULL. + * + * WITH OBJECTS LOCK TAKEN + */ +static GNode * +get_stack_list (NleComposition * comp, GstClockTime timestamp, + guint32 priority, gboolean activeonly, GstClockTime * start, + GstClockTime * stop, guint * highprio) +{ + GList *tmp; + GList *stack = NULL; + GNode *ret = NULL; + GstClockTime nstart = GST_CLOCK_TIME_NONE; + GstClockTime nstop = GST_CLOCK_TIME_NONE; + GstClockTime first_out_of_stack = GST_CLOCK_TIME_NONE; + guint32 highest = 0; + gboolean reverse = (comp->priv->segment->rate < 0.0); + + GST_DEBUG_OBJECT (comp, + "timestamp:%" GST_TIME_FORMAT ", priority:%u, activeonly:%d", + GST_TIME_ARGS (timestamp), priority, activeonly); + + GST_LOG ("objects_start:%p objects_stop:%p", comp->priv->objects_start, + comp->priv->objects_stop); + + if (reverse) { + for (tmp = comp->priv->objects_stop; tmp; tmp = g_list_next (tmp)) { + NleObject *object = (NleObject *) tmp->data; + + GST_LOG_OBJECT (object, + "start: %" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT " , duration:%" + GST_TIME_FORMAT ", priority:%u, active:%d", + GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop), + GST_TIME_ARGS (object->duration), object->priority, object->active); + + if (object->stop >= timestamp) { + if ((object->start < timestamp) && + (object->priority >= priority) && + ((!activeonly) || (NLE_OBJECT_ACTIVE (object)))) { + GST_LOG_OBJECT (comp, "adding %s: sorted to the stack", + GST_OBJECT_NAME (object)); + stack = g_list_insert_sorted (stack, object, + (GCompareFunc) priority_comp); + if (NLE_IS_OPERATION (object)) + nle_operation_update_base_time (NLE_OPERATION (object), timestamp); + } + } else { + GST_LOG_OBJECT (comp, "too far, stopping iteration"); + first_out_of_stack = object->stop; + break; + } + } + } else { + for (tmp = comp->priv->objects_start; tmp; tmp = g_list_next (tmp)) { + NleObject *object = (NleObject *) tmp->data; + + GST_LOG_OBJECT (object, + "start: %" GST_TIME_FORMAT " , stop:%" GST_TIME_FORMAT " , duration:%" + GST_TIME_FORMAT ", priority:%u", GST_TIME_ARGS (object->start), + GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->duration), + object->priority); + + if (object->start <= timestamp) { + if ((object->stop > timestamp) && + (object->priority >= priority) && + ((!activeonly) || (NLE_OBJECT_ACTIVE (object)))) { + GST_LOG_OBJECT (comp, "adding %s: sorted to the stack", + GST_OBJECT_NAME (object)); + stack = g_list_insert_sorted (stack, object, + (GCompareFunc) priority_comp); + if (NLE_IS_OPERATION (object)) + nle_operation_update_base_time (NLE_OPERATION (object), timestamp); + } + } else { + GST_LOG_OBJECT (comp, "too far, stopping iteration"); + first_out_of_stack = object->start; + break; + } + } + } + + /* Insert the expandables */ + if (G_LIKELY (timestamp < NLE_OBJECT_STOP (comp))) + for (tmp = comp->priv->expandables; tmp; tmp = tmp->next) { + GST_DEBUG_OBJECT (comp, "Adding expandable %s sorted to the list", + GST_OBJECT_NAME (tmp->data)); + stack = g_list_insert_sorted (stack, tmp->data, + (GCompareFunc) priority_comp); + if (NLE_IS_OPERATION (tmp->data)) + nle_operation_update_base_time (NLE_OPERATION (tmp->data), timestamp); + } + + /* convert that list to a stack */ + tmp = stack; + ret = convert_list_to_tree (&tmp, &nstart, &nstop, &highest); + if (GST_CLOCK_TIME_IS_VALID (first_out_of_stack)) { + if (reverse && nstart < first_out_of_stack) + nstart = first_out_of_stack; + else if (!reverse && nstop > first_out_of_stack) + nstop = first_out_of_stack; + } + + GST_DEBUG ("nstart:%" GST_TIME_FORMAT ", nstop:%" GST_TIME_FORMAT, + GST_TIME_ARGS (nstart), GST_TIME_ARGS (nstop)); + + if (*stop) + *stop = nstop; + if (*start) + *start = nstart; + if (highprio) + *highprio = highest; + + g_list_free (stack); + + return ret; +} + +/* + * get_clean_toplevel_stack: + * @comp: The #NleComposition + * @timestamp: The #GstClockTime to look at + * @stop_time: Pointer to a #GstClockTime for min stop time of returned stack + * @start_time: Pointer to a #GstClockTime for greatest start time of returned stack + * + * Returns: The new current stack for the given #NleComposition and @timestamp. + * + * WITH OBJECTS LOCK TAKEN + */ +static GNode * +get_clean_toplevel_stack (NleComposition * comp, GstClockTime * timestamp, + GstClockTime * start_time, GstClockTime * stop_time) +{ + GNode *stack = NULL; + GstClockTime start = G_MAXUINT64; + GstClockTime stop = G_MAXUINT64; + guint highprio; + gboolean reverse = (comp->priv->segment->rate < 0.0); + + GST_DEBUG_OBJECT (comp, "timestamp:%" GST_TIME_FORMAT, + GST_TIME_ARGS (*timestamp)); + GST_DEBUG ("start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT, + GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); + + stack = get_stack_list (comp, *timestamp, 0, TRUE, &start, &stop, &highprio); + + if (!stack && + ((reverse && (*timestamp > COMP_REAL_START (comp))) || + (!reverse && (*timestamp < COMP_REAL_STOP (comp))))) { + GST_ELEMENT_ERROR (comp, STREAM, WRONG_TYPE, + ("Gaps ( at %" GST_TIME_FORMAT + ") in the stream is not supported, the application is responsible" + " for filling them", GST_TIME_ARGS (*timestamp)), + ("Gap in the composition this should never" + "append, make sure to fill them")); + + return NULL; + } + + GST_DEBUG ("start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT, + GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); + + if (stack) { + guint32 top_priority = NLE_OBJECT_PRIORITY (stack->data); + + /* Figure out if there's anything blocking us with smaller priority */ + refine_start_stop_in_region_above_priority (comp, *timestamp, start, + stop, &start, &stop, (highprio == 0) ? top_priority : highprio); + } + + if (*stop_time) { + if (stack) + *stop_time = stop; + else + *stop_time = 0; + } + + if (*start_time) { + if (stack) + *start_time = start; + else + *start_time = 0; + } + + GST_DEBUG_OBJECT (comp, + "Returning timestamp:%" GST_TIME_FORMAT " , start_time:%" + GST_TIME_FORMAT " , stop_time:%" GST_TIME_FORMAT, + GST_TIME_ARGS (*timestamp), GST_TIME_ARGS (*start_time), + GST_TIME_ARGS (*stop_time)); + + return stack; +} + +static GstPadProbeReturn +_drop_all_cb (GstPad * pad G_GNUC_UNUSED, + GstPadProbeInfo * info, NleComposition * comp) +{ + return GST_PAD_PROBE_DROP; +} + +/* Must be called with OBJECTS_LOCK taken */ +static void +_set_current_bin_to_ready (NleComposition * comp, gboolean flush_downstream) +{ + gint probe_id = -1; + GstPad *ptarget = NULL; + NleCompositionPrivate *priv = comp->priv; + GstEvent *flush_event; + + comp->priv->tearing_down_stack = TRUE; + if (flush_downstream) { + ptarget = gst_ghost_pad_get_target (GST_GHOST_PAD (NLE_OBJECT_SRC (comp))); + if (ptarget) { + + /* Make sure that between the flush_start/flush_stop + * and the time we set the current_bin to READY, no + * buffer can ever get prerolled which would lead to + * a deadlock */ + probe_id = gst_pad_add_probe (ptarget, + GST_PAD_PROBE_TYPE_DATA_BOTH | GST_PAD_PROBE_TYPE_EVENT_BOTH, + (GstPadProbeCallback) _drop_all_cb, comp, NULL); + + GST_DEBUG_OBJECT (comp, "added event probe %lu", priv->ghosteventprobe); + + flush_event = gst_event_new_flush_start (); + priv->flush_seqnum = gst_event_get_seqnum (flush_event); + GST_INFO_OBJECT (comp, "sending flushes downstream with seqnum %d", + priv->flush_seqnum); + gst_pad_push_event (ptarget, flush_event); + + } + + } + + gst_element_set_locked_state (priv->current_bin, TRUE); + gst_element_set_state (priv->current_bin, GST_STATE_READY); + + if (ptarget) { + if (flush_downstream) { + flush_event = gst_event_new_flush_stop (TRUE); + gst_event_set_seqnum (flush_event, priv->flush_seqnum); + + /* Force ad activation so that the event can actually travel. + * Not doing that would lead to the event being discarded. + */ + gst_pad_set_active (ptarget, TRUE); + gst_pad_push_event (ptarget, flush_event); + gst_pad_set_active (ptarget, FALSE); + } + + gst_pad_remove_probe (ptarget, probe_id); + gst_object_unref (ptarget); + } + + comp->priv->tearing_down_stack = FALSE; +} + +static void +_emit_commited_signal_func (NleComposition * comp, gpointer udata) +{ + GST_INFO_OBJECT (comp, "Emiting COMMITED now that the stack " "is ready"); + + g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE); +} + +static void +_restart_task (NleComposition * comp) +{ + GST_INFO_OBJECT (comp, "Restarting task! after %s DONE", + UPDATE_PIPELINE_REASONS[comp->priv->updating_reason]); + + if (comp->priv->updating_reason == COMP_UPDATE_STACK_ON_COMMIT) + _add_action (comp, G_CALLBACK (_emit_commited_signal_func), comp, + G_PRIORITY_HIGH); + + comp->priv->seqnum_to_restart_task = 0; + comp->priv->waiting_for_buffer = FALSE; + + comp->priv->updating_reason = COMP_UPDATE_STACK_NONE; + GST_OBJECT_LOCK (comp); + if (comp->task) + gst_task_start (comp->task); + GST_OBJECT_UNLOCK (comp); +} + +static gboolean +_is_ready_to_restart_task (NleComposition * comp, GstEvent * event) +{ + NleCompositionPrivate *priv = comp->priv; + gint seqnum = gst_event_get_seqnum (event); + + + if (comp->priv->seqnum_to_restart_task == seqnum) { + gchar *name = g_strdup_printf ("%s-new-stack__%" GST_TIME_FORMAT "--%" + GST_TIME_FORMAT "", GST_OBJECT_NAME (comp), + GST_TIME_ARGS (comp->priv->segment_start), + GST_TIME_ARGS (comp->priv->segment_stop)); + + GST_INFO_OBJECT (comp, "Got %s with proper seqnum" + " done with stack reconfiguration %" GST_PTR_FORMAT, + GST_EVENT_TYPE_NAME (event), event); + + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (comp), + GST_DEBUG_GRAPH_SHOW_ALL, name); + g_free (name); + + if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { + GST_INFO_OBJECT (comp, "update_pipeline DONE"); + return TRUE; + } + + priv->waiting_for_buffer = TRUE; + return FALSE; + + } else if (comp->priv->seqnum_to_restart_task) { + GST_INFO_OBJECT (comp, "WARNING: %s seqnum %i != wanted %i", + GST_EVENT_TYPE_NAME (event), seqnum, + comp->priv->seqnum_to_restart_task); + } + + return FALSE; +} + +static void +_commit_func (NleComposition * comp, UpdateCompositionData * ucompo) +{ + GstClockTime curpos; + NleCompositionPrivate *priv = comp->priv; + + _post_start_composition_update (comp, ucompo->seqnum, ucompo->reason); + + /* Get current so that it represent the duration it was + * before commiting children */ + curpos = get_current_position (comp); + + if (!_commit_all_values (comp)) { + GST_DEBUG_OBJECT (comp, "Nothing to commit, leaving"); + + g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, FALSE); + _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason); + + return; + } + + if (priv->initialized == FALSE) { + GST_DEBUG_OBJECT (comp, "Not initialized yet, just updating values"); + + update_start_stop_duration (comp); + + g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE); + + } else { + /* And update the pipeline at current position if needed */ + + update_start_stop_duration (comp); + update_pipeline (comp, curpos, ucompo->seqnum, COMP_UPDATE_STACK_ON_COMMIT); + + if (!priv->current) { + GST_INFO_OBJECT (comp, "No new stack set, we can go and keep acting on" + " our children"); + + g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE); + } + } + + _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason); +} + +static void +_update_pipeline_func (NleComposition * comp, UpdateCompositionData * ucompo) +{ + gboolean reverse; + NleCompositionPrivate *priv = comp->priv; + + _post_start_composition_update (comp, ucompo->seqnum, ucompo->reason); + + /* Set up a non-initial seek on segment_stop */ + reverse = (priv->segment->rate < 0.0); + if (!reverse) { + GST_DEBUG_OBJECT (comp, + "Setting segment->start to segment_stop:%" GST_TIME_FORMAT, + GST_TIME_ARGS (priv->segment_stop)); + priv->segment->start = priv->segment_stop; + } else { + GST_DEBUG_OBJECT (comp, + "Setting segment->stop to segment_start:%" GST_TIME_FORMAT, + GST_TIME_ARGS (priv->segment_start)); + priv->segment->stop = priv->segment_start; + } + + seek_handling (comp, ucompo->seqnum, COMP_UPDATE_STACK_ON_EOS); + + /* Post segment done if last seek was a segment seek */ + if (!priv->current && (priv->segment->flags & GST_SEEK_FLAG_SEGMENT)) { + gint64 epos; + + if (GST_CLOCK_TIME_IS_VALID (priv->segment->stop)) + epos = (MIN (priv->segment->stop, NLE_OBJECT_STOP (comp))); + else + epos = NLE_OBJECT_STOP (comp); + + GST_LOG_OBJECT (comp, "Emitting segment done pos %" GST_TIME_FORMAT, + GST_TIME_ARGS (epos)); + gst_element_post_message (GST_ELEMENT_CAST (comp), + gst_message_new_segment_done (GST_OBJECT (comp), + priv->segment->format, epos)); + gst_pad_push_event (NLE_OBJECT (comp)->srcpad, + gst_event_new_segment_done (priv->segment->format, epos)); + } + + _post_start_composition_update_done (comp, ucompo->seqnum, ucompo->reason); +} + +static void +_set_all_children_state (NleComposition * comp, GstState state) +{ + GList *tmp; + + GST_DEBUG_OBJECT (comp, "Setting all children state to %s", + gst_element_state_get_name (state)); + + comp->priv->tearing_down_stack = TRUE; + gst_element_set_state (comp->priv->current_bin, state); + for (tmp = comp->priv->objects_start; tmp; tmp = tmp->next) + gst_element_set_state (tmp->data, state); + + for (tmp = comp->priv->expandables; tmp; tmp = tmp->next) + gst_element_set_state (tmp->data, state); + comp->priv->tearing_down_stack = FALSE; +} + +static GstStateChangeReturn +nle_composition_change_state (GstElement * element, GstStateChange transition) +{ + NleComposition *comp = (NleComposition *) element; + + GST_DEBUG_OBJECT (comp, "%s => %s", + gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)), + gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition))); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + _start_task (comp); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + /* state-lock all elements */ + GST_DEBUG_OBJECT (comp, + "Setting all children to READY and locking their state"); + + _add_update_compo_action (comp, G_CALLBACK (_initialize_stack_func), + COMP_UPDATE_STACK_INITIALIZE); + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + _stop_task (comp); + + _remove_update_actions (comp); + _remove_seek_actions (comp); + _set_all_children_state (comp, GST_STATE_READY); + nle_composition_reset (comp); + + _start_task (comp); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + _stop_task (comp); + + _remove_update_actions (comp); + _remove_seek_actions (comp); + _set_all_children_state (comp, GST_STATE_NULL); + break; + default: + break; + } + + return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); +} + +static gint +objects_start_compare (NleObject * a, NleObject * b) +{ + if (a->start == b->start) { + if (a->priority < b->priority) + return -1; + if (a->priority > b->priority) + return 1; + return 0; + } + if (a->start < b->start) + return -1; + if (a->start > b->start) + return 1; + return 0; +} + +static gint +objects_stop_compare (NleObject * a, NleObject * b) +{ + if (a->stop == b->stop) { + if (a->priority < b->priority) + return -1; + if (a->priority > b->priority) + return 1; + return 0; + } + if (b->stop < a->stop) + return -1; + if (b->stop > a->stop) + return 1; + return 0; +} + +/* WITH OBJECTS LOCK TAKEN */ +static void +update_start_stop_duration (NleComposition * comp) +{ + NleObject *obj; + NleObject *cobj = (NleObject *) comp; + NleCompositionPrivate *priv = comp->priv; + + _assert_proper_thread (comp); + + if (!priv->objects_start) { + GST_INFO_OBJECT (comp, "no objects, resetting everything to 0"); + + if (cobj->start) { + cobj->start = cobj->pending_start = 0; + g_object_notify_by_pspec (G_OBJECT (cobj), + nleobject_properties[NLEOBJECT_PROP_START]); + } + + if (cobj->duration) { + cobj->pending_duration = cobj->duration = 0; + g_object_notify_by_pspec (G_OBJECT (cobj), + nleobject_properties[NLEOBJECT_PROP_DURATION]); + signal_duration_change (comp); + } + + if (cobj->stop) { + cobj->stop = 0; + g_object_notify_by_pspec (G_OBJECT (cobj), + nleobject_properties[NLEOBJECT_PROP_STOP]); + } + + return; + } + + /* If we have a default object, the start position is 0 */ + if (priv->expandables) { + GST_INFO_OBJECT (cobj, + "Setting start to 0 because we have a default object"); + + if (cobj->start != 0) { + cobj->pending_start = cobj->start = 0; + g_object_notify_by_pspec (G_OBJECT (cobj), + nleobject_properties[NLEOBJECT_PROP_START]); + } + + } else { + + /* Else it's the first object's start value */ + obj = (NleObject *) priv->objects_start->data; + + if (obj->start != cobj->start) { + GST_INFO_OBJECT (obj, "setting start from %s to %" GST_TIME_FORMAT, + GST_OBJECT_NAME (obj), GST_TIME_ARGS (obj->start)); + cobj->pending_start = cobj->start = obj->start; + g_object_notify_by_pspec (G_OBJECT (cobj), + nleobject_properties[NLEOBJECT_PROP_START]); + } + + } + + obj = (NleObject *) priv->objects_stop->data; + + if (obj->stop != cobj->stop) { + GST_INFO_OBJECT (obj, "setting stop from %s to %" GST_TIME_FORMAT, + GST_OBJECT_NAME (obj), GST_TIME_ARGS (obj->stop)); + + if (priv->expandables) { + GList *tmp; + + GST_INFO_OBJECT (comp, "RE-setting all expandables duration and commit"); + for (tmp = priv->expandables; tmp; tmp = tmp->next) { + g_object_set (tmp->data, "duration", obj->stop, NULL); + nle_object_commit (NLE_OBJECT (tmp->data), FALSE); + } + } + + priv->segment->stop = obj->stop; + cobj->stop = obj->stop; + g_object_notify_by_pspec (G_OBJECT (cobj), + nleobject_properties[NLEOBJECT_PROP_STOP]); + } + + if ((cobj->stop - cobj->start) != cobj->duration) { + cobj->pending_duration = cobj->duration = cobj->stop - cobj->start; + g_object_notify_by_pspec (G_OBJECT (cobj), + nleobject_properties[NLEOBJECT_PROP_DURATION]); + signal_duration_change (comp); + } + + GST_INFO_OBJECT (comp, + "start:%" GST_TIME_FORMAT + " stop:%" GST_TIME_FORMAT + " duration:%" GST_TIME_FORMAT, + GST_TIME_ARGS (cobj->start), + GST_TIME_ARGS (cobj->stop), GST_TIME_ARGS (cobj->duration)); +} + +static void +_link_to_parent (NleComposition * comp, NleObject * newobj, + NleObject * newparent) +{ + GstPad *sinkpad; + + /* relink to new parent in required order */ + GST_LOG_OBJECT (comp, "Linking %s and %s", + GST_ELEMENT_NAME (GST_ELEMENT (newobj)), + GST_ELEMENT_NAME (GST_ELEMENT (newparent))); + + sinkpad = get_unlinked_sink_ghost_pad ((NleOperation *) newparent); + + if (G_UNLIKELY (sinkpad == NULL)) { + GST_WARNING_OBJECT (comp, + "Couldn't find an unlinked sinkpad from %s", + GST_ELEMENT_NAME (newparent)); + } else { + if (G_UNLIKELY (gst_pad_link_full (NLE_OBJECT_SRC (newobj), sinkpad, + GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK)) { + GST_WARNING_OBJECT (comp, "Failed to link pads %s:%s - %s:%s", + GST_DEBUG_PAD_NAME (NLE_OBJECT_SRC (newobj)), + GST_DEBUG_PAD_NAME (sinkpad)); + } + gst_object_unref (sinkpad); + } +} + +static void +_relink_children_recursively (NleComposition * comp, + NleObject * newobj, GNode * node, GstEvent * toplevel_seek) +{ + GNode *child; + guint nbchildren = g_node_n_children (node); + NleOperation *oper = (NleOperation *) newobj; + + GST_INFO_OBJECT (newobj, "is a %s operation, analyzing the %d children", + oper->dynamicsinks ? "dynamic" : "regular", nbchildren); + /* Update the operation's number of sinks, that will make it have the proper + * number of sink pads to connect the children to. */ + if (oper->dynamicsinks) + g_object_set (G_OBJECT (newobj), "sinks", nbchildren, NULL); + + for (child = node->children; child; child = child->next) + _relink_single_node (comp, child, toplevel_seek); + + if (G_UNLIKELY (nbchildren < oper->num_sinks)) + GST_ERROR_OBJECT (comp, + "%" GST_PTR_FORMAT + " Not enough sinkpads to link all objects to the operation ! " + "%d / %d", oper, oper->num_sinks, nbchildren); + + if (G_UNLIKELY (nbchildren == 0)) { + GST_ERROR_OBJECT (comp, + "Operation %" GST_PTR_FORMAT + " has no child objects to be connected to !!!", oper); + } + /* Make sure we have enough sinkpads */ +} + +/* + * recursive depth-first relink stack function on new stack + * + * _ relink nodes with changed parent/order + * _ links new nodes with parents + * _ unblocks available source pads (except for toplevel) + * + * WITH OBJECTS LOCK TAKEN + */ +static void +_relink_single_node (NleComposition * comp, GNode * node, + GstEvent * toplevel_seek) +{ + NleObject *newobj; + NleObject *newparent; + GstPad *srcpad = NULL, *sinkpad = NULL; + GstEvent *translated_seek; + + if (G_UNLIKELY (!node)) + return; + + newparent = G_NODE_IS_ROOT (node) ? NULL : (NleObject *) node->parent->data; + newobj = (NleObject *) node->data; + + GST_DEBUG_OBJECT (comp, "newobj:%s", + GST_ELEMENT_NAME ((GstElement *) newobj)); + + srcpad = NLE_OBJECT_SRC (newobj); + + gst_bin_add (GST_BIN (comp->priv->current_bin), GST_ELEMENT (newobj)); + gst_element_sync_state_with_parent (GST_ELEMENT_CAST (newobj)); + + translated_seek = nle_object_translate_incoming_seek (newobj, toplevel_seek); + + gst_element_send_event (GST_ELEMENT (newobj), translated_seek); + + /* link to parent if needed. */ + if (newparent) { + _link_to_parent (comp, newobj, newparent); + + /* If there's an operation, inform it about priority changes */ + sinkpad = gst_pad_get_peer (srcpad); + nle_operation_signal_input_priority_changed ((NleOperation *) + newparent, sinkpad, newobj->priority); + gst_object_unref (sinkpad); + } + + /* Handle children */ + if (NLE_IS_OPERATION (newobj)) + _relink_children_recursively (comp, newobj, node, toplevel_seek); + + GST_LOG_OBJECT (comp, "done with object %s", + GST_ELEMENT_NAME (GST_ELEMENT (newobj))); +} + + + +/* + * compare_relink_stack: + * @comp: The #NleComposition + * @stack: The new stack + * @modify: TRUE if the timeline has changed and needs downstream flushes. + * + * Compares the given stack to the current one and relinks it if needed. + * + * WITH OBJECTS LOCK TAKEN + * + * Returns: The #GList of #NleObject no longer used + */ + +static void +_deactivate_stack (NleComposition * comp, gboolean flush_downstream) +{ + GstPad *ptarget; + + GST_INFO_OBJECT (comp, "Deactivating current stack (flushing downstream: %d", + flush_downstream); + _set_current_bin_to_ready (comp, flush_downstream); + + ptarget = gst_ghost_pad_get_target (GST_GHOST_PAD (NLE_OBJECT_SRC (comp))); + _empty_bin (GST_BIN_CAST (comp->priv->current_bin)); + + if (comp->priv->ghosteventprobe) { + GST_INFO_OBJECT (comp, "Removing old ghost pad probe"); + + gst_pad_remove_probe (ptarget, comp->priv->ghosteventprobe); + comp->priv->ghosteventprobe = 0; + } + + if (ptarget) + gst_object_unref (ptarget); + + GST_INFO_OBJECT (comp, "Stack desctivated"); + +/* priv->current = NULL; + */ +} + +static void +_relink_new_stack (NleComposition * comp, GNode * stack, + GstEvent * toplevel_seek) +{ + _relink_single_node (comp, stack, toplevel_seek); + + gst_event_unref (toplevel_seek); +} + +/* static void + * unlock_activate_stack (NleComposition * comp, GNode * node, GstState state) + * { + * GNode *child; + * + * GST_LOG_OBJECT (comp, "object:%s", + * GST_ELEMENT_NAME ((GstElement *) (node->data))); + * + * gst_element_set_locked_state ((GstElement *) (node->data), FALSE); + * gst_element_set_state (GST_ELEMENT (node->data), state); + * + * for (child = node->children; child; child = child->next) + * unlock_activate_stack (comp, child, state); + * } + */ + +static gboolean +are_same_stacks (GNode * stack1, GNode * stack2) +{ + gboolean res = FALSE; + + /* TODO : FIXME : we should also compare start/inpoint */ + /* stacks are not equal if one of them is NULL but not the other */ + if ((!stack1 && stack2) || (stack1 && !stack2)) + goto beach; + + if (stack1 && stack2) { + GNode *child1, *child2; + + /* if they don't contain the same source, not equal */ + if (!(stack1->data == stack2->data)) + goto beach; + + /* if they don't have the same number of children, not equal */ + if (!(g_node_n_children (stack1) == g_node_n_children (stack2))) + goto beach; + + child1 = stack1->children; + child2 = stack2->children; + while (child1 && child2) { + if (!(are_same_stacks (child1, child2))) + goto beach; + child1 = g_node_next_sibling (child1); + child2 = g_node_next_sibling (child2); + } + + /* if there's a difference in child number, stacks are not equal */ + if (child1 || child2) + goto beach; + } + + /* if stack1 AND stack2 are NULL, then they're equal (both empty) */ + res = TRUE; + +beach: + GST_LOG ("Stacks are equal : %d", res); + + return res; +} + +static inline gboolean +_activate_new_stack (NleComposition * comp) +{ + GstPad *pad; + GstElement *topelement; + + NleCompositionPrivate *priv = comp->priv; + + if (!priv->current) { + if ((!priv->objects_start)) { + nle_composition_reset_target_pad (comp); + priv->segment_start = 0; + priv->segment_stop = GST_CLOCK_TIME_NONE; + } + + GST_DEBUG_OBJECT (comp, "Nothing else in the composition" + ", update 'worked'"); + goto resync_state; + } + + /* The stack is entirely ready, send seek out synchronously */ + topelement = GST_ELEMENT (priv->current->data); + /* Get toplevel object source pad */ + pad = NLE_OBJECT_SRC (topelement); + + GST_INFO_OBJECT (comp, + "We have a valid toplevel element pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + nle_composition_ghost_pad_set_target (comp, pad); + + GST_DEBUG_OBJECT (comp, "New stack activated!"); + +resync_state: + gst_element_set_locked_state (priv->current_bin, FALSE); + GST_DEBUG ("going back to parent state"); + gst_element_sync_state_with_parent (priv->current_bin); + GST_DEBUG ("gone back to parent state"); + + return TRUE; +} + +/* WITH OBJECTS LOCK TAKEN */ +static gboolean +_set_real_eos_seqnum_from_seek (NleComposition * comp, GstEvent * event) +{ + GList *tmp; + + gboolean should_check_objects = FALSE; + NleCompositionPrivate *priv = comp->priv; + gboolean reverse = (priv->segment->rate < 0); + gint stack_seqnum = gst_event_get_seqnum (event); + + if (reverse && GST_CLOCK_TIME_IS_VALID (priv->segment_start)) + should_check_objects = TRUE; + else if (!reverse && GST_CLOCK_TIME_IS_VALID (priv->segment_stop)) + should_check_objects = TRUE; + + if (should_check_objects) { + for (tmp = priv->objects_stop; tmp; tmp = g_list_next (tmp)) { + NleObject *object = (NleObject *) tmp->data; + + if (!NLE_IS_SOURCE (object)) + continue; + + if ((!reverse && priv->segment_stop < object->stop) || + (reverse && priv->segment_start > object->start)) { + priv->next_eos_seqnum = stack_seqnum; + g_atomic_int_set (&priv->real_eos_seqnum, 0); + return FALSE; + } + } + } + + priv->next_eos_seqnum = stack_seqnum; + g_atomic_int_set (&priv->real_eos_seqnum, stack_seqnum); + + return TRUE; +} + +/* + * update_pipeline: + * @comp: The #NleComposition + * @currenttime: The #GstClockTime to update at, can be GST_CLOCK_TIME_NONE. + * @update_reason: Reason why we are updating the pipeline + * + * Updates the internal pipeline and properties. If @currenttime is + * GST_CLOCK_TIME_NONE, it will not modify the current pipeline + * + * Returns: FALSE if there was an error updating the pipeline. + * + * WITH OBJECTS LOCK TAKEN + */ +static gboolean +update_pipeline (NleComposition * comp, GstClockTime currenttime, gint32 seqnum, + NleUpdateStackReason update_reason) +{ + + GstEvent *toplevel_seek; + + GNode *stack = NULL; + gboolean samestack = FALSE; + gboolean updatestoponly = FALSE; + GstState state = GST_STATE (comp); + NleCompositionPrivate *priv = comp->priv; + GstClockTime new_stop = GST_CLOCK_TIME_NONE; + GstClockTime new_start = GST_CLOCK_TIME_NONE; + GstClockTime duration = NLE_OBJECT (comp)->duration - 1; + + GstState nextstate = (GST_STATE_NEXT (comp) == GST_STATE_VOID_PENDING) ? + GST_STATE (comp) : GST_STATE_NEXT (comp); + + _assert_proper_thread (comp); + + if (currenttime >= duration) { + currenttime = duration; + priv->segment->start = GST_CLOCK_TIME_NONE; + priv->segment->stop = GST_CLOCK_TIME_NONE; + } + + GST_INFO_OBJECT (comp, + "currenttime:%" GST_TIME_FORMAT + " Reason: %s, Seqnum: %i", GST_TIME_ARGS (currenttime), + UPDATE_PIPELINE_REASONS[update_reason], seqnum); + + if (!GST_CLOCK_TIME_IS_VALID (currenttime)) + return FALSE; + + if (state == GST_STATE_NULL && nextstate == GST_STATE_NULL) { + GST_DEBUG_OBJECT (comp, "STATE_NULL: not updating pipeline"); + return FALSE; + } + + GST_DEBUG_OBJECT (comp, + "now really updating the pipeline, current-state:%s", + gst_element_state_get_name (state)); + + /* Get new stack and compare it to current one */ + stack = get_clean_toplevel_stack (comp, ¤ttime, &new_start, &new_stop); + samestack = are_same_stacks (priv->current, stack); + + /* set new segment_start/stop (the current zone over which the new stack + * is valid) */ + if (priv->segment->rate >= 0.0) { + priv->segment_start = currenttime; + priv->segment_stop = new_stop; + } else { + priv->segment_start = new_start; + priv->segment_stop = currenttime; + } + +# if 0 + /* FIXME -- We should be ablt to use updatestoponly in that case, + * but it simply does not work! Not using it leads to same + * behaviour, but less optimized */ + + gboolean startchanged, stopchanged; + + if (priv->segment->rate >= 0.0) { + startchanged = priv->segment_start != currenttime; + stopchanged = priv->segment_stop != new_stop; + } else { + startchanged = priv->segment_start != new_start; + stopchanged = priv->segment_stop != currenttime; + } + + if (samestack) { + if (startchanged || stopchanged) { + /* Update seek events need to be flushing if not in PLAYING, + * else we will encounter deadlocks. */ + updatestoponly = (state == GST_STATE_PLAYING) ? FALSE : TRUE; + } + } +#endif + + toplevel_seek = get_new_seek_event (comp, TRUE, updatestoponly); + gst_event_set_seqnum (toplevel_seek, seqnum); + _set_real_eos_seqnum_from_seek (comp, toplevel_seek); + + _remove_update_actions (comp); + + /* If stacks are different, unlink/relink objects */ + if (!samestack) { + _deactivate_stack (comp, _have_to_flush_downstream (update_reason)); + _relink_new_stack (comp, stack, toplevel_seek); + } + + /* Unlock all elements in new stack */ + GST_INFO_OBJECT (comp, "Setting current stack [%" GST_TIME_FORMAT " - %" + GST_TIME_FORMAT "]", GST_TIME_ARGS (priv->segment_start), + GST_TIME_ARGS (priv->segment_stop)); + priv->current = stack; + + if (priv->current) { + UpdateCompositionData *ucompo = g_slice_new0 (UpdateCompositionData); + + GST_INFO_OBJECT (comp, "New stack set and ready to run, probing src pad" + " and stopping children thread until we are actually ready with" + " that new stack"); + + ucompo->comp = comp; + ucompo->reason = update_reason; + ucompo->seqnum = seqnum; + + comp->priv->updating_reason = update_reason; + comp->priv->seqnum_to_restart_task = seqnum; + + GST_OBJECT_LOCK (comp); + if (comp->task == NULL) { + GST_INFO_OBJECT (comp, + "No task set, it must have been stopped, returning"); + GST_OBJECT_UNLOCK (comp); + return FALSE; + } + + gst_task_pause (comp->task); + GST_OBJECT_UNLOCK (comp); + } + + /* Activate stack */ + if (!samestack) + return _activate_new_stack (comp); + else + return _seek_current_stack (comp, toplevel_seek, + _have_to_flush_downstream (update_reason)); +} + +static gboolean +nle_composition_add_object (GstBin * bin, GstElement * element) +{ + NleObject *object; + NleComposition *comp = (NleComposition *) bin; + + if (element == comp->priv->current_bin) { + GST_INFO_OBJECT (comp, "Adding internal bin"); + return GST_BIN_CLASS (parent_class)->add_element (bin, element); + } + + g_return_val_if_fail (NLE_IS_OBJECT (element), FALSE); + + object = NLE_OBJECT (element); + gst_object_ref_sink (object); + + object->in_composition = TRUE; + _add_add_object_action (comp, object); + + return TRUE; +} + +static gboolean +_nle_composition_add_object (NleComposition * comp, NleObject * object) +{ + gboolean ret = TRUE; + NleCompositionPrivate *priv = comp->priv; + + GST_DEBUG_OBJECT (comp, "element %s", GST_OBJECT_NAME (object)); + GST_DEBUG_OBJECT (object, "%" GST_TIME_FORMAT "--%" GST_TIME_FORMAT, + GST_TIME_ARGS (NLE_OBJECT_START (object)), + GST_TIME_ARGS (NLE_OBJECT_STOP (object))); + + if ((NLE_OBJECT_IS_EXPANDABLE (object)) && + g_list_find (priv->expandables, object)) { + GST_WARNING_OBJECT (comp, + "We already have an expandable, remove it before adding new one"); + ret = FALSE; + + goto chiringuito; + } + + nle_object_set_caps (object, NLE_OBJECT (comp)->caps); + nle_object_set_commit_needed (NLE_OBJECT (comp)); + + if (!ret) { + GST_WARNING_OBJECT (comp, "couldn't add object"); + goto chiringuito; + } + + /* lock state of child ! */ + GST_LOG_OBJECT (comp, "Locking state of %s", GST_ELEMENT_NAME (object)); + + if (NLE_OBJECT_IS_EXPANDABLE (object)) { + /* Only react on non-default objects properties */ + g_object_set (object, + "start", (GstClockTime) 0, + "inpoint", (GstClockTime) 0, + "duration", (GstClockTimeDiff) NLE_OBJECT_STOP (comp), NULL); + + GST_INFO_OBJECT (object, "Used as expandable, commiting now"); + nle_object_commit (NLE_OBJECT (object), FALSE); + } + + /* ...and add it to the hash table */ + g_hash_table_add (priv->objects_hash, object); + + /* Set the caps of the composition on the NleObject it handles */ + if (G_UNLIKELY (!gst_caps_is_any (((NleObject *) comp)->caps))) + nle_object_set_caps ((NleObject *) object, ((NleObject *) comp)->caps); + + /* Special case for default source. */ + if (NLE_OBJECT_IS_EXPANDABLE (object)) { + /* It doesn't get added to objects_start and objects_stop. */ + priv->expandables = g_list_prepend (priv->expandables, object); + goto beach; + } + + /* add it sorted to the objects list */ + priv->objects_start = g_list_insert_sorted + (priv->objects_start, object, (GCompareFunc) objects_start_compare); + + if (priv->objects_start) + GST_LOG_OBJECT (comp, + "Head of objects_start is now %s [%" GST_TIME_FORMAT "--%" + GST_TIME_FORMAT "]", + GST_OBJECT_NAME (priv->objects_start->data), + GST_TIME_ARGS (NLE_OBJECT_START (priv->objects_start->data)), + GST_TIME_ARGS (NLE_OBJECT_STOP (priv->objects_start->data))); + + priv->objects_stop = g_list_insert_sorted + (priv->objects_stop, object, (GCompareFunc) objects_stop_compare); + + /* Now the object is ready to be commited and then used */ + +beach: + return ret; + +chiringuito: + { + update_start_stop_duration (comp); + goto beach; + } +} + +static gboolean +nle_composition_remove_object (GstBin * bin, GstElement * element) +{ + NleObject *object; + NleComposition *comp = (NleComposition *) bin; + + if (element == comp->priv->current_bin) { + GST_INFO_OBJECT (comp, "Adding internal bin"); + return GST_BIN_CLASS (parent_class)->remove_element (bin, element); + } + + g_return_val_if_fail (NLE_IS_OBJECT (element), FALSE); + + object = NLE_OBJECT (element); + + object->in_composition = FALSE; + _add_remove_object_action (comp, object); + + return TRUE; +} + +static gboolean +_nle_composition_remove_object (NleComposition * comp, NleObject * object) +{ + NleCompositionPrivate *priv = comp->priv; + + GST_DEBUG_OBJECT (comp, "removing object %s", GST_OBJECT_NAME (object)); + + if (!g_hash_table_contains (priv->objects_hash, object)) { + GST_INFO_OBJECT (comp, "object was not in composition"); + return FALSE; + } + + gst_element_set_locked_state (GST_ELEMENT (object), FALSE); + gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL); + + /* handle default source */ + if (NLE_OBJECT_IS_EXPANDABLE (object)) { + /* Find it in the list */ + priv->expandables = g_list_remove (priv->expandables, object); + } else { + /* remove it from the objects list and resort the lists */ + priv->objects_start = g_list_remove (priv->objects_start, object); + priv->objects_stop = g_list_remove (priv->objects_stop, object); + GST_LOG_OBJECT (object, "Removed from the objects start/stop list"); + } + + if (priv->current && NLE_OBJECT (priv->current->data) == NLE_OBJECT (object)) + nle_composition_reset_target_pad (comp); + + g_hash_table_remove (priv->objects_hash, object); + + GST_LOG_OBJECT (object, "Done removing from the composition, now updating"); + + /* Make it possible to reuse the same object later */ + nle_object_reset (NLE_OBJECT (object)); + gst_object_unref (object); + + return TRUE; +} diff --git a/plugins/nle/nlecomposition.h b/plugins/nle/nlecomposition.h new file mode 100644 index 00000000..9f5da3d8 --- /dev/null +++ b/plugins/nle/nlecomposition.h @@ -0,0 +1,66 @@ +/* GStreamer + * Copyright (C) 2001 Wim Taymans + * 2004-2008 Edward Hervey + * + * nlecomposition.h: Header for base NleComposition + * + * 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. + */ + + +#ifndef __NLE_COMPOSITION_H__ +#define __NLE_COMPOSITION_H__ + +#include +#include "nleobject.h" + +G_BEGIN_DECLS +#define NLE_TYPE_COMPOSITION \ + (nle_composition_get_type()) +#define NLE_COMPOSITION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_COMPOSITION,NleComposition)) +#define NLE_COMPOSITION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_COMPOSITION,NleCompositionClass)) +#define NLE_COMPOSITION_GET_CLASS(obj) \ + (NLE_COMPOSITION_CLASS (G_OBJECT_GET_CLASS (obj))) +#define NLE_IS_COMPOSITION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_COMPOSITION)) +#define NLE_IS_COMPOSITION_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_COMPOSITION)) + +typedef struct _NleCompositionPrivate NleCompositionPrivate; + +struct _NleComposition +{ + NleObject parent; + + GstTask * task; + GRecMutex task_rec_lock; + + /*< private >*/ + NleCompositionPrivate * priv; + +}; + +struct _NleCompositionClass +{ + NleObjectClass parent_class; +}; + +GType nle_composition_get_type (void) G_GNUC_INTERNAL; + +G_END_DECLS +#endif /* __NLE_COMPOSITION_H__ */ diff --git a/plugins/nle/nleghostpad.c b/plugins/nle/nleghostpad.c new file mode 100644 index 00000000..c98364ae --- /dev/null +++ b/plugins/nle/nleghostpad.c @@ -0,0 +1,803 @@ +/* Gnonlin + * Copyright (C) <2009> Edward Hervey + * + * 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" + +GST_DEBUG_CATEGORY_STATIC (nleghostpad); +#define GST_CAT_DEFAULT nleghostpad + +typedef struct _NlePadPrivate NlePadPrivate; + +struct _NlePadPrivate +{ + NleObject *object; + NlePadPrivate *ghostpriv; + GstPadDirection dir; + GstPadEventFunction eventfunc; + GstPadQueryFunction queryfunc; + + GstEvent *pending_seek; +}; + +GstEvent * +nle_object_translate_incoming_seek (NleObject * object, GstEvent * event) +{ + GstEvent *event2; + GstFormat format; + gdouble rate; + GstSeekFlags flags; + GstSeekType curtype, stoptype; + GstSeekType ncurtype; + gint64 cur; + guint64 ncur; + gint64 stop; + guint64 nstop; + guint32 seqnum = GST_EVENT_SEQNUM (event); + + gst_event_parse_seek (event, &rate, &format, &flags, + &curtype, &cur, &stoptype, &stop); + + GST_DEBUG_OBJECT (object, + "GOT SEEK rate:%f, format:%d, flags:%d, curtype:%d, stoptype:%d, %" + GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, format, flags, curtype, + stoptype, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop)); + + if (G_UNLIKELY (format != GST_FORMAT_TIME)) + goto invalid_format; + + /* convert cur */ + ncurtype = GST_SEEK_TYPE_SET; + if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET) + && (nle_object_to_media_time (object, cur, &ncur)))) { + /* cur is TYPE_SET and value is valid */ + if (ncur > G_MAXINT64) + GST_WARNING_OBJECT (object, "return value too big..."); + GST_LOG_OBJECT (object, "Setting cur to %" GST_TIME_FORMAT, + GST_TIME_ARGS (ncur)); + } else if ((curtype != GST_SEEK_TYPE_NONE)) { + GST_DEBUG_OBJECT (object, "Limiting seek start to inpoint"); + ncur = object->inpoint; + } else { + GST_DEBUG_OBJECT (object, "leaving GST_SEEK_TYPE_NONE"); + ncur = cur; + ncurtype = GST_SEEK_TYPE_NONE; + } + + /* convert stop, we also need to limit it to object->stop */ + if (G_LIKELY ((stoptype == GST_SEEK_TYPE_SET) + && (nle_object_to_media_time (object, stop, &nstop)))) { + if (nstop > G_MAXINT64) + GST_WARNING_OBJECT (object, "return value too big..."); + GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT, + GST_TIME_ARGS (nstop)); + } else { + GST_DEBUG_OBJECT (object, "Limiting end of seek to media_stop"); + nle_object_to_media_time (object, object->stop, &nstop); + if (nstop > G_MAXINT64) + GST_WARNING_OBJECT (object, "return value too big..."); + GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT, + GST_TIME_ARGS (nstop)); + } + + + /* add accurate seekflags */ + if (G_UNLIKELY (!(flags & GST_SEEK_FLAG_ACCURATE))) { + GST_DEBUG_OBJECT (object, "Adding GST_SEEK_FLAG_ACCURATE"); + flags |= GST_SEEK_FLAG_ACCURATE; + } else { + GST_DEBUG_OBJECT (object, + "event already has GST_SEEK_FLAG_ACCURATE : %d", flags); + } + + + + GST_DEBUG_OBJECT (object, + "SENDING SEEK rate:%f, format:TIME, flags:%d, curtype:%d, stoptype:SET, %" + GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, flags, ncurtype, + GST_TIME_ARGS (ncur), GST_TIME_ARGS (nstop)); + + event2 = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, + ncurtype, (gint64) ncur, GST_SEEK_TYPE_SET, (gint64) nstop); + GST_EVENT_SEQNUM (event2) = seqnum; + + return event2; + + /* ERRORS */ +invalid_format: + { + GST_WARNING ("GNonLin time shifting only works with GST_FORMAT_TIME"); + return event; + } +} + +static GstEvent * +translate_outgoing_seek (NleObject * object, GstEvent * event) +{ + GstEvent *event2; + GstFormat format; + gdouble rate; + GstSeekFlags flags; + GstSeekType curtype, stoptype; + GstSeekType ncurtype; + gint64 cur; + guint64 ncur; + gint64 stop; + guint64 nstop; + guint32 seqnum = GST_EVENT_SEQNUM (event); + + gst_event_parse_seek (event, &rate, &format, &flags, + &curtype, &cur, &stoptype, &stop); + + GST_DEBUG_OBJECT (object, + "GOT SEEK rate:%f, format:%d, flags:%d, curtype:%d, stoptype:%d, %" + GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, format, flags, curtype, + stoptype, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop)); + + if (G_UNLIKELY (format != GST_FORMAT_TIME)) + goto invalid_format; + + /* convert cur */ + ncurtype = GST_SEEK_TYPE_SET; + if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET) + && (nle_media_to_object_time (object, cur, &ncur)))) { + /* cur is TYPE_SET and value is valid */ + if (ncur > G_MAXINT64) + GST_WARNING_OBJECT (object, "return value too big..."); + GST_LOG_OBJECT (object, "Setting cur to %" GST_TIME_FORMAT, + GST_TIME_ARGS (ncur)); + } else if ((curtype != GST_SEEK_TYPE_NONE)) { + GST_DEBUG_OBJECT (object, "Limiting seek start to start"); + ncur = object->start; + } else { + GST_DEBUG_OBJECT (object, "leaving GST_SEEK_TYPE_NONE"); + ncur = cur; + ncurtype = GST_SEEK_TYPE_NONE; + } + + /* convert stop, we also need to limit it to object->stop */ + if (G_LIKELY ((stoptype == GST_SEEK_TYPE_SET) + && (nle_media_to_object_time (object, stop, &nstop)))) { + if (nstop > G_MAXINT64) + GST_WARNING_OBJECT (object, "return value too big..."); + GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT, + GST_TIME_ARGS (nstop)); + } else { + GST_DEBUG_OBJECT (object, "Limiting end of seek to stop"); + nstop = object->stop; + if (nstop > G_MAXINT64) + GST_WARNING_OBJECT (object, "return value too big..."); + GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT, + GST_TIME_ARGS (nstop)); + } + + GST_DEBUG_OBJECT (object, + "SENDING SEEK rate:%f, format:TIME, flags:%d, curtype:%d, stoptype:SET, %" + GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, flags, ncurtype, + GST_TIME_ARGS (ncur), GST_TIME_ARGS (nstop)); + + event2 = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, + ncurtype, (gint64) ncur, GST_SEEK_TYPE_SET, (gint64) nstop); + GST_EVENT_SEQNUM (event2) = seqnum; + + gst_event_unref (event); + + return event2; + + /* ERRORS */ +invalid_format: + { + GST_WARNING ("GNonLin time shifting only works with GST_FORMAT_TIME"); + return event; + } +} + +static GstEvent * +translate_outgoing_segment (NleObject * object, GstEvent * event) +{ + const GstSegment *orig; + GstSegment segment; + GstEvent *event2; + guint32 seqnum = GST_EVENT_SEQNUM (event); + + /* only modify the streamtime */ + gst_event_parse_segment (event, &orig); + + GST_DEBUG_OBJECT (object, + "Got SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %" + GST_TIME_FORMAT, GST_TIME_ARGS (orig->start), GST_TIME_ARGS (orig->stop), + GST_TIME_ARGS (orig->time)); + + if (G_UNLIKELY (orig->format != GST_FORMAT_TIME)) { + GST_WARNING_OBJECT (object, + "Can't translate segments with format != GST_FORMAT_TIME"); + return event; + } + + gst_segment_copy_into (orig, &segment); + + nle_media_to_object_time (object, orig->time, &segment.time); + + if (G_UNLIKELY (segment.time > G_MAXINT64)) + GST_WARNING_OBJECT (object, "Return value too big..."); + + GST_DEBUG_OBJECT (object, + "Sending SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %" + GST_TIME_FORMAT, GST_TIME_ARGS (segment.start), + GST_TIME_ARGS (segment.stop), GST_TIME_ARGS (segment.time)); + + event2 = gst_event_new_segment (&segment); + GST_EVENT_SEQNUM (event2) = seqnum; + gst_event_unref (event); + + return event2; +} + +static GstEvent * +translate_incoming_segment (NleObject * object, GstEvent * event) +{ + GstEvent *event2; + const GstSegment *orig; + GstSegment segment; + guint32 seqnum = GST_EVENT_SEQNUM (event); + + /* only modify the streamtime */ + gst_event_parse_segment (event, &orig); + + GST_DEBUG_OBJECT (object, + "Got SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %" + GST_TIME_FORMAT, GST_TIME_ARGS (orig->start), GST_TIME_ARGS (orig->stop), + GST_TIME_ARGS (orig->time)); + + if (G_UNLIKELY (orig->format != GST_FORMAT_TIME)) { + GST_WARNING_OBJECT (object, + "Can't translate segments with format != GST_FORMAT_TIME"); + return event; + } + + gst_segment_copy_into (orig, &segment); + + if (!nle_object_to_media_time (object, orig->time, &segment.time)) { + GST_DEBUG ("Can't convert media_time, using 0"); + segment.time = 0; + }; + + if (NLE_IS_OPERATION (object)) { + segment.base = NLE_OPERATION (object)->next_base_time; + GST_INFO_OBJECT (object, "Using operation base time %" GST_TIME_FORMAT, + GST_TIME_ARGS (NLE_OPERATION (object)->next_base_time)); + } + + if (G_UNLIKELY (segment.time > G_MAXINT64)) + GST_WARNING_OBJECT (object, "Return value too big..."); + + GST_DEBUG_OBJECT (object, + "Sending SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %" + GST_TIME_FORMAT, GST_TIME_ARGS (segment.start), + GST_TIME_ARGS (segment.stop), GST_TIME_ARGS (segment.time)); + + event2 = gst_event_new_segment (&segment); + GST_EVENT_SEQNUM (event2) = seqnum; + gst_event_unref (event); + + return event2; +} + +static gboolean +internalpad_event_function (GstPad * internal, GstObject * parent, + GstEvent * event) +{ + NlePadPrivate *priv = gst_pad_get_element_private (internal); + NleObject *object = priv->object; + gboolean res; + + GST_DEBUG_OBJECT (internal, "event:%s (seqnum::%d)", + GST_EVENT_TYPE_NAME (event), GST_EVENT_SEQNUM (event)); + + if (G_UNLIKELY (!(priv->eventfunc))) { + GST_WARNING_OBJECT (internal, + "priv->eventfunc == NULL !! What is going on ?"); + return FALSE; + } + + switch (priv->dir) { + case GST_PAD_SRC:{ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT: + event = translate_outgoing_segment (object, event); + break; + case GST_EVENT_EOS: + break; + default: + break; + } + + break; + } + case GST_PAD_SINK:{ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + event = translate_outgoing_seek (object, event); + break; + default: + break; + } + break; + } + default: + break; + } + GST_DEBUG_OBJECT (internal, "Calling priv->eventfunc %p", priv->eventfunc); + res = priv->eventfunc (internal, parent, event); + + return res; +} + +/* + translate_outgoing_position_query + + Should only be called: + _ if the query is a GST_QUERY_POSITION + _ after the query was sent upstream + _ if the upstream query returned TRUE +*/ + +static gboolean +translate_incoming_position_query (NleObject * object, GstQuery * query) +{ + GstFormat format; + gint64 cur, cur2; + + gst_query_parse_position (query, &format, &cur); + if (G_UNLIKELY (format != GST_FORMAT_TIME)) { + GST_WARNING_OBJECT (object, + "position query is in a format different from time, returning without modifying values"); + goto beach; + } + + nle_media_to_object_time (object, (guint64) cur, (guint64 *) & cur2); + + GST_DEBUG_OBJECT (object, + "Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, + GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2)); + gst_query_set_position (query, GST_FORMAT_TIME, cur2); + +beach: + return TRUE; +} + +static gboolean +translate_outgoing_position_query (NleObject * object, GstQuery * query) +{ + GstFormat format; + gint64 cur, cur2; + + gst_query_parse_position (query, &format, &cur); + if (G_UNLIKELY (format != GST_FORMAT_TIME)) { + GST_WARNING_OBJECT (object, + "position query is in a format different from time, returning without modifying values"); + goto beach; + } + + if (G_UNLIKELY (!(nle_object_to_media_time (object, (guint64) cur, + (guint64 *) & cur2)))) { + GST_WARNING_OBJECT (object, + "Couldn't get media time for %" GST_TIME_FORMAT, GST_TIME_ARGS (cur)); + goto beach; + } + + GST_DEBUG_OBJECT (object, + "Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, + GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2)); + gst_query_set_position (query, GST_FORMAT_TIME, cur2); + +beach: + return TRUE; +} + +static gboolean +translate_incoming_duration_query (NleObject * object, GstQuery * query) +{ + GstFormat format; + gint64 cur; + + gst_query_parse_duration (query, &format, &cur); + if (G_UNLIKELY (format != GST_FORMAT_TIME)) { + GST_WARNING_OBJECT (object, + "We can only handle duration queries in GST_FORMAT_TIME"); + return FALSE; + } + + gst_query_set_duration (query, GST_FORMAT_TIME, object->duration); + + return TRUE; +} + +static gboolean +internalpad_query_function (GstPad * internal, GstObject * parent, + GstQuery * query) +{ + NlePadPrivate *priv = gst_pad_get_element_private (internal); + NleObject *object = priv->object; + gboolean ret; + + GST_DEBUG_OBJECT (internal, "querytype:%s", + gst_query_type_get_name (GST_QUERY_TYPE (query))); + + if (!(priv->queryfunc)) { + GST_WARNING_OBJECT (internal, + "priv->queryfunc == NULL !! What is going on ?"); + return FALSE; + } + + if ((ret = priv->queryfunc (internal, parent, query))) { + + switch (priv->dir) { + case GST_PAD_SRC: + break; + case GST_PAD_SINK: + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + ret = translate_outgoing_position_query (object, query); + break; + default: + break; + } + break; + default: + break; + } + } + return ret; +} + +static gboolean +ghostpad_event_function (GstPad * ghostpad, GstObject * parent, + GstEvent * event) +{ + NlePadPrivate *priv; + NleObject *object; + gboolean ret = FALSE; + + priv = gst_pad_get_element_private (ghostpad); + object = priv->object; + + GST_DEBUG_OBJECT (ghostpad, "event:%s", GST_EVENT_TYPE_NAME (event)); + + if (G_UNLIKELY (priv->eventfunc == NULL)) + goto no_function; + + switch (priv->dir) { + case GST_PAD_SRC: + { + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + GstPad *target; + + event = nle_object_translate_incoming_seek (object, event); + if (!(target = gst_ghost_pad_get_target (GST_GHOST_PAD (ghostpad)))) { + g_assert ("Seeked a pad with not target SHOULD NOT HAPPEND"); + ret = FALSE; + event = NULL; + } else { + gst_object_unref (target); + } + } + break; + default: + break; + } + } + break; + case GST_PAD_SINK:{ + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT: + event = translate_incoming_segment (object, event); + break; + default: + break; + } + } + break; + default: + break; + } + + if (event) { + GST_DEBUG_OBJECT (ghostpad, "Calling priv->eventfunc"); + ret = priv->eventfunc (ghostpad, parent, event); + GST_DEBUG_OBJECT (ghostpad, "Returned from calling priv->eventfunc : %d", + ret); + } + + return ret; + + /* ERRORS */ +no_function: + { + GST_WARNING_OBJECT (ghostpad, + "priv->eventfunc == NULL !! What's going on ?"); + return FALSE; + } +} + +static gboolean +ghostpad_query_function (GstPad * ghostpad, GstObject * parent, + GstQuery * query) +{ + NlePadPrivate *priv = gst_pad_get_element_private (ghostpad); + NleObject *object = NLE_OBJECT (parent); + gboolean pret = TRUE; + + GST_DEBUG_OBJECT (ghostpad, "querytype:%s", GST_QUERY_TYPE_NAME (query)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_DURATION: + /* skip duration upstream query, we'll fill it in ourselves */ + break; + default: + pret = priv->queryfunc (ghostpad, parent, query); + } + + if (pret) { + /* translate result */ + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + pret = translate_incoming_position_query (object, query); + break; + case GST_QUERY_DURATION: + pret = translate_incoming_duration_query (object, query); + break; + default: + break; + } + } + + return pret; +} + +/* internal pad going away */ +static void +internal_pad_finalizing (NlePadPrivate * priv, GObject * pad G_GNUC_UNUSED) +{ + g_slice_free (NlePadPrivate, priv); +} + +static inline GstPad * +get_proxy_pad (GstPad * ghostpad) +{ + GValue item = { 0, }; + GstIterator *it; + GstPad *ret = NULL; + + it = gst_pad_iterate_internal_links (ghostpad); + g_assert (it); + gst_iterator_next (it, &item); + ret = g_value_dup_object (&item); + g_value_unset (&item); + g_assert (ret); + gst_iterator_free (it); + + return ret; +} + +static void +control_internal_pad (GstPad * ghostpad, NleObject * object) +{ + NlePadPrivate *priv; + NlePadPrivate *privghost; + GstPad *internal; + + if (!ghostpad) { + GST_DEBUG_OBJECT (object, "We don't have a valid ghostpad !"); + return; + } + privghost = gst_pad_get_element_private (ghostpad); + + GST_LOG_OBJECT (ghostpad, "overriding ghostpad's internal pad function"); + + internal = get_proxy_pad (ghostpad); + + if (G_UNLIKELY (!(priv = gst_pad_get_element_private (internal)))) { + GST_DEBUG_OBJECT (internal, + "Creating a NlePadPrivate to put in element_private"); + priv = g_slice_new0 (NlePadPrivate); + + /* Remember existing pad functions */ + priv->eventfunc = GST_PAD_EVENTFUNC (internal); + priv->queryfunc = GST_PAD_QUERYFUNC (internal); + gst_pad_set_element_private (internal, priv); + + g_object_weak_ref ((GObject *) internal, + (GWeakNotify) internal_pad_finalizing, priv); + + /* add query/event function overrides on internal pad */ + gst_pad_set_event_function (internal, + GST_DEBUG_FUNCPTR (internalpad_event_function)); + gst_pad_set_query_function (internal, + GST_DEBUG_FUNCPTR (internalpad_query_function)); + } + + priv->object = object; + priv->ghostpriv = privghost; + priv->dir = GST_PAD_DIRECTION (ghostpad); + gst_object_unref (internal); + + GST_DEBUG_OBJECT (ghostpad, "Done with pad %s:%s", + GST_DEBUG_PAD_NAME (ghostpad)); +} + + +/** + * nle_object_ghost_pad: + * @object: #NleObject to add the ghostpad to + * @name: Name for the new pad + * @target: Target #GstPad to ghost + * + * Adds a #GstGhostPad overridding the correct pad [query|event]_function so + * that time shifting is done correctly + * The #GstGhostPad is added to the #NleObject + * + * /!\ This function doesn't check if the existing [src|sink] pad was removed + * first, so you might end up with more pads than wanted + * + * Returns: The #GstPad if everything went correctly, else NULL. + */ +GstPad * +nle_object_ghost_pad (NleObject * object, const gchar * name, GstPad * target) +{ + GstPadDirection dir = GST_PAD_DIRECTION (target); + GstPad *ghost; + + GST_DEBUG_OBJECT (object, "name:%s, target:%p", name, target); + + g_return_val_if_fail (target, FALSE); + g_return_val_if_fail ((dir != GST_PAD_UNKNOWN), FALSE); + + ghost = nle_object_ghost_pad_no_target (object, name, dir, NULL); + if (!ghost) { + GST_WARNING_OBJECT (object, "Couldn't create ghostpad"); + return NULL; + } + + if (!(nle_object_ghost_pad_set_target (object, ghost, target))) { + GST_WARNING_OBJECT (object, + "Couldn't set the target pad... removing ghostpad"); + gst_object_unref (ghost); + return NULL; + } + + GST_DEBUG_OBJECT (object, "activating ghostpad"); + /* activate pad */ + gst_pad_set_active (ghost, TRUE); + /* add it to element */ + if (!(gst_element_add_pad (GST_ELEMENT (object), ghost))) { + GST_WARNING ("couldn't add newly created ghostpad"); + return NULL; + } + + return ghost; +} + +/* + * nle_object_ghost_pad_no_target: + * /!\ Doesn't add the pad to the NleObject.... + */ +GstPad * +nle_object_ghost_pad_no_target (NleObject * object, const gchar * name, + GstPadDirection dir, GstPadTemplate * template) +{ + GstPad *ghost; + NlePadPrivate *priv; + + /* create a no_target ghostpad */ + if (template) + ghost = gst_ghost_pad_new_no_target_from_template (name, template); + else + ghost = gst_ghost_pad_new_no_target (name, dir); + if (!ghost) + return NULL; + + + /* remember the existing ghostpad event/query/link/unlink functions */ + priv = g_slice_new0 (NlePadPrivate); + priv->dir = dir; + priv->object = object; + + /* grab/replace event/query functions */ + GST_DEBUG_OBJECT (ghost, "Setting priv->eventfunc to %p", + GST_PAD_EVENTFUNC (ghost)); + priv->eventfunc = GST_PAD_EVENTFUNC (ghost); + priv->queryfunc = GST_PAD_QUERYFUNC (ghost); + + gst_pad_set_event_function (ghost, + GST_DEBUG_FUNCPTR (ghostpad_event_function)); + gst_pad_set_query_function (ghost, + GST_DEBUG_FUNCPTR (ghostpad_query_function)); + + gst_pad_set_element_private (ghost, priv); + control_internal_pad (ghost, object); + + return ghost; +} + + + +void +nle_object_remove_ghost_pad (NleObject * object, GstPad * ghost) +{ + NlePadPrivate *priv; + + GST_DEBUG_OBJECT (object, "ghostpad %s:%s", GST_DEBUG_PAD_NAME (ghost)); + + priv = gst_pad_get_element_private (ghost); + gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), NULL); + gst_element_remove_pad (GST_ELEMENT (object), ghost); + if (priv) + g_slice_free (NlePadPrivate, priv); +} + +gboolean +nle_object_ghost_pad_set_target (NleObject * object, GstPad * ghost, + GstPad * target) +{ + NlePadPrivate *priv = gst_pad_get_element_private (ghost); + + g_return_val_if_fail (priv, FALSE); + g_return_val_if_fail (GST_IS_PAD (ghost), FALSE); + + if (target) { + GST_DEBUG_OBJECT (object, "setting target %s:%s on %s:%s", + GST_DEBUG_PAD_NAME (target), GST_DEBUG_PAD_NAME (ghost)); + } else { + GST_DEBUG_OBJECT (object, "removing target from ghostpad"); + priv->pending_seek = NULL; + } + + /* set target */ + if (!(gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), target))) { + GST_WARNING_OBJECT (priv->object, "Could not set ghost %s:%s " + "target to: %s:%s", GST_DEBUG_PAD_NAME (ghost), + GST_DEBUG_PAD_NAME (target)); + return FALSE; + } + + if (target && priv->pending_seek) { + gboolean res = gst_pad_send_event (ghost, priv->pending_seek); + + GST_INFO_OBJECT (object, "Sending our pending seek event: %" GST_PTR_FORMAT + " -- Result is %i", priv->pending_seek, res); + + priv->pending_seek = NULL; + } + + return TRUE; +} + +void +nle_init_ghostpad_category (void) +{ + GST_DEBUG_CATEGORY_INIT (nleghostpad, "nleghostpad", + GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin GhostPad"); + +} diff --git a/plugins/nle/nleghostpad.h b/plugins/nle/nleghostpad.h new file mode 100644 index 00000000..25fd3626 --- /dev/null +++ b/plugins/nle/nleghostpad.h @@ -0,0 +1,48 @@ +/* GStreamer + * Copyright (C) 2009 Edward Hervey + * + * nleghostpad.h: Header for helper ghostpad + * + * 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. + */ + + +#ifndef __NLE_GHOSTPAD_H__ +#define __NLE_GHOSTPAD_H__ + +#include + +#include "nletypes.h" + +G_BEGIN_DECLS + +GstPad *nle_object_ghost_pad (NleObject * object, + const gchar * name, GstPad * target) G_GNUC_INTERNAL; + +GstPad *nle_object_ghost_pad_no_target (NleObject * object, + const gchar * name, GstPadDirection dir, GstPadTemplate *templ) G_GNUC_INTERNAL; + +gboolean nle_object_ghost_pad_set_target (NleObject * object, + GstPad * ghost, GstPad * target) G_GNUC_INTERNAL; + +void nle_object_remove_ghost_pad (NleObject * object, GstPad * ghost) G_GNUC_INTERNAL; +GstEvent * nle_object_translate_incoming_seek (NleObject * object, GstEvent * event) G_GNUC_INTERNAL; + +void nle_init_ghostpad_category (void) G_GNUC_INTERNAL; + +G_END_DECLS + +#endif /* __NLE_GHOSTPAD_H__ */ diff --git a/plugins/nle/nleobject.c b/plugins/nle/nleobject.c new file mode 100644 index 00000000..94be16d2 --- /dev/null +++ b/plugins/nle/nleobject.c @@ -0,0 +1,738 @@ +/* Gnonlin + * Copyright (C) <2001> Wim Taymans + * <2004-2008> Edward Hervey + * + * 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 +#include "nle.h" + +/** + * SECTION:nleobject + * @short_description: Base class for GNonLin elements + * + * + * + * NleObject encapsulates default behaviour and implements standard + * properties provided by all the GNonLin elements. + * + * + * + */ + + +GST_DEBUG_CATEGORY_STATIC (nleobject_debug); +#define GST_CAT_DEFAULT nleobject_debug + +static GObjectClass *parent_class = NULL; + +/**************************************************** + * Helper macros * + ****************************************************/ +#define CHECK_AND_SET(PROPERTY, property, prop_str, print_format) \ +{ \ +if (object->pending_##property != object->property) { \ + object->property = object->pending_##property; \ + GST_DEBUG_OBJECT(object, "Setting " prop_str " to %" \ + print_format, object->property); \ +} else \ + GST_DEBUG_OBJECT(object, "Nothing to do for " prop_str); \ +} + +#define SET_PENDING_VALUE(property, property_str, type, print_format) \ +nleobject->pending_##property = g_value_get_##type (value); \ +if (nleobject->property != nleobject->pending_##property) { \ + GST_DEBUG_OBJECT(object, "Setting pending " property_str " to %" \ + print_format, nleobject->pending_##property); \ + nle_object_set_commit_needed (nleobject); \ +} else \ + GST_DEBUG_OBJECT(object, "Pending " property_str " did not change"); + +enum +{ + PROP_0, + PROP_START, + PROP_DURATION, + PROP_STOP, + PROP_INPOINT, + PROP_PRIORITY, + PROP_ACTIVE, + PROP_CAPS, + PROP_EXPANDABLE, + PROP_LAST +}; + +enum +{ + COMMIT_SIGNAL, + LAST_SIGNAL +}; + +static guint _signals[LAST_SIGNAL] = { 0 }; + +static GParamSpec *properties[PROP_LAST]; + +static void nle_object_dispose (GObject * object); + +static void nle_object_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void nle_object_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void nle_object_constructed (GObject * object); + +static GstStateChangeReturn nle_object_change_state (GstElement * element, + GstStateChange transition); + +static gboolean nle_object_prepare_func (NleObject * object); +static gboolean nle_object_cleanup_func (NleObject * object); +static gboolean nle_object_commit_func (NleObject * object, gboolean recurse); + +static GstStateChangeReturn nle_object_prepare (NleObject * object); + +static void +nle_object_class_init (NleObjectClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + NleObjectClass *nleobject_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + nleobject_class = (NleObjectClass *) klass; + GST_DEBUG_CATEGORY_INIT (nleobject_debug, "nleobject", + GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin object"); + parent_class = g_type_class_ref (GST_TYPE_BIN); + + gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_object_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_object_get_property); + gobject_class->constructed = GST_DEBUG_FUNCPTR (nle_object_constructed); + gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_object_dispose); + + gstelement_class->change_state = GST_DEBUG_FUNCPTR (nle_object_change_state); + + nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_object_prepare_func); + nleobject_class->cleanup = GST_DEBUG_FUNCPTR (nle_object_cleanup_func); + nleobject_class->commit_signal_handler = + GST_DEBUG_FUNCPTR (nle_object_commit); + nleobject_class->commit = GST_DEBUG_FUNCPTR (nle_object_commit_func); + + /** + * NleObject:start + * + * The start position relative to the parent in nanoseconds. + */ + properties[PROP_START] = g_param_spec_uint64 ("start", "Start", + "The start position relative to the parent (in nanoseconds)", + 0, G_MAXUINT64, 0, G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_START, + properties[PROP_START]); + + /** + * NleObject:duration + * + * The outgoing duration in nanoseconds. + */ + properties[PROP_DURATION] = g_param_spec_int64 ("duration", "Duration", + "Outgoing duration (in nanoseconds)", 0, G_MAXINT64, 0, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_DURATION, + properties[PROP_DURATION]); + + /** + * NleObject:stop + * + * The stop position relative to the parent in nanoseconds. + * + * This value is computed based on the values of start and duration. + */ + properties[PROP_STOP] = g_param_spec_uint64 ("stop", "Stop", + "The stop position relative to the parent (in nanoseconds)", + 0, G_MAXUINT64, 0, G_PARAM_READABLE); + g_object_class_install_property (gobject_class, PROP_STOP, + properties[PROP_STOP]); + + /** + * NleObject:inpoint + * + * The media start position in nanoseconds. + * + * Also called 'in-point' in video-editing, this corresponds to + * what position in the 'contained' object we should start outputting from. + */ + properties[PROP_INPOINT] = + g_param_spec_uint64 ("inpoint", "Media start", + "The media start position (in nanoseconds)", 0, G_MAXUINT64, + GST_CLOCK_TIME_NONE, G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_INPOINT, + properties[PROP_INPOINT]); + + /** + * NleObject:priority + * + * The priority of the object in the container. + * + * The highest priority is 0, meaning this object will be selected over + * any other between start and stop. + * + * The lowest priority is G_MAXUINT32. + * + * Objects whose priority is (-1) will be considered as 'default' objects + * in NleComposition and their start/stop values will be modified as to + * fit the whole duration of the composition. + */ + properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority", + "The priority of the object (0 = highest priority)", 0, G_MAXUINT, 0, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_PRIORITY, + properties[PROP_PRIORITY]); + + /** + * NleObject:active + * + * Indicates whether this object should be used by its container. + * + * Set to #TRUE to temporarily disable this object in a #NleComposition. + */ + properties[PROP_ACTIVE] = g_param_spec_boolean ("active", "Active", + "Use this object in the NleComposition", TRUE, G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_ACTIVE, + properties[PROP_ACTIVE]); + + /** + * NleObject:caps + * + * Caps used to filter/choose the output stream. + * + * If the controlled object produces several stream, you can set this + * property to choose a specific stream. + * + * If nothing is specified then a source pad will be chosen at random. + */ + properties[PROP_CAPS] = g_param_spec_boxed ("caps", "Caps", + "Caps used to filter/choose the output stream", + GST_TYPE_CAPS, G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_CAPS, + properties[PROP_CAPS]); + + /** + * NleObject:expandable + * + * Indicates whether this object should expand to the full duration of its + * container #NleComposition. + */ + properties[PROP_EXPANDABLE] = + g_param_spec_boolean ("expandable", "Expandable", + "Expand to the full duration of the container composition", FALSE, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, PROP_EXPANDABLE, + properties[PROP_EXPANDABLE]); + + /** + * NleObject::commit + * @object: a #NleObject + * @recurse: Whether to commit recursiverly into (NleComposition) children of + * @object. This is used in case we have composition inside + * a nlesource composition, telling it to commit the included + * composition state. + * + * Action signal to commit all the pending changes of the composition and + * its children timing properties + * + * Returns: %TRUE if changes have been commited, %FALSE if nothing had to + * be commited + */ + _signals[COMMIT_SIGNAL] = g_signal_new ("commit", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (NleObjectClass, commit_signal_handler), NULL, NULL, NULL, + G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); +} + +static void +nle_object_init (NleObject * object, NleObjectClass * klass) +{ + object->start = object->pending_start = 0; + object->duration = object->pending_duration = 0; + object->stop = 0; + + object->inpoint = object->pending_inpoint = GST_CLOCK_TIME_NONE; + object->priority = object->pending_priority = 0; + object->active = object->pending_active = TRUE; + + object->caps = gst_caps_new_any (); + + object->segment_rate = 1.0; + object->segment_start = -1; + object->segment_stop = -1; + + object->srcpad = nle_object_ghost_pad_no_target (object, + "src", GST_PAD_SRC, + gst_element_class_get_pad_template ((GstElementClass *) klass, "src")); + + gst_element_add_pad (GST_ELEMENT (object), object->srcpad); +} + +static void +nle_object_dispose (GObject * object) +{ + NleObject *nle = (NleObject *) object; + + if (nle->caps) { + gst_caps_unref (nle->caps); + nle->caps = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +/** + * nle_object_to_media_time: + * @object: a #NleObject + * @objecttime: The #GstClockTime we want to convert + * @mediatime: A pointer on a #GstClockTime to fill + * + * Converts a #GstClockTime from the object (container) context to the media context + * + * Returns: TRUE if @objecttime was within the limits of the @object start/stop time, + * FALSE otherwise + */ +gboolean +nle_object_to_media_time (NleObject * object, GstClockTime otime, + GstClockTime * mtime) +{ + g_return_val_if_fail (mtime, FALSE); + + GST_DEBUG_OBJECT (object, "ObjectTime : %" GST_TIME_FORMAT, + GST_TIME_ARGS (otime)); + + GST_DEBUG_OBJECT (object, + "Start/Stop:[%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "] " + "Media start: %" GST_TIME_FORMAT, GST_TIME_ARGS (object->start), + GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->inpoint)); + + /* limit check */ + if (G_UNLIKELY ((otime < object->start))) { + GST_DEBUG_OBJECT (object, "ObjectTime is before start"); + *mtime = (object->inpoint == GST_CLOCK_TIME_NONE) ? 0 : object->inpoint; + return FALSE; + } + + if (G_UNLIKELY ((otime >= object->stop))) { + GST_DEBUG_OBJECT (object, "ObjectTime is after stop"); + if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint))) + *mtime = object->inpoint + object->duration; + else + *mtime = object->stop - object->start; + return FALSE; + } + + if (G_UNLIKELY (object->inpoint == GST_CLOCK_TIME_NONE)) { + /* no time shifting, for live sources ? */ + *mtime = otime - object->start; + } else { + *mtime = otime - object->start + object->inpoint; + } + + GST_DEBUG_OBJECT (object, "Returning MediaTime : %" GST_TIME_FORMAT, + GST_TIME_ARGS (*mtime)); + + return TRUE; +} + +/** + * nle_media_to_object_time: + * @object: The #NleObject + * @mediatime: The #GstClockTime we want to convert + * @objecttime: A pointer on a #GstClockTime to fill + * + * Converts a #GstClockTime from the media context to the object (container) context + * + * Returns: TRUE if @objecttime was within the limits of the @object media start/stop time, + * FALSE otherwise + */ + +gboolean +nle_media_to_object_time (NleObject * object, GstClockTime mtime, + GstClockTime * otime) +{ + g_return_val_if_fail (otime, FALSE); + + GST_DEBUG_OBJECT (object, "MediaTime : %" GST_TIME_FORMAT, + GST_TIME_ARGS (mtime)); + + GST_DEBUG_OBJECT (object, + "Start/Stop:[%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "] " + "inpoint %" GST_TIME_FORMAT, GST_TIME_ARGS (object->start), + GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->inpoint)); + + + /* limit check */ + if (G_UNLIKELY ((object->inpoint != GST_CLOCK_TIME_NONE) + && (mtime < object->inpoint))) { + GST_DEBUG_OBJECT (object, "media time is before inpoint, forcing to start"); + *otime = object->start; + return FALSE; + } + + if (G_LIKELY (object->inpoint != GST_CLOCK_TIME_NONE)) { + *otime = mtime - object->inpoint + object->start; + } else + *otime = mtime + object->start; + + GST_DEBUG_OBJECT (object, "Returning ObjectTime : %" GST_TIME_FORMAT, + GST_TIME_ARGS (*otime)); + return TRUE; +} + +static gboolean +nle_object_prepare_func (NleObject * object) +{ + GST_DEBUG_OBJECT (object, "default prepare function, returning TRUE"); + + return TRUE; +} + +static GstStateChangeReturn +nle_object_prepare (NleObject * object) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + GST_DEBUG_OBJECT (object, "preparing"); + + if (!(NLE_OBJECT_GET_CLASS (object)->prepare (object))) + ret = GST_STATE_CHANGE_FAILURE; + + GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret); + + return ret; +} + +static gboolean +nle_object_cleanup_func (NleObject * object) +{ + GST_DEBUG_OBJECT (object, "default cleanup function, returning TRUE"); + + return TRUE; +} + +GstStateChangeReturn +nle_object_cleanup (NleObject * object) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + GST_DEBUG_OBJECT (object, "cleaning-up"); + + if (!(NLE_OBJECT_GET_CLASS (object)->cleanup (object))) + ret = GST_STATE_CHANGE_FAILURE; + + GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret); + + return ret; +} + +void +nle_object_set_caps (NleObject * object, const GstCaps * caps) +{ + if (object->caps) + gst_caps_unref (object->caps); + + object->caps = gst_caps_copy (caps); +} + +static inline void +_update_stop (NleObject * nleobject) +{ + /* check if start/duration has changed */ + + if ((nleobject->pending_start + nleobject->pending_duration) != + nleobject->stop) { + nleobject->stop = nleobject->pending_start + nleobject->pending_duration; + + GST_LOG_OBJECT (nleobject, + "Updating stop value : %" GST_TIME_FORMAT " [start:%" GST_TIME_FORMAT + ", duration:%" GST_TIME_FORMAT "]", GST_TIME_ARGS (nleobject->stop), + GST_TIME_ARGS (nleobject->pending_start), + GST_TIME_ARGS (nleobject->pending_duration)); + g_object_notify_by_pspec (G_OBJECT (nleobject), properties[PROP_STOP]); + } +} + +static void +update_values (NleObject * object) +{ + CHECK_AND_SET (START, start, "start", G_GUINT64_FORMAT); + CHECK_AND_SET (INPOINT, inpoint, "inpoint", G_GUINT64_FORMAT); + CHECK_AND_SET (DURATION, duration, "duration", G_GINT64_FORMAT); + CHECK_AND_SET (PRIORITY, priority, "priority", G_GUINT32_FORMAT); + CHECK_AND_SET (ACTIVE, active, "active", G_GUINT32_FORMAT); + + _update_stop (object); +} + +static gboolean +nle_object_commit_func (NleObject * object, gboolean recurse) +{ + GST_DEBUG_OBJECT (object, "Commiting object changed"); + + if (object->commit_needed == FALSE) { + GST_INFO_OBJECT (object, "No changes to commit"); + + return FALSE; + } + + update_values (object); + + GST_DEBUG_OBJECT (object, "Done commiting"); + + return TRUE; +} + +static void +nle_object_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + NleObject *nleobject = (NleObject *) object; + + g_return_if_fail (NLE_IS_OBJECT (object)); + + GST_OBJECT_LOCK (object); + switch (prop_id) { + case PROP_START: + SET_PENDING_VALUE (start, "start", uint64, G_GUINT64_FORMAT); + break; + case PROP_DURATION: + SET_PENDING_VALUE (duration, "duration", int64, G_GINT64_FORMAT); + break; + case PROP_INPOINT: + SET_PENDING_VALUE (inpoint, "inpoint", uint64, G_GUINT64_FORMAT); + break; + case PROP_PRIORITY: + SET_PENDING_VALUE (priority, "priority", uint, G_GUINT32_FORMAT); + break; + case PROP_ACTIVE: + SET_PENDING_VALUE (active, "active", boolean, G_GUINT32_FORMAT); + break; + case PROP_CAPS: + nle_object_set_caps (nleobject, gst_value_get_caps (value)); + break; + case PROP_EXPANDABLE: + if (g_value_get_boolean (value)) + GST_OBJECT_FLAG_SET (nleobject, NLE_OBJECT_EXPANDABLE); + else + GST_OBJECT_FLAG_UNSET (nleobject, NLE_OBJECT_EXPANDABLE); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_OBJECT_UNLOCK (object); +} + +static void +nle_object_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + NleObject *nleobject = (NleObject *) object; + + switch (prop_id) { + case PROP_START: + g_value_set_uint64 (value, nleobject->pending_start); + break; + case PROP_DURATION: + g_value_set_int64 (value, nleobject->pending_duration); + break; + case PROP_STOP: + g_value_set_uint64 (value, nleobject->stop); + break; + case PROP_INPOINT: + g_value_set_uint64 (value, nleobject->pending_inpoint); + break; + case PROP_PRIORITY: + g_value_set_uint (value, nleobject->pending_priority); + break; + case PROP_ACTIVE: + g_value_set_boolean (value, nleobject->pending_active); + break; + case PROP_CAPS: + gst_value_set_caps (value, nleobject->caps); + break; + case PROP_EXPANDABLE: + g_value_set_boolean (value, NLE_OBJECT_IS_EXPANDABLE (object)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nle_object_constructed (GObject * object) +{ + NleObject *nleobject = (NleObject *) object; + + _update_stop (nleobject); +} + +static GstStateChangeReturn +nle_object_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + { + GstObject *parent = gst_object_get_parent (GST_OBJECT (element)); + + /* Going to READY and if we are not in a composition, we need to make + * sure that the object positioning state is properly commited */ + if (parent) { + if (g_strcmp0 (gst_element_get_name (GST_ELEMENT (parent)), + "current-bin") + && !NLE_OBJECT_IS_COMPOSITION (NLE_OBJECT (element))) { + GST_INFO ("Adding nleobject to something that is not a composition," + " commiting ourself"); + nle_object_commit (NLE_OBJECT (element), FALSE); + } + + gst_object_unref (parent); + } + } + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (nle_object_prepare (NLE_OBJECT (element)) == GST_STATE_CHANGE_FAILURE) { + ret = GST_STATE_CHANGE_FAILURE; + goto beach; + } + break; + default: + break; + } + + GST_DEBUG_OBJECT (element, "Calling parent change_state"); + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + GST_DEBUG_OBJECT (element, "Return from parent change_state was %d", ret); + + if (ret == GST_STATE_CHANGE_FAILURE) + goto beach; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + /* cleanup nleobject */ + if (nle_object_cleanup (NLE_OBJECT (element)) == GST_STATE_CHANGE_FAILURE) + ret = GST_STATE_CHANGE_FAILURE; + break; + default: + break; + } + +beach: + return ret; +} + +void +nle_object_set_commit_needed (NleObject * object) +{ + if (G_UNLIKELY (object->commiting)) { + GST_WARNING_OBJECT (object, + "Trying to set 'commit-needed' while commiting"); + + return; + } + + GST_DEBUG_OBJECT (object, "Setting 'commit_needed'"); + object->commit_needed = TRUE; +} + +gboolean +nle_object_commit (NleObject * object, gboolean recurse) +{ + gboolean ret; + + GST_DEBUG_OBJECT (object, "Commiting object state"); + + object->commiting = TRUE; + ret = NLE_OBJECT_GET_CLASS (object)->commit (object, recurse); + object->commiting = FALSE; + + return ret; + +} + +static void +_send_seek_event (const GValue * item, gpointer seek_event) +{ + GstElement *child = g_value_get_object (item); + + gst_element_send_event (child, gst_event_ref (seek_event)); +} + +void +nle_object_seek_all_children (NleObject * object, GstEvent * seek_event) +{ + GstIterator *it = gst_bin_iterate_recurse (GST_BIN (object)); + + while (gst_iterator_foreach (it, _send_seek_event, + seek_event) == GST_ITERATOR_RESYNC) + gst_iterator_resync (it); + + gst_iterator_free (it); + gst_event_unref (seek_event); +} + +void +nle_object_reset (NleObject * object) +{ + GST_INFO_OBJECT (object, "Resetting child timing values to default"); + + object->start = 0; + object->duration = 0; + object->stop = 0; + object->inpoint = GST_CLOCK_TIME_NONE; + object->priority = 0; + object->active = TRUE; +} + +GType +nle_object_get_type (void) +{ + static volatile gsize type = 0; + + if (g_once_init_enter (&type)) { + GType _type; + static const GTypeInfo info = { + sizeof (NleObjectClass), + NULL, + NULL, + (GClassInitFunc) nle_object_class_init, + NULL, + NULL, + sizeof (NleObject), + 0, + (GInstanceInitFunc) nle_object_init, + }; + + _type = g_type_register_static (GST_TYPE_BIN, + "NleObject", &info, G_TYPE_FLAG_ABSTRACT); + g_once_init_leave (&type, _type); + } + return type; +} diff --git a/plugins/nle/nleobject.h b/plugins/nle/nleobject.h new file mode 100644 index 00000000..1e0272e4 --- /dev/null +++ b/plugins/nle/nleobject.h @@ -0,0 +1,173 @@ +/* GStreamer + * Copyright (C) 2001 Wim Taymans + * 2004-2008 Edward Hervey + * + * nleobject.h: Header for base NleObject + * + * 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. + */ + + +#ifndef __NLE_OBJECT_H__ +#define __NLE_OBJECT_H__ + +#include + +#include "nletypes.h" + +G_BEGIN_DECLS +#define NLE_TYPE_OBJECT \ + (nle_object_get_type()) +#define NLE_OBJECT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_OBJECT,NleObject)) +#define NLE_OBJECT_CAST(obj) ((NleObject*) (obj)) +#define NLE_OBJECT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_OBJECT,NleObjectClass)) +#define NLE_OBJECT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), NLE_TYPE_OBJECT, NleObjectClass)) +#define NLE_IS_OBJECT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_OBJECT)) +#define NLE_IS_OBJECT_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_OBJECT)) + +#define NLE_OBJECT_SRC(obj) (((NleObject *) obj)->srcpad) + +/** + * NleObjectFlags: + * @NLE_OBJECT_IS_SOURCE: + * @NLE_OBJECT_IS_OPERATION: + * @NLE_OBJECT_IS_EXPANDABLE: The #NleObject start/stop will extend accross the full composition. + * @NLE_OBJECT_LAST_FLAG: +*/ + +typedef enum +{ + NLE_OBJECT_SOURCE = (GST_BIN_FLAG_LAST << 0), + NLE_OBJECT_OPERATION = (GST_BIN_FLAG_LAST << 1), + NLE_OBJECT_EXPANDABLE = (GST_BIN_FLAG_LAST << 2), + NLE_OBJECT_COMPOSITION = (GST_BIN_FLAG_LAST << 3), + /* padding */ + NLE_OBJECT_LAST_FLAG = (GST_BIN_FLAG_LAST << 5) +} NleObjectFlags; + + +#define NLE_OBJECT_IS_SOURCE(obj) \ + (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_SOURCE)) +#define NLE_OBJECT_IS_OPERATION(obj) \ + (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_OPERATION)) +#define NLE_OBJECT_IS_EXPANDABLE(obj) \ + (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_EXPANDABLE)) +#define NLE_OBJECT_IS_COMPOSITION(obj) \ + (GST_OBJECT_FLAG_IS_SET(obj, NLE_OBJECT_COMPOSITION)) + +/* For internal usage only */ +#define NLE_OBJECT_START(obj) (NLE_OBJECT_CAST (obj)->start) +#define NLE_OBJECT_STOP(obj) (NLE_OBJECT_CAST (obj)->stop) +#define NLE_OBJECT_DURATION(obj) (NLE_OBJECT_CAST (obj)->duration) +#define NLE_OBJECT_INPOINT(obj) (NLE_OBJECT_CAST (obj)->inpoint) +#define NLE_OBJECT_PRIORITY(obj) (NLE_OBJECT_CAST (obj)->priority) +#define NLE_OBJECT_ACTIVE(obj) (NLE_OBJECT_DURATION(obj) > 0 && NLE_OBJECT_CAST (obj)->active) + +#define NLE_OBJECT_IS_COMMITING(obj) (NLE_OBJECT_CAST (obj)->commiting) + +struct _NleObject +{ + GstBin parent; + + GstPad *srcpad; + + /* Time positionning */ + GstClockTime start; + GstClockTime inpoint; + GstClockTimeDiff duration; + + /* Pending time positionning + * Should be == GST_CLOCK_TIME_NONE when nothing to do + */ + GstClockTime pending_start; + GstClockTime pending_inpoint; + GstClockTimeDiff pending_duration; + guint32 pending_priority; + gboolean pending_active; + + gboolean commit_needed; + gboolean commiting; /* Set to TRUE during the commiting time only */ + + gboolean expandable; + + /* read-only */ + GstClockTime stop; + + /* priority in parent */ + guint32 priority; + + /* active in parent */ + gboolean active; + + /* Filtering caps */ + GstCaps *caps; + + /* current segment seek */ + gdouble segment_rate; + GstSeekFlags segment_flags; + gint64 segment_start; + gint64 segment_stop; + + gboolean in_composition; +}; + +struct _NleObjectClass +{ + GstBinClass parent_class; + + /* Signal method handler */ + gboolean (*commit_signal_handler) (NleObject * object, gboolean recurse); + + /* virtual methods for subclasses */ + gboolean (*prepare) (NleObject * object); + gboolean (*cleanup) (NleObject * object); + gboolean (*commit) (NleObject * object, gboolean recurse); +}; + +GType nle_object_get_type (void)G_GNUC_INTERNAL; + +gboolean +nle_object_to_media_time (NleObject * object, GstClockTime otime, + GstClockTime * mtime) G_GNUC_INTERNAL; + +gboolean +nle_media_to_object_time (NleObject * object, GstClockTime mtime, + GstClockTime * otime) G_GNUC_INTERNAL; + +void +nle_object_set_caps (NleObject * object, const GstCaps * caps) G_GNUC_INTERNAL; + +void +nle_object_set_commit_needed (NleObject *object) G_GNUC_INTERNAL; + +gboolean +nle_object_commit (NleObject *object, gboolean recurse) G_GNUC_INTERNAL; + +void +nle_object_reset (NleObject *object) G_GNUC_INTERNAL; + +GstStateChangeReturn +nle_object_cleanup (NleObject * object) G_GNUC_INTERNAL; + +void nle_object_seek_all_children (NleObject *object, GstEvent *seek_event) G_GNUC_INTERNAL; + +G_END_DECLS +#endif /* __NLE_OBJECT_H__ */ diff --git a/plugins/nle/nleoperation.c b/plugins/nle/nleoperation.c new file mode 100644 index 00000000..fa59734c --- /dev/null +++ b/plugins/nle/nleoperation.c @@ -0,0 +1,843 @@ +/* GStreamer + * Copyright (C) 2001 Wim Taymans + * 2004-2008 Edward Hervey + * + * 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-nleoperation + * + * + * + * A NleOperation performs a transformation or mixing operation on the + * data from one or more #NleSources, which is used to implement filters or + * effects. + * + * + */ + +static GstStaticPadTemplate nle_operation_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate nle_operation_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS_ANY); + +GST_DEBUG_CATEGORY_STATIC (nleoperation); +#define GST_CAT_DEFAULT nleoperation + +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (nleoperation, "nleoperation", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Operation element"); +#define nle_operation_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (NleOperation, nle_operation, NLE_TYPE_OBJECT, + _do_init); + +enum +{ + ARG_0, + ARG_SINKS, +}; + +enum +{ + INPUT_PRIORITY_CHANGED, + LAST_SIGNAL +}; + +static guint nle_operation_signals[LAST_SIGNAL] = { 0 }; + +static void nle_operation_dispose (GObject * object); + +static void nle_operation_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void nle_operation_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean nle_operation_prepare (NleObject * object); +static gboolean nle_operation_cleanup (NleObject * object); + +static gboolean nle_operation_add_element (GstBin * bin, GstElement * element); +static gboolean nle_operation_remove_element (GstBin * bin, + GstElement * element); + +static GstPad *nle_operation_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name, const GstCaps * caps); +static void nle_operation_release_pad (GstElement * element, GstPad * pad); + +static void synchronize_sinks (NleOperation * operation); +static gboolean remove_sink_pad (NleOperation * operation, GstPad * sinkpad); + + +static gboolean +nle_operation_send_event (GstElement * element, GstEvent * event) +{ + gboolean res = TRUE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + nle_object_seek_all_children (NLE_OBJECT (element), event); + break; + default: + res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event); + break; + } + + return res; +} + +static void +nle_operation_class_init (NleOperationClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstBinClass *gstbin_class = (GstBinClass *) klass; + + GstElementClass *gstelement_class = (GstElementClass *) klass; + NleObjectClass *nleobject_class = (NleObjectClass *) klass; + + gst_element_class_set_static_metadata (gstelement_class, "GNonLin Operation", + "Filter/Editor", + "Encapsulates filters/effects for use with NLE Objects", + "Wim Taymans , Edward Hervey "); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (nle_operation_dispose); + + gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_operation_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_operation_get_property); + + /** + * NleOperation:sinks: + * + * Specifies the number of sink pads the operation should provide. + * If the sinks property is -1 (the default) pads are only created as + * demanded via get_request_pad() calls on the element. + */ + g_object_class_install_property (gobject_class, ARG_SINKS, + g_param_spec_int ("sinks", "Sinks", + "Number of input sinks (-1 for automatic handling)", -1, G_MAXINT, -1, + G_PARAM_READWRITE)); + + /** + * NleOperation:input-priority-changed: + * @pad: The operation's input pad whose priority changed. + * @priority: The new priority + * + * Signals that the @priority of the stream being fed to the given @pad + * might have changed. + */ + nle_operation_signals[INPUT_PRIORITY_CHANGED] = + g_signal_new ("input-priority-changed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NleOperationClass, + input_priority_changed), NULL, NULL, g_cclosure_marshal_generic, + G_TYPE_NONE, 2, GST_TYPE_PAD, G_TYPE_UINT); + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (nle_operation_request_new_pad); + gstelement_class->release_pad = GST_DEBUG_FUNCPTR (nle_operation_release_pad); + gstelement_class->send_event = GST_DEBUG_FUNCPTR (nle_operation_send_event); + + gstbin_class->add_element = GST_DEBUG_FUNCPTR (nle_operation_add_element); + gstbin_class->remove_element = + GST_DEBUG_FUNCPTR (nle_operation_remove_element); + + nleobject_class->prepare = GST_DEBUG_FUNCPTR (nle_operation_prepare); + nleobject_class->cleanup = GST_DEBUG_FUNCPTR (nle_operation_cleanup); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&nle_operation_src_template)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&nle_operation_sink_template)); + +} + +static void +nle_operation_dispose (GObject * object) +{ + NleOperation *oper = (NleOperation *) object; + + GST_DEBUG_OBJECT (object, "Disposing of source pad"); + + nle_object_ghost_pad_set_target (NLE_OBJECT (object), + NLE_OBJECT (object)->srcpad, NULL); + + GST_DEBUG_OBJECT (object, "Disposing of sink pad(s)"); + while (oper->sinks) { + GstPad *ghost = (GstPad *) oper->sinks->data; + remove_sink_pad (oper, ghost); + } + + GST_DEBUG_OBJECT (object, "Done, calling parent class ::dispose()"); + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +nle_operation_reset (NleOperation * operation) +{ + operation->num_sinks = 1; + operation->realsinks = 0; + operation->next_base_time = 0; +} + +static void +nle_operation_init (NleOperation * operation) +{ + nle_operation_reset (operation); + operation->element = NULL; +} + +static gboolean +element_is_valid_filter (GstElement * element, gboolean * isdynamic) +{ + gboolean havesink = FALSE; + gboolean havesrc = FALSE; + gboolean done = FALSE; + GstIterator *pads; + GValue item = { 0, }; + + if (isdynamic) + *isdynamic = FALSE; + + pads = gst_element_iterate_pads (element); + + while (!done) { + switch (gst_iterator_next (pads, &item)) { + case GST_ITERATOR_OK: + { + GstPad *pad = g_value_get_object (&item); + + if (gst_pad_get_direction (pad) == GST_PAD_SRC) + havesrc = TRUE; + else if (gst_pad_get_direction (pad) == GST_PAD_SINK) + havesink = TRUE; + + g_value_reset (&item); + break; + } + case GST_ITERATOR_RESYNC: + gst_iterator_resync (pads); + havesrc = FALSE; + havesink = FALSE; + break; + default: + /* ERROR and DONE */ + done = TRUE; + break; + } + } + + g_value_unset (&item); + gst_iterator_free (pads); + + /* just look at the element's class, not the factory, since there might + * not be a factory (in case of python elements) or the factory is the + * wrong one (in case of a GstBin sub-class) and doesn't have complete + * information. */ + { + GList *tmp = + gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS + (element)); + + while (tmp) { + GstPadTemplate *template = (GstPadTemplate *) tmp->data; + + if (template->direction == GST_PAD_SRC) + havesrc = TRUE; + else if (template->direction == GST_PAD_SINK) { + if (!havesink && (template->presence == GST_PAD_REQUEST) && isdynamic) + *isdynamic = TRUE; + havesink = TRUE; + } + tmp = tmp->next; + } + } + return (havesink && havesrc); +} + +/* + * get_src_pad: + * element: a #GstElement + * + * Returns: The src pad for the given element. A reference was added to the + * returned pad, remove it when you don't need that pad anymore. + * Returns NULL if there's no source pad. + */ + +static GstPad * +get_src_pad (GstElement * element) +{ + GstIterator *it; + GstIteratorResult itres; + GValue item = { 0, }; + GstPad *srcpad = NULL; + + it = gst_element_iterate_src_pads (element); + itres = gst_iterator_next (it, &item); + if (itres != GST_ITERATOR_OK) { + GST_DEBUG ("%s doesn't have a src pad !", GST_ELEMENT_NAME (element)); + } else { + srcpad = g_value_get_object (&item); + gst_object_ref (srcpad); + } + g_value_reset (&item); + gst_iterator_free (it); + + return srcpad; +} + +/* get_nb_static_sinks: + * + * Returns : The number of static sink pads of the controlled element. + */ +static guint +get_nb_static_sinks (NleOperation * oper) +{ + GstIterator *sinkpads; + gboolean done = FALSE; + guint nbsinks = 0; + GValue item = { 0, }; + + sinkpads = gst_element_iterate_sink_pads (oper->element); + + while (!done) { + switch (gst_iterator_next (sinkpads, &item)) { + case GST_ITERATOR_OK:{ + nbsinks++; + g_value_unset (&item); + } + break; + case GST_ITERATOR_RESYNC: + nbsinks = 0; + gst_iterator_resync (sinkpads); + break; + default: + /* ERROR and DONE */ + done = TRUE; + break; + } + } + + g_value_reset (&item); + gst_iterator_free (sinkpads); + + GST_DEBUG ("We found %d static sinks", nbsinks); + + return nbsinks; +} + +static gboolean +nle_operation_add_element (GstBin * bin, GstElement * element) +{ + NleOperation *operation = (NleOperation *) bin; + gboolean res = FALSE; + gboolean isdynamic; + + GST_DEBUG_OBJECT (bin, "element:%s", GST_ELEMENT_NAME (element)); + + if (operation->element) { + GST_WARNING_OBJECT (operation, + "We already control an element : %s , remove it first", + GST_OBJECT_NAME (operation->element)); + } else { + if (!element_is_valid_filter (element, &isdynamic)) { + GST_WARNING_OBJECT (operation, + "Element %s is not a valid filter element", + GST_ELEMENT_NAME (element)); + } else { + if ((res = GST_BIN_CLASS (parent_class)->add_element (bin, element))) { + GstPad *srcpad; + + srcpad = get_src_pad (element); + if (!srcpad) + return FALSE; + + operation->element = element; + operation->dynamicsinks = isdynamic; + + nle_object_ghost_pad_set_target (NLE_OBJECT (operation), + NLE_OBJECT (operation)->srcpad, srcpad); + + /* Remove the reference get_src_pad gave us */ + gst_object_unref (srcpad); + + /* Figure out number of static sink pads */ + operation->num_sinks = get_nb_static_sinks (operation); + + /* Finally sync the ghostpads with the real pads */ + synchronize_sinks (operation); + } + } + } + + return res; +} + +static gboolean +nle_operation_remove_element (GstBin * bin, GstElement * element) +{ + NleOperation *operation = (NleOperation *) bin; + gboolean res = FALSE; + + if (operation->element) { + if ((res = GST_BIN_CLASS (parent_class)->remove_element (bin, element))) + operation->element = NULL; + } else { + GST_WARNING_OBJECT (bin, + "Element %s is not the one controlled by this operation", + GST_ELEMENT_NAME (element)); + } + return res; +} + +static void +nle_operation_set_sinks (NleOperation * operation, guint sinks) +{ + /* FIXME : Check if sinkpad of element is on-demand .... */ + + operation->num_sinks = sinks; + synchronize_sinks (operation); +} + +static void +nle_operation_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + NleOperation *operation = (NleOperation *) object; + + switch (prop_id) { + case ARG_SINKS: + nle_operation_set_sinks (operation, g_value_get_int (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nle_operation_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + NleOperation *operation = (NleOperation *) object; + + switch (prop_id) { + case ARG_SINKS: + g_value_set_int (value, operation->num_sinks); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +/* + * Returns the first unused sink pad of the controlled element. + * Only use with static element. Unref after usage. + * Returns NULL if there's no more unused sink pads. + */ +static GstPad * +get_unused_static_sink_pad (NleOperation * operation) +{ + GstIterator *pads; + gboolean done = FALSE; + GValue item = { 0, }; + GstPad *ret = NULL; + + if (!operation->element) + return NULL; + + pads = gst_element_iterate_pads (operation->element); + + while (!done) { + switch (gst_iterator_next (pads, &item)) { + case GST_ITERATOR_OK: + { + GstPad *pad = g_value_get_object (&item); + + if (gst_pad_get_direction (pad) == GST_PAD_SINK) { + GList *tmp; + gboolean istaken = FALSE; + + /* 1. figure out if one of our sink ghostpads has this pad as target */ + for (tmp = operation->sinks; tmp; tmp = tmp->next) { + GstGhostPad *gpad = (GstGhostPad *) tmp->data; + GstPad *target = gst_ghost_pad_get_target (gpad); + + GST_LOG ("found ghostpad with target %s:%s", + GST_DEBUG_PAD_NAME (target)); + + if (target) { + if (target == pad) + istaken = TRUE; + gst_object_unref (target); + } + } + + /* 2. if not taken, return that pad */ + if (!istaken) { + gst_object_ref (pad); + ret = pad; + done = TRUE; + } + } + g_value_reset (&item); + break; + } + case GST_ITERATOR_RESYNC: + gst_iterator_resync (pads); + break; + default: + /* ERROR and DONE */ + done = TRUE; + break; + } + } + + g_value_unset (&item); + gst_iterator_free (pads); + + if (ret) + GST_DEBUG_OBJECT (operation, "found free sink pad %s:%s", + GST_DEBUG_PAD_NAME (ret)); + else + GST_DEBUG_OBJECT (operation, "Couldn't find an unused sink pad"); + + return ret; +} + +GstPad * +get_unlinked_sink_ghost_pad (NleOperation * operation) +{ + GstIterator *pads; + gboolean done = FALSE; + GValue item = { 0, }; + GstPad *ret = NULL; + + if (!operation->element) + return NULL; + + pads = gst_element_iterate_sink_pads ((GstElement *) operation); + + while (!done) { + switch (gst_iterator_next (pads, &item)) { + case GST_ITERATOR_OK: + { + GstPad *pad = g_value_get_object (&item); + GstPad *peer = gst_pad_get_peer (pad); + + if (peer == NULL) { + ret = pad; + gst_object_ref (ret); + done = TRUE; + } else { + gst_object_unref (peer); + } + g_value_reset (&item); + break; + } + case GST_ITERATOR_RESYNC: + gst_iterator_resync (pads); + break; + default: + /* ERROR and DONE */ + done = TRUE; + break; + } + } + + g_value_unset (&item); + gst_iterator_free (pads); + + if (ret) + GST_DEBUG_OBJECT (operation, "found unlinked ghost sink pad %s:%s", + GST_DEBUG_PAD_NAME (ret)); + else + GST_DEBUG_OBJECT (operation, "Couldn't find an unlinked ghost sink pad"); + + return ret; + +} + +static GstPad * +get_request_sink_pad (NleOperation * operation) +{ + GstPad *pad = NULL; + GList *templates; + + if (!operation->element) + return NULL; + + templates = gst_element_class_get_pad_template_list + (GST_ELEMENT_GET_CLASS (operation->element)); + + for (; templates; templates = templates->next) { + GstPadTemplate *templ = (GstPadTemplate *) templates->data; + + GST_LOG_OBJECT (operation->element, "Trying template %s", + GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); + + if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SINK) && + (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_REQUEST)) { + pad = + gst_element_get_request_pad (operation->element, + GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); + if (pad) + break; + } + } + + return pad; +} + +static GstPad * +add_sink_pad (NleOperation * operation) +{ + GstPad *gpad = NULL; + GstPad *ret = NULL; + + if (!operation->element) + return NULL; + + /* FIXME : implement */ + GST_LOG_OBJECT (operation, "element:%s , dynamicsinks:%d", + GST_ELEMENT_NAME (operation->element), operation->dynamicsinks); + + + if (!operation->dynamicsinks) { + /* static sink pads */ + ret = get_unused_static_sink_pad (operation); + if (ret) { + gpad = nle_object_ghost_pad ((NleObject *) operation, GST_PAD_NAME (ret), + ret); + gst_object_unref (ret); + } + } + + if (!gpad) { + /* request sink pads */ + ret = get_request_sink_pad (operation); + if (ret) { + gpad = nle_object_ghost_pad ((NleObject *) operation, GST_PAD_NAME (ret), + ret); + gst_object_unref (ret); + } + } + + if (gpad) { + operation->sinks = g_list_append (operation->sinks, gpad); + operation->realsinks++; + GST_DEBUG ("Created new pad %s:%s ghosting %s:%s", + GST_DEBUG_PAD_NAME (gpad), GST_DEBUG_PAD_NAME (ret)); + } else { + GST_WARNING ("Couldn't find a usable sink pad!"); + } + + return gpad; +} + +static gboolean +remove_sink_pad (NleOperation * operation, GstPad * sinkpad) +{ + gboolean ret = TRUE; + + GST_DEBUG ("sinkpad %s:%s", GST_DEBUG_PAD_NAME (sinkpad)); + + /* + We can't remove any random pad. + We should remove an unused pad ... which is hard to figure out in a + thread-safe way. + */ + + if ((sinkpad == NULL) && operation->dynamicsinks) { + /* Find an unlinked sinkpad */ + if ((sinkpad = get_unlinked_sink_ghost_pad (operation)) == NULL) { + ret = FALSE; + goto beach; + } + } + + if (sinkpad) { + GstPad *target = gst_ghost_pad_get_target ((GstGhostPad *) sinkpad); + + if (target) { + /* release the target pad */ + nle_object_ghost_pad_set_target ((NleObject *) operation, sinkpad, NULL); + if (operation->dynamicsinks) + gst_element_release_request_pad (operation->element, target); + gst_object_unref (target); + } + operation->sinks = g_list_remove (operation->sinks, sinkpad); + nle_object_remove_ghost_pad ((NleObject *) operation, sinkpad); + operation->realsinks--; + } + +beach: + return ret; +} + +static void +synchronize_sinks (NleOperation * operation) +{ + + GST_DEBUG_OBJECT (operation, "num_sinks:%d , realsinks:%d, dynamicsinks:%d", + operation->num_sinks, operation->realsinks, operation->dynamicsinks); + + if (operation->num_sinks == operation->realsinks) + return; + + if (operation->num_sinks > operation->realsinks) { + while (operation->num_sinks > operation->realsinks) /* Add pad */ + if (!(add_sink_pad (operation))) { + break; + } + } else { + /* Remove pad */ + /* FIXME, which one do we remove ? :) */ + while (operation->num_sinks < operation->realsinks) + if (!remove_sink_pad (operation, NULL)) + break; + } +} + +static gboolean +nle_operation_prepare (NleObject * object) +{ + /* Prepare the pads */ + synchronize_sinks ((NleOperation *) object); + + return TRUE; +} + +static gboolean +nle_operation_cleanup (NleObject * object) +{ + NleOperation *oper = (NleOperation *) object; + + if (oper->dynamicsinks) { + GST_DEBUG ("Resetting dynamic sinks"); + nle_operation_set_sinks (oper, 0); + } + + return TRUE; +} + +void +nle_operation_hard_cleanup (NleOperation * operation) +{ + gboolean done = FALSE; + + GValue item = { 0, }; + GstIterator *pads; + + GST_INFO_OBJECT (operation, "Hard reset of the operation"); + + pads = gst_element_iterate_sink_pads (GST_ELEMENT (operation)); + while (!done) { + switch (gst_iterator_next (pads, &item)) { + case GST_ITERATOR_OK: + { + GstPad *sinkpad = g_value_get_object (&item); + GstPad *srcpad = gst_pad_get_peer (sinkpad); + + if (srcpad) { + GST_ERROR ("Unlinking %" GST_PTR_FORMAT " and %" + GST_PTR_FORMAT, srcpad, sinkpad); + gst_pad_unlink (srcpad, sinkpad); + } + + g_value_reset (&item); + break; + } + case GST_ITERATOR_RESYNC: + gst_iterator_resync (pads); + break; + default: + /* ERROR and DONE */ + done = TRUE; + break; + } + } + nle_object_cleanup (NLE_OBJECT (operation)); +} + + +static GstPad * +nle_operation_request_new_pad (GstElement * element, GstPadTemplate * templ, + const gchar * name, const GstCaps * caps) +{ + NleOperation *operation = (NleOperation *) element; + GstPad *ret; + + GST_DEBUG ("template:%s name:%s", templ->name_template, name); + + if (operation->num_sinks == operation->realsinks) { + GST_WARNING_OBJECT (element, + "We already have the maximum number of pads : %d", + operation->num_sinks); + return NULL; + } + + ret = add_sink_pad ((NleOperation *) element); + + return ret; +} + +static void +nle_operation_release_pad (GstElement * element, GstPad * pad) +{ + GST_DEBUG ("pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + remove_sink_pad ((NleOperation *) element, pad); +} + +void +nle_operation_signal_input_priority_changed (NleOperation * operation, + GstPad * pad, guint32 priority) +{ + GST_DEBUG_OBJECT (operation, "pad:%s:%s, priority:%d", + GST_DEBUG_PAD_NAME (pad), priority); + g_signal_emit (operation, nle_operation_signals[INPUT_PRIORITY_CHANGED], + 0, pad, priority); +} + +void +nle_operation_update_base_time (NleOperation * operation, + GstClockTime timestamp) +{ + if (!nle_object_to_media_time (NLE_OBJECT (operation), + timestamp, &operation->next_base_time)) { + GST_WARNING_OBJECT (operation, "Trying to set a basetime outside of " + "ourself"); + + return; + } + + GST_INFO_OBJECT (operation, "Setting next_basetime to %" + GST_TIME_FORMAT, GST_TIME_ARGS (operation->next_base_time)); +} diff --git a/plugins/nle/nleoperation.h b/plugins/nle/nleoperation.h new file mode 100644 index 00000000..3f68f5d8 --- /dev/null +++ b/plugins/nle/nleoperation.h @@ -0,0 +1,90 @@ +/* GStreamer + * Copyright (C) 2001 Wim Taymans + * 2004 Edward Hervey + * + * nleoperation.h: Header for base NleOperation + * + * 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. + */ + + +#ifndef __NLE_OPERATION_H__ +#define __NLE_OPERATION_H__ + +#include +#include "nleobject.h" + +G_BEGIN_DECLS +#define NLE_TYPE_OPERATION \ + (nle_operation_get_type()) +#define NLE_OPERATION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_OPERATION,NleOperation)) +#define NLE_OPERATION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_OPERATION,NleOperationClass)) +#define NLE_IS_OPERATION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_OPERATION)) +#define NLE_IS_OPERATION_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_OPERATION)) + struct _NleOperation +{ + NleObject parent; + + /* */ + + /* num_sinks: + * Number of sink inputs of the controlled element. + * -1 if the sink pads are dynamic */ + gint num_sinks; + + /* TRUE if element has request pads */ + gboolean dynamicsinks; + + /* realsinks: + * Number of sink pads currently used on the contolled element. */ + gint realsinks; + + /* FIXME : We might need to use a lock to access this list */ + GList * sinks; /* The sink ghostpads */ + + GstElement *element; /* controlled element */ + + GstClockTime next_base_time; +}; + +struct _NleOperationClass +{ + NleObjectClass parent_class; + + void (*input_priority_changed) (NleOperation * operation, GstPad *pad, guint32 priority); +}; + +GstPad * get_unlinked_sink_ghost_pad (NleOperation * operation) G_GNUC_INTERNAL; + +void +nle_operation_signal_input_priority_changed(NleOperation * operation, GstPad *pad, + guint32 priority) G_GNUC_INTERNAL; + +void nle_operation_update_base_time (NleOperation *operation, + GstClockTime timestamp) G_GNUC_INTERNAL; + +void nle_operation_hard_cleanup (NleOperation *operation) G_GNUC_INTERNAL; + + +/* normal GOperation stuff */ +GType nle_operation_get_type (void) G_GNUC_INTERNAL; + +G_END_DECLS +#endif /* __NLE_OPERATION_H__ */ 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 + * <2004-2008> Edward Hervey + * + * 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 , Edward Hervey "); + + 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; +} diff --git a/plugins/nle/nlesource.h b/plugins/nle/nlesource.h new file mode 100644 index 00000000..6fcb1af4 --- /dev/null +++ b/plugins/nle/nlesource.h @@ -0,0 +1,66 @@ +/* GStreamer + * Copyright (C) 2001 Wim Taymans + * 2004-2008 Edward Hervey + * + * nlesource.h: Header for base NleSource + * + * 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. + */ + + +#ifndef __NLE_SOURCE_H__ +#define __NLE_SOURCE_H__ + +#include +#include "nleobject.h" + +G_BEGIN_DECLS +#define NLE_TYPE_SOURCE \ + (nle_source_get_type()) +#define NLE_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_SOURCE,NleSource)) +#define NLE_SOURCE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_SOURCE,NleSourceClass)) +#define NLE_SOURCE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), NLE_TYPE_SOURCE, NleSourceClass)) +#define NLE_IS_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_SOURCE)) +#define NLE_IS_SOURCE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_SOURCE)) +typedef struct _NleSourcePrivate NleSourcePrivate; + +struct _NleSource +{ + NleObject parent; + + /* controlled source element, acces with gst_bin_[add|remove]_element */ + GstElement *element; + + NleSourcePrivate *priv; +}; + +struct _NleSourceClass +{ + NleObjectClass parent_class; + + /* control_element() takes care of controlling the given element */ + gboolean (*control_element) (NleSource * source, GstElement * element); +}; + +GType nle_source_get_type (void) G_GNUC_INTERNAL; + +G_END_DECLS +#endif /* __NLE_SOURCE_H__ */ diff --git a/plugins/nle/nletypes.h b/plugins/nle/nletypes.h new file mode 100644 index 00000000..f84cb23b --- /dev/null +++ b/plugins/nle/nletypes.h @@ -0,0 +1,42 @@ +/* GStreamer + * Copyright (C) 2004 Edward Hervey + * + * nletypes.h: Header for class definition + * + * 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. + */ + +#ifndef __NLE_TYPES_H__ +#define __NLE_TYPES_H__ + +#include + +typedef struct _NleObject NleObject; +typedef struct _NleObjectClass NleObjectClass; + +typedef struct _NleComposition NleComposition; +typedef struct _NleCompositionClass NleCompositionClass; + +typedef struct _NleOperation NleOperation; +typedef struct _NleOperationClass NleOperationClass; + +typedef struct _NleSource NleSource; +typedef struct _NleSourceClass NleSourceClass; + +typedef struct _NleURISource NleURISource; +typedef struct _NleURISourceClass NleURISourceClass; + +#endif diff --git a/plugins/nle/nleurisource.c b/plugins/nle/nleurisource.c new file mode 100644 index 00000000..8fd2950a --- /dev/null +++ b/plugins/nle/nleurisource.c @@ -0,0 +1,166 @@ +/* Gnonlin + * Copyright (C) <2005-2008> Edward Hervey + * + * 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" +#include "nleurisource.h" + +/** + * SECTION:element-nleurisource + * + * NleURISource is a #NleSource which reads and decodes the contents + * of a given file. The data in the file is decoded using any available + * GStreamer plugins. + */ + +static GstStaticPadTemplate nle_urisource_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS_ANY); + +GST_DEBUG_CATEGORY_STATIC (nleurisource); +#define GST_CAT_DEFAULT nleurisource + +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (nleurisource, "nleurisource", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin URI Source Element"); +#define nle_urisource_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (NleURISource, nle_urisource, NLE_TYPE_SOURCE, + _do_init); + +enum +{ + ARG_0, + ARG_URI, +}; + +static gboolean nle_urisource_prepare (NleObject * object); + +static void +nle_urisource_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static void +nle_urisource_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void +nle_urisource_class_init (NleURISourceClass * klass) +{ + GObjectClass *gobject_class; + NleObjectClass *nleobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + nleobject_class = (NleObjectClass *) klass; + parent_class = g_type_class_ref (NLE_TYPE_SOURCE); + + gst_element_class_set_static_metadata (gstelement_class, "GNonLin URI Source", + "Filter/Editor", + "High-level URI Source element", "Edward Hervey "); + + gobject_class->set_property = GST_DEBUG_FUNCPTR (nle_urisource_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (nle_urisource_get_property); + + g_object_class_install_property (gobject_class, ARG_URI, + g_param_spec_string ("uri", "Uri", + "Uri of the file to use", NULL, G_PARAM_READWRITE)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&nle_urisource_src_template)); + + nleobject_class->prepare = nle_urisource_prepare; +} + +static void +nle_urisource_init (NleURISource * urisource) +{ + GstElement *decodebin = NULL; + + GST_OBJECT_FLAG_SET (urisource, NLE_OBJECT_SOURCE); + + /* We create a bin with source and decodebin within */ + decodebin = + gst_element_factory_make ("uridecodebin", "internal-uridecodebin"); + g_object_set (decodebin, "expose-all-streams", FALSE, NULL); + + gst_bin_add (GST_BIN (urisource), decodebin); +} + +static inline void +nle_urisource_set_uri (NleURISource * fs, const gchar * uri) +{ + g_object_set (NLE_SOURCE (fs)->element, "uri", uri, NULL); +} + +static void +nle_urisource_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + NleURISource *fs = (NleURISource *) object; + + switch (prop_id) { + case ARG_URI: + nle_urisource_set_uri (fs, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nle_urisource_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + NleURISource *fs = (NleURISource *) object; + + switch (prop_id) { + case ARG_URI: + g_object_get_property ((GObject *) NLE_SOURCE (fs)->element, "uri", + value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + +} + +static gboolean +nle_urisource_prepare (NleObject * object) +{ + NleSource *fs = (NleSource *) object; + + GST_DEBUG ("prepare"); + + /* Set the caps on uridecodebin */ + if (!gst_caps_is_any (object->caps)) { + GST_DEBUG_OBJECT (object, "Setting uridecodebin caps to %" GST_PTR_FORMAT, + object->caps); + g_object_set (fs->element, "caps", object->caps, NULL); + } + + return NLE_OBJECT_CLASS (parent_class)->prepare (object); +} diff --git a/plugins/nle/nleurisource.h b/plugins/nle/nleurisource.h new file mode 100644 index 00000000..a2750e59 --- /dev/null +++ b/plugins/nle/nleurisource.h @@ -0,0 +1,57 @@ +/* GStreamer + * Copyright (C) 2001 Wim Taymans + * 2004-2008 Edward Hervey + * + * nleurisource.h: Header for NleURISource + * + * 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. + */ + + +#ifndef __NLE_URI_SOURCE_H__ +#define __NLE_URI_SOURCE_H__ + +#include +#include "nlesource.h" + +G_BEGIN_DECLS +#define NLE_TYPE_URI_SOURCE \ + (nle_urisource_get_type()) +#define NLE_URI_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),NLE_TYPE_URI_SOURCE,NleURIsource)) +#define NLE_URI_SOURCE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),NLE_TYPE_URI_SOURCE,NleURIsourceClass)) +#define NLE_IS_URI_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),NLE_TYPE_URI_SOURCE)) +#define NLE_IS_URI_SOURCE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),NLE_TYPE_URI_SOURCE)) + +struct _NleURISource +{ + NleSource parent; + + gchar *uri; +}; + +struct _NleURISourceClass +{ + NleSourceClass parent_class; +}; + +GType nle_urisource_get_type (void) G_GNUC_INTERNAL; + +G_END_DECLS +#endif /* __NLE_URI_SOURCE_H__ */ -- cgit v1.2.3