gstframepositioner: fix operator magic number
[platform/upstream/gst-editing-services.git] / ges / gstframepositioner.c
1 /* GStreamer
2  * Copyright (C) 2013 Mathieu Duponchelle <mduponchelle1@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
17  * Boston, MA 02110-1335, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <math.h>
25 #include <gst/gst.h>
26 #include <gst/video/video.h>
27
28 #include "gstframepositioner.h"
29 #include "ges-internal.h"
30
31 GST_DEBUG_CATEGORY_STATIC (_framepositioner);
32 #undef GST_CAT_DEFAULT
33 #define GST_CAT_DEFAULT _framepositioner
34
35 /* We  need to define a max number of pixel so we can interpolate them */
36 #define MAX_PIXELS 100000
37 #define MIN_PIXELS -100000
38
39 static void gst_frame_positioner_set_property (GObject * object,
40     guint property_id, const GValue * value, GParamSpec * pspec);
41 static void gst_frame_positioner_get_property (GObject * object,
42     guint property_id, GValue * value, GParamSpec * pspec);
43 static GstFlowReturn gst_frame_positioner_transform_ip (GstBaseTransform *
44     trans, GstBuffer * buf);
45
46 static gboolean
47 gst_frame_positioner_meta_init (GstMeta * meta, gpointer params,
48     GstBuffer * buffer);
49 static gboolean gst_frame_positioner_meta_transform (GstBuffer * dest,
50     GstMeta * meta, GstBuffer * buffer, GQuark type, gpointer data);
51
52 enum
53 {
54   PROP_0,
55   PROP_ALPHA,
56   PROP_POSX,
57   PROP_POSY,
58   PROP_ZORDER,
59   PROP_WIDTH,
60   PROP_HEIGHT,
61   PROP_OPERATOR,
62   PROP_LAST,
63 };
64
65 static GParamSpec *properties[PROP_LAST];
66
67 static GstStaticPadTemplate gst_frame_positioner_src_template =
68 GST_STATIC_PAD_TEMPLATE ("src",
69     GST_PAD_SRC,
70     GST_PAD_ALWAYS,
71     GST_STATIC_CAPS ("video/x-raw")
72     );
73
74 static GstStaticPadTemplate gst_frame_positioner_sink_template =
75 GST_STATIC_PAD_TEMPLATE ("sink",
76     GST_PAD_SINK,
77     GST_PAD_ALWAYS,
78     GST_STATIC_CAPS ("video/x-raw")
79     );
80
81 G_DEFINE_TYPE (GstFramePositioner, gst_frame_positioner,
82     GST_TYPE_BASE_TRANSFORM);
83
84 static GType
85 gst_compositor_operator_get_type_and_default_value (int *default_operator_value)
86 {
87   GstElement *compositor =
88       gst_element_factory_create (ges_get_compositor_factory (), NULL);
89
90   GstPad *compositorPad = gst_element_get_request_pad (compositor, "sink_%u");
91
92   GParamSpec *pspec =
93       g_object_class_find_property (G_OBJECT_GET_CLASS (compositorPad),
94       "operator");
95
96   *default_operator_value =
97       g_value_get_enum (g_param_spec_get_default_value (pspec));
98   g_return_val_if_fail (pspec, G_TYPE_NONE);
99
100   gst_element_release_request_pad (compositor, compositorPad);
101   gst_object_unref (compositorPad);
102   gst_object_unref (compositor);
103
104   return pspec->value_type;
105 }
106
107 static void
108 _weak_notify_cb (GstFramePositioner * pos, GObject * old)
109 {
110   pos->current_track = NULL;
111 }
112
113 static gboolean
114 is_user_positionned (GstFramePositioner * self)
115 {
116   gint i;
117   GParamSpec *positioning_props[] = {
118     properties[PROP_WIDTH],
119     properties[PROP_HEIGHT],
120     properties[PROP_POSX],
121     properties[PROP_POSY],
122   };
123
124   if (self->user_positioned)
125     return TRUE;
126
127   for (i = 0; i < G_N_ELEMENTS (positioning_props); i++) {
128     GstControlBinding *b = gst_object_get_control_binding (GST_OBJECT (self),
129         positioning_props[i]->name);
130
131     if (b) {
132       gst_object_unref (b);
133       return TRUE;
134     }
135   }
136
137   return FALSE;
138 }
139
140 static gboolean
141 auto_position (GstFramePositioner * self)
142 {
143   gdouble scaled_width = -1, scaled_height = -1, x, y;
144
145   if (is_user_positionned (self)) {
146     GST_DEBUG_OBJECT (self, "Was positioned by the user, not auto positioning");
147     return FALSE;
148   }
149
150   if (!self->natural_width || !self->natural_height)
151     return FALSE;
152
153   if (self->track_width == self->natural_width &&
154       self->track_height == self->natural_height)
155     return TRUE;
156
157   scaled_height =
158       gst_util_uint64_scale_int (self->natural_height, self->track_width,
159       self->natural_width);
160   scaled_width = self->track_width;
161   if (scaled_height > self->track_height) {
162     scaled_height = self->track_height;
163     scaled_width =
164         gst_util_uint64_scale_int (self->natural_width, self->track_height,
165         self->natural_height);
166   }
167
168   x = MAX (0, (self->track_width - scaled_width) / 2.f);
169   y = MAX (0, (self->track_height - scaled_height) / 2.f);
170
171   GST_INFO_OBJECT (self, "Scalling video to match track size from "
172       "%dx%d to %fx%f",
173       self->natural_width, self->natural_height, scaled_width, scaled_height);
174   self->width = scaled_width;
175   self->height = scaled_height;
176   self->posx = x;
177   self->posy = y;
178
179   return TRUE;
180 }
181
182 typedef struct
183 {
184   gdouble *value;
185   gint old_track_value;
186   gint track_value;
187   GParamSpec *pspec;
188 } RepositionPropertyData;
189
190 static void
191 reposition_properties (GstFramePositioner * pos, gint old_track_width,
192     gint old_track_height)
193 {
194   gint i;
195   RepositionPropertyData props_data[] = {
196     {&pos->width, old_track_width, pos->track_width, properties[PROP_WIDTH]},
197     {&pos->height, old_track_height, pos->track_height,
198         properties[PROP_HEIGHT]},
199     {&pos->posx, old_track_width, pos->track_width, properties[PROP_POSX]},
200     {&pos->posy, old_track_height, pos->track_height, properties[PROP_POSY]},
201   };
202
203   for (i = 0; i < G_N_ELEMENTS (props_data); i++) {
204     GList *values, *tmp;
205     gboolean absolute;
206     GstTimedValueControlSource *source = NULL;
207
208     RepositionPropertyData d = props_data[i];
209     GstControlBinding *binding =
210         gst_object_get_control_binding (GST_OBJECT (pos), d.pspec->name);
211
212     *(d.value) =
213         *(d.value) * (gdouble) d.track_value / (gdouble) d.old_track_value;
214
215     if (!binding)
216       continue;
217
218     if (!GST_IS_DIRECT_CONTROL_BINDING (binding)) {
219       GST_FIXME_OBJECT (pos, "Implement support for control binding type: %s",
220           G_OBJECT_TYPE_NAME (binding));
221
222       goto next;
223     }
224
225     g_object_get (binding, "control_source", &source, NULL);
226     if (!source || !GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)) {
227       GST_FIXME_OBJECT (pos, "Implement support for control source type: %s",
228           source ? G_OBJECT_TYPE_NAME (source) : "NULL");
229
230       goto next;
231     }
232
233     values =
234         gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
235         (source));
236
237     if (!values)
238       goto next;
239
240     g_object_get (binding, "absolute", &absolute, NULL);
241     for (tmp = values; tmp; tmp = tmp->next) {
242       GstTimedValue *value = tmp->data;
243
244       gst_timed_value_control_source_set (source, value->timestamp,
245           value->value * d.track_value / d.old_track_value);
246     }
247
248     g_list_free (values);
249
250   next:
251     gst_clear_object (&source);
252     gst_object_unref (binding);
253   }
254 }
255
256 static void
257 gst_frame_positioner_update_properties (GstFramePositioner * pos,
258     gboolean track_mixing, gint old_track_width, gint old_track_height)
259 {
260   GstCaps *caps;
261
262   if (pos->capsfilter == NULL)
263     return;
264
265   if (pos->track_width && pos->track_height &&
266       (!track_mixing || !pos->scale_in_compositor)) {
267     caps =
268         gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT,
269         pos->track_width, "height", G_TYPE_INT, pos->track_height, NULL);
270   } else {
271     caps = gst_caps_new_empty_simple ("video/x-raw");
272   }
273
274   if (pos->fps_n != -1)
275     gst_caps_set_simple (caps, "framerate", GST_TYPE_FRACTION, pos->fps_n,
276         pos->fps_d, NULL);
277
278   if (pos->par_n != -1)
279     gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
280         pos->par_n, pos->par_d, NULL);
281
282   if (!pos->track_width || !pos->track_height) {
283     GST_INFO_OBJECT (pos, "Track doesn't have a proper size, not "
284         "positioning the source");
285     goto done;
286   } else if (auto_position (pos))
287     goto done;
288
289   if (!old_track_height || !old_track_width) {
290     GST_DEBUG_OBJECT (pos, "No old track size, can not properly reposition");
291     goto done;
292   }
293
294   if ((!pos->natural_width || !pos->natural_height) &&
295       (!pos->width || !pos->height)) {
296     GST_DEBUG_OBJECT (pos, "No natural aspect ratio and no user set "
297         " image size, can't not reposition.");
298     goto done;
299   }
300
301   if (gst_util_fraction_compare (old_track_width, old_track_height,
302           pos->track_width, pos->track_height)) {
303     GST_INFO_OBJECT (pos, "Not repositioning as track size change didn't"
304         " keep the same aspect ratio (previous %dx%d("
305         "ratio=%f), new: %dx%d(ratio=%f)",
306         old_track_width, old_track_height,
307         (gdouble) old_track_width / (gdouble) old_track_height,
308         pos->track_width, pos->track_height,
309         (gdouble) pos->track_width / (gdouble) pos->track_height);
310     goto done;
311   }
312
313   reposition_properties (pos, old_track_width, old_track_height);
314
315 done:
316   GST_DEBUG_OBJECT (pos, "setting caps %" GST_PTR_FORMAT, caps);
317
318   g_object_set (pos->capsfilter, "caps", caps, NULL);
319
320   gst_caps_unref (caps);
321 }
322
323 static void
324 sync_properties_from_track (GstFramePositioner * pos, GESTrack * track)
325 {
326   gint width, height;
327   gint old_track_width, old_track_height;
328   GstCaps *caps;
329
330   g_object_get (track, "restriction-caps", &caps, NULL);
331
332   width = height = 0;
333
334   if (caps && gst_caps_get_size (caps) > 0) {
335     GstStructure *structure;
336
337     structure = gst_caps_get_structure (caps, 0);
338     if (!gst_structure_get_int (structure, "width", &width))
339       width = 0;
340     if (!gst_structure_get_int (structure, "height", &height))
341       height = 0;
342     if (!gst_structure_get_fraction (structure, "framerate", &(pos->fps_n),
343             &(pos->fps_d)))
344       pos->fps_n = -1;
345
346     if (!gst_structure_get_fraction (structure, "pixel-aspect-ratio",
347             &(pos->par_n), &(pos->par_d)))
348       pos->par_n = -1;
349   }
350
351   old_track_width = pos->track_width;
352   old_track_height = pos->track_height;
353
354   pos->track_width = width;
355   pos->track_height = height;
356
357   GST_DEBUG_OBJECT (pos, "syncing framerate from caps : %d/%d", pos->fps_n,
358       pos->fps_d);
359   if (caps)
360     gst_caps_unref (caps);
361
362   gst_frame_positioner_update_properties (pos, ges_track_get_mixing (track),
363       old_track_width, old_track_height);
364 }
365
366 static void
367 _track_restriction_changed_cb (GESTrack * track, GParamSpec * arg G_GNUC_UNUSED,
368     GstFramePositioner * pos)
369 {
370   sync_properties_from_track (pos, track);
371 }
372
373 static void
374 set_track (GstFramePositioner * pos)
375 {
376   GESTrack *new_track;
377
378   if (pos->current_track) {
379     g_signal_handlers_disconnect_by_func (pos->current_track,
380         (GCallback) _track_restriction_changed_cb, pos);
381     g_object_weak_unref (G_OBJECT (pos->current_track),
382         (GWeakNotify) _weak_notify_cb, pos);
383   }
384
385   new_track = ges_track_element_get_track (pos->track_source);
386   if (new_track) {
387     pos->current_track = new_track;
388     g_object_weak_ref (G_OBJECT (new_track), (GWeakNotify) _weak_notify_cb,
389         pos);
390     GST_DEBUG_OBJECT (pos, "connecting to track : %p", pos->current_track);
391
392     g_signal_connect (pos->current_track, "notify::restriction-caps",
393         (GCallback) _track_restriction_changed_cb, pos);
394     sync_properties_from_track (pos, pos->current_track);
395   } else {
396     pos->current_track = NULL;
397   }
398 }
399
400 static void
401 _track_changed_cb (GESTrackElement * trksrc, GParamSpec * arg G_GNUC_UNUSED,
402     GstFramePositioner * pos)
403 {
404   set_track (pos);
405 }
406
407 static void
408 _trk_element_weak_notify_cb (GstFramePositioner * pos, GObject * old)
409 {
410   pos->track_source = NULL;
411   gst_object_unref (pos);
412 }
413
414 void
415 ges_frame_positioner_set_source_and_filter (GstFramePositioner * pos,
416     GESTrackElement * trksrc, GstElement * capsfilter)
417 {
418   pos->track_source = trksrc;
419   pos->capsfilter = capsfilter;
420   pos->current_track = ges_track_element_get_track (trksrc);
421
422   g_object_weak_ref (G_OBJECT (trksrc),
423       (GWeakNotify) _trk_element_weak_notify_cb, gst_object_ref (pos));
424   g_signal_connect (trksrc, "notify::track", (GCallback) _track_changed_cb,
425       pos);
426   set_track (pos);
427 }
428
429 static void
430 gst_frame_positioner_dispose (GObject * object)
431 {
432   GstFramePositioner *pos = GST_FRAME_POSITIONNER (object);
433
434   if (pos->track_source) {
435     g_signal_handlers_disconnect_by_func (pos->track_source, _track_changed_cb,
436         pos);
437     pos->track_source = NULL;
438   }
439
440   if (pos->current_track) {
441     g_signal_handlers_disconnect_by_func (pos->current_track,
442         _track_restriction_changed_cb, pos);
443     g_object_weak_unref (G_OBJECT (pos->current_track),
444         (GWeakNotify) _weak_notify_cb, pos);
445     pos->current_track = NULL;
446   }
447
448   G_OBJECT_CLASS (gst_frame_positioner_parent_class)->dispose (object);
449 }
450
451 static void
452 gst_frame_positioner_class_init (GstFramePositionerClass * klass)
453 {
454   int default_operator_value;
455   GType operator_gtype =
456       gst_compositor_operator_get_type_and_default_value
457       (&default_operator_value);
458
459   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
460   GstBaseTransformClass *base_transform_class =
461       GST_BASE_TRANSFORM_CLASS (klass);
462
463   GST_DEBUG_CATEGORY_INIT (_framepositioner, "framepositioner",
464       GST_DEBUG_FG_YELLOW, "ges frame positioner");
465
466   gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
467       &gst_frame_positioner_src_template);
468   gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
469       &gst_frame_positioner_sink_template);
470
471   gobject_class->set_property = gst_frame_positioner_set_property;
472   gobject_class->get_property = gst_frame_positioner_get_property;
473   gobject_class->dispose = gst_frame_positioner_dispose;
474   base_transform_class->transform_ip =
475       GST_DEBUG_FUNCPTR (gst_frame_positioner_transform_ip);
476
477   /**
478    * gstframepositioner:alpha:
479    *
480    * The desired alpha for the stream.
481    */
482   properties[PROP_ALPHA] =
483       g_param_spec_double ("alpha", "alpha", "alpha of the stream", 0.0, 1.0,
484       1.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
485
486   /**
487    * gstframepositioner:posx:
488    *
489    * The desired x position for the stream.
490    */
491   properties[PROP_POSX] =
492       g_param_spec_int ("posx", "posx", "x position of the stream", MIN_PIXELS,
493       MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
494
495
496   /**
497    * gstframepositioner:posy:
498    *
499    * The desired y position for the stream.
500    */
501   properties[PROP_POSY] =
502       g_param_spec_int ("posy", "posy", "y position of the stream", MIN_PIXELS,
503       MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
504
505   /**
506    * gstframepositioner:zorder:
507    *
508    * The desired z order for the stream.
509    */
510   properties[PROP_ZORDER] =
511       g_param_spec_uint ("zorder", "zorder", "z order of the stream", 0,
512       G_MAXUINT, 0, G_PARAM_READWRITE);
513
514   /**
515    * gesframepositioner:width:
516    *
517    * The desired width for that source.
518    * Set to 0 if size is not mandatory, will be set to width of the current track.
519    */
520   properties[PROP_WIDTH] =
521       g_param_spec_int ("width", "width", "width of the source", 0, MAX_PIXELS,
522       0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
523
524   /**
525    * gesframepositioner:height:
526    *
527    * The desired height for that source.
528    * Set to 0 if size is not mandatory, will be set to height of the current track.
529    */
530   properties[PROP_HEIGHT] =
531       g_param_spec_int ("height", "height", "height of the source", 0,
532       MAX_PIXELS, 0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE);
533
534   /**
535    * gesframepositioner:operator:
536    *
537    * The blending operator for the source.
538    */
539   properties[PROP_OPERATOR] =
540       g_param_spec_enum ("operator", "Operator",
541       "Blending operator to use for blending this pad over the previous ones",
542       operator_gtype, default_operator_value,
543       (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
544
545   g_object_class_install_properties (gobject_class, PROP_LAST, properties);
546
547   gst_element_class_set_static_metadata (GST_ELEMENT_CLASS (klass),
548       "frame positioner", "Metadata",
549       "This element provides with tagging facilities",
550       "mduponchelle1@gmail.com");
551 }
552
553 static void
554 gst_frame_positioner_init (GstFramePositioner * framepositioner)
555 {
556   int default_operator_value;
557   gst_compositor_operator_get_type_and_default_value (&default_operator_value);
558
559   framepositioner->alpha = 1.0;
560   framepositioner->posx = 0.0;
561   framepositioner->posy = 0.0;
562   framepositioner->zorder = 0;
563   framepositioner->width = 0;
564   framepositioner->height = 0;
565   framepositioner->operator = default_operator_value;
566   framepositioner->fps_n = -1;
567   framepositioner->fps_d = -1;
568   framepositioner->track_width = 0;
569   framepositioner->track_height = 0;
570   framepositioner->capsfilter = NULL;
571   framepositioner->track_source = NULL;
572   framepositioner->current_track = NULL;
573   framepositioner->scale_in_compositor = TRUE;
574
575   framepositioner->par_n = -1;
576   framepositioner->par_d = 1;
577 }
578
579 void
580 gst_frame_positioner_set_property (GObject * object, guint property_id,
581     const GValue * value, GParamSpec * pspec)
582 {
583   GstFramePositioner *framepositioner = GST_FRAME_POSITIONNER (object);
584   gboolean track_mixing = TRUE;
585
586   if (framepositioner->current_track)
587     track_mixing = ges_track_get_mixing (framepositioner->current_track);
588
589
590   GST_OBJECT_LOCK (framepositioner);
591   switch (property_id) {
592     case PROP_ALPHA:
593       framepositioner->alpha = g_value_get_double (value);
594       break;
595     case PROP_POSX:
596       framepositioner->posx = g_value_get_int (value);
597       framepositioner->user_positioned = TRUE;
598       break;
599     case PROP_POSY:
600       framepositioner->posy = g_value_get_int (value);
601       framepositioner->user_positioned = TRUE;
602       break;
603     case PROP_ZORDER:
604       framepositioner->zorder = g_value_get_uint (value);
605       break;
606     case PROP_WIDTH:
607       framepositioner->user_positioned = TRUE;
608       framepositioner->width = g_value_get_int (value);
609       gst_frame_positioner_update_properties (framepositioner, track_mixing,
610           0, 0);
611       break;
612     case PROP_HEIGHT:
613       framepositioner->user_positioned = TRUE;
614       framepositioner->height = g_value_get_int (value);
615       gst_frame_positioner_update_properties (framepositioner, track_mixing,
616           0, 0);
617       break;
618     case PROP_OPERATOR:
619       framepositioner->operator = g_value_get_enum (value);
620       gst_frame_positioner_update_properties (framepositioner, track_mixing,
621           0, 0);
622       break;
623     default:
624       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
625       break;
626   }
627   GST_OBJECT_UNLOCK (framepositioner);
628 }
629
630 void
631 gst_frame_positioner_get_property (GObject * object, guint property_id,
632     GValue * value, GParamSpec * pspec)
633 {
634   GstFramePositioner *pos = GST_FRAME_POSITIONNER (object);
635   gint real_width, real_height;
636
637   switch (property_id) {
638     case PROP_ALPHA:
639       g_value_set_double (value, pos->alpha);
640       break;
641     case PROP_POSX:
642       g_value_set_int (value, round (pos->posx));
643       break;
644     case PROP_POSY:
645       g_value_set_int (value, round (pos->posy));
646       break;
647     case PROP_ZORDER:
648       g_value_set_uint (value, pos->zorder);
649       break;
650     case PROP_WIDTH:
651       if (pos->scale_in_compositor) {
652         g_value_set_int (value, round (pos->width));
653       } else {
654         real_width =
655             pos->width > 0 ? round (pos->width) : round (pos->track_width);
656         g_value_set_int (value, real_width);
657       }
658       break;
659     case PROP_HEIGHT:
660       if (pos->scale_in_compositor) {
661         g_value_set_int (value, round (pos->height));
662       } else {
663         real_height =
664             pos->height > 0 ? round (pos->height) : round (pos->track_height);
665         g_value_set_int (value, real_height);
666       }
667       break;
668     case PROP_OPERATOR:
669       g_value_set_enum (value, pos->operator);
670       break;
671     default:
672       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
673       break;
674   }
675 }
676
677 GType
678 gst_frame_positioner_meta_api_get_type (void)
679 {
680   static GType type;
681   static const gchar *tags[] = { "video", NULL };
682
683   if (g_once_init_enter (&type)) {
684     GType _type = gst_meta_api_type_register ("GstFramePositionerApi", tags);
685     g_once_init_leave (&type, _type);
686   }
687   return type;
688 }
689
690 static const GstMetaInfo *
691 gst_frame_positioner_get_info (void)
692 {
693   static const GstMetaInfo *meta_info = NULL;
694
695   if (g_once_init_enter ((GstMetaInfo **) & meta_info)) {
696     const GstMetaInfo *meta =
697         gst_meta_register (gst_frame_positioner_meta_api_get_type (),
698         "GstFramePositionerMeta",
699         sizeof (GstFramePositionerMeta), gst_frame_positioner_meta_init,
700         NULL,
701         gst_frame_positioner_meta_transform);
702     g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) meta);
703   }
704   return meta_info;
705 }
706
707 static gboolean
708 gst_frame_positioner_meta_init (GstMeta * meta, gpointer params,
709     GstBuffer * buffer)
710 {
711   int default_operator_value;
712   GstFramePositionerMeta *smeta;
713
714   smeta = (GstFramePositionerMeta *) meta;
715
716   gst_compositor_operator_get_type_and_default_value (&default_operator_value);
717
718   smeta->alpha = 0.0;
719   smeta->posx = smeta->posy = smeta->height = smeta->width = 0;
720   smeta->zorder = 0;
721   smeta->operator = default_operator_value;
722
723   return TRUE;
724 }
725
726 static gboolean
727 gst_frame_positioner_meta_transform (GstBuffer * dest, GstMeta * meta,
728     GstBuffer * buffer, GQuark type, gpointer data)
729 {
730   GstFramePositionerMeta *dmeta, *smeta;
731
732   smeta = (GstFramePositionerMeta *) meta;
733
734   if (GST_META_TRANSFORM_IS_COPY (type)) {
735     /* only copy if the complete data is copied as well */
736     dmeta =
737         (GstFramePositionerMeta *) gst_buffer_add_meta (dest,
738         gst_frame_positioner_get_info (), NULL);
739     dmeta->alpha = smeta->alpha;
740     dmeta->posx = smeta->posx;
741     dmeta->posy = smeta->posy;
742     dmeta->width = smeta->width;
743     dmeta->height = smeta->height;
744     dmeta->zorder = smeta->zorder;
745     dmeta->operator = smeta->operator;
746   }
747
748   return TRUE;
749 }
750
751 static GstFlowReturn
752 gst_frame_positioner_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
753 {
754   GstFramePositionerMeta *meta;
755   GstFramePositioner *framepositioner = GST_FRAME_POSITIONNER (trans);
756   GstClockTime timestamp = GST_BUFFER_PTS (buf);
757
758   if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
759     gst_object_sync_values (GST_OBJECT (trans), timestamp);
760   }
761
762   meta =
763       (GstFramePositionerMeta *) gst_buffer_add_meta (buf,
764       gst_frame_positioner_get_info (), NULL);
765
766   GST_OBJECT_LOCK (framepositioner);
767   meta->alpha = framepositioner->alpha;
768   meta->posx = round (framepositioner->posx);
769   meta->posy = round (framepositioner->posy);
770   meta->width = round (framepositioner->width);
771   meta->height = round (framepositioner->height);
772   meta->zorder = framepositioner->zorder;
773   meta->operator = framepositioner->operator;
774   GST_OBJECT_UNLOCK (framepositioner);
775
776   return GST_FLOW_OK;
777 }