gst/videomixer/videomixer.c: Use gst_pad_get_parent and drop the ref that was added...
[platform/upstream/gst-plugins-good.git] / gst / videomixer / videomixer.c
1 /* Generic video mixer plugin
2  * Copyright (C) 2004 Wim Taymans <wim@fluendo.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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <gst/gst.h>
25 #include <gst/base/gstcollectpads.h>
26
27 #include <string.h>
28
29 GST_DEBUG_CATEGORY_STATIC (gst_videomixer_debug);
30 #define GST_CAT_DEFAULT gst_videomixer_debug
31
32 #define GST_TYPE_VIDEO_MIXER_PAD (gst_videomixer_pad_get_type())
33 #define GST_VIDEO_MIXER_PAD(obj) \
34         (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_MIXER_PAD, GstVideoMixerPad))
35 #define GST_VIDEO_MIXER_PAD_CLASS(klass) \
36         (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_MIXER_PAD, GstVideoMixerPadiClass))
37 #define GST_IS_VIDEO_MIXER_PAD(obj) \
38         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_MIXER_PAD))
39 #define GST_IS_VIDEO_MIXER_PAD_CLASS(obj) \
40         (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_MIXER_PAD))
41
42 typedef struct _GstVideoMixerPad GstVideoMixerPad;
43 typedef struct _GstVideoMixerPadClass GstVideoMixerPadClass;
44 typedef struct _GstVideoMixerCollect GstVideoMixerCollect;
45
46 #define GST_TYPE_VIDEO_MIXER (gst_videomixer_get_type())
47 #define GST_VIDEO_MIXER(obj) \
48         (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_MIXER, GstVideoMixer))
49 #define GST_VIDEO_MIXER_CLASS(klass) \
50         (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_MIXER, GstVideoMixerClass))
51 #define GST_IS_VIDEO_MIXER(obj) \
52         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_MIXER))
53 #define GST_IS_VIDEO_MIXER_CLASS(obj) \
54         (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_MIXER))
55
56 static GType gst_videomixer_get_type (void);
57
58 typedef struct _GstVideoMixer GstVideoMixer;
59 typedef struct _GstVideoMixerClass GstVideoMixerClass;
60
61 static void gst_videomixer_pad_base_init (gpointer g_class);
62 static void gst_videomixer_pad_class_init (GstVideoMixerPadClass * klass);
63 static void gst_videomixer_pad_init (GstVideoMixerPad * mixerpad);
64
65 static void gst_videomixer_pad_get_property (GObject * object, guint prop_id,
66     GValue * value, GParamSpec * pspec);
67 static void gst_videomixer_pad_set_property (GObject * object, guint prop_id,
68     const GValue * value, GParamSpec * pspec);
69
70 static void gst_videomixer_sort_pads (GstVideoMixer * mix);
71
72 #define DEFAULT_PAD_ZORDER 0
73 #define DEFAULT_PAD_XPOS   0
74 #define DEFAULT_PAD_YPOS   0
75 #define DEFAULT_PAD_ALPHA  1.0
76 enum
77 {
78   ARG_PAD_0,
79   ARG_PAD_ZORDER,
80   ARG_PAD_XPOS,
81   ARG_PAD_YPOS,
82   ARG_PAD_ALPHA,
83 };
84
85 struct _GstVideoMixerCollect
86 {
87   GstCollectData collect;       /* we extend the CollectData */
88
89   GstBuffer *buffer;            /* the queued buffer for this pad */
90
91   GstVideoMixerPad *mixpad;
92 };
93
94 /* all information needed for one video stream */
95 struct _GstVideoMixerPad
96 {
97   GstPad parent;                /* subclass the pad */
98
99   guint64 queued;
100
101   guint in_width, in_height;
102   gdouble in_framerate;
103
104   gint xpos, ypos;
105   guint zorder;
106   gint blend_mode;
107   gdouble alpha;
108
109   GstVideoMixerCollect *mixcol;
110 };
111
112 struct _GstVideoMixerPadClass
113 {
114   GstPadClass parent_class;
115 };
116
117 static GType
118 gst_videomixer_pad_get_type (void)
119 {
120   static GType videomixer_pad_type = 0;
121
122   if (!videomixer_pad_type) {
123     static const GTypeInfo videomixer_pad_info = {
124       sizeof (GstVideoMixerPadClass),
125       gst_videomixer_pad_base_init,
126       NULL,
127       (GClassInitFunc) gst_videomixer_pad_class_init,
128       NULL,
129       NULL,
130       sizeof (GstVideoMixerPad),
131       0,
132       (GInstanceInitFunc) gst_videomixer_pad_init,
133     };
134
135     videomixer_pad_type = g_type_register_static (GST_TYPE_PAD,
136         "GstVideoMixerPad", &videomixer_pad_info, 0);
137   }
138   return videomixer_pad_type;
139 }
140
141 static void
142 gst_videomixer_pad_base_init (gpointer g_class)
143 {
144 }
145
146 static void
147 gst_videomixer_pad_class_init (GstVideoMixerPadClass * klass)
148 {
149   GObjectClass *gobject_class;
150
151   gobject_class = (GObjectClass *) klass;
152
153   gobject_class->set_property =
154       GST_DEBUG_FUNCPTR (gst_videomixer_pad_set_property);
155   gobject_class->get_property =
156       GST_DEBUG_FUNCPTR (gst_videomixer_pad_get_property);
157
158   g_object_class_install_property (gobject_class, ARG_PAD_ZORDER,
159       g_param_spec_uint ("zorder", "Z-Order", "Z Order of the picture",
160           0, 10000, DEFAULT_PAD_ZORDER, G_PARAM_READWRITE));
161   g_object_class_install_property (gobject_class, ARG_PAD_XPOS,
162       g_param_spec_int ("xpos", "X Position", "X Position of the picture",
163           G_MININT, G_MAXINT, DEFAULT_PAD_XPOS, G_PARAM_READWRITE));
164   g_object_class_install_property (gobject_class, ARG_PAD_YPOS,
165       g_param_spec_int ("ypos", "Y Position", "Y Position of the picture",
166           G_MININT, G_MAXINT, DEFAULT_PAD_YPOS, G_PARAM_READWRITE));
167   g_object_class_install_property (gobject_class, ARG_PAD_ALPHA,
168       g_param_spec_double ("alpha", "Alpha", "Alpha of the picture",
169           0.0, 1.0, DEFAULT_PAD_ALPHA, G_PARAM_READWRITE));
170 }
171
172 static void
173 gst_videomixer_pad_get_property (GObject * object, guint prop_id,
174     GValue * value, GParamSpec * pspec)
175 {
176   GstVideoMixerPad *pad;
177
178   g_return_if_fail (GST_IS_VIDEO_MIXER_PAD (object));
179
180   pad = GST_VIDEO_MIXER_PAD (object);
181
182   switch (prop_id) {
183     case ARG_PAD_ZORDER:
184       g_value_set_uint (value, pad->zorder);
185       break;
186     case ARG_PAD_XPOS:
187       g_value_set_int (value, pad->xpos);
188       break;
189     case ARG_PAD_YPOS:
190       g_value_set_int (value, pad->ypos);
191       break;
192     case ARG_PAD_ALPHA:
193       g_value_set_double (value, pad->alpha);
194       break;
195     default:
196       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
197       break;
198   }
199 }
200
201 static void
202 gst_videomixer_pad_set_property (GObject * object, guint prop_id,
203     const GValue * value, GParamSpec * pspec)
204 {
205   GstVideoMixerPad *pad;
206   GstVideoMixer *mix;
207
208   g_return_if_fail (GST_IS_PAD (object));
209
210   pad = GST_VIDEO_MIXER_PAD (object);
211   mix = GST_VIDEO_MIXER (gst_pad_get_parent (GST_PAD (pad)));
212
213   switch (prop_id) {
214     case ARG_PAD_ZORDER:
215       pad->zorder = g_value_get_uint (value);
216       gst_videomixer_sort_pads (mix);
217       break;
218     case ARG_PAD_XPOS:
219       pad->xpos = g_value_get_int (value);
220       break;
221     case ARG_PAD_YPOS:
222       pad->ypos = g_value_get_int (value);
223       break;
224     case ARG_PAD_ALPHA:
225       pad->alpha = g_value_get_double (value);
226       break;
227     default:
228       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
229       break;
230   }
231
232   gst_object_unref (mix);
233 }
234
235 typedef enum
236 {
237   VIDEO_MIXER_BACKGROUND_CHECKER,
238   VIDEO_MIXER_BACKGROUND_BLACK,
239   VIDEO_MIXER_BACKGROUND_WHITE,
240 }
241 GstVideoMixerBackground;
242
243 struct _GstVideoMixer
244 {
245   GstElement element;
246
247   /* pad */
248   GstPad *srcpad;
249
250   /* Sink pads using Collect Pads from core's base library */
251   GstCollectPads *collect;
252   /* sinkpads, a GSList of GstVideoMixerPads */
253   GSList *sinkpads;
254
255   gint numpads;
256
257   /* the master pad */
258   GstVideoMixerPad *master;
259
260   gint in_width, in_height;
261   gint out_width, out_height;
262
263   GstVideoMixerBackground background;
264
265   gdouble in_framerate;
266 };
267
268 struct _GstVideoMixerClass
269 {
270   GstElementClass parent_class;
271 };
272
273 static gboolean
274 gst_videomixer_pad_sink_setcaps (GstPad * pad, GstCaps * vscaps)
275 {
276   GstVideoMixer *mix;
277   GstVideoMixerPad *mixpad;
278   GstStructure *structure;
279   gint in_width, in_height;
280   gboolean ret = FALSE;
281
282   mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
283   mixpad = GST_VIDEO_MIXER_PAD (pad);
284
285   GST_DEBUG ("videomixer: setcaps triggered on %s", gst_pad_get_name (pad));
286
287   structure = gst_caps_get_structure (vscaps, 0);
288
289   if (!gst_structure_get_int (structure, "width", &in_width)
290       || !gst_structure_get_int (structure, "height", &in_height)
291       || !gst_structure_get_double (structure, "framerate",
292           &mixpad->in_framerate))
293     goto beach;
294
295   mixpad->in_width = in_width;
296   mixpad->in_height = in_height;
297
298   mixpad->xpos = 0;
299   mixpad->ypos = 0;
300
301   mix->in_width = MAX (mix->in_width, mixpad->in_width);
302   mix->in_height = MAX (mix->in_height, mixpad->in_height);
303   if (mix->in_framerate < mixpad->in_framerate) {
304     mix->in_framerate = mixpad->in_framerate;
305     mix->master = mixpad;
306   }
307
308   ret = TRUE;
309
310 beach:
311   gst_object_unref (mix);
312
313   return ret;
314 }
315
316 static void
317 gst_videomixer_pad_link (GstPad * pad, GstPad * peer, gpointer data)
318 {
319   GST_DEBUG ("pad '%s' connected", gst_pad_get_name (pad));
320 }
321
322 static void
323 gst_videomixer_pad_unlink (GstPad * pad, GstPad * peer, gpointer data)
324 {
325   GST_DEBUG ("pad '%s' unlinked", gst_pad_get_name (pad));
326 }
327
328 static void
329 gst_videomixer_pad_init (GstVideoMixerPad * mixerpad)
330 {
331   g_signal_connect (mixerpad, "linked",
332       G_CALLBACK (gst_videomixer_pad_link), (gpointer) mixerpad);
333   g_signal_connect (mixerpad, "unlinked",
334       G_CALLBACK (gst_videomixer_pad_unlink), (gpointer) mixerpad);
335
336   /* setup some pad functions */
337   gst_pad_set_setcaps_function (GST_PAD (mixerpad),
338       gst_videomixer_pad_sink_setcaps);
339
340   mixerpad->zorder = DEFAULT_PAD_ZORDER;
341   mixerpad->xpos = DEFAULT_PAD_XPOS;
342   mixerpad->ypos = DEFAULT_PAD_YPOS;
343   mixerpad->alpha = DEFAULT_PAD_ALPHA;
344 }
345
346
347
348 /* elementfactory information */
349 static GstElementDetails gst_videomixer_details =
350 GST_ELEMENT_DETAILS ("video mixer",
351     "Filter/Editor/Video",
352     "Mix multiple video streams",
353     "Wim Taymans <wim@fluendo.com>");
354
355 /* VideoMixer signals and args */
356 enum
357 {
358   /* FILL ME */
359   LAST_SIGNAL
360 };
361
362 #define DEFAULT_BACKGROUND VIDEO_MIXER_BACKGROUND_CHECKER
363 enum
364 {
365   ARG_0,
366   ARG_BACKGROUND,
367 };
368
369 #define GST_TYPE_VIDEO_MIXER_BACKGROUND (gst_video_mixer_background_get_type())
370 static GType
371 gst_video_mixer_background_get_type (void)
372 {
373   static GType video_mixer_background_type = 0;
374   static GEnumValue video_mixer_background[] = {
375     {VIDEO_MIXER_BACKGROUND_CHECKER, "0", "Checker pattern"},
376     {VIDEO_MIXER_BACKGROUND_BLACK, "1", "Black"},
377     {VIDEO_MIXER_BACKGROUND_WHITE, "2", "White"},
378     {0, NULL, NULL},
379   };
380
381   if (!video_mixer_background_type) {
382     video_mixer_background_type =
383         g_enum_register_static ("GstVideoMixerBackground",
384         video_mixer_background);
385   }
386   return video_mixer_background_type;
387 }
388
389 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
390     GST_PAD_SRC,
391     GST_PAD_ALWAYS,
392     GST_STATIC_CAPS ("video/x-raw-yuv,"
393         "format = (fourcc) AYUV,"
394         "width = (int) [ 1, max ],"
395         "height = (int) [ 1, max ]," "framerate = (double) [ 0, max ]")
396     );
397
398 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
399     GST_PAD_SINK,
400     GST_PAD_REQUEST,
401     GST_STATIC_CAPS ("video/x-raw-yuv,"
402         "format = (fourcc) AYUV,"
403         "width = (int) [ 1, max ],"
404         "height = (int) [ 1, max ]," "framerate = (double) [ 0, max ]")
405     );
406
407 static void gst_videomixer_base_init (gpointer g_class);
408 static void gst_videomixer_class_init (GstVideoMixerClass * klass);
409 static void gst_videomixer_init (GstVideoMixer * videomixer);
410
411 static GstCaps *gst_videomixer_getcaps (GstPad * pad);
412
413 static GstFlowReturn gst_videomixer_collected (GstCollectPads * pads,
414     GstVideoMixer * mix);
415 static GstPad *gst_videomixer_request_new_pad (GstElement * element,
416     GstPadTemplate * templ, const gchar * name);
417 static void gst_videomixer_set_property (GObject * object, guint prop_id,
418     const GValue * value, GParamSpec * pspec);
419 static void gst_videomixer_get_property (GObject * object, guint prop_id,
420     GValue * value, GParamSpec * pspec);
421 static GstStateChangeReturn gst_videomixer_change_state (GstElement * element,
422     GstStateChange transition);
423
424 static GstElementClass *parent_class = NULL;
425
426 /*static guint gst_videomixer_signals[LAST_SIGNAL] = { 0 }; */
427
428 static GType
429 gst_videomixer_get_type (void)
430 {
431   static GType videomixer_type = 0;
432
433   if (!videomixer_type) {
434     static const GTypeInfo videomixer_info = {
435       sizeof (GstVideoMixerClass),
436       gst_videomixer_base_init,
437       NULL,
438       (GClassInitFunc) gst_videomixer_class_init,
439       NULL,
440       NULL,
441       sizeof (GstVideoMixer),
442       0,
443       (GInstanceInitFunc) gst_videomixer_init,
444     };
445
446     videomixer_type =
447         g_type_register_static (GST_TYPE_ELEMENT, "GstVideoMixer",
448         &videomixer_info, 0);
449   }
450   return videomixer_type;
451 }
452
453 static void
454 gst_videomixer_base_init (gpointer g_class)
455 {
456   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
457
458   gst_element_class_add_pad_template (element_class,
459       gst_static_pad_template_get (&src_factory));
460   gst_element_class_add_pad_template (element_class,
461       gst_static_pad_template_get (&sink_factory));
462
463   gst_element_class_set_details (element_class, &gst_videomixer_details);
464 }
465
466 static void
467 gst_videomixer_class_init (GstVideoMixerClass * klass)
468 {
469   GObjectClass *gobject_class;
470   GstElementClass *gstelement_class;
471
472   gobject_class = (GObjectClass *) klass;
473   gstelement_class = (GstElementClass *) klass;
474
475   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
476
477   gobject_class->get_property = gst_videomixer_get_property;
478   gobject_class->set_property = gst_videomixer_set_property;
479
480   g_object_class_install_property (gobject_class, ARG_BACKGROUND,
481       g_param_spec_enum ("background", "Background", "Background type",
482           GST_TYPE_VIDEO_MIXER_BACKGROUND,
483           DEFAULT_BACKGROUND, G_PARAM_READWRITE));
484
485   gstelement_class->request_new_pad = gst_videomixer_request_new_pad;
486   gstelement_class->change_state = gst_videomixer_change_state;
487 }
488
489 static void
490 gst_videomixer_init (GstVideoMixer * mix)
491 {
492   GstElementClass *klass = GST_ELEMENT_GET_CLASS (mix);
493
494   mix->srcpad =
495       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
496           "src"), "src");
497   gst_pad_set_getcaps_function (GST_PAD (mix->srcpad), gst_videomixer_getcaps);
498   gst_element_add_pad (GST_ELEMENT (mix), mix->srcpad);
499
500   mix->collect = gst_collectpads_new ();
501   mix->background = DEFAULT_BACKGROUND;
502   mix->in_width = 0;
503   mix->in_height = 0;
504   mix->out_width = 0;
505   mix->out_height = 0;
506
507   gst_collectpads_set_function (mix->collect,
508       (GstCollectPadsFunction) gst_videomixer_collected, mix);
509 }
510
511 static GstCaps *
512 gst_videomixer_getcaps (GstPad * pad)
513 {
514   GstVideoMixer *mix;
515   GstCaps *caps;
516   GstStructure *structure;
517
518   mix = GST_VIDEO_MIXER (gst_pad_get_parent (pad));
519   caps = gst_caps_copy (gst_pad_get_pad_template_caps (mix->srcpad));
520
521   structure = gst_caps_get_structure (caps, 0);
522
523   if (mix->out_width != 0) {
524     gst_structure_set (structure, "width", G_TYPE_INT, mix->out_width, NULL);
525   }
526   if (mix->out_height != 0) {
527     gst_structure_set (structure, "height", G_TYPE_INT, mix->out_height, NULL);
528   }
529   if (mix->in_framerate != 0) {
530     gst_structure_set (structure,
531         "framerate", G_TYPE_DOUBLE, mix->in_framerate, NULL);
532   }
533
534   gst_object_unref (mix);
535
536   return caps;
537 }
538
539 static GstPad *
540 gst_videomixer_request_new_pad (GstElement * element,
541     GstPadTemplate * templ, const gchar * req_name)
542 {
543   GstVideoMixer *mix = NULL;
544   GstVideoMixerPad *mixpad = NULL;
545   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
546
547   g_return_val_if_fail (templ != NULL, NULL);
548
549   if (templ->direction != GST_PAD_SINK) {
550     g_warning ("videomixer: request pad that is not a SINK pad\n");
551     return NULL;
552   }
553
554   g_return_val_if_fail (GST_IS_VIDEO_MIXER (element), NULL);
555
556   mix = GST_VIDEO_MIXER (element);
557
558   if (templ == gst_element_class_get_pad_template (klass, "sink_%d")) {
559     gint serial = 0;
560     gchar *name = NULL;
561     GstVideoMixerCollect *mixcol = NULL;
562
563     if (req_name == NULL || strlen (req_name) < 6) {
564       /* no name given when requesting the pad, use random serial number */
565       serial = rand ();
566     } else {
567       /* parse serial number from requested padname */
568       serial = atoi (&req_name[5]);
569     }
570     /* create new pad with the name */
571     name = g_strdup_printf ("sink_%d", serial);
572     mixpad = g_object_new (GST_TYPE_VIDEO_MIXER_PAD, "name", name, "direction",
573         templ->direction, "template", templ, NULL);
574     g_free (name);
575
576     mixpad->zorder = mix->numpads;
577     mixpad->xpos = DEFAULT_PAD_XPOS;
578     mixpad->ypos = DEFAULT_PAD_YPOS;
579     mixpad->alpha = DEFAULT_PAD_ALPHA;
580
581     mixcol = (GstVideoMixerCollect *)
582         gst_collectpads_add_pad (mix->collect, GST_PAD (mixpad),
583         sizeof (GstVideoMixerCollect));
584
585     /* Keep track of eachother */
586     mixcol->mixpad = mixpad;
587     mixpad->mixcol = mixcol;
588
589     /* Keep an internal list of mixpads for zordering */
590     mix->sinkpads = g_slist_append (mix->sinkpads, mixpad);
591     mix->numpads++;
592   } else {
593     g_warning ("videomixer: this is not our template!\n");
594     return NULL;
595   }
596
597   /* dd the pad to the element */
598   gst_element_add_pad (element, GST_PAD (mixpad));
599
600   return GST_PAD (mixpad);
601 }
602
603 #define BLEND_NORMAL(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)     \
604         Y = ((Y1*(255-alpha))+(Y2*alpha))>>8;           \
605         U = ((U1*(255-alpha))+(U2*alpha))>>8;           \
606         V = ((V1*(255-alpha))+(V2*alpha))>>8;
607
608 #define BLEND_ADD(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)                \
609         Y = Y1+((Y2*alpha)>>8);                                 \
610         U = U1+(((127*(255-alpha)+(U2*alpha)))>>8)-127;         \
611         V = V1+(((127*(255-alpha)+(V2*alpha)))>>8)-127;         \
612         if (Y>255) {                                            \
613           gint mult = MAX (0, 288-Y);                           \
614           U = ((U*mult) + (127*(32-mult)))>>5;                  \
615           V = ((V*mult) + (127*(32-mult)))>>5;                  \
616           Y = 255;                                              \
617         }                                                       \
618         U = MIN (U,255);                                        \
619         V = MIN (V,255);
620
621 #define BLEND_SUBTRACT(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)           \
622         Y = Y1-((Y2*alpha)>>8);                                 \
623         U = U1+(((127*(255-alpha)+(U2*alpha)))>>8)-127;         \
624         V = V1+(((127*(255-alpha)+(V2*alpha)))>>8)-127;         \
625         if (Y<0) {                                              \
626           gint mult = MIN (32, -Y);                             \
627           U = ((U*(32-mult)) + (127*mult))>>5;                  \
628           V = ((V*(32-mult)) + (127*mult))>>5;                  \
629           Y = 0;                                                \
630         }
631
632 #define BLEND_DARKEN(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)     \
633         if (Y1 < Y2) {                                  \
634           Y = Y1; U = U1; V = V1;                       \
635         }                                               \
636         else {                                          \
637           Y = ((Y1*(255-alpha))+(Y2*alpha))>>8;         \
638           U = ((U1*(255-alpha))+(U2*alpha))>>8;         \
639           V = ((V1*(255-alpha))+(V2*alpha))>>8;         \
640         }
641
642 #define BLEND_LIGHTEN(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)    \
643         if (Y1 > Y2) {                                  \
644           Y = Y1; U = U1; V = V1;                       \
645         }                                               \
646         else {                                          \
647           Y = ((Y1*(255-alpha))+(Y2*alpha))>>8;         \
648           U = ((U1*(255-alpha))+(U2*alpha))>>8;         \
649           V = ((V1*(255-alpha))+(V2*alpha))>>8;         \
650         }
651
652 #define BLEND_MULTIPLY(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)                   \
653         Y = (Y1*(256*(255-alpha) +(Y2*alpha)))>>16;                     \
654         U = ((U1*(255-alpha)*256)+(alpha*(U1*Y2+128*(256-Y2))))>>16;    \
655         V = ((V1*(255-alpha)*256)+(alpha*(V1*Y2+128*(256-Y2))))>>16;
656
657 #define BLEND_DIFFERENCE(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)         \
658         Y = ABS((gint)Y1-(gint)Y2)+127;                         \
659         U = ABS((gint)U1-(gint)U2)+127;                         \
660         V = ABS((gint)V1-(gint)V2)+127;                         \
661         Y = ((Y*alpha)+(Y1*(255-alpha)))>>8;                    \
662         U = ((U*alpha)+(U1*(255-alpha)))>>8;                    \
663         V = ((V*alpha)+(V1*(255-alpha)))>>8;                    \
664         if (Y>255) {                                            \
665           gint mult = MAX (0, 288-Y);                           \
666           U = ((U*mult) + (127*(32-mult)))>>5;                  \
667           V = ((V*mult) + (127*(32-mult)))>>5;                  \
668           Y = 255;                                              \
669         } else if (Y<0) {                                       \
670           gint mult = MIN (32, -Y);                             \
671           U = ((U*(32-mult)) + (127*mult))>>5;                  \
672           V = ((V*(32-mult)) + (127*mult))>>5;                  \
673           Y = 0;                                                \
674         }                                                       \
675         U = CLAMP(U, 0, 255);                                   \
676         V = CLAMP(V, 0, 255);
677
678 #define BLEND_EXCLUSION(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)          \
679         Y = ((gint)(Y1^0xff)*Y2+(gint)(Y2^0xff)*Y1)>>8;         \
680         U = ((gint)(U1^0xff)*Y2+(gint)(Y2^0xff)*U1)>>8;         \
681         V = ((gint)(V1^0xff)*Y2+(gint)(Y2^0xff)*V1)>>8;         \
682         Y = ((Y*alpha)+(Y1*(255-alpha)))>>8;                    \
683         U = ((U*alpha)+(U1*(255-alpha)))>>8;                    \
684         V = ((V*alpha)+(V1*(255-alpha)))>>8;                    \
685         if (Y>255) {                                            \
686           gint mult = MAX (0, 288-Y);                           \
687           U = ((U*mult) + (127*(32-mult)))>>5;                  \
688           V = ((V*mult) + (127*(32-mult)))>>5;                  \
689           Y = 255;                                              \
690         } else if (Y<0) {                                       \
691           gint mult = MIN (32, -Y);                             \
692           U = ((U*(32-mult)) + (127*mult))>>5;                  \
693           V = ((V*(32-mult)) + (127*mult))>>5;                  \
694           Y = 0;                                                \
695         }                                                       \
696         U = CLAMP(U, 0, 255);                                   \
697         V = CLAMP(V, 0, 255);
698
699 #define BLEND_SOFTLIGHT(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)          \
700         Y = (gint)Y1+(gint)Y2 - 127;                            \
701         U = (gint)U1+(gint)U2 - 127;                            \
702         V = (gint)V1+(gint)V2 - 127;                            \
703         Y = ((Y*alpha)+(Y1*(255-alpha)))>>8;                    \
704         U = ((U*alpha)+(U1*(255-alpha)))>>8;                    \
705         V = ((V*alpha)+(V1*(255-alpha)))>>8;                    \
706         if (Y>255) {                                            \
707           gint mult = MAX (0, 288-Y);                           \
708           U = ((U*mult) + (127*(32-mult)))>>5;                  \
709           V = ((V*mult) + (127*(32-mult)))>>5;                  \
710           Y = 255;                                              \
711         } else if (Y<0) {                                       \
712           gint mult = MIN (32, -Y);                             \
713           U = ((U*(32-mult)) + (127*mult))>>5;                  \
714           V = ((V*(32-mult)) + (127*mult))>>5;                  \
715           Y = 0;                                                \
716         }                                                       \
717
718 #define BLEND_HARDLIGHT(Y1,U1,V1,Y2,U2,V2,alpha,Y,U,V)          \
719         Y = (gint)Y1+(gint)Y2*2 - 255;                          \
720         U = (gint)U1+(gint)U2 - 127;                            \
721         V = (gint)V1+(gint)V2 - 127;                            \
722         Y = ((Y*alpha)+(Y1*(255-alpha)))>>8;                    \
723         U = ((U*alpha)+(U1*(255-alpha)))>>8;                    \
724         V = ((V*alpha)+(V1*(255-alpha)))>>8;                    \
725         if (Y>255) {                                            \
726           gint mult = MAX (0, 288-Y);                           \
727           U = ((U*mult) + (127*(32-mult)))>>5;                  \
728           V = ((V*mult) + (127*(32-mult)))>>5;                  \
729           Y = 255;                                              \
730         } else if (Y<0) {                                       \
731           gint mult = MIN (32, -Y);                             \
732           U = ((U*(32-mult)) + (127*mult))>>5;                  \
733           V = ((V*(32-mult)) + (127*mult))>>5;                  \
734           Y = 0;                                                \
735         }                                                       \
736
737 #define BLEND_MODE BLEND_NORMAL
738 #if 0
739 #define BLEND_MODE BLEND_NORMAL
740 #define BLEND_MODE BLEND_ADD
741 #define BLEND_MODE BLEND_SUBTRACT
742 #define BLEND_MODE BLEND_LIGHTEN
743 #define BLEND_MODE BLEND_DARKEN
744 #define BLEND_MODE BLEND_MULTIPLY
745 #define BLEND_MODE BLEND_DIFFERENCE
746 #define BLEND_MODE BLEND_EXCLUSION
747 #define BLEND_MODE BLEND_SOFTLIGHT
748 #define BLEND_MODE BLEND_HARDLIGHT
749 #endif
750
751 #define ROUND_UP_2(x) (((x) + 1) & ~1)
752 #define ROUND_UP_4(x) (((x) + 3) & ~3)
753 #define ROUND_UP_8(x) (((x) + 7) & ~7)
754
755 /* note that this function does packing conversion and blending at the
756  * same time */
757 static void
758 gst_videomixer_blend_ayuv_ayuv (guint8 * src, gint xpos, gint ypos,
759     gint src_width, gint src_height, gdouble src_alpha,
760     guint8 * dest, gint dest_width, gint dest_height)
761 {
762   gint alpha, b_alpha;
763   gint i, j;
764   gint src_stride, dest_stride;
765   gint src_add, dest_add;
766   gint Y, U, V;
767
768   src_stride = ROUND_UP_2 (src_width) * 4;
769   dest_stride = ROUND_UP_2 (dest_width) * 4;
770
771   b_alpha = (gint) (src_alpha * 255);
772
773   /* adjust src pointers for negative sizes */
774   if (xpos < 0) {
775     src += -xpos * 4;
776     src_width -= -xpos;
777     xpos = 0;
778   }
779   if (ypos < 0) {
780     src += -ypos * src_stride;
781     src_height -= -ypos;
782     ypos = 0;
783   }
784   /* adjust width/height if the src is bigger than dest */
785   if (xpos + src_width > dest_width) {
786     src_width = dest_width - xpos;
787   }
788   if (ypos + src_height > dest_height) {
789     src_height = dest_height - ypos;
790   }
791
792   src_add = src_stride - (4 * ROUND_UP_2 (src_width));
793   dest_add = dest_stride - (4 * ROUND_UP_2 (src_width));
794
795   dest = dest + 4 * xpos + (ypos * dest_stride);
796
797   /* we convert a square of 2x2 samples to generate 4 Luma and 2 chroma samples */
798   for (i = 0; i < ROUND_UP_2 (src_height); i++) {
799     for (j = 0; j < ROUND_UP_2 (src_width); j++) {
800       alpha = (src[0] * b_alpha) >> 8;
801       BLEND_MODE (dest[1], dest[2], dest[3], src[1], src[2], src[3],
802           alpha, Y, U, V);
803       dest[0] = 0xff;
804       dest[1] = Y;
805       dest[2] = U;
806       dest[3] = V;
807
808       src += 4;
809       dest += 4;
810     }
811     src += src_add;
812     dest += dest_add;
813   }
814 }
815
816 #undef BLEND_MODE
817
818 static int
819 pad_zorder_compare (const GstVideoMixerPad * pad1,
820     const GstVideoMixerPad * pad2)
821 {
822   return pad1->zorder - pad2->zorder;
823 }
824
825 static void
826 gst_videomixer_sort_pads (GstVideoMixer * mix)
827 {
828   mix->sinkpads = g_slist_sort (mix->sinkpads,
829       (GCompareFunc) pad_zorder_compare);
830 }
831
832 /* fill a buffer with a checkerboard pattern */
833 static void
834 gst_videomixer_fill_checker (guint8 * dest, gint width, gint height)
835 {
836   gint stride;
837   gint i, j;
838   static int tab[] = { 80, 160, 80, 160 };
839
840   stride = ROUND_UP_2 (width);
841
842   for (i = 0; i < height; i++) {
843     for (j = 0; j < stride; j++) {
844       *dest++ = 0xff;
845       *dest++ = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)];
846       *dest++ = 128;
847       *dest++ = 128;
848     }
849   }
850 }
851
852 static void
853 gst_videomixer_fill_color (guint8 * dest, gint width, gint height,
854     gint colY, gint colU, gint colV)
855 {
856   gint stride;
857   gint i, j;
858
859   stride = ROUND_UP_2 (width);
860
861   for (i = 0; i < height; i++) {
862     for (j = 0; j < stride; j++) {
863       *dest++ = 0xff;
864       *dest++ = colY;
865       *dest++ = colU;
866       *dest++ = colV;
867     }
868   }
869 }
870
871 /* try to get a buffer on all pads. As long as the queued value is
872  * negative, we skip buffers */
873 static gboolean
874 gst_videomixer_fill_queues (GstVideoMixer * mix)
875 {
876   GSList *walk = NULL;
877   gboolean eos = TRUE;
878
879   g_return_val_if_fail (GST_IS_VIDEO_MIXER (mix), FALSE);
880
881   /* try to make sure we have a buffer from each usable pad first */
882   walk = mix->collect->data;
883   while (walk) {
884     GstCollectData *data = (GstCollectData *) walk->data;
885     GstVideoMixerCollect *mixcol = (GstVideoMixerCollect *) data;
886     GstVideoMixerPad *mixpad = mixcol->mixpad;
887
888     walk = g_slist_next (walk);
889
890     if (mixcol->buffer == NULL) {
891       GstBuffer *buf = NULL;
892
893       GST_LOG ("we need a new buffer");
894
895       buf = gst_collectpads_pop (mix->collect, data);
896
897       if (buf) {
898         guint64 duration;
899
900         GST_LOG ("we have a buffer !");
901
902         mixcol->buffer = buf;
903         duration = GST_BUFFER_DURATION (mixcol->buffer);
904         /* no duration on the buffer, use the framerate */
905         if (!GST_CLOCK_TIME_IS_VALID (duration)) {
906           if (mixpad->in_framerate == 0.0) {
907             duration = GST_CLOCK_TIME_NONE;
908           } else {
909             duration = GST_SECOND / mixpad->in_framerate;
910           }
911         }
912         if (GST_CLOCK_TIME_IS_VALID (duration))
913           mixpad->queued += duration;
914         else if (!mixpad->queued)
915           mixpad->queued = GST_CLOCK_TIME_NONE;
916       } else {
917         GST_LOG ("pop returned a NULL buffer");
918       }
919     }
920     if (mixcol->buffer !=
921         NULL /* && GST_CLOCK_TIME_IS_VALID (mixpad->queued) */ ) {
922       /* got a buffer somewhere so we're not eos */
923       eos = FALSE;
924     }
925   }
926
927   return eos;
928 }
929
930 /* blend all buffers present on the pads */
931 static void
932 gst_videomixer_blend_buffers (GstVideoMixer * mix, GstBuffer * outbuf)
933 {
934   GSList *walk;
935
936   walk = mix->sinkpads;
937   while (walk) {                /* We walk with this list because it's ordered */
938     GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (walk->data);
939     GstVideoMixerCollect *mixcol = pad->mixcol;
940
941     walk = g_slist_next (walk);
942
943     if (mixcol->buffer != NULL) {
944       gst_videomixer_blend_ayuv_ayuv (GST_BUFFER_DATA (mixcol->buffer),
945           pad->xpos, pad->ypos,
946           pad->in_width, pad->in_height,
947           pad->alpha,
948           GST_BUFFER_DATA (outbuf), mix->out_width, mix->out_height);
949       if (pad == mix->master) {
950         GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (mixcol->buffer);
951         GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (mixcol->buffer);
952       }
953     }
954   }
955 }
956
957 /* remove buffers from the queue that were expired in the
958  * interval of the master, we also prepare the queued value
959  * in the pad so that we can skip and fill buffers later on */
960 static void
961 gst_videomixer_update_queues (GstVideoMixer * mix)
962 {
963   GSList *walk;
964   guint64 interval;
965
966   interval = mix->master->queued;
967   if (interval <= 0) {
968     if (mix->in_framerate == 0.0) {
969       interval = G_MAXINT64;
970     } else {
971       interval = GST_SECOND / mix->in_framerate;
972     }
973   }
974
975   walk = mix->sinkpads;
976   while (walk) {
977     GstVideoMixerPad *pad = GST_VIDEO_MIXER_PAD (walk->data);
978     GstVideoMixerCollect *mixcol = pad->mixcol;
979
980     walk = g_slist_next (walk);
981
982     if (mixcol->buffer != NULL && GST_CLOCK_TIME_IS_VALID (pad->queued)) {
983       pad->queued -= interval;
984       GST_DEBUG ("queued now %s %lld", gst_pad_get_name (GST_PAD (pad)),
985           pad->queued);
986       if (pad->queued == 0) {
987         GST_DEBUG ("unreffing buffer");
988         gst_buffer_unref (mixcol->buffer);
989         mixcol->buffer = NULL;
990       }
991     }
992   }
993 }
994
995 /*
996  * The basic idea is to get a buffer on all pads and mix them together.
997  * Based on the framerate, buffers are removed from the queues to make room
998  * for a new buffer. 
999  
1000 static void
1001 gst_videomixer_loop (GstElement * element)
1002 {
1003   GstVideoMixer *mix;
1004   GstBuffer *outbuf;
1005   gint outsize;
1006   gint new_width, new_height;
1007   gboolean eos;
1008
1009   mix = GST_VIDEO_MIXER (element);
1010
1011   eos = gst_videomixer_fill_queues (mix);
1012   if (eos) {
1013     gst_pad_push (mix->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
1014     gst_element_set_eos (GST_ELEMENT (mix));
1015     return;
1016   }
1017
1018   new_width = mix->in_width;
1019   new_height = mix->in_height;
1020
1021   if (new_width != mix->out_width ||
1022       new_height != mix->out_height || !GST_PAD_CAPS (mix->srcpad)) {
1023     GstCaps *newcaps;
1024
1025     newcaps =
1026         gst_caps_copy (gst_pad_get_negotiated_caps (GST_PAD (mix->master)));
1027     gst_caps_set_simple (newcaps, "format", GST_TYPE_FOURCC,
1028         GST_STR_FOURCC ("AYUV"), "width", G_TYPE_INT, new_width, "height",
1029         G_TYPE_INT, new_height, NULL);
1030
1031     if (GST_PAD_LINK_FAILED (gst_pad_try_set_caps (mix->srcpad, newcaps))) {
1032       GST_ELEMENT_ERROR (mix, CORE, NEGOTIATION, (NULL), (NULL));
1033       return;
1034     }
1035
1036     mix->out_width = new_width;
1037     mix->out_height = new_height;
1038   }
1039
1040   outsize = ROUND_UP_2 (mix->out_width) * ROUND_UP_2 (mix->out_height) * 4;
1041
1042   outbuf = gst_pad_alloc_buffer (mix->srcpad, GST_BUFFER_OFFSET_NONE, outsize);
1043   switch (mix->background) {
1044     case VIDEO_MIXER_BACKGROUND_CHECKER:
1045       gst_videomixer_fill_checker (GST_BUFFER_DATA (outbuf),
1046           new_width, new_height);
1047       break;
1048     case VIDEO_MIXER_BACKGROUND_BLACK:
1049       gst_videomixer_fill_color (GST_BUFFER_DATA (outbuf),
1050           new_width, new_height, 16, 128, 128);
1051       break;
1052     case VIDEO_MIXER_BACKGROUND_WHITE:
1053       gst_videomixer_fill_color (GST_BUFFER_DATA (outbuf),
1054           new_width, new_height, 240, 128, 128);
1055       break;
1056   }
1057
1058   gst_videomixer_blend_buffers (mix, outbuf);
1059
1060   gst_videomixer_update_queues (mix);
1061
1062   gst_pad_push (mix->srcpad, GST_DATA (outbuf));
1063 }*/
1064
1065 static GstFlowReturn
1066 gst_videomixer_collected (GstCollectPads * pads, GstVideoMixer * mix)
1067 {
1068   GstFlowReturn ret = GST_FLOW_OK;
1069   GstBuffer *outbuf = NULL;
1070   size_t outsize = 0;
1071   gboolean eos = FALSE;
1072
1073   g_return_val_if_fail (GST_IS_VIDEO_MIXER (mix), GST_FLOW_ERROR);
1074
1075   GST_LOG ("all pads are collected");
1076
1077   eos = gst_videomixer_fill_queues (mix);
1078
1079   if (eos) {
1080     /* Push EOS downstream */
1081     GST_LOG ("all our sinkpads are EOS, pushing downstream");
1082     gst_pad_push_event (mix->srcpad, gst_event_new_eos ());
1083     ret = GST_FLOW_WRONG_STATE;
1084     goto beach;
1085   }
1086
1087   /* Calculating out buffer size from input size */
1088   outsize = ROUND_UP_2 (mix->in_width) * ROUND_UP_2 (mix->in_height) * 4;
1089
1090   /* If geometry has changed we need to set new caps on the buffer */
1091   if (mix->in_width != mix->out_width || mix->in_height != mix->out_height) {
1092     GstCaps *newcaps = NULL;
1093
1094     newcaps =
1095         gst_caps_copy (gst_pad_get_negotiated_caps (GST_PAD (mix->master)));
1096     gst_caps_set_simple (newcaps,
1097         "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
1098         "width", G_TYPE_INT, mix->in_width,
1099         "height", G_TYPE_INT, mix->in_height, NULL);
1100
1101     mix->out_width = mix->in_width;
1102     mix->out_height = mix->in_height;
1103
1104     ret = gst_pad_alloc_buffer (mix->srcpad, GST_BUFFER_OFFSET_NONE, outsize,
1105         newcaps, &outbuf);
1106     gst_caps_unref (newcaps);
1107   } else {                      /* Otherwise we just allocate a buffer from current caps */
1108     ret = gst_pad_alloc_buffer (mix->srcpad, GST_BUFFER_OFFSET_NONE, outsize,
1109         GST_PAD_CAPS (mix->srcpad), &outbuf);
1110   }
1111
1112   if (ret != GST_FLOW_OK) {
1113     goto beach;
1114   }
1115
1116   switch (mix->background) {
1117     case VIDEO_MIXER_BACKGROUND_CHECKER:
1118       gst_videomixer_fill_checker (GST_BUFFER_DATA (outbuf),
1119           mix->out_width, mix->out_height);
1120       break;
1121     case VIDEO_MIXER_BACKGROUND_BLACK:
1122       gst_videomixer_fill_color (GST_BUFFER_DATA (outbuf),
1123           mix->out_width, mix->out_height, 16, 128, 128);
1124       break;
1125     case VIDEO_MIXER_BACKGROUND_WHITE:
1126       gst_videomixer_fill_color (GST_BUFFER_DATA (outbuf),
1127           mix->out_width, mix->out_height, 240, 128, 128);
1128       break;
1129   }
1130
1131   gst_videomixer_blend_buffers (mix, outbuf);
1132
1133   gst_videomixer_update_queues (mix);
1134
1135   ret = gst_pad_push (mix->srcpad, outbuf);
1136
1137 beach:
1138   return ret;
1139 }
1140
1141 static void
1142 gst_videomixer_get_property (GObject * object,
1143     guint prop_id, GValue * value, GParamSpec * pspec)
1144 {
1145   GstVideoMixer *mix = GST_VIDEO_MIXER (object);
1146
1147   switch (prop_id) {
1148     case ARG_BACKGROUND:
1149       g_value_set_enum (value, mix->background);
1150       break;
1151     default:
1152       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1153       break;
1154   }
1155 }
1156
1157 static void
1158 gst_videomixer_set_property (GObject * object,
1159     guint prop_id, const GValue * value, GParamSpec * pspec)
1160 {
1161   GstVideoMixer *mix = GST_VIDEO_MIXER (object);
1162
1163   switch (prop_id) {
1164     case ARG_BACKGROUND:
1165       mix->background = g_value_get_enum (value);
1166       break;
1167     default:
1168       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1169       break;
1170   }
1171 }
1172
1173 static GstStateChangeReturn
1174 gst_videomixer_change_state (GstElement * element, GstStateChange transition)
1175 {
1176   GstVideoMixer *mix;
1177   GstStateChangeReturn ret;
1178
1179   g_return_val_if_fail (GST_IS_VIDEO_MIXER (element), GST_STATE_CHANGE_FAILURE);
1180
1181   mix = GST_VIDEO_MIXER (element);
1182
1183   switch (transition) {
1184     case GST_STATE_CHANGE_READY_TO_PAUSED:
1185       GST_LOG ("starting collectpads");
1186       gst_collectpads_start (mix->collect);
1187       break;
1188     case GST_STATE_CHANGE_PAUSED_TO_READY:
1189       GST_LOG ("stopping collectpads");
1190       gst_collectpads_stop (mix->collect);
1191       break;
1192     default:
1193       break;
1194   }
1195
1196   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1197
1198   return ret;
1199 }
1200
1201 static gboolean
1202 plugin_init (GstPlugin * plugin)
1203 {
1204   GST_DEBUG_CATEGORY_INIT (gst_videomixer_debug, "videomixer", 0,
1205       "video mixer");
1206
1207   return gst_element_register (plugin, "videomixer", GST_RANK_PRIMARY,
1208       GST_TYPE_VIDEO_MIXER);
1209 }
1210
1211 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1212     GST_VERSION_MINOR,
1213     "videomixer",
1214     "Video mixer", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)