2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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.
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.
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.
24 #include <gst/video/video.h>
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))
40 typedef struct _GstAlpha GstAlpha;
41 typedef struct _GstAlphaClass GstAlphaClass;
52 #define ROUND_UP_2(x) (((x) + 1) & ~1)
53 #define ROUND_UP_4(x) (((x) + 3) & ~3)
54 #define ROUND_UP_8(x) (((x) + 7) & ~7)
65 gint in_width, in_height;
66 gint out_width, out_height;
75 GstAlphaMethod method;
80 gfloat y; /* chroma color */
83 gfloat accept_angle_cos;
84 gfloat accept_angle_sin;
85 guint8 accept_angle_tg;
86 guint8 accept_angle_ctg;
93 GstElementClass parent_class;
96 /* elementfactory information */
97 static GstElementDetails gst_alpha_details =
98 GST_ELEMENT_DETAILS ("alpha filter",
99 "Filter/Effect/Video",
100 "Adds an alpha channel to video",
101 "Wim Taymans <wim@fluendo.com>");
104 /* Alpha signals and args */
111 #define DEFAULT_METHOD ALPHA_METHOD_SET
112 #define DEFAULT_ALPHA 1.0
113 #define DEFAULT_TARGET_R 0
114 #define DEFAULT_TARGET_G 255
115 #define DEFAULT_TARGET_B 0
116 #define DEFAULT_ANGLE 20.0
117 #define DEFAULT_NOISE_LEVEL 2.0
132 static GstStaticPadTemplate gst_alpha_src_template =
133 GST_STATIC_PAD_TEMPLATE ("src",
136 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
139 static GstStaticPadTemplate gst_alpha_sink_template =
140 GST_STATIC_PAD_TEMPLATE ("sink",
143 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")
144 ";" GST_VIDEO_CAPS_YUV ("I420")
149 static void gst_alpha_base_init (gpointer g_class);
150 static void gst_alpha_class_init (GstAlphaClass * klass);
151 static void gst_alpha_init (GstAlpha * alpha);
152 static void gst_alpha_init_params (GstAlpha * alpha);
154 static void gst_alpha_set_property (GObject * object, guint prop_id,
155 const GValue * value, GParamSpec * pspec);
156 static void gst_alpha_get_property (GObject * object, guint prop_id,
157 GValue * value, GParamSpec * pspec);
159 static gboolean gst_alpha_sink_setcaps (GstPad * pad, GstCaps * caps);
160 static GstFlowReturn gst_alpha_chain (GstPad * pad, GstBuffer * buffer);
162 static GstElementStateReturn gst_alpha_change_state (GstElement * element);
165 static GstElementClass *parent_class = NULL;
167 #define GST_TYPE_ALPHA_METHOD (gst_alpha_method_get_type())
169 gst_alpha_method_get_type (void)
171 static GType alpha_method_type = 0;
172 static GEnumValue alpha_method[] = {
173 {ALPHA_METHOD_SET, "0", "Set/adjust alpha channel"},
174 {ALPHA_METHOD_GREEN, "1", "Chroma Key green"},
175 {ALPHA_METHOD_BLUE, "2", "Chroma Key blue"},
176 {ALPHA_METHOD_CUSTOM, "3", "Chroma Key on target_r/g/b"},
180 if (!alpha_method_type) {
181 alpha_method_type = g_enum_register_static ("GstAlphaMethod", alpha_method);
183 return alpha_method_type;
186 /* static guint gst_alpha_signals[LAST_SIGNAL] = { 0 }; */
189 gst_alpha_get_type (void)
191 static GType alpha_type = 0;
194 static const GTypeInfo alpha_info = {
195 sizeof (GstAlphaClass),
198 (GClassInitFunc) gst_alpha_class_init,
203 (GInstanceInitFunc) gst_alpha_init,
207 g_type_register_static (GST_TYPE_ELEMENT, "GstAlpha", &alpha_info, 0);
213 gst_alpha_base_init (gpointer g_class)
215 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
217 gst_element_class_set_details (element_class, &gst_alpha_details);
219 gst_element_class_add_pad_template (element_class,
220 gst_static_pad_template_get (&gst_alpha_sink_template));
221 gst_element_class_add_pad_template (element_class,
222 gst_static_pad_template_get (&gst_alpha_src_template));
225 gst_alpha_class_init (GstAlphaClass * klass)
227 GObjectClass *gobject_class;
228 GstElementClass *gstelement_class;
230 gobject_class = (GObjectClass *) klass;
231 gstelement_class = (GstElementClass *) klass;
233 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
235 gobject_class->set_property = gst_alpha_set_property;
236 gobject_class->get_property = gst_alpha_get_property;
238 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_METHOD,
239 g_param_spec_enum ("method", "Method",
240 "How the alpha channels should be created", GST_TYPE_ALPHA_METHOD,
241 DEFAULT_METHOD, (GParamFlags) G_PARAM_READWRITE));
242 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ALPHA,
243 g_param_spec_double ("alpha", "Alpha", "The value for the alpha channel",
244 0.0, 1.0, DEFAULT_ALPHA, (GParamFlags) G_PARAM_READWRITE));
245 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_R,
246 g_param_spec_uint ("target_r", "Target Red", "The Red target", 0,
247 255, DEFAULT_TARGET_R, (GParamFlags) G_PARAM_READWRITE));
248 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_G,
249 g_param_spec_uint ("target_g", "Target Green", "The Green target", 0,
250 255, DEFAULT_TARGET_G, (GParamFlags) G_PARAM_READWRITE));
251 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_B,
252 g_param_spec_uint ("target_b", "Target Blue", "The Blue target",
253 0, 255, DEFAULT_TARGET_B, (GParamFlags) G_PARAM_READWRITE));
254 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANGLE,
255 g_param_spec_float ("angle", "Angle", "Size of the colorcube to change",
256 0.0, 90.0, DEFAULT_ANGLE, (GParamFlags) G_PARAM_READWRITE));
257 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NOISE_LEVEL,
258 g_param_spec_float ("noise_level", "Noise Level", "Size of noise radius",
259 0.0, 64.0, DEFAULT_NOISE_LEVEL, (GParamFlags) G_PARAM_READWRITE));
261 gstelement_class->change_state = gst_alpha_change_state;
265 gst_alpha_init (GstAlpha * alpha)
267 /* create the sink and src pads */
269 gst_pad_new_from_template (gst_static_pad_template_get
270 (&gst_alpha_sink_template), "sink");
271 gst_element_add_pad (GST_ELEMENT (alpha), alpha->sinkpad);
272 gst_pad_set_chain_function (alpha->sinkpad, gst_alpha_chain);
273 gst_pad_set_setcaps_function (alpha->sinkpad, gst_alpha_sink_setcaps);
276 gst_pad_new_from_template (gst_static_pad_template_get
277 (&gst_alpha_src_template), "src");
278 gst_element_add_pad (GST_ELEMENT (alpha), alpha->srcpad);
280 alpha->alpha = DEFAULT_ALPHA;
281 alpha->method = DEFAULT_METHOD;
282 alpha->target_r = DEFAULT_TARGET_R;
283 alpha->target_g = DEFAULT_TARGET_G;
284 alpha->target_b = DEFAULT_TARGET_B;
285 alpha->angle = DEFAULT_ANGLE;
286 alpha->noise_level = DEFAULT_NOISE_LEVEL;
289 /* do we need this function? */
291 gst_alpha_set_property (GObject * object, guint prop_id,
292 const GValue * value, GParamSpec * pspec)
296 /* it's not null if we got it, but it might not be ours */
297 g_return_if_fail (GST_IS_ALPHA (object));
299 alpha = GST_ALPHA (object);
303 alpha->method = g_value_get_enum (value);
304 switch (alpha->method) {
305 case ALPHA_METHOD_GREEN:
307 alpha->target_g = 255;
310 case ALPHA_METHOD_BLUE:
313 alpha->target_b = 255;
318 gst_alpha_init_params (alpha);
321 alpha->alpha = g_value_get_double (value);
324 alpha->target_r = g_value_get_uint (value);
325 gst_alpha_init_params (alpha);
328 alpha->target_g = g_value_get_uint (value);
329 gst_alpha_init_params (alpha);
332 alpha->target_b = g_value_get_uint (value);
333 gst_alpha_init_params (alpha);
336 alpha->angle = g_value_get_float (value);
337 gst_alpha_init_params (alpha);
339 case ARG_NOISE_LEVEL:
340 alpha->noise_level = g_value_get_float (value);
341 gst_alpha_init_params (alpha);
344 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
349 gst_alpha_get_property (GObject * object, guint prop_id, GValue * value,
354 /* it's not null if we got it, but it might not be ours */
355 g_return_if_fail (GST_IS_ALPHA (object));
357 alpha = GST_ALPHA (object);
361 g_value_set_enum (value, alpha->method);
364 g_value_set_double (value, alpha->alpha);
367 g_value_set_uint (value, alpha->target_r);
370 g_value_set_uint (value, alpha->target_g);
373 g_value_set_uint (value, alpha->target_b);
376 g_value_set_float (value, alpha->angle);
378 case ARG_NOISE_LEVEL:
379 g_value_set_float (value, alpha->noise_level);
382 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
388 gst_alpha_sink_setcaps (GstPad * pad, GstCaps * caps)
391 GstStructure *structure;
395 alpha = GST_ALPHA (GST_PAD_PARENT (pad));
396 structure = gst_caps_get_structure (caps, 0);
398 if (gst_structure_get_fourcc (structure, "format", &fourcc)) {
400 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
403 case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
413 ret = gst_structure_get_int (structure, "width", &alpha->in_width);
414 ret &= gst_structure_get_int (structure, "height", &alpha->in_height);
420 gst_alpha_set_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
423 gint b_alpha = (gint) (alpha * 255);
429 width = ROUND_UP_2 (width);
430 height = ROUND_UP_2 (height);
432 stride = ROUND_UP_4 (width);
433 size = stride * height;
435 wrap = stride - width;
437 for (i = 0; i < height; i++) {
438 for (j = 0; j < width; j++) {
439 *dest++ = (*src++ * b_alpha) >> 8;
450 gst_alpha_set_i420 (guint8 * src, guint8 * dest, gint width, gint height,
453 gint b_alpha = (gint) (alpha * 255);
459 gint stride, stride2;
462 width = ROUND_UP_2 (width);
463 height = ROUND_UP_2 (height);
465 stride = ROUND_UP_4 (width);
466 size = stride * height;
467 stride2 = ROUND_UP_8 (width) / 2;
468 size2 = stride2 * height / 2;
470 wrap = stride - 2 * (width / 2);
471 wrap2 = stride2 - width / 2;
477 for (i = 0; i < height; i++) {
478 for (j = 0; j < width / 2; j++) {
500 gst_alpha_chroma_key_ayuv (gchar * src, gchar * dest, gint width, gint height,
507 gint x, z, u, v, y, a;
514 width = ROUND_UP_2 (width);
515 height = ROUND_UP_2 (height);
517 stride = ROUND_UP_4 (width);
518 size = stride * height;
523 wrap = stride - width;
525 for (i = 0; i < height; i++) {
526 for (j = 0; j < width; j++) {
527 a = *src1++ * (alpha->alpha);
532 /* Convert foreground to XZ coords where X direction is defined by
534 tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
535 x = CLAMP (tmp, -128, 127);
536 tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
537 z = CLAMP (tmp, -128, 127);
539 /* WARNING: accept angle should never be set greater than "somewhat less
540 than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
541 80 degrees should be enough if foreground is reasonable. If this seems
542 to be a problem, go to alternative ways of checking point position
543 (scalar product or line equations). This angle should not be too small
544 either to avoid infinite ctg (used to suppress foreground without use of
547 tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
548 tmp = MIN (tmp, 127);
551 /* keep foreground Kfg = 0 */
554 /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
556 tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
557 tmp = CLAMP (tmp, -128, 127);
562 tmp1 = MAX (tmp1, 0);
563 b_alpha = (((unsigned char) (tmp1) *
564 (unsigned short) (alpha->one_over_kc)) / 2);
565 b_alpha = 255 - CLAMP (b_alpha, 0, 255);
566 b_alpha = (a * b_alpha) >> 8;
568 tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
569 tmp1 = MIN (tmp, 255);
574 /* Convert suppressed foreground back to CbCr */
575 tmp = ((char) (x1) * (short) (alpha->cb) -
576 (char) (y1) * (short) (alpha->cr)) >> 7;
577 u = CLAMP (tmp, -128, 127);
579 tmp = ((char) (x1) * (short) (alpha->cr) +
580 (char) (y1) * (short) (alpha->cb)) >> 7;
581 v = CLAMP (tmp, -128, 127);
583 /* Deal with noise. For now, a circle around the key color with
584 radius of noise_level treated as exact key color. Introduces
587 tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
588 tmp = MIN (tmp, 0xffff);
590 if (tmp < alpha->noise_level * alpha->noise_level) {
608 /* based on http://www.cs.utah.edu/~michael/chroma/
611 gst_alpha_chroma_key_i420 (gchar * src, gchar * dest, gint width, gint height,
615 guint8 *srcY1, *srcY2, *srcU, *srcV;
616 guint8 *dest1, *dest2;
618 gint x, z, u, v, y11, y12, y21, y22, a;
620 gint stride, stride2;
621 gint wrap, wrap2, wrap3;
625 width = ROUND_UP_2 (width);
626 height = ROUND_UP_2 (height);
628 stride = ROUND_UP_4 (width);
629 size = stride * height;
630 stride2 = ROUND_UP_8 (width) / 2;
631 size2 = stride2 * height / 2;
634 srcY2 = src + stride;
639 dest2 = dest + width * 4;
641 wrap = 2 * stride - 2 * (width / 2);
642 wrap2 = stride2 - width / 2;
643 wrap3 = 8 * width - 8 * (width / 2);
645 a = 255 * alpha->alpha;
647 for (i = 0; i < height / 2; i++) {
648 for (j = 0; j < width / 2; j++) {
656 /* Convert foreground to XZ coords where X direction is defined by
658 tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
659 x = CLAMP (tmp, -128, 127);
660 tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
661 z = CLAMP (tmp, -128, 127);
663 /* WARNING: accept angle should never be set greater than "somewhat less
664 than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
665 80 degrees should be enough if foreground is reasonable. If this seems
666 to be a problem, go to alternative ways of checking point position
667 (scalar product or line equations). This angle should not be too small
668 either to avoid infinite ctg (used to suppress foreground without use of
671 tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
672 tmp = MIN (tmp, 127);
675 /* keep foreground Kfg = 0 */
678 /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
680 tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
681 tmp = CLAMP (tmp, -128, 127);
686 tmp1 = MAX (tmp1, 0);
687 b_alpha = (((unsigned char) (tmp1) *
688 (unsigned short) (alpha->one_over_kc)) / 2);
689 b_alpha = 255 - CLAMP (b_alpha, 0, 255);
690 b_alpha = (a * b_alpha) >> 8;
692 tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
693 tmp1 = MIN (tmp, 255);
704 /* Convert suppressed foreground back to CbCr */
705 tmp = ((char) (x1) * (short) (alpha->cb) -
706 (char) (y1) * (short) (alpha->cr)) >> 7;
707 u = CLAMP (tmp, -128, 127);
709 tmp = ((char) (x1) * (short) (alpha->cr) +
710 (char) (y1) * (short) (alpha->cb)) >> 7;
711 v = CLAMP (tmp, -128, 127);
713 /* Deal with noise. For now, a circle around the key color with
714 radius of noise_level treated as exact key color. Introduces
717 tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
718 tmp = MIN (tmp, 0xffff);
720 if (tmp < alpha->noise_level * alpha->noise_level) {
721 /* Uncomment this if you want total suppression within the noise circle */
756 gst_alpha_init_params (GstAlpha * alpha)
763 0.257 * alpha->target_r + 0.504 * alpha->target_g +
764 0.098 * alpha->target_b;
766 -0.148 * alpha->target_r - 0.291 * alpha->target_g +
767 0.439 * alpha->target_b;
769 0.439 * alpha->target_r - 0.368 * alpha->target_g -
770 0.071 * alpha->target_b;
771 kgl = sqrt (tmp1 * tmp1 + tmp2 * tmp2);
772 alpha->cb = 127 * (tmp1 / kgl);
773 alpha->cr = 127 * (tmp2 / kgl);
775 alpha->accept_angle_cos = cos (M_PI * alpha->angle / 180);
776 alpha->accept_angle_sin = sin (M_PI * alpha->angle / 180);
777 tmp = 15 * tan (M_PI * alpha->angle / 180);
778 tmp = MIN (tmp, 255);
779 alpha->accept_angle_tg = tmp;
780 tmp = 15 / tan (M_PI * alpha->angle / 180);
781 tmp = MIN (tmp, 255);
782 alpha->accept_angle_ctg = tmp;
784 alpha->one_over_kc = 255 * 2 * tmp - 255;
785 tmp = 15 * (float) (alpha->y) / kgl;
786 tmp = MIN (tmp, 255);
787 alpha->kfgy_scale = tmp;
788 alpha->kg = MIN (kgl, 127);
792 gst_alpha_chain (GstPad * pad, GstBuffer * buffer)
796 gint new_width, new_height;
799 alpha = GST_ALPHA (GST_PAD_PARENT (pad));
801 GST_STREAM_LOCK (pad);
803 new_width = alpha->in_width;
804 new_height = alpha->in_height;
806 if (new_width != alpha->out_width ||
807 new_height != alpha->out_height || !GST_RPAD_CAPS (alpha->srcpad)) {
810 newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (alpha->sinkpad));
811 gst_caps_set_simple (newcaps,
812 "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
813 "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
815 gst_pad_set_caps (alpha->srcpad, newcaps);
817 alpha->out_width = new_width;
818 alpha->out_height = new_height;
822 gst_buffer_new_and_alloc (ROUND_UP_2 (new_width) *
823 ROUND_UP_2 (new_height) * 4);
824 gst_buffer_set_caps (outbuf, GST_RPAD_CAPS (alpha->srcpad));
825 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
826 GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
828 switch (alpha->method) {
829 case ALPHA_METHOD_SET:
831 gst_alpha_set_ayuv (GST_BUFFER_DATA (buffer),
832 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
834 gst_alpha_set_i420 (GST_BUFFER_DATA (buffer),
835 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
838 case ALPHA_METHOD_GREEN:
839 case ALPHA_METHOD_BLUE:
840 case ALPHA_METHOD_CUSTOM:
842 gst_alpha_chroma_key_ayuv (GST_BUFFER_DATA (buffer),
843 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
845 gst_alpha_chroma_key_i420 (GST_BUFFER_DATA (buffer),
846 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
853 gst_buffer_unref (buffer);
855 ret = gst_pad_push (alpha->srcpad, outbuf);
857 GST_STREAM_UNLOCK (pad);
862 static GstElementStateReturn
863 gst_alpha_change_state (GstElement * element)
867 alpha = GST_ALPHA (element);
869 switch (GST_STATE_TRANSITION (element)) {
870 case GST_STATE_NULL_TO_READY:
872 case GST_STATE_READY_TO_PAUSED:
873 gst_alpha_init_params (alpha);
875 case GST_STATE_PAUSED_TO_PLAYING:
877 case GST_STATE_PLAYING_TO_PAUSED:
879 case GST_STATE_PAUSED_TO_READY:
881 case GST_STATE_READY_TO_NULL:
885 parent_class->change_state (element);
887 return GST_STATE_SUCCESS;
891 plugin_init (GstPlugin * plugin)
893 return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
896 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
899 "resizes a video by adding borders or cropping",
900 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)