mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-29 19:50:40 +00:00
84f7f04a64
Before this patch, NLE and GES did not support NleOperations (respectively GESEffects) that changed the speed/tempo/rate at which the source plays. For example, the 'pitch' element can make audio play faster or slower. In GES 1.5.90 and before, an NleOperation containing the pitch element to change the rate (or tempo) would cause a pipeline state change to PAUSED after that stack; that has been fixed in 1.5.91 (see #755012 [0]). But even then, in 1.5.91 and later, NleComposition would send segment events to its NleSources assuming that one source second is equal to one pipeline second. The resulting early EOS event (in the case of a source rate higher than 1.0) would cause it to switch stacks too early, causing confusion in the timeline and spectacularly messed up output. This patch fixes that by searching for rate-changing elements in GESTrackElements such as GESEffects. If such rate-changing elements are found, their final effect on the playing rate is stored in the corresponding NleObject as the 'media duration factor', named like this because the 'media duration', or source duration, of an NleObject can be computed by multiplying the duration with the media duration factor of that object and its parents (this is called the 'recursive media duration factor'). For example, a 4-second NleSource with an NleOperation with a media duration factor of 2.0 will have an 8-second media duration, which means that for playing 4 seconds in the pipeline, the seek event sent to it must span 8 seconds of media. (So, the 'duration' of an NleObject or GES object always refers to its duration in the timeline, not the media duration.) To summarize: * Rate-changing elements are registered in the GESEffectClass (pitch::tempo and pitch::rate are registered by default); * GESTimelineElement is responsible for detecting rate-changing elements and computing the media_duration_factor; * GESTrackElement is responsible for storing the media_duration_factor in NleObject; * NleComposition is responsible for the recursive_media_duration_factor; * The latter property finally fixes media time computations in NleObject. NLE and GES tests are included. [0] https://bugzilla.gnome.org/show_bug.cgi?id=755012 Differential Revision: https://phabricator.freedesktop.org/D276
772 lines
23 KiB
C
772 lines
23 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"
|
|
|
|
/**
|
|
* SECTION:nleobject
|
|
* @short_description: Base class for GNonLin elements
|
|
*
|
|
* <refsect2>
|
|
* <para>
|
|
* NleObject encapsulates default behaviour and implements standard
|
|
* properties provided by all the GNonLin elements.
|
|
* </para>
|
|
* </refsect2>
|
|
*
|
|
*/
|
|
|
|
|
|
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_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: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.
|
|
*/
|
|
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_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);
|
|
}
|
|
|
|
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->media_duration_factor = 1.0;
|
|
object->recursive_media_duration_factor = 1.0;
|
|
|
|
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 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 * object->recursive_media_duration_factor;
|
|
else
|
|
*mtime =
|
|
(object->stop -
|
|
object->start) * object->recursive_media_duration_factor;
|
|
return FALSE;
|
|
}
|
|
|
|
if (G_UNLIKELY (object->inpoint == GST_CLOCK_TIME_NONE)) {
|
|
/* no time shifting, for live sources ? */
|
|
*mtime = (otime - object->start) * object->recursive_media_duration_factor;
|
|
} else {
|
|
*mtime =
|
|
(otime - object->start) * object->recursive_media_duration_factor +
|
|
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;
|
|
case PROP_MEDIA_DURATION_FACTOR:
|
|
nleobject->media_duration_factor = g_value_get_double (value);
|
|
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, nleobject->media_duration_factor);
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|