mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
5294a720f8
This transition is meant to be very similar to crossfade, but
instead of fading out the background video at the same time as the
foreground fades in, the background video stays at 100% opacity
during the whole transition.
This essentially "restores" the old crossfade behaviour that was changed in:
eb48faf342
but using a new type enum, so that both behaviours are available,
letting applications choose.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2385>
719 lines
22 KiB
C
719 lines
22 KiB
C
/* GStreamer Editing Services
|
|
* Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
|
|
* 2010 Nokia Corporation
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:gesvideotransition
|
|
* @title: GESVideoTransition
|
|
* @short_description: implements video crossfade transition
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <ges/ges.h>
|
|
#include "ges-internal.h"
|
|
#include "ges-smart-video-mixer.h"
|
|
|
|
#include <gst/controller/gstdirectcontrolbinding.h>
|
|
|
|
#define parent_class ges_video_transition_parent_class
|
|
|
|
static inline void
|
|
ges_video_transition_set_border_internal (GESVideoTransition * self,
|
|
guint border);
|
|
static inline void
|
|
ges_video_transition_set_inverted_internal (GESVideoTransition *
|
|
self, gboolean inverted);
|
|
static inline gboolean
|
|
ges_video_transition_set_transition_type_internal (GESVideoTransition
|
|
* self, GESVideoStandardTransitionType type);
|
|
struct _GESVideoTransitionPrivate
|
|
{
|
|
GESVideoStandardTransitionType type;
|
|
|
|
/* prevents cases where the transitions have not been created yet */
|
|
GESVideoStandardTransitionType pending_type;
|
|
|
|
/* these enable video interpolation */
|
|
GstTimedValueControlSource *fade_in_control_source;
|
|
GstTimedValueControlSource *fade_out_control_source;
|
|
GstTimedValueControlSource *smpte_control_source;
|
|
|
|
/* so we can support changing between wipes */
|
|
GstElement *smpte;
|
|
|
|
GstPad *mixer_sink;
|
|
|
|
GstElement *mixer;
|
|
GstPad *mixer_sinka;
|
|
GstPad *mixer_sinkb;
|
|
|
|
GstPad *mixer_ghosta;
|
|
GstPad *mixer_ghostb;
|
|
|
|
/* This is in case the smpte doesn't exist yet */
|
|
gint pending_border_value;
|
|
gboolean pending_inverted;
|
|
|
|
GstElement *positioner;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_BORDER,
|
|
PROP_TRANSITION_TYPE,
|
|
PROP_INVERT,
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *properties[PROP_LAST];
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GESVideoTransition, ges_video_transition,
|
|
GES_TYPE_TRANSITION);
|
|
|
|
#define fast_element_link(a,b) gst_element_link_pads_full((a),"src",(b),"sink",GST_PAD_LINK_CHECK_NOTHING)
|
|
|
|
static GObject *link_element_to_mixer_with_smpte (GstBin * bin,
|
|
GstElement * element, GstElement * mixer, gint type,
|
|
GstElement ** smpteref, GESVideoTransitionPrivate * priv, GstPad ** ghost);
|
|
|
|
static void
|
|
ges_video_transition_duration_changed (GESTrackElement * self,
|
|
guint64 duration);
|
|
|
|
static GstElement *ges_video_transition_create_element (GESTrackElement * self);
|
|
|
|
static void ges_video_transition_dispose (GObject * object);
|
|
|
|
static void ges_video_transition_finalize (GObject * object);
|
|
|
|
static void ges_video_transition_get_property (GObject * object, guint
|
|
property_id, GValue * value, GParamSpec * pspec);
|
|
|
|
static void ges_video_transition_set_property (GObject * object, guint
|
|
property_id, const GValue * value, GParamSpec * pspec);
|
|
|
|
static void
|
|
duration_changed_cb (GESTrackElement * self, GParamSpec * arg G_GNUC_UNUSED)
|
|
{
|
|
ges_video_transition_duration_changed (self,
|
|
ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (self)));
|
|
}
|
|
|
|
static gboolean
|
|
_set_priority (GESTimelineElement * element, guint32 priority)
|
|
{
|
|
gboolean res;
|
|
GESVideoTransition *self = GES_VIDEO_TRANSITION (element);
|
|
|
|
res = GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_priority (element,
|
|
priority);
|
|
|
|
if (res && self->priv->positioner)
|
|
g_object_set (self->priv->positioner, "zorder", G_MAXUINT - priority, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
ges_video_transition_class_init (GESVideoTransitionClass * klass)
|
|
{
|
|
GObjectClass *object_class;
|
|
GESTrackElementClass *toclass;
|
|
GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
|
|
GESTrackElementClass *track_element_class = GES_TRACK_ELEMENT_CLASS (klass);
|
|
|
|
object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->get_property = ges_video_transition_get_property;
|
|
object_class->set_property = ges_video_transition_set_property;
|
|
object_class->dispose = ges_video_transition_dispose;
|
|
object_class->finalize = ges_video_transition_finalize;
|
|
|
|
track_element_class->ABI.abi.default_track_type = GES_TRACK_TYPE_VIDEO;
|
|
|
|
/**
|
|
* GESVideoTransition:border:
|
|
*
|
|
* This value represents the border width of the transition.
|
|
*
|
|
*/
|
|
properties[PROP_BORDER] =
|
|
g_param_spec_uint ("border", "Border", "The border width", 0,
|
|
G_MAXUINT, 0, G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_BORDER,
|
|
properties[PROP_BORDER]);
|
|
|
|
/**
|
|
* GESVideoTransition:type:
|
|
*
|
|
* The #GESVideoStandardTransitionType currently applied on the object
|
|
*
|
|
* Deprecated:1.20: Use ges_timeline_element_[sg]et_child_property instead.
|
|
*/
|
|
properties[PROP_TRANSITION_TYPE] =
|
|
g_param_spec_enum ("transition-type", "Transition type",
|
|
"The type of the transition", GES_VIDEO_STANDARD_TRANSITION_TYPE_TYPE,
|
|
GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE, G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_TRANSITION_TYPE,
|
|
properties[PROP_TRANSITION_TYPE]);
|
|
|
|
/**
|
|
* GESVideoTransition:invert:
|
|
*
|
|
* This value represents the direction of the transition.
|
|
*
|
|
* Deprecated:1.20: Use ges_timeline_element_[sg]et_child_property instead.
|
|
*/
|
|
properties[PROP_INVERT] =
|
|
g_param_spec_boolean ("invert", "Invert",
|
|
"Whether the transition is inverted", FALSE, G_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_INVERT,
|
|
properties[PROP_INVERT]);
|
|
|
|
toclass = GES_TRACK_ELEMENT_CLASS (klass);
|
|
toclass->create_element = ges_video_transition_create_element;
|
|
|
|
element_class->set_priority = _set_priority;
|
|
}
|
|
|
|
static void
|
|
ges_video_transition_init (GESVideoTransition * self)
|
|
{
|
|
self->priv = ges_video_transition_get_instance_private (self);
|
|
|
|
self->priv->fade_in_control_source = NULL;
|
|
self->priv->fade_out_control_source = NULL;
|
|
self->priv->smpte_control_source = NULL;
|
|
self->priv->smpte = NULL;
|
|
self->priv->mixer_sink = NULL;
|
|
self->priv->mixer = NULL;
|
|
self->priv->mixer_sinka = NULL;
|
|
self->priv->mixer_sinkb = NULL;
|
|
self->priv->pending_type = GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE;
|
|
self->priv->pending_border_value = 0;
|
|
self->priv->pending_inverted = TRUE;
|
|
}
|
|
|
|
static void
|
|
ges_video_transition_release_mixer (GESVideoTransition * self)
|
|
{
|
|
GESVideoTransitionPrivate *priv = self->priv;
|
|
|
|
if (priv->mixer_ghosta && priv->mixer_ghostb) {
|
|
gst_element_release_request_pad (priv->mixer, priv->mixer_ghosta);
|
|
gst_element_release_request_pad (priv->mixer, priv->mixer_ghostb);
|
|
gst_clear_object (&priv->mixer_ghosta);
|
|
gst_clear_object (&priv->mixer_ghostb);
|
|
}
|
|
|
|
gst_clear_object (&priv->mixer_sinka);
|
|
gst_clear_object (&priv->mixer_sinkb);
|
|
gst_clear_object (&priv->mixer);
|
|
}
|
|
|
|
static void
|
|
ges_video_transition_dispose (GObject * object)
|
|
{
|
|
GESVideoTransition *self = GES_VIDEO_TRANSITION (object);
|
|
GESVideoTransitionPrivate *priv = self->priv;
|
|
|
|
GST_DEBUG ("disposing");
|
|
|
|
if (priv->fade_in_control_source) {
|
|
gst_object_unref (priv->fade_in_control_source);
|
|
priv->fade_in_control_source = NULL;
|
|
}
|
|
|
|
if (priv->fade_out_control_source) {
|
|
gst_object_unref (priv->fade_out_control_source);
|
|
priv->fade_out_control_source = NULL;
|
|
}
|
|
|
|
if (priv->smpte_control_source) {
|
|
gst_object_unref (priv->smpte_control_source);
|
|
priv->smpte_control_source = NULL;
|
|
}
|
|
|
|
ges_video_transition_release_mixer (self);
|
|
|
|
g_signal_handlers_disconnect_by_func (GES_TRACK_ELEMENT (self),
|
|
duration_changed_cb, NULL);
|
|
|
|
G_OBJECT_CLASS (ges_video_transition_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
ges_video_transition_finalize (GObject * object)
|
|
{
|
|
G_OBJECT_CLASS (ges_video_transition_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
ges_video_transition_get_property (GObject * object,
|
|
guint property_id, GValue * value, GParamSpec * pspec)
|
|
{
|
|
GESVideoTransition *tr = GES_VIDEO_TRANSITION (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_BORDER:
|
|
g_value_set_uint (value, ges_video_transition_get_border (tr));
|
|
break;
|
|
case PROP_TRANSITION_TYPE:
|
|
g_value_set_enum (value, ges_video_transition_get_transition_type (tr));
|
|
break;
|
|
case PROP_INVERT:
|
|
g_value_set_boolean (value, ges_video_transition_is_inverted (tr));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ges_video_transition_set_property (GObject * object,
|
|
guint property_id, const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GESVideoTransition *tr = GES_VIDEO_TRANSITION (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_BORDER:
|
|
ges_video_transition_set_border_internal (tr, g_value_get_uint (value));
|
|
break;
|
|
case PROP_TRANSITION_TYPE:
|
|
ges_video_transition_set_transition_type_internal (tr,
|
|
g_value_get_enum (value));
|
|
break;
|
|
case PROP_INVERT:
|
|
ges_video_transition_set_inverted_internal (tr,
|
|
g_value_get_boolean (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
static GstTimedValueControlSource *
|
|
set_interpolation (GstObject * element, GESVideoTransitionPrivate * priv,
|
|
const gchar * propname)
|
|
{
|
|
GstControlSource *control_source;
|
|
|
|
g_object_set (element, propname, (gfloat) 0.0, NULL);
|
|
|
|
control_source = gst_interpolation_control_source_new ();
|
|
gst_object_add_control_binding (GST_OBJECT (element),
|
|
gst_direct_control_binding_new (GST_OBJECT (element), propname,
|
|
control_source));
|
|
g_object_set (control_source, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
|
|
|
|
return GST_TIMED_VALUE_CONTROL_SOURCE (control_source);
|
|
}
|
|
|
|
static GstElement *
|
|
ges_video_transition_create_element (GESTrackElement * object)
|
|
{
|
|
GstElement *topbin, *iconva, *iconvb;
|
|
GstElement *mixer = NULL;
|
|
GstPad *sinka_target, *sinkb_target, *src_target, *sinka, *sinkb, *src;
|
|
GESVideoTransition *self;
|
|
GESVideoTransitionPrivate *priv;
|
|
const gchar *smpte_properties[] = { "invert", "border", NULL };
|
|
gboolean is_fade;
|
|
|
|
self = GES_VIDEO_TRANSITION (object);
|
|
priv = self->priv;
|
|
|
|
is_fade = (priv->type == GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE ||
|
|
priv->type == GES_VIDEO_STANDARD_TRANSITION_TYPE_FADE_IN);
|
|
|
|
GST_LOG ("creating a video bin");
|
|
|
|
topbin = gst_bin_new ("transition-bin");
|
|
|
|
iconva = gst_element_factory_make ("videoconvert", "tr-csp-a");
|
|
iconvb = gst_element_factory_make ("videoconvert", "tr-csp-b");
|
|
priv->positioner =
|
|
gst_element_factory_make ("framepositioner", "frame_tagger");
|
|
g_object_set (priv->positioner, "zorder",
|
|
G_MAXUINT - GES_TIMELINE_ELEMENT_PRIORITY (self), NULL);
|
|
|
|
gst_bin_add_many (GST_BIN (topbin), iconva, iconvb, priv->positioner, NULL);
|
|
|
|
mixer =
|
|
g_object_new (GES_TYPE_SMART_MIXER, "name",
|
|
GES_TIMELINE_ELEMENT_NAME (object), NULL);
|
|
GES_SMART_MIXER (mixer)->is_transition = TRUE;
|
|
gst_util_set_object_arg (G_OBJECT (GES_SMART_MIXER (mixer)->mixer),
|
|
"background", "transparent");
|
|
gst_bin_add (GST_BIN (topbin), mixer);
|
|
|
|
priv->mixer_sinka =
|
|
(GstPad *) link_element_to_mixer_with_smpte (GST_BIN (topbin), iconva,
|
|
mixer, GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR, NULL, priv,
|
|
&priv->mixer_ghosta);
|
|
priv->mixer_sinkb =
|
|
(GstPad *) link_element_to_mixer_with_smpte (GST_BIN (topbin), iconvb,
|
|
mixer, GES_VIDEO_STANDARD_TRANSITION_TYPE_BAR_WIPE_LR, &priv->smpte,
|
|
priv, &priv->mixer_ghostb);
|
|
g_object_set (priv->mixer_sinka, "zorder", 0, NULL);
|
|
g_object_set (priv->mixer_sinkb, "zorder", 1, NULL);
|
|
gst_util_set_object_arg (G_OBJECT (priv->mixer_sinka), "operator",
|
|
is_fade ? "source" : "over");
|
|
gst_util_set_object_arg (G_OBJECT (priv->mixer_sinkb), "operator",
|
|
is_fade ? "add" : "over");
|
|
|
|
fast_element_link (mixer, priv->positioner);
|
|
|
|
sinka_target = gst_element_get_static_pad (iconva, "sink");
|
|
sinkb_target = gst_element_get_static_pad (iconvb, "sink");
|
|
src_target = gst_element_get_static_pad (priv->positioner, "src");
|
|
|
|
sinka = gst_ghost_pad_new ("sinka", sinka_target);
|
|
sinkb = gst_ghost_pad_new ("sinkb", sinkb_target);
|
|
src = gst_ghost_pad_new ("src", src_target);
|
|
|
|
gst_element_add_pad (topbin, src);
|
|
gst_element_add_pad (topbin, sinka);
|
|
gst_element_add_pad (topbin, sinkb);
|
|
|
|
gst_object_unref (sinka_target);
|
|
gst_object_unref (sinkb_target);
|
|
gst_object_unref (src_target);
|
|
|
|
/* set up interpolation */
|
|
|
|
priv->fade_out_control_source =
|
|
set_interpolation (GST_OBJECT (priv->mixer_ghosta), priv, "alpha");
|
|
priv->fade_in_control_source =
|
|
set_interpolation (GST_OBJECT (priv->mixer_ghostb), priv, "alpha");
|
|
priv->smpte_control_source =
|
|
set_interpolation (GST_OBJECT (priv->smpte), priv, "position");
|
|
priv->mixer = gst_object_ref (mixer);
|
|
|
|
if (priv->pending_type)
|
|
ges_video_transition_set_transition_type_internal (self,
|
|
priv->pending_type);
|
|
else
|
|
ges_video_transition_set_transition_type_internal (self, priv->type);
|
|
|
|
ges_video_transition_duration_changed (object,
|
|
ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (object)));
|
|
|
|
g_signal_connect (object, "notify::duration",
|
|
G_CALLBACK (duration_changed_cb), NULL);
|
|
|
|
priv->pending_type = GES_VIDEO_STANDARD_TRANSITION_TYPE_NONE;
|
|
|
|
ges_track_element_add_children_props (GES_TRACK_ELEMENT (self),
|
|
priv->smpte, NULL, NULL, smpte_properties);
|
|
|
|
return topbin;
|
|
}
|
|
|
|
static GObject *
|
|
link_element_to_mixer_with_smpte (GstBin * bin, GstElement * element,
|
|
GstElement * mixer, gint type, GstElement ** smpteref,
|
|
GESVideoTransitionPrivate * priv, GstPad ** ghost)
|
|
{
|
|
GstPad *srcpad, *mixerpad;
|
|
GstElement *smptealpha = gst_element_factory_make ("smptealpha", NULL);
|
|
|
|
g_object_set (G_OBJECT (smptealpha),
|
|
"type", (gint) type, "invert", (gboolean) priv->pending_inverted,
|
|
"border", priv->pending_border_value, NULL);
|
|
gst_bin_add (bin, smptealpha);
|
|
|
|
fast_element_link (element, smptealpha);
|
|
|
|
/* crack */
|
|
if (smpteref) {
|
|
*smpteref = smptealpha;
|
|
}
|
|
|
|
srcpad = gst_element_get_static_pad (smptealpha, "src");
|
|
*ghost = ges_smart_mixer_get_mixer_pad (GES_SMART_MIXER (mixer), &mixerpad);
|
|
gst_pad_link_full (srcpad, *ghost, GST_PAD_LINK_CHECK_NOTHING);
|
|
gst_object_unref (srcpad);
|
|
|
|
return G_OBJECT (mixerpad);
|
|
}
|
|
|
|
static void
|
|
ges_video_transition_update_control_source (GstTimedValueControlSource * ts,
|
|
guint64 duration, gdouble start_value, gdouble end_value)
|
|
{
|
|
gst_timed_value_control_source_unset_all (ts);
|
|
gst_timed_value_control_source_set (ts, 0, start_value);
|
|
gst_timed_value_control_source_set (ts, duration, end_value);
|
|
}
|
|
|
|
static void
|
|
ges_video_transition_update_control_sources (GESVideoTransition * self,
|
|
GESVideoStandardTransitionType type)
|
|
{
|
|
GESVideoTransitionPrivate *priv = self->priv;
|
|
guint64 duration =
|
|
ges_timeline_element_get_duration (GES_TIMELINE_ELEMENT (self));
|
|
|
|
GST_LOG ("updating controller");
|
|
if (type == GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE) {
|
|
ges_video_transition_update_control_source
|
|
(priv->fade_in_control_source, duration, 0.0, 1.0);
|
|
ges_video_transition_update_control_source
|
|
(priv->fade_out_control_source, duration, 1.0, 0.0);
|
|
ges_video_transition_update_control_source (priv->smpte_control_source,
|
|
duration, 0.0, 0.0);
|
|
} else if (type == GES_VIDEO_STANDARD_TRANSITION_TYPE_FADE_IN) {
|
|
ges_video_transition_update_control_source
|
|
(priv->fade_in_control_source, duration, 0.0, 1.0);
|
|
ges_video_transition_update_control_source
|
|
(priv->fade_out_control_source, duration, 1.0, 1.0);
|
|
ges_video_transition_update_control_source (priv->smpte_control_source,
|
|
duration, 0.0, 0.0);
|
|
} else {
|
|
ges_video_transition_update_control_source
|
|
(priv->fade_in_control_source, duration, 1.0, 1.0);
|
|
ges_video_transition_update_control_source
|
|
(priv->fade_out_control_source, duration, 1.0, 1.0);
|
|
ges_video_transition_update_control_source (priv->smpte_control_source,
|
|
duration, 1.0, 0.0);
|
|
}
|
|
GST_LOG ("done updating controller");
|
|
}
|
|
|
|
static void
|
|
ges_video_transition_duration_changed (GESTrackElement * object,
|
|
guint64 duration)
|
|
{
|
|
GESVideoTransition *self = GES_VIDEO_TRANSITION (object);
|
|
|
|
ges_video_transition_update_control_sources (self, self->priv->type);
|
|
}
|
|
|
|
static inline void
|
|
ges_video_transition_set_border_internal (GESVideoTransition * self,
|
|
guint value)
|
|
{
|
|
GESVideoTransitionPrivate *priv = self->priv;
|
|
|
|
if (!priv->smpte) {
|
|
priv->pending_border_value = value;
|
|
return;
|
|
}
|
|
g_object_set (priv->smpte, "border", value, NULL);
|
|
}
|
|
|
|
static inline void
|
|
ges_video_transition_set_inverted_internal (GESVideoTransition *
|
|
self, gboolean inverted)
|
|
{
|
|
GESVideoTransitionPrivate *priv = self->priv;
|
|
|
|
if (!priv->smpte) {
|
|
priv->pending_inverted = !inverted;
|
|
return;
|
|
}
|
|
g_object_set (priv->smpte, "invert", !inverted, NULL);
|
|
}
|
|
|
|
static inline gboolean
|
|
ges_video_transition_set_transition_type_internal (GESVideoTransition
|
|
* self, GESVideoStandardTransitionType type)
|
|
{
|
|
GESVideoTransitionPrivate *priv = self->priv;
|
|
gboolean is_fade;
|
|
|
|
GST_DEBUG ("%p %d => %d", self, priv->type, type);
|
|
|
|
if (!priv->mixer) {
|
|
priv->pending_type = type;
|
|
return TRUE;
|
|
}
|
|
|
|
if (type == priv->type) {
|
|
GST_DEBUG ("%d type is already set on this transition\n", type);
|
|
return TRUE;
|
|
}
|
|
|
|
ges_video_transition_update_control_sources (self, type);
|
|
|
|
priv->type = type;
|
|
is_fade = (type == GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE ||
|
|
type == GES_VIDEO_STANDARD_TRANSITION_TYPE_FADE_IN);
|
|
|
|
if (!is_fade) {
|
|
g_object_set (priv->smpte, "type", (gint) type, NULL);
|
|
}
|
|
|
|
gst_util_set_object_arg (G_OBJECT (priv->mixer_sinka), "operator",
|
|
is_fade ? "source" : "over");
|
|
gst_util_set_object_arg (G_OBJECT (priv->mixer_sinkb), "operator",
|
|
is_fade ? "add" : "over");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* ges_video_transition_set_border:
|
|
* @self: The #GESVideoTransition to set the border to
|
|
* @value: The value of the border to set on @object
|
|
*
|
|
* Set the border property of @self, this value represents
|
|
* the border width of the transition. In case this value does
|
|
* not make sense for the current transition type, it is cached
|
|
* for later use.
|
|
*
|
|
* Deprecated:1.20: Use ges_timeline_element_set_child_property instead.
|
|
*/
|
|
void
|
|
ges_video_transition_set_border (GESVideoTransition * self, guint value)
|
|
{
|
|
ges_video_transition_set_border_internal (self, value);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BORDER]);
|
|
}
|
|
|
|
/**
|
|
* ges_video_transition_get_border:
|
|
* @self: The #GESVideoTransition to get the border from
|
|
*
|
|
* Get the border property of @self, this value represents
|
|
* the border width of the transition.
|
|
*
|
|
* Returns: The border values of @self or -1 if not meaningful
|
|
* (this will happen when not using a smpte transition).
|
|
*
|
|
* Deprecated:1.20: Use ges_timeline_element_get_child_property instead.
|
|
*/
|
|
gint
|
|
ges_video_transition_get_border (GESVideoTransition * self)
|
|
{
|
|
gint value;
|
|
|
|
if (!self->priv->smpte) {
|
|
return -1;
|
|
}
|
|
|
|
g_object_get (self->priv->smpte, "border", &value, NULL);
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* ges_video_transition_set_inverted:
|
|
* @self: The #GESVideoTransition to set invert on
|
|
* @inverted: %TRUE if the transition should be inverted %FALSE otherwise
|
|
*
|
|
* Set the invert property of @self, this value represents
|
|
* the direction of the transition. In case this value does
|
|
* not make sense for the current transition type, it is cached
|
|
* for later use.
|
|
*
|
|
* Deprecated:1.20: Use ges_timeline_element_set_child_property instead.
|
|
*/
|
|
void
|
|
ges_video_transition_set_inverted (GESVideoTransition * self, gboolean inverted)
|
|
{
|
|
ges_video_transition_set_inverted_internal (self, inverted);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_INVERT]);
|
|
}
|
|
|
|
/**
|
|
* ges_video_transition_is_inverted:
|
|
* @self: The #GESVideoTransition to get the inversion from
|
|
*
|
|
* Get the invert property of @self, this value represents
|
|
* the direction of the transition.
|
|
*
|
|
* Returns: The invert value of @self
|
|
*
|
|
* Deprecated:1.20: Use ges_timeline_element_get_child_property instead.
|
|
*/
|
|
gboolean
|
|
ges_video_transition_is_inverted (GESVideoTransition * self)
|
|
{
|
|
gboolean inverted;
|
|
|
|
if (!self->priv->smpte) {
|
|
return FALSE;
|
|
}
|
|
|
|
g_object_get (self->priv->smpte, "invert", &inverted, NULL);
|
|
|
|
return !inverted;
|
|
}
|
|
|
|
/**
|
|
* ges_video_transition_set_transition_type:
|
|
* @self: a #GESVideoTransition
|
|
* @type: a #GESVideoStandardTransitionType
|
|
*
|
|
* Sets the transition being used to @type.
|
|
*
|
|
* Returns: %TRUE if the transition type was properly changed, else %FALSE.
|
|
*/
|
|
gboolean
|
|
ges_video_transition_set_transition_type (GESVideoTransition * self,
|
|
GESVideoStandardTransitionType type)
|
|
{
|
|
gboolean ret = ges_video_transition_set_transition_type_internal (self, type);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TRANSITION_TYPE]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ges_video_transition_get_transition_type:
|
|
* @trans: a #GESVideoTransition
|
|
*
|
|
* Get the transition type used by @trans.
|
|
*
|
|
* Returns: The transition type used by @trans.
|
|
*/
|
|
GESVideoStandardTransitionType
|
|
ges_video_transition_get_transition_type (GESVideoTransition * trans)
|
|
{
|
|
if (trans->priv->pending_type)
|
|
return trans->priv->pending_type;
|
|
return trans->priv->type;
|
|
}
|
|
|
|
/* ges_video_transition_new:
|
|
*
|
|
* Creates a new #GESVideoTransition.
|
|
*
|
|
* Returns: (transfer floating) (nullable): The newly created
|
|
* #GESVideoTransition, or %NULL if there was an error.
|
|
*/
|
|
GESVideoTransition *
|
|
ges_video_transition_new (void)
|
|
{
|
|
GESVideoTransition *res;
|
|
GESAsset *asset = ges_asset_request (GES_TYPE_VIDEO_TRANSITION, NULL, NULL);
|
|
|
|
res = GES_VIDEO_TRANSITION (ges_asset_extract (asset, NULL));
|
|
gst_object_unref (asset);
|
|
|
|
return res;
|
|
}
|