mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
37602d1d02
Timeline StreamCollection are very specific to inner working of nested timelines and should not interfere with the usual stream selection process and are now handled as element messages. Stream selection inside `nleobject` need to be handled internally by the application or GES itself so we should just drop all those as they would interfere and fail if they are exposed to other elements. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5983>
913 lines
29 KiB
C
913 lines
29 KiB
C
/* Gnonlin
|
|
* Copyright (C) <2001> Wim Taymans <wim.taymans@gmail.com>
|
|
* <2004-2008> Edward Hervey <bilboed@bilboed.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include "nle.h"
|
|
#include "../shared/nlegesplugin.h"
|
|
|
|
/**
|
|
* SECTION:nleobject
|
|
* @short_description: Base class for GNonLin elements
|
|
*
|
|
* NleObject encapsulates default behaviour and implements standard
|
|
* properties provided by all the GNonLin elements.
|
|
*/
|
|
|
|
static void
|
|
nle_query_parent_nle_object_free (NleQueryParentNleObject * query)
|
|
{
|
|
gst_clear_object (&query->nle_object);
|
|
}
|
|
|
|
void
|
|
nle_query_parent_nle_object_release (NleQueryParentNleObject * query)
|
|
{
|
|
g_atomic_rc_box_release_full (query,
|
|
(GDestroyNotify) nle_query_parent_nle_object_free);
|
|
}
|
|
|
|
G_DEFINE_BOXED_TYPE (NleQueryParentNleObject,
|
|
nle_query_parent_nle_object,
|
|
g_atomic_rc_box_acquire, nle_query_parent_nle_object_release);
|
|
|
|
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_MEDIA_DURATION_FACTOR,
|
|
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_bin_handle_message (GstBin * bin, GstMessage * message)
|
|
{
|
|
if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) {
|
|
const GstStructure *s = gst_message_get_structure (message);
|
|
|
|
if (gst_structure_has_name (s, NLE_QUERY_PARENT_NLE_OBJECT)) {
|
|
NleQueryParentNleObject *query;
|
|
|
|
gst_structure_get (s, "query", NLE_TYPE_QUERY_PARENT_NLE_OBJECT, &query,
|
|
NULL);
|
|
g_assert (query);
|
|
|
|
g_mutex_lock (&query->lock);
|
|
query->nle_object = gst_object_ref (GST_ELEMENT (bin));
|
|
g_mutex_unlock (&query->lock);
|
|
nle_query_parent_nle_object_release (query);
|
|
|
|
return;
|
|
}
|
|
} else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STREAM_COLLECTION) {
|
|
GST_INFO_OBJECT (bin, "Dropping stream collection message, "
|
|
" those are internal to and should be kept as such");
|
|
|
|
return;
|
|
}
|
|
|
|
return GST_BIN_CLASS (parent_class)->handle_message (bin, message);
|
|
}
|
|
|
|
static void
|
|
nle_object_class_init (NleObjectClass * 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;
|
|
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);
|
|
|
|
/* Ensure the NleQueryParentObject GType is registered */
|
|
GType t = NLE_TYPE_QUERY_PARENT_NLE_OBJECT;
|
|
g_assert (t);
|
|
|
|
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);
|
|
gstbin_class->handle_message = GST_DEBUG_FUNCPTR (nle_bin_handle_message);
|
|
|
|
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:media-duration-factor
|
|
*
|
|
* Indicates the relative rate caused by this object, in other words, the
|
|
* relation between the rate of media entering and leaving this object. I.e.
|
|
* if object pulls data at twice the speed it sends it (e.g. `pitch
|
|
* tempo=2.0`), this value is set to 2.0.
|
|
*
|
|
* Deprecated: 1.18: This property is ignored since the wrapped
|
|
* #GstElement-s themselves should internally perform any additional time
|
|
* translations.
|
|
*/
|
|
properties[PROP_MEDIA_DURATION_FACTOR] =
|
|
g_param_spec_double ("media-duration-factor", "Media duration factor",
|
|
"The relative rate caused by this object", 0.01, G_MAXDOUBLE,
|
|
1.0, G_PARAM_READWRITE | G_PARAM_DEPRECATED);
|
|
g_object_class_install_property (gobject_class, PROP_MEDIA_DURATION_FACTOR,
|
|
properties[PROP_MEDIA_DURATION_FACTOR]);
|
|
|
|
/**
|
|
* 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);
|
|
|
|
gst_type_mark_as_plugin_api (NLE_TYPE_OBJECT, 0);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (nle->srcpad) {
|
|
nle_object_remove_ghost_pad (nle, nle->srcpad);
|
|
nle->srcpad = 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 timestamp received from another nleobject pad
|
|
* in the same nlecomposition, or from the nlecomposition itself, to an
|
|
* internal source time.
|
|
*
|
|
* If the object is furthest downstream in the nlecomposition (highest
|
|
* priority in the current stack), this will convert the timestamp from
|
|
* the composition coordinate time to the internal source coordinate time
|
|
* of the object.
|
|
*
|
|
* If the object is upstream from another nleobject, then this can convert
|
|
* the timestamp received from the downstream sink pad to the internal
|
|
* source coordinates of the object, to be passed to its internal
|
|
* elements.
|
|
*
|
|
* If the object is downstream from another nleobject, then this can
|
|
* convert the timestamp received from the upstream source pad to the
|
|
* internal sink coordinates of the object, to be passed to its internal
|
|
* elements.
|
|
*
|
|
* In these latter two cases, the timestamp should have been converted
|
|
* by the peer pad using nle_media_to_object_time().
|
|
*
|
|
* Note, if an object introduces a time effect, it must have a 0 in-point
|
|
* and the same #nleobject:start and #nleobject:duration as all the other
|
|
* objects that are further upstream.
|
|
*
|
|
* Returns: TRUE if @objecttime was below the @object start time,
|
|
* FALSE otherwise.
|
|
*/
|
|
gboolean
|
|
nle_object_to_media_time (NleObject * object, GstClockTime otime,
|
|
GstClockTime * mtime)
|
|
{
|
|
gboolean ret = TRUE;
|
|
|
|
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));
|
|
|
|
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (otime))) {
|
|
GST_DEBUG_OBJECT (object, "converting none object time to none");
|
|
*mtime = GST_CLOCK_TIME_NONE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* We do not allow @otime to be below the start of the object.
|
|
* If it was below, then the object would have a negative external
|
|
* source/sink time.
|
|
*
|
|
* Note that ges only supports time effects that map the time 0 to
|
|
* 0. Such time effects would also not produce an external timestamp
|
|
* below start, nor can they receive such a timestamp. */
|
|
if (G_UNLIKELY ((otime < object->start))) {
|
|
GST_DEBUG_OBJECT (object, "ObjectTime is before start");
|
|
otime = object->start;
|
|
ret = FALSE;
|
|
}
|
|
/* NOTE: if an nlecomposition contains time effect operations, then
|
|
* @otime can reasonably exceed the stop time of the object. So we
|
|
* do not limit it here. */
|
|
|
|
/* first we convert the timestamp to the object's external source/sink
|
|
* coordinates:
|
|
* + For an object that is furthest downstream, we translate from the
|
|
* composition coordinates to the external source coordinates by
|
|
* subtracting the object start.
|
|
* + For an object that is upstream from d_object, we need to
|
|
* translate from its external sink coordinates to our external
|
|
* source coordinates. This is done by adding
|
|
* (d_object->start - object->start)
|
|
* However, the sink pad of d_object should have already added the
|
|
* d_object->start to the timestamp (see nle_media_to_object_time)
|
|
* so we also only need to subtract the object start.
|
|
* + For an object that is downstream from u_object, we need to
|
|
* translate from its external source coordinates to our external
|
|
* sink coordinates. This is similarly done by adding
|
|
* (u_object->start - object->start)
|
|
* However, the source pad of u_object should have already added the
|
|
* u_object->start to the timestamp (see nle_media_to_object_time)
|
|
* so we also only need to subtract the object start.
|
|
*/
|
|
*mtime = otime - object->start;
|
|
|
|
/* we then convert the timestamp from the object's external source/sink
|
|
* coordinates to its internal source/sink coordinates, to be used by
|
|
* internal elements that the object wraps. This is done by adding
|
|
* the object in-point. */
|
|
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint)))
|
|
*mtime += object->inpoint;
|
|
|
|
GST_DEBUG_OBJECT (object, "Returning MediaTime : %" GST_TIME_FORMAT,
|
|
GST_TIME_ARGS (*mtime));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* 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 timestamp from an internal time to an
|
|
* nleobject pad time.
|
|
*
|
|
* If the object is furthest downstream in an nlecomposition (highest
|
|
* priority in the current stack), this will convert the timestamp from
|
|
* the internal source coordinate time of the object to the composition
|
|
* coordinate time.
|
|
*
|
|
* If the object is upstream from another nleobject, then this can convert
|
|
* the timestamp from the internal source coordinates of the object to be
|
|
* sent to the downstream sink pad.
|
|
*
|
|
* If the object is downstream from another nleobject, then this can
|
|
* convert the timestamp from the internal sink coordinates of the object
|
|
* to be sent to the upstream source pad.
|
|
*
|
|
* Note, if an object introduces a time effect, it must have a 0 in-point
|
|
* and the same #nleobject:start and #nleobject:duration as all the other
|
|
* objects that are further upstream.
|
|
*
|
|
* Returns: TRUE if @objecttime was below the @object in-point 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));
|
|
|
|
if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (mtime))) {
|
|
GST_DEBUG_OBJECT (object, "converting none media time to none");
|
|
*otime = GST_CLOCK_TIME_NONE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* the internal time should never go below the in-point! */
|
|
if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint)
|
|
&& (mtime < object->inpoint))) {
|
|
GST_DEBUG_OBJECT (object, "media time is before inpoint, forcing to start");
|
|
*otime = object->start;
|
|
return FALSE;
|
|
}
|
|
|
|
/* first we convert the timestamp to the object's external source/sink
|
|
* coordinates by removing the in-point */
|
|
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint)))
|
|
*otime = mtime - object->inpoint;
|
|
else
|
|
*otime = mtime;
|
|
|
|
/* then we convert the timestamp by adding start.
|
|
* If the object is furthest downstream, this will translate it from
|
|
* the external source coordinates to the composition coordinates.
|
|
* Otherwise, this will perform part of the conversion from the object's
|
|
* source/sink coordinates to the downstream/upstream sink/source
|
|
* coordinates (the conversion is completed in
|
|
* nle_object_to_media_time). */
|
|
*otime += 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;
|
|
case PROP_MEDIA_DURATION_FACTOR:
|
|
{
|
|
gdouble val = g_value_get_double (value);
|
|
if (val != 1.0)
|
|
g_warning ("Ignoring media-duration-factor value of %g since the "
|
|
"property is deprecated", val);
|
|
}
|
|
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;
|
|
case PROP_MEDIA_DURATION_FACTOR:
|
|
g_value_set_double (value, 1.0);
|
|
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_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;
|
|
object->in_composition = FALSE;
|
|
}
|
|
|
|
GType
|
|
nle_object_get_type (void)
|
|
{
|
|
static 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;
|
|
}
|