gst/alpha/gstalpha.c: Updated the chroma keying algorithm with something more sophist...
[platform/upstream/gst-plugins-good.git] / gst / alpha / gstalpha.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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 #include <gst/gst.h>
24 #include <gst/video/video.h>
25
26 #include <string.h>
27 #include <math.h>
28
29 #define GST_TYPE_ALPHA \
30   (gst_alpha_get_type())
31 #define GST_ALPHA(obj) \
32   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALPHA,GstAlpha))
33 #define GST_ALPHA_CLASS(klass) \
34   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALPHA,GstAlphaClass))
35 #define GST_IS_ALPHA(obj) \
36   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALPHA))
37 #define GST_IS_ALPHA_CLASS(obj) \
38   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALPHA))
39
40 typedef struct _GstAlpha GstAlpha;
41 typedef struct _GstAlphaClass GstAlphaClass;
42
43 typedef enum
44 {
45   ALPHA_METHOD_ADD,
46   ALPHA_METHOD_GREEN,
47   ALPHA_METHOD_BLUE,
48   ALPHA_METHOD_CUSTOM,
49 }
50 GstAlphaMethod;
51
52 #define ROUND_UP_4(x) (((x) + 3) & ~3)
53 #define ROUND_UP_2(x) (((x) + 1) & ~1)
54
55 struct _GstAlpha
56 {
57   GstElement element;
58
59   /* pads */
60   GstPad *sinkpad;
61   GstPad *srcpad;
62
63   /* caps */
64   gint in_width, in_height;
65   gint out_width, out_height;
66
67   gdouble alpha;
68
69   guint target_r;
70   guint target_g;
71   guint target_b;
72
73   GstAlphaMethod method;
74
75   gfloat angle;
76   gfloat noise_level;
77
78   gfloat y;                     /* chroma color */
79   gint8 cb, cr;
80   gint8 kg;
81   gfloat accept_angle_cos;
82   gfloat accept_angle_sin;
83   guint8 accept_angle_tg;
84   guint8 accept_angle_ctg;
85   guint8 one_over_kc;
86   guint8 kfgy_scale;
87 };
88
89 struct _GstAlphaClass
90 {
91   GstElementClass parent_class;
92 };
93
94 /* elementfactory information */
95 static GstElementDetails gst_alpha_details =
96 GST_ELEMENT_DETAILS ("alpha filter",
97     "Filter/Effect/Video",
98     "Adds an alpha channel to video",
99     "Wim Taymans <wim@fluendo.com>");
100
101
102 /* Alpha signals and args */
103 enum
104 {
105   /* FILL ME */
106   LAST_SIGNAL
107 };
108
109 #define DEFAULT_METHOD ALPHA_METHOD_ADD
110 #define DEFAULT_ALPHA 1.0
111 #define DEFAULT_TARGET_R 0
112 #define DEFAULT_TARGET_G 255
113 #define DEFAULT_TARGET_B 0
114 #define DEFAULT_ANGLE 20.0
115 #define DEFAULT_NOISE_LEVEL 2.0
116
117 enum
118 {
119   ARG_0,
120   ARG_METHOD,
121   ARG_ALPHA,
122   ARG_TARGET_R,
123   ARG_TARGET_G,
124   ARG_TARGET_B,
125   ARG_ANGLE,
126   ARG_NOISE_LEVEL,
127   /* FILL ME */
128 };
129
130 static GstStaticPadTemplate gst_alpha_src_template =
131 GST_STATIC_PAD_TEMPLATE ("src",
132     GST_PAD_SRC,
133     GST_PAD_ALWAYS,
134     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
135     );
136
137 static GstStaticPadTemplate gst_alpha_sink_template =
138 GST_STATIC_PAD_TEMPLATE ("sink",
139     GST_PAD_SINK,
140     GST_PAD_ALWAYS,
141     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
142     );
143
144
145 static void gst_alpha_base_init (gpointer g_class);
146 static void gst_alpha_class_init (GstAlphaClass * klass);
147 static void gst_alpha_init (GstAlpha * alpha);
148 static void gst_alpha_init_params (GstAlpha * alpha);
149
150 static void gst_alpha_set_property (GObject * object, guint prop_id,
151     const GValue * value, GParamSpec * pspec);
152 static void gst_alpha_get_property (GObject * object, guint prop_id,
153     GValue * value, GParamSpec * pspec);
154
155 static GstPadLinkReturn
156 gst_alpha_sink_link (GstPad * pad, const GstCaps * caps);
157 static void gst_alpha_chain (GstPad * pad, GstData * _data);
158
159 static GstElementStateReturn gst_alpha_change_state (GstElement * element);
160
161
162 static GstElementClass *parent_class = NULL;
163
164 #define GST_TYPE_ALPHA_METHOD (gst_alpha_method_get_type())
165 static GType
166 gst_alpha_method_get_type (void)
167 {
168   static GType alpha_method_type = 0;
169   static GEnumValue alpha_method[] = {
170     {ALPHA_METHOD_ADD, "0", "Add alpha channel"},
171     {ALPHA_METHOD_GREEN, "1", "Chroma Key green"},
172     {ALPHA_METHOD_BLUE, "2", "Chroma Key blue"},
173     {ALPHA_METHOD_CUSTOM, "3", "Chroma Key on target_r/g/b"},
174     {0, NULL, NULL},
175   };
176
177   if (!alpha_method_type) {
178     alpha_method_type = g_enum_register_static ("GstAlphaMethod", alpha_method);
179   }
180   return alpha_method_type;
181 }
182
183 /* static guint gst_alpha_signals[LAST_SIGNAL] = { 0 }; */
184
185 GType
186 gst_alpha_get_type (void)
187 {
188   static GType alpha_type = 0;
189
190   if (!alpha_type) {
191     static const GTypeInfo alpha_info = {
192       sizeof (GstAlphaClass),
193       gst_alpha_base_init,
194       NULL,
195       (GClassInitFunc) gst_alpha_class_init,
196       NULL,
197       NULL,
198       sizeof (GstAlpha),
199       0,
200       (GInstanceInitFunc) gst_alpha_init,
201     };
202
203     alpha_type =
204         g_type_register_static (GST_TYPE_ELEMENT, "GstAlpha", &alpha_info, 0);
205   }
206   return alpha_type;
207 }
208
209 static void
210 gst_alpha_base_init (gpointer g_class)
211 {
212   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
213
214   gst_element_class_set_details (element_class, &gst_alpha_details);
215
216   gst_element_class_add_pad_template (element_class,
217       gst_static_pad_template_get (&gst_alpha_sink_template));
218   gst_element_class_add_pad_template (element_class,
219       gst_static_pad_template_get (&gst_alpha_src_template));
220 }
221 static void
222 gst_alpha_class_init (GstAlphaClass * klass)
223 {
224   GObjectClass *gobject_class;
225   GstElementClass *gstelement_class;
226
227   gobject_class = (GObjectClass *) klass;
228   gstelement_class = (GstElementClass *) klass;
229
230   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
231
232   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_METHOD,
233       g_param_spec_enum ("method", "Method",
234           "How the alpha channels should be created", GST_TYPE_ALPHA_METHOD,
235           DEFAULT_METHOD, (GParamFlags) G_PARAM_READWRITE));
236   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ALPHA,
237       g_param_spec_double ("alpha", "Alpha", "The value for the alpha channel",
238           0.0, 1.0, DEFAULT_ALPHA, (GParamFlags) G_PARAM_READWRITE));
239   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_R,
240       g_param_spec_uint ("target_r", "Target Red", "The Red target", 0,
241           255, DEFAULT_TARGET_R, (GParamFlags) G_PARAM_READWRITE));
242   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_G,
243       g_param_spec_uint ("target_g", "Target Green", "The Green target", 0,
244           255, DEFAULT_TARGET_G, (GParamFlags) G_PARAM_READWRITE));
245   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_B,
246       g_param_spec_uint ("target_b", "Target Blue", "The Blue target",
247           0, 255, DEFAULT_TARGET_B, (GParamFlags) G_PARAM_READWRITE));
248   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANGLE,
249       g_param_spec_float ("angle", "Angle", "Size of the colorcube to change",
250           0.0, 90.0, DEFAULT_ANGLE, (GParamFlags) G_PARAM_READWRITE));
251   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NOISE_LEVEL,
252       g_param_spec_float ("noise_level", "Noise Level", "Size of noise radius",
253           0.0, 64.0, DEFAULT_NOISE_LEVEL, (GParamFlags) G_PARAM_READWRITE));
254
255   gobject_class->set_property = gst_alpha_set_property;
256   gobject_class->get_property = gst_alpha_get_property;
257
258   gstelement_class->change_state = gst_alpha_change_state;
259 }
260
261 static void
262 gst_alpha_init (GstAlpha * alpha)
263 {
264   /* create the sink and src pads */
265   alpha->sinkpad =
266       gst_pad_new_from_template (gst_static_pad_template_get
267       (&gst_alpha_sink_template), "sink");
268   gst_element_add_pad (GST_ELEMENT (alpha), alpha->sinkpad);
269   gst_pad_set_chain_function (alpha->sinkpad, gst_alpha_chain);
270   gst_pad_set_link_function (alpha->sinkpad, gst_alpha_sink_link);
271
272   alpha->srcpad =
273       gst_pad_new_from_template (gst_static_pad_template_get
274       (&gst_alpha_src_template), "src");
275   gst_element_add_pad (GST_ELEMENT (alpha), alpha->srcpad);
276
277   alpha->alpha = DEFAULT_ALPHA;
278   alpha->method = DEFAULT_METHOD;
279   alpha->target_r = DEFAULT_TARGET_R;
280   alpha->target_g = DEFAULT_TARGET_G;
281   alpha->target_b = DEFAULT_TARGET_B;
282   alpha->angle = DEFAULT_ANGLE;
283   alpha->noise_level = DEFAULT_NOISE_LEVEL;
284
285   GST_FLAG_SET (alpha, GST_ELEMENT_EVENT_AWARE);
286 }
287
288 /* do we need this function? */
289 static void
290 gst_alpha_set_property (GObject * object, guint prop_id,
291     const GValue * value, GParamSpec * pspec)
292 {
293   GstAlpha *alpha;
294
295   /* it's not null if we got it, but it might not be ours */
296   g_return_if_fail (GST_IS_ALPHA (object));
297
298   alpha = GST_ALPHA (object);
299
300   switch (prop_id) {
301     case ARG_METHOD:
302       alpha->method = g_value_get_enum (value);
303       switch (alpha->method) {
304         case ALPHA_METHOD_GREEN:
305           alpha->target_r = 0;
306           alpha->target_g = 255;
307           alpha->target_b = 0;
308           break;
309         case ALPHA_METHOD_BLUE:
310           alpha->target_r = 0;
311           alpha->target_g = 0;
312           alpha->target_b = 255;
313           break;
314         default:
315           break;
316       }
317       gst_alpha_init_params (alpha);
318       break;
319     case ARG_ALPHA:
320       alpha->alpha = g_value_get_double (value);
321       break;
322     case ARG_TARGET_R:
323       alpha->target_r = g_value_get_uint (value);
324       gst_alpha_init_params (alpha);
325       break;
326     case ARG_TARGET_G:
327       alpha->target_g = g_value_get_uint (value);
328       gst_alpha_init_params (alpha);
329       break;
330     case ARG_TARGET_B:
331       alpha->target_b = g_value_get_uint (value);
332       gst_alpha_init_params (alpha);
333       break;
334     case ARG_ANGLE:
335       alpha->angle = g_value_get_float (value);
336       gst_alpha_init_params (alpha);
337       break;
338     case ARG_NOISE_LEVEL:
339       alpha->noise_level = g_value_get_float (value);
340       gst_alpha_init_params (alpha);
341       break;
342     default:
343       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
344       break;
345   }
346 }
347 static void
348 gst_alpha_get_property (GObject * object, guint prop_id, GValue * value,
349     GParamSpec * pspec)
350 {
351   GstAlpha *alpha;
352
353   /* it's not null if we got it, but it might not be ours */
354   g_return_if_fail (GST_IS_ALPHA (object));
355
356   alpha = GST_ALPHA (object);
357
358   switch (prop_id) {
359     case ARG_METHOD:
360       g_value_set_enum (value, alpha->method);
361       break;
362     case ARG_ALPHA:
363       g_value_set_double (value, alpha->alpha);
364       break;
365     case ARG_TARGET_R:
366       g_value_set_uint (value, alpha->target_r);
367       break;
368     case ARG_TARGET_G:
369       g_value_set_uint (value, alpha->target_g);
370       break;
371     case ARG_TARGET_B:
372       g_value_set_uint (value, alpha->target_b);
373       break;
374     case ARG_ANGLE:
375       g_value_set_float (value, alpha->angle);
376       break;
377     case ARG_NOISE_LEVEL:
378       g_value_set_float (value, alpha->noise_level);
379       break;
380     default:
381       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
382       break;
383   }
384 }
385
386 static GstPadLinkReturn
387 gst_alpha_sink_link (GstPad * pad, const GstCaps * caps)
388 {
389   GstAlpha *alpha;
390   GstStructure *structure;
391   gboolean ret;
392
393   alpha = GST_ALPHA (gst_pad_get_parent (pad));
394   structure = gst_caps_get_structure (caps, 0);
395
396   ret = gst_structure_get_int (structure, "width", &alpha->in_width);
397   ret &= gst_structure_get_int (structure, "height", &alpha->in_height);
398
399   return GST_PAD_LINK_OK;
400 }
401
402 #define ROUND_UP_4(x) (((x) + 3) & ~3)
403
404 static void
405 gst_alpha_add (guint8 * src, guint8 * dest, gint width, gint height,
406     gdouble alpha)
407 {
408   gint b_alpha = (gint) (alpha * 255);
409   guint8 *srcY;
410   guint8 *srcU;
411   guint8 *srcV;
412   gint i, j;
413   gint w2, h2;
414   gint size, size2;
415   gint stride, stride2;
416   gint wrap, wrap2;
417
418   stride = ROUND_UP_4 (width);
419   size = stride * height;
420   w2 = (width + 1) >> 1;
421   stride2 = ROUND_UP_4 (w2);
422   h2 = (height + 1) >> 1;
423   size2 = stride2 * h2;
424
425   wrap = stride - 2 * (width / 2);
426   wrap2 = stride2 - width / 2;
427
428   srcY = src;
429   srcU = srcY + size;
430   srcV = srcU + size2;
431
432   for (i = 0; i < height; i++) {
433     for (j = 0; j < width / 2; j++) {
434       *dest++ = b_alpha;
435       *dest++ = *srcY++;
436       *dest++ = *srcU;
437       *dest++ = *srcV;
438       *dest++ = b_alpha;
439       *dest++ = *srcY++;
440       *dest++ = *srcU++;
441       *dest++ = *srcV++;
442     }
443     if (i % 2 == 0) {
444       srcU -= width / 2;
445       srcV -= width / 2;
446     } else {
447       srcU += wrap2;
448       srcV += wrap2;
449     }
450     srcY += wrap;
451   }
452 }
453
454
455 /* based on http://www.cs.utah.edu/~michael/chroma/
456  */
457 static void
458 gst_alpha_chroma_key (gchar * src, gchar * dest, gint width, gint height,
459     GstAlpha * alpha)
460 {
461   gint b_alpha;
462   guint8 *srcY1, *srcY2, *srcU, *srcV;
463   guint8 *dest1, *dest2;
464   gint i, j;
465   gint x, z, u, v, y11, y12, y21, y22;
466   gint w2, h2;
467   gint size, size2;
468   gint stride, stride2;
469   gint wrap, wrap2, wrap3;
470   gint tmp, tmp1;
471   gint x1, y1;
472
473   stride = ROUND_UP_4 (width);
474   size = stride * height;
475   w2 = (width + 1) >> 1;
476   stride2 = ROUND_UP_4 (w2);
477   h2 = (height + 1) >> 1;
478   size2 = stride2 * h2;
479
480   srcY1 = src;
481   srcY2 = src + stride;
482   srcU = srcY1 + size;
483   srcV = srcU + size2;
484
485   dest1 = dest;
486   dest2 = dest + width * 4;
487
488   wrap = 2 * stride - 2 * (width / 2);
489   wrap2 = stride2 - width / 2;
490   wrap3 = 8 * width - 8 * (ROUND_UP_2 (width) / 2);
491
492   for (i = 0; i < height / 2; i++) {
493     for (j = 0; j < width / 2; j++) {
494       y11 = *srcY1++;
495       y12 = *srcY1++;
496       y21 = *srcY2++;
497       y22 = *srcY2++;
498       u = *srcU++ - 128;
499       v = *srcV++ - 128;
500
501       /* Convert foreground to XZ coords where X direction is defined by
502          the key color */
503       tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
504       x = CLAMP (tmp, -128, 127);
505       tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
506       z = CLAMP (tmp, -128, 127);
507
508       /* WARNING: accept angle should never be set greater than "somewhat less
509          than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
510          80 degrees should be enough if foreground is reasonable. If this seems
511          to be a problem, go to alternative ways of checking point position
512          (scalar product or line equations). This angle should not be too small
513          either to avoid infinite ctg (used to suppress foreground without use of
514          division) */
515
516       tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
517       tmp = MIN (tmp, 127);
518
519       if (abs (z) > tmp) {
520         /* keep foreground Kfg = 0 */
521         b_alpha = 255;
522       } else {
523         /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
524            according to Kfg */
525         tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
526         tmp = CLAMP (tmp, -128, 127);
527         x1 = abs (tmp);
528         y1 = z;
529
530         tmp1 = x - x1;
531         tmp1 = MAX (tmp1, 0);
532         b_alpha = (((unsigned char) (tmp1) *
533                 (unsigned short) (alpha->one_over_kc)) / 2);
534         b_alpha = 255 - CLAMP (b_alpha, 0, 255);
535
536         tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
537         tmp1 = MIN (tmp, 255);
538
539         tmp = y11 - tmp1;
540         y11 = MAX (tmp, 0);
541         tmp = y12 - tmp1;
542         y12 = MAX (tmp, 0);
543         tmp = y21 - tmp1;
544         y21 = MAX (tmp, 0);
545         tmp = y22 - tmp1;
546         y22 = MAX (tmp, 0);
547
548         /* Convert suppressed foreground back to CbCr */
549         tmp = ((char) (x1) * (short) (alpha->cb) -
550             (char) (y1) * (short) (alpha->cr)) >> 7;
551         u = CLAMP (tmp, -128, 127);
552
553         tmp = ((char) (x1) * (short) (alpha->cr) +
554             (char) (y1) * (short) (alpha->cb)) >> 7;
555         v = CLAMP (tmp, -128, 127);
556
557         /* Deal with noise. For now, a circle around the key color with
558            radius of noise_level treated as exact key color. Introduces
559            sharp transitions.
560          */
561         tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
562         tmp = MIN (tmp, 0xffff);
563
564         if (tmp < alpha->noise_level * alpha->noise_level) {
565           /* Uncomment this if you want total suppression within the noise circle */
566           b_alpha = 0;
567         }
568       }
569
570       u += 128;
571       v += 128;
572
573       *dest1++ = b_alpha;
574       *dest1++ = y11;
575       *dest1++ = u;
576       *dest1++ = v;
577       *dest1++ = b_alpha;
578       *dest1++ = y12;
579       *dest1++ = u;
580       *dest1++ = v;
581       *dest2++ = b_alpha;
582       *dest2++ = y21;
583       *dest2++ = u;
584       *dest2++ = v;
585       *dest2++ = b_alpha;
586       *dest2++ = y22;
587       *dest2++ = u;
588       *dest2++ = v;
589     }
590     dest1 += wrap3;
591     dest2 += wrap3;
592     srcY1 += wrap;
593     srcY2 += wrap;
594     srcU += wrap2;
595     srcV += wrap2;
596   }
597 }
598
599 static void
600 gst_alpha_init_params (GstAlpha * alpha)
601 {
602   float kgl;
603   float tmp;
604   float tmp1, tmp2;
605
606   alpha->y =
607       0.257 * alpha->target_r + 0.504 * alpha->target_g +
608       0.098 * alpha->target_b;
609   tmp1 =
610       -0.148 * alpha->target_r - 0.291 * alpha->target_g +
611       0.439 * alpha->target_b;
612   tmp2 =
613       0.439 * alpha->target_r - 0.368 * alpha->target_g -
614       0.071 * alpha->target_b;
615   kgl = sqrt (tmp1 * tmp1 + tmp2 * tmp2);
616   alpha->cb = 127 * (tmp1 / kgl);
617   alpha->cr = 127 * (tmp2 / kgl);
618
619   alpha->accept_angle_cos = cos (M_PI * alpha->angle / 180);
620   alpha->accept_angle_sin = sin (M_PI * alpha->angle / 180);
621   tmp = 15 * tan (M_PI * alpha->angle / 180);
622   tmp = MIN (tmp, 255);
623   alpha->accept_angle_tg = tmp;
624   tmp = 15 / tan (M_PI * alpha->angle / 180);
625   tmp = MIN (tmp, 255);
626   alpha->accept_angle_ctg = tmp;
627   tmp = 1 / (kgl);
628   alpha->one_over_kc = 255 * 2 * tmp - 255;
629   tmp = 15 * (float) (alpha->y) / kgl;
630   tmp = MIN (tmp, 255);
631   alpha->kfgy_scale = tmp;
632   alpha->kg = MIN (kgl, 127);
633 }
634
635 static void
636 gst_alpha_chain (GstPad * pad, GstData * _data)
637 {
638   GstBuffer *buffer;
639   GstAlpha *alpha;
640   GstBuffer *outbuf;
641   gint new_width, new_height;
642
643   alpha = GST_ALPHA (gst_pad_get_parent (pad));
644
645   if (GST_IS_EVENT (_data)) {
646     GstEvent *event = GST_EVENT (_data);
647
648     switch (GST_EVENT_TYPE (event)) {
649       default:
650         gst_pad_event_default (pad, event);
651         break;
652     }
653     return;
654   }
655
656   buffer = GST_BUFFER (_data);
657
658   new_width = alpha->in_width;
659   new_height = alpha->in_height;
660
661   if (new_width != alpha->out_width ||
662       new_height != alpha->out_height || !GST_PAD_CAPS (alpha->srcpad)) {
663     GstCaps *newcaps;
664
665     newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (alpha->sinkpad));
666     gst_caps_set_simple (newcaps,
667         "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
668         "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
669
670     if (!gst_pad_try_set_caps (alpha->srcpad, newcaps)) {
671       GST_ELEMENT_ERROR (alpha, CORE, NEGOTIATION, (NULL), (NULL));
672       return;
673     }
674
675     alpha->out_width = new_width;
676     alpha->out_height = new_height;
677   }
678
679   outbuf = gst_buffer_new_and_alloc (new_width * new_height * 4);
680   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
681   GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
682
683   switch (alpha->method) {
684     case ALPHA_METHOD_ADD:
685       gst_alpha_add (GST_BUFFER_DATA (buffer),
686           GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
687       break;
688     case ALPHA_METHOD_GREEN:
689       gst_alpha_chroma_key (GST_BUFFER_DATA (buffer),
690           GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
691       break;
692     case ALPHA_METHOD_BLUE:
693       gst_alpha_chroma_key (GST_BUFFER_DATA (buffer),
694           GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
695       break;
696     case ALPHA_METHOD_CUSTOM:
697       gst_alpha_chroma_key (GST_BUFFER_DATA (buffer),
698           GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
699       break;
700     default:
701       break;
702   }
703
704   gst_buffer_unref (buffer);
705
706   gst_pad_push (alpha->srcpad, GST_DATA (outbuf));
707 }
708
709 static GstElementStateReturn
710 gst_alpha_change_state (GstElement * element)
711 {
712   GstAlpha *alpha;
713
714   alpha = GST_ALPHA (element);
715
716   switch (GST_STATE_TRANSITION (element)) {
717     case GST_STATE_NULL_TO_READY:
718       break;
719     case GST_STATE_READY_TO_PAUSED:
720       gst_alpha_init_params (alpha);
721       break;
722     case GST_STATE_PAUSED_TO_PLAYING:
723       break;
724     case GST_STATE_PLAYING_TO_PAUSED:
725       break;
726     case GST_STATE_PAUSED_TO_READY:
727       break;
728     case GST_STATE_READY_TO_NULL:
729       break;
730   }
731
732   parent_class->change_state (element);
733
734   return GST_STATE_SUCCESS;
735 }
736
737 static gboolean
738 plugin_init (GstPlugin * plugin)
739 {
740   return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
741 }
742
743 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
744     GST_VERSION_MINOR,
745     "alpha",
746     "resizes a video by adding borders or cropping",
747     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)