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