/* 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 "gnl.h" /** * SECTION:gnlobject * @short_description: Base class for GNonLin elements * * * * GnlObject encapsulates default behaviour and implements standard * properties provided by all the GNonLin elements. * * * */ GST_DEBUG_CATEGORY_STATIC (gnlobject_debug); #define GST_CAT_DEFAULT gnlobject_debug #define _do_init \ GST_DEBUG_CATEGORY_INIT (gnlobject_debug, "gnlobject", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Object base class"); #define gnl_object_parent_class parent_class G_DEFINE_TYPE_WITH_CODE (GnlObject, gnl_object, GST_TYPE_BIN, _do_init); enum { PROP_0, PROP_START, PROP_DURATION, PROP_STOP, PROP_INPOINT, PROP_PRIORITY, PROP_ACTIVE, PROP_CAPS, PROP_EXPANDABLE, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; static void gnl_object_dispose (GObject * object); static void gnl_object_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gnl_object_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static GstStateChangeReturn gnl_object_change_state (GstElement * element, GstStateChange transition); static gboolean gnl_object_prepare_func (GnlObject * object); static gboolean gnl_object_cleanup_func (GnlObject * object); static GstStateChangeReturn gnl_object_prepare (GnlObject * object); static void gnl_object_class_init (GnlObjectClass * klass) { GObjectClass *gobject_class; GstElementClass *gstelement_class; GnlObjectClass *gnlobject_class; gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; gnlobject_class = (GnlObjectClass *) klass; gobject_class->set_property = GST_DEBUG_FUNCPTR (gnl_object_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gnl_object_get_property); gobject_class->dispose = GST_DEBUG_FUNCPTR (gnl_object_dispose); gstelement_class->change_state = GST_DEBUG_FUNCPTR (gnl_object_change_state); gnlobject_class->prepare = GST_DEBUG_FUNCPTR (gnl_object_prepare_func); gnlobject_class->cleanup = GST_DEBUG_FUNCPTR (gnl_object_cleanup_func); /** * GnlObject: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]); /** * GnlObject: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]); /** * GnlObject: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]); /** * GnlObject: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]); /** * GnlObject: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 GnlComposition 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]); /** * GnlObject:active * * Indicates whether this object should be used by its container. * * Set to #TRUE to temporarily disable this object in a #GnlComposition. */ properties[PROP_ACTIVE] = g_param_spec_boolean ("active", "Active", "Use this object in the GnlComposition", TRUE, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_ACTIVE, properties[PROP_ACTIVE]); /** * GnlObject: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]); /** * GnlObject:expandable * * Indicates whether this object should expand to the full duration of its * container #GnlComposition. */ 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]); } static void gnl_object_init (GnlObject * object) { object->start = 0; object->duration = 0; object->stop = 0; object->inpoint = GST_CLOCK_TIME_NONE; object->priority = 0; object->active = TRUE; object->caps = gst_caps_new_any (); object->segment_rate = 1.0; object->segment_start = -1; object->segment_stop = -1; } static void gnl_object_dispose (GObject * object) { GnlObject *gnl = (GnlObject *) object; if (gnl->caps) { gst_caps_unref (gnl->caps); gnl->caps = NULL; } G_OBJECT_CLASS (parent_class)->dispose (object); } /** * gnl_object_to_media_time: * @object: a #GnlObject * @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 gnl_object_to_media_time (GnlObject * 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; } /** * gnl_media_to_object_time: * @object: The #GnlObject * @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 gnl_media_to_object_time (GnlObject * 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 gnl_object_prepare_func (GnlObject * object) { GST_DEBUG_OBJECT (object, "default prepare function, returning TRUE"); return TRUE; } static GstStateChangeReturn gnl_object_prepare (GnlObject * object) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GST_DEBUG_OBJECT (object, "preparing"); if (!(GNL_OBJECT_GET_CLASS (object)->prepare (object))) ret = GST_STATE_CHANGE_FAILURE; GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret); return ret; } static gboolean gnl_object_cleanup_func (GnlObject * object) { GST_DEBUG_OBJECT (object, "default cleanup function, returning TRUE"); return TRUE; } static GstStateChangeReturn gnl_object_cleanup (GnlObject * object) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GST_DEBUG_OBJECT (object, "cleaning-up"); if (!(GNL_OBJECT_GET_CLASS (object)->cleanup (object))) ret = GST_STATE_CHANGE_FAILURE; GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret); return ret; } void gnl_object_set_caps (GnlObject * object, const GstCaps * caps) { if (object->caps) gst_caps_unref (object->caps); object->caps = gst_caps_copy (caps); } static void update_values (GnlObject * object) { /* check if start/duration has changed */ if ((object->start + object->duration) != object->stop) { object->stop = object->start + object->duration; GST_LOG_OBJECT (object, "Updating stop value : %" GST_TIME_FORMAT " [start:%" GST_TIME_FORMAT ", duration:%" GST_TIME_FORMAT "]", GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->duration)); g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_STOP]); } } static void gnl_object_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GnlObject *gnlobject = (GnlObject *) object; g_return_if_fail (GNL_IS_OBJECT (object)); switch (prop_id) { case PROP_START: gnlobject->start = g_value_get_uint64 (value); update_values (gnlobject); break; case PROP_DURATION: gnlobject->duration = g_value_get_int64 (value); update_values (gnlobject); break; case PROP_INPOINT: gnlobject->inpoint = g_value_get_uint64 (value); break; case PROP_PRIORITY: gnlobject->priority = g_value_get_uint (value); break; case PROP_ACTIVE: gnlobject->active = g_value_get_boolean (value); break; case PROP_CAPS: gnl_object_set_caps (gnlobject, gst_value_get_caps (value)); break; case PROP_EXPANDABLE: if (g_value_get_boolean (value)) GST_OBJECT_FLAG_SET (gnlobject, GNL_OBJECT_EXPANDABLE); else GST_OBJECT_FLAG_UNSET (gnlobject, GNL_OBJECT_EXPANDABLE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gnl_object_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GnlObject *gnlobject = (GnlObject *) object; switch (prop_id) { case PROP_START: g_value_set_uint64 (value, gnlobject->start); break; case PROP_DURATION: g_value_set_int64 (value, gnlobject->duration); break; case PROP_STOP: g_value_set_uint64 (value, gnlobject->stop); break; case PROP_INPOINT: g_value_set_uint64 (value, gnlobject->inpoint); break; case PROP_PRIORITY: g_value_set_uint (value, gnlobject->priority); break; case PROP_ACTIVE: g_value_set_boolean (value, gnlobject->active); break; case PROP_CAPS: gst_value_set_caps (value, gnlobject->caps); break; case PROP_EXPANDABLE: g_value_set_boolean (value, GNL_OBJECT_IS_EXPANDABLE (object)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GstStateChangeReturn gnl_object_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: if (gnl_object_prepare (GNL_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 gnlobject */ if (gnl_object_cleanup (GNL_OBJECT (element)) == GST_STATE_CHANGE_FAILURE) ret = GST_STATE_CHANGE_FAILURE; break; default: break; } beach: return ret; }