diff --git a/ges/Makefile.am b/ges/Makefile.am index 7ba4a26c75..9642e3a315 100644 --- a/ges/Makefile.am +++ b/ges/Makefile.am @@ -63,7 +63,8 @@ libges_@GST_API_VERSION@_la_SOURCES = \ ges-effect-asset.c \ ges-smart-adder.c \ ges-smart-video-mixer.c \ - ges-utils.c + ges-utils.c \ + gstframepositionner.c libges_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/ges/ libges_@GST_API_VERSION@include_HEADERS = \ @@ -123,7 +124,8 @@ libges_@GST_API_VERSION@include_HEADERS = \ ges-effect-asset.h \ ges-smart-adder.h \ ges-smart-video-mixer.h \ - ges-utils.h + ges-utils.h \ + gstframepositionner.h noinst_HEADERS = \ ges-internal.h \ diff --git a/ges/ges-smart-video-mixer.c b/ges/ges-smart-video-mixer.c index 8da61bfac5..a318843e2f 100644 --- a/ges/ges-smart-video-mixer.c +++ b/ges/ges-smart-video-mixer.c @@ -17,6 +17,7 @@ * along with this program. If not, see ."; */ +#include "gstframepositionner.h" #include "ges-types.h" #include "ges-internal.h" #include "ges-smart-video-mixer.h" @@ -44,11 +45,14 @@ typedef struct _PadInfos GESSmartMixer *self; GstPad *mixer_pad; GstElement *bin; + gulong probe_id; } PadInfos; static void destroy_pad (PadInfos * infos) { + gst_pad_remove_probe (infos->mixer_pad, infos->probe_id); + if (G_LIKELY (infos->bin)) { gst_element_set_state (infos->bin, GST_STATE_NULL); gst_element_unlink (infos->bin, infos->self->mixer); @@ -57,9 +61,32 @@ destroy_pad (PadInfos * infos) if (infos->mixer_pad) gst_element_release_request_pad (infos->self->mixer, infos->mixer_pad); + g_slice_free (PadInfos, infos); } +/* These metadata will get set by the upstream framepositionner element, + added in the video sources' bin */ +static GstPadProbeReturn +parse_metadata (GstPad * mixer_pad, GstPadProbeInfo * info, gpointer unused) +{ + GstFramePositionnerMeta *meta; + + meta = + (GstFramePositionnerMeta *) gst_buffer_get_meta ((GstBuffer *) info->data, + gst_frame_positionner_meta_api_get_type ()); + + if (!meta) { + GST_WARNING ("The current source should use a framepositionner"); + return GST_PAD_PROBE_OK; + } + + g_object_set (mixer_pad, "alpha", meta->alpha, "xpos", meta->posx, "ypos", + meta->posy, "zorder", meta->zorder, NULL); + + return GST_PAD_PROBE_OK; +} + /**************************************************** * GstElement vmetods * ****************************************************/ @@ -77,8 +104,6 @@ _request_new_pad (GstElement * element, GstPadTemplate * templ, gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self->mixer), "sink_%u"), NULL, NULL); - g_object_set (G_OBJECT (infos->mixer_pad), "alpha", 0.5, NULL); - if (infos->mixer_pad == NULL) { GST_WARNING_OBJECT (element, "Could not get any pad from GstMixer"); @@ -111,6 +136,10 @@ _request_new_pad (GstElement * element, GstPadTemplate * templ, gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost); gst_pad_link (tmpghost, infos->mixer_pad); + infos->probe_id = + gst_pad_add_probe (infos->mixer_pad, GST_PAD_PROBE_TYPE_BUFFER, + (GstPadProbeCallback) parse_metadata, NULL, NULL); + LOCK (self); g_hash_table_insert (self->pads_infos, ghost, infos); UNLOCK (self); diff --git a/ges/ges-uri-source.c b/ges/ges-uri-source.c index 251a5e026e..000e8ac761 100644 --- a/ges/ges-uri-source.c +++ b/ges/ges-uri-source.c @@ -32,10 +32,13 @@ #include "ges-uri-source.h" #include "ges-uri-asset.h" #include "ges-extractable.h" +#include "ges-layer.h" +#include "gstframepositionner.h" struct _GESUriSourcePrivate { GHashTable *props_hashtable; + GstFramePositionner *positionner; }; enum @@ -185,6 +188,23 @@ _sync_element_to_layer_property_float (GESTrackElement * trksrc, /* TrackElement VMethods */ +static void +update_z_order_cb (GESClip * clip, GParamSpec * arg G_GNUC_UNUSED, + GESUriSource * self) +{ + GESLayer *layer = ges_clip_get_layer (clip); + + if (layer == NULL) + return; + + /* 10000 is the max value of zorder on videomixerpad, hardcoded */ + + g_object_set (self->priv->positionner, "zorder", + 10000 - ges_layer_get_priority (layer), NULL); + + gst_object_unref (layer); +} + static GstElement * ges_uri_source_create_element (GESTrackElement * trksrc) { @@ -192,6 +212,8 @@ ges_uri_source_create_element (GESTrackElement * trksrc) GESTrack *track; GstElement *decodebin; GstElement *topbin, *volume; + GstElement *positionner; + GESTimelineElement *parent; self = (GESUriSource *) trksrc; track = ges_track_element_get_track (trksrc); @@ -210,6 +232,27 @@ ges_uri_source_create_element (GESTrackElement * trksrc) _add_element_properties_to_hashtable (self, volume, "volume", "mute", NULL); break; + case GES_TRACK_TYPE_VIDEO: + decodebin = gst_element_factory_make ("uridecodebin", NULL); + + /* That positionner will add metadata to buffers according to its + properties, acting like a proxy for our smart-mixer dynamic pads. */ + positionner = + gst_element_factory_make ("framepositionner", "frame_tagger"); + _add_element_properties_to_hashtable (self, positionner, "alpha", "posx", + "posy", NULL); + topbin = _create_bin ("video-src-bin", decodebin, positionner, NULL); + parent = ges_timeline_element_get_parent (GES_TIMELINE_ELEMENT (trksrc)); + if (parent) { + self->priv->positionner = GST_FRAME_POSITIONNER (positionner); + g_signal_connect (parent, "notify::layer", + (GCallback) update_z_order_cb, trksrc); + update_z_order_cb (GES_CLIP (parent), NULL, self); + gst_object_unref (parent); + } else { + GST_WARNING ("No parent timeline element, SHOULD NOT HAPPEN"); + } + break; default: decodebin = gst_element_factory_make ("uridecodebin", NULL); topbin = _create_bin ("video-src-bin", decodebin, NULL); @@ -347,6 +390,7 @@ ges_track_filesource_init (GESUriSource * self) self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GES_TYPE_URI_SOURCE, GESUriSourcePrivate); self->priv->props_hashtable = NULL; + self->priv->positionner = NULL; } /** diff --git a/ges/ges.c b/ges/ges.c index d59664faec..2b4b54c491 100644 --- a/ges/ges.c +++ b/ges/ges.c @@ -19,6 +19,7 @@ */ #include +#include "ges/gstframepositionner.h" #include "ges-internal.h" #define GES_GNONLIN_VERSION_NEEDED_MAJOR 0 @@ -93,6 +94,9 @@ ges_init (void) if (!ges_check_gnonlin_availability ()) return FALSE; + gst_element_register (NULL, "framepositionner", 0, + GST_TYPE_FRAME_POSITIONNER); + /* TODO: user-defined types? */ ges_initialized = TRUE; diff --git a/ges/gstframepositionner.c b/ges/gstframepositionner.c new file mode 100644 index 0000000000..120451509b --- /dev/null +++ b/ges/gstframepositionner.c @@ -0,0 +1,263 @@ +/* GStreamer + * Copyright (C) 2013 Mathieu Duponchelle + * + * 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 Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstframepositionner.h" + +static void gst_frame_positionner_set_property (GObject * object, + guint property_id, const GValue * value, GParamSpec * pspec); +static void gst_frame_positionner_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec); +static GstFlowReturn gst_frame_positionner_transform_ip (GstBaseTransform * + trans, GstBuffer * buf); + +static gboolean +gst_frame_positionner_meta_transform (GstBuffer * dest, GstMeta * meta, + GstBuffer * buffer, GQuark type, gpointer data); + +enum +{ + PROP_0, + PROP_ALPHA, + PROP_POSX, + PROP_POSY, + PROP_ZORDER +}; + +static GstStaticPadTemplate gst_frame_positionner_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw") + ); + +static GstStaticPadTemplate gst_frame_positionner_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw") + ); + +G_DEFINE_TYPE (GstFramePositionner, gst_frame_positionner, + GST_TYPE_BASE_TRANSFORM); + +static void +gst_frame_positionner_class_init (GstFramePositionnerClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseTransformClass *base_transform_class = + GST_BASE_TRANSFORM_CLASS (klass); + + gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), + gst_static_pad_template_get (&gst_frame_positionner_src_template)); + gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), + gst_static_pad_template_get (&gst_frame_positionner_sink_template)); + + gobject_class->set_property = gst_frame_positionner_set_property; + gobject_class->get_property = gst_frame_positionner_get_property; + base_transform_class->transform_ip = + GST_DEBUG_FUNCPTR (gst_frame_positionner_transform_ip); + + /** + * gstframepositionner:alpha: + * + * The desired alpha for the stream. + */ + g_object_class_install_property (gobject_class, PROP_ALPHA, + g_param_spec_double ("alpha", "alpha", "alpha of the stream", + 0.0, 1.0, 1.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); + + /** + * gstframepositionner:posx: + * + * The desired x position for the stream. + */ + g_object_class_install_property (gobject_class, PROP_ALPHA, + g_param_spec_int ("posx", "posx", "x position of the stream", + G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); + + /** + * gstframepositionner:posy: + * + * The desired y position for the stream. + */ + g_object_class_install_property (gobject_class, PROP_ALPHA, + g_param_spec_int ("posy", "posy", "y position of the stream", + G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); + + /** + * gstframepositionner:zorder: + * + * The desired z order for the stream. + */ + g_object_class_install_property (gobject_class, PROP_ZORDER, + g_param_spec_uint ("zorder", "zorder", "z order of the stream", + 0, 10000, 0, G_PARAM_READWRITE)); + + gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass), + "frame positionner", "Metadata", + "This element provides with tagging facilities", + "mduponchelle1@gmail.com"); +} + +static void +gst_frame_positionner_init (GstFramePositionner * framepositionner) +{ + framepositionner->alpha = 1.0; + framepositionner->posx = 0.0; + framepositionner->posy = 0.0; + framepositionner->zorder = 0; +} + +void +gst_frame_positionner_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + GstFramePositionner *framepositionner = GST_FRAME_POSITIONNER (object); + + + GST_OBJECT_LOCK (framepositionner); + switch (property_id) { + case PROP_ALPHA: + framepositionner->alpha = g_value_get_double (value); + break; + case PROP_POSX: + framepositionner->posx = g_value_get_int (value); + break; + case PROP_POSY: + framepositionner->posy = g_value_get_int (value); + break; + case PROP_ZORDER: + framepositionner->zorder = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } + GST_OBJECT_UNLOCK (framepositionner); +} + +void +gst_frame_positionner_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + GstFramePositionner *framepositionner = GST_FRAME_POSITIONNER (object); + + GST_DEBUG_OBJECT (framepositionner, "get_property"); + + switch (property_id) { + case PROP_ALPHA: + g_value_set_double (value, framepositionner->alpha); + break; + case PROP_POSX: + g_value_set_int (value, framepositionner->posx); + break; + case PROP_POSY: + g_value_set_int (value, framepositionner->posy); + break; + case PROP_ZORDER: + g_value_set_uint (value, framepositionner->zorder); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +GType +gst_frame_positionner_meta_api_get_type (void) +{ + static volatile GType type; + static const gchar *tags[] = { "alpha", "posx", "posy", "zorder", NULL }; + + if (g_once_init_enter (&type)) { + GType _type = gst_meta_api_type_register ("GstFramePositionnerApi", tags); + g_once_init_leave (&type, _type); + } + return type; +} + +static const GstMetaInfo * +gst_frame_positionner_get_info (void) +{ + static const GstMetaInfo *meta_info = NULL; + + if (g_once_init_enter (&meta_info)) { + const GstMetaInfo *meta = + gst_meta_register (gst_frame_positionner_meta_api_get_type (), + "GstFramePositionnerMeta", + sizeof (GstFramePositionnerMeta), (GstMetaInitFunction) NULL, + (GstMetaFreeFunction) NULL, + (GstMetaTransformFunction) gst_frame_positionner_meta_transform); + g_once_init_leave (&meta_info, meta); + } + return meta_info; +} + +static gboolean +gst_frame_positionner_meta_transform (GstBuffer * dest, GstMeta * meta, + GstBuffer * buffer, GQuark type, gpointer data) +{ + GstFramePositionnerMeta *dmeta, *smeta; + + smeta = (GstFramePositionnerMeta *) meta; + + if (GST_META_TRANSFORM_IS_COPY (type)) { + /* only copy if the complete data is copied as well */ + dmeta = + (GstFramePositionnerMeta *) gst_buffer_add_meta (dest, + gst_frame_positionner_get_info (), NULL); + dmeta->alpha = smeta->alpha; + dmeta->posx = smeta->posx; + dmeta->posy = smeta->posy; + dmeta->zorder = smeta->zorder; + } + + return TRUE; +} + +static GstFlowReturn +gst_frame_positionner_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + GstFramePositionnerMeta *meta; + GstFramePositionner *framepositionner = GST_FRAME_POSITIONNER (trans); + GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buf); + + if (GST_CLOCK_TIME_IS_VALID (timestamp)) { + gst_object_sync_values (GST_OBJECT (trans), timestamp); + } + + meta = + (GstFramePositionnerMeta *) gst_buffer_add_meta (buf, + gst_frame_positionner_get_info (), NULL); + + GST_OBJECT_LOCK (framepositionner); + meta->alpha = framepositionner->alpha; + meta->posx = framepositionner->posx; + meta->posy = framepositionner->posy; + meta->zorder = framepositionner->zorder; + GST_OBJECT_UNLOCK (framepositionner); + + return GST_FLOW_OK; +} diff --git a/ges/gstframepositionner.h b/ges/gstframepositionner.h new file mode 100644 index 0000000000..502c9efaf5 --- /dev/null +++ b/ges/gstframepositionner.h @@ -0,0 +1,67 @@ +/* GStreamer + * Copyright (C) 2013 Mathieu Duponchelle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _GST_FRAME_POSITIONNER_H_ +#define _GST_FRAME_POSITIONNER_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_FRAME_POSITIONNER (gst_frame_positionner_get_type()) +#define GST_FRAME_POSITIONNER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FRAME_POSITIONNER,GstFramePositionner)) +#define GST_FRAME_POSITIONNER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FRAME_POSITIONNER,GstFramePositionnerClass)) +#define GST_IS_FRAME_POSITIONNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FRAME_POSITIONNER)) +#define GST_IS_FRAME_POSITIONNER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FRAME_POSITIONNER)) + +typedef struct _GstFramePositionner GstFramePositionner; +typedef struct _GstFramePositionnerClass GstFramePositionnerClass; +typedef struct _GstFramePositionnerMeta GstFramePositionnerMeta; + +struct _GstFramePositionner +{ + GstBaseTransform base_framepositionner; + + gdouble alpha; + gint posx; + gint posy; + guint zorder; +}; + +struct _GstFramePositionnerClass +{ + GstBaseTransformClass base_framepositionner_class; +}; + +struct _GstFramePositionnerMeta { + GstMeta meta; + + gdouble alpha; + gint posx; + gint posy; + guint zorder; +}; + +GType gst_frame_positionner_get_type (void); +GType +gst_frame_positionner_meta_api_get_type (void); + +G_END_DECLS + +#endif