gst_plugins_good/gst/alpha/gstalpha.c
changeset 27 d43ce56a1534
parent 23 29ecd5cb86b3
child 31 aec498aab1d3
equal deleted inserted replaced
23:29ecd5cb86b3 27:d43ce56a1534
     1 /* GStreamer
       
     2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
       
     3  * Copyright (C) <2007> Wim Taymans <wim.taymans@collabora.co.uk>
       
     4  * Copyright (C) <2007> Edward Hervey <edward.hervey@collabora.co.uk>
       
     5  * Copyright (C) <2007> Jan Schmidt <thaytan@noraisin.net>
       
     6  *
       
     7  * This library is free software; you can redistribute it and/or
       
     8  * modify it under the terms of the GNU Library General Public
       
     9  * License as published by the Free Software Foundation; either
       
    10  * version 2 of the License, or (at your option) any later version.
       
    11  *
       
    12  * This library is distributed in the hope that it will be useful,
       
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    15  * Library General Public License for more details.
       
    16  *
       
    17  * You should have received a copy of the GNU Library General Public
       
    18  * License along with this library; if not, write to the
       
    19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    20  * Boston, MA 02111-1307, USA.
       
    21  */
       
    22 
       
    23 #ifdef HAVE_CONFIG_H
       
    24 #include "config.h"
       
    25 #endif
       
    26 #include <gst/gst.h>
       
    27 #include <gst/base/gstbasetransform.h>
       
    28 #include <gst/video/video.h>
       
    29 #include <gst/controller/gstcontroller.h>
       
    30 
       
    31 #include <stdlib.h>
       
    32 #include <string.h>
       
    33 #include <math.h>
       
    34 
       
    35 #ifndef M_PI
       
    36 #define M_PI  3.14159265358979323846
       
    37 #endif
       
    38 
       
    39 #define GST_TYPE_ALPHA \
       
    40   (gst_alpha_get_type())
       
    41 #define GST_ALPHA(obj) \
       
    42   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALPHA,GstAlpha))
       
    43 #define GST_ALPHA_CLASS(klass) \
       
    44   (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALPHA,GstAlphaClass))
       
    45 #define GST_IS_ALPHA(obj) \
       
    46   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALPHA))
       
    47 #define GST_IS_ALPHA_CLASS(klass) \
       
    48   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALPHA))
       
    49 
       
    50 typedef struct _GstAlpha GstAlpha;
       
    51 typedef struct _GstAlphaClass GstAlphaClass;
       
    52 
       
    53 typedef enum
       
    54 {
       
    55   ALPHA_METHOD_SET,
       
    56   ALPHA_METHOD_GREEN,
       
    57   ALPHA_METHOD_BLUE,
       
    58   ALPHA_METHOD_CUSTOM,
       
    59 }
       
    60 GstAlphaMethod;
       
    61 
       
    62 GST_DEBUG_CATEGORY_STATIC (gst_alpha_debug);
       
    63 #define GST_CAT_DEFAULT gst_alpha_debug
       
    64 
       
    65 struct _GstAlpha
       
    66 {
       
    67   GstBaseTransform parent;
       
    68 
       
    69   /* caps */
       
    70   GstVideoFormat format;
       
    71   gint width, height;
       
    72   gboolean ayuv;
       
    73 
       
    74   gdouble alpha;
       
    75 
       
    76   guint target_r;
       
    77   guint target_g;
       
    78   guint target_b;
       
    79 
       
    80   GstAlphaMethod method;
       
    81 
       
    82   gfloat angle;
       
    83   gfloat noise_level;
       
    84   guint black_sensitivity;
       
    85   guint white_sensitivity;
       
    86 
       
    87   gfloat y;                     /* chroma color */
       
    88   gint8 cb, cr;
       
    89   gint8 kg;
       
    90   gfloat accept_angle_cos;
       
    91   gfloat accept_angle_sin;
       
    92   guint8 accept_angle_tg;
       
    93   guint8 accept_angle_ctg;
       
    94   guint8 one_over_kc;
       
    95   guint8 kfgy_scale;
       
    96 };
       
    97 
       
    98 struct _GstAlphaClass
       
    99 {
       
   100   GstBaseTransformClass parent_class;
       
   101 };
       
   102 
       
   103 /* elementfactory information */
       
   104 static const GstElementDetails gst_alpha_details =
       
   105 GST_ELEMENT_DETAILS ("Alpha filter",
       
   106     "Filter/Effect/Video",
       
   107     "Adds an alpha channel to video - uniform or via chroma-keying",
       
   108     "Wim Taymans <wim@fluendo.com>\n"
       
   109     "Edward Hervey <edward.hervey@collabora.co.uk>\n"
       
   110     "Jan Schmidt <thaytan@noraisin.net>");
       
   111 
       
   112 /* Alpha signals and args */
       
   113 enum
       
   114 {
       
   115   /* FILL ME */
       
   116   LAST_SIGNAL
       
   117 };
       
   118 
       
   119 #define DEFAULT_METHOD ALPHA_METHOD_SET
       
   120 #define DEFAULT_ALPHA 1.0
       
   121 #define DEFAULT_TARGET_R 0
       
   122 #define DEFAULT_TARGET_G 255
       
   123 #define DEFAULT_TARGET_B 0
       
   124 #define DEFAULT_ANGLE 20.0
       
   125 #define DEFAULT_NOISE_LEVEL 2.0
       
   126 #define DEFAULT_BLACK_SENSITIVITY 100
       
   127 #define DEFAULT_WHITE_SENSITIVITY 100
       
   128 
       
   129 enum
       
   130 {
       
   131   PROP_0,
       
   132   PROP_METHOD,
       
   133   PROP_ALPHA,
       
   134   PROP_TARGET_R,
       
   135   PROP_TARGET_G,
       
   136   PROP_TARGET_B,
       
   137   PROP_ANGLE,
       
   138   PROP_NOISE_LEVEL,
       
   139   PROP_BLACK_SENSITIVITY,
       
   140   PROP_WHITE_SENSITIVITY,
       
   141   PROP_LAST
       
   142 };
       
   143 
       
   144 static GstStaticPadTemplate gst_alpha_src_template =
       
   145 GST_STATIC_PAD_TEMPLATE ("src",
       
   146     GST_PAD_SRC,
       
   147     GST_PAD_ALWAYS,
       
   148     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
       
   149     );
       
   150 
       
   151 static GstStaticPadTemplate gst_alpha_sink_template =
       
   152     GST_STATIC_PAD_TEMPLATE ("sink",
       
   153     GST_PAD_SINK,
       
   154     GST_PAD_ALWAYS,
       
   155     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";" GST_VIDEO_CAPS_YUV ("I420")
       
   156     )
       
   157     );
       
   158 
       
   159 static gboolean gst_alpha_start (GstBaseTransform * trans);
       
   160 static gboolean gst_alpha_get_unit_size (GstBaseTransform * btrans,
       
   161     GstCaps * caps, guint * size);
       
   162 static GstCaps *gst_alpha_transform_caps (GstBaseTransform * btrans,
       
   163     GstPadDirection direction, GstCaps * caps);
       
   164 static gboolean gst_alpha_set_caps (GstBaseTransform * btrans,
       
   165     GstCaps * incaps, GstCaps * outcaps);
       
   166 static GstFlowReturn gst_alpha_transform (GstBaseTransform * btrans,
       
   167     GstBuffer * in, GstBuffer * out);
       
   168 
       
   169 static void gst_alpha_init_params (GstAlpha * alpha);
       
   170 
       
   171 static void gst_alpha_set_property (GObject * object, guint prop_id,
       
   172     const GValue * value, GParamSpec * pspec);
       
   173 static void gst_alpha_get_property (GObject * object, guint prop_id,
       
   174     GValue * value, GParamSpec * pspec);
       
   175 
       
   176 GST_BOILERPLATE (GstAlpha, gst_alpha, GstBaseTransform,
       
   177     GST_TYPE_BASE_TRANSFORM);
       
   178 
       
   179 #define GST_TYPE_ALPHA_METHOD (gst_alpha_method_get_type())
       
   180 static GType
       
   181 gst_alpha_method_get_type (void)
       
   182 {
       
   183   static GType alpha_method_type = 0;
       
   184   static const GEnumValue alpha_method[] = {
       
   185     {ALPHA_METHOD_SET, "Set/adjust alpha channel", "set"},
       
   186     {ALPHA_METHOD_GREEN, "Chroma Key green", "green"},
       
   187     {ALPHA_METHOD_BLUE, "Chroma Key blue", "blue"},
       
   188     {ALPHA_METHOD_CUSTOM, "Chroma Key on target_r/g/b", "custom"},
       
   189     {0, NULL, NULL},
       
   190   };
       
   191 
       
   192   if (!alpha_method_type) {
       
   193     alpha_method_type = g_enum_register_static ("GstAlphaMethod", alpha_method);
       
   194   }
       
   195   return alpha_method_type;
       
   196 }
       
   197 
       
   198 static void
       
   199 gst_alpha_base_init (gpointer g_class)
       
   200 {
       
   201   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   202 
       
   203   gst_element_class_set_details (element_class, &gst_alpha_details);
       
   204 
       
   205   gst_element_class_add_pad_template (element_class,
       
   206       gst_static_pad_template_get (&gst_alpha_sink_template));
       
   207   gst_element_class_add_pad_template (element_class,
       
   208       gst_static_pad_template_get (&gst_alpha_src_template));
       
   209 
       
   210   GST_DEBUG_CATEGORY_INIT (gst_alpha_debug, "alpha", 0,
       
   211       "alpha - Element for adding alpha channel to streams");
       
   212 }
       
   213 static void
       
   214 gst_alpha_class_init (GstAlphaClass * klass)
       
   215 {
       
   216   GObjectClass *gobject_class;
       
   217   GstBaseTransformClass *btrans_class;
       
   218 
       
   219   gobject_class = (GObjectClass *) klass;
       
   220   btrans_class = (GstBaseTransformClass *) klass;
       
   221 
       
   222   gobject_class->set_property = gst_alpha_set_property;
       
   223   gobject_class->get_property = gst_alpha_get_property;
       
   224 
       
   225   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_METHOD,
       
   226       g_param_spec_enum ("method", "Method",
       
   227           "How the alpha channels should be created", GST_TYPE_ALPHA_METHOD,
       
   228           DEFAULT_METHOD, (GParamFlags) G_PARAM_READWRITE));
       
   229   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ALPHA,
       
   230       g_param_spec_double ("alpha", "Alpha", "The value for the alpha channel",
       
   231           0.0, 1.0, DEFAULT_ALPHA,
       
   232           (GParamFlags) G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   233   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TARGET_R,
       
   234       g_param_spec_uint ("target_r", "Target Red", "The Red target", 0, 255,
       
   235           DEFAULT_TARGET_R,
       
   236           (GParamFlags) G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   237   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TARGET_G,
       
   238       g_param_spec_uint ("target_g", "Target Green", "The Green target", 0, 255,
       
   239           DEFAULT_TARGET_G,
       
   240           (GParamFlags) G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   241   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TARGET_B,
       
   242       g_param_spec_uint ("target_b", "Target Blue", "The Blue target", 0, 255,
       
   243           DEFAULT_TARGET_B,
       
   244           (GParamFlags) G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   245   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ANGLE,
       
   246       g_param_spec_float ("angle", "Angle", "Size of the colorcube to change",
       
   247           0.0, 90.0, DEFAULT_ANGLE,
       
   248           (GParamFlags) G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   249   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_NOISE_LEVEL,
       
   250       g_param_spec_float ("noise_level", "Noise Level", "Size of noise radius",
       
   251           0.0, 64.0, DEFAULT_NOISE_LEVEL,
       
   252           (GParamFlags) G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   253   g_object_class_install_property (G_OBJECT_CLASS (klass),
       
   254       PROP_BLACK_SENSITIVITY, g_param_spec_uint ("black-sensitivity",
       
   255           "Black Sensitivity", "Sensitivity to dark colors", 0, 128,
       
   256           DEFAULT_BLACK_SENSITIVITY,
       
   257           (GParamFlags) G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   258   g_object_class_install_property (G_OBJECT_CLASS (klass),
       
   259       PROP_WHITE_SENSITIVITY, g_param_spec_uint ("white-sensitivity",
       
   260           "Sensitivity", "Sensitivity to bright colors", 0, 128,
       
   261           DEFAULT_WHITE_SENSITIVITY,
       
   262           (GParamFlags) G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   263 
       
   264 
       
   265   btrans_class->start = GST_DEBUG_FUNCPTR (gst_alpha_start);
       
   266   btrans_class->transform = GST_DEBUG_FUNCPTR (gst_alpha_transform);
       
   267   btrans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_alpha_get_unit_size);
       
   268   btrans_class->transform_caps = GST_DEBUG_FUNCPTR (gst_alpha_transform_caps);
       
   269   btrans_class->set_caps = GST_DEBUG_FUNCPTR (gst_alpha_set_caps);
       
   270 }
       
   271 
       
   272 static void
       
   273 gst_alpha_init (GstAlpha * alpha, GstAlphaClass * klass)
       
   274 {
       
   275   alpha->alpha = DEFAULT_ALPHA;
       
   276   alpha->method = DEFAULT_METHOD;
       
   277   alpha->target_r = DEFAULT_TARGET_R;
       
   278   alpha->target_g = DEFAULT_TARGET_G;
       
   279   alpha->target_b = DEFAULT_TARGET_B;
       
   280   alpha->angle = DEFAULT_ANGLE;
       
   281   alpha->noise_level = DEFAULT_NOISE_LEVEL;
       
   282   alpha->black_sensitivity = DEFAULT_BLACK_SENSITIVITY;
       
   283   alpha->white_sensitivity = DEFAULT_WHITE_SENSITIVITY;
       
   284 }
       
   285 
       
   286 /* do we need this function? */
       
   287 static void
       
   288 gst_alpha_set_property (GObject * object, guint prop_id,
       
   289     const GValue * value, GParamSpec * pspec)
       
   290 {
       
   291   GstAlpha *alpha;
       
   292 
       
   293   g_return_if_fail (GST_IS_ALPHA (object));
       
   294 
       
   295   alpha = GST_ALPHA (object);
       
   296 
       
   297   switch (prop_id) {
       
   298     case PROP_METHOD:
       
   299       alpha->method = g_value_get_enum (value);
       
   300       switch (alpha->method) {
       
   301         case ALPHA_METHOD_GREEN:
       
   302           alpha->target_r = 0;
       
   303           alpha->target_g = 255;
       
   304           alpha->target_b = 0;
       
   305           break;
       
   306         case ALPHA_METHOD_BLUE:
       
   307           alpha->target_r = 0;
       
   308           alpha->target_g = 0;
       
   309           alpha->target_b = 255;
       
   310           break;
       
   311         default:
       
   312           break;
       
   313       }
       
   314       gst_alpha_init_params (alpha);
       
   315       break;
       
   316     case PROP_ALPHA:
       
   317       alpha->alpha = g_value_get_double (value);
       
   318       break;
       
   319     case PROP_TARGET_R:
       
   320       alpha->target_r = g_value_get_uint (value);
       
   321       gst_alpha_init_params (alpha);
       
   322       break;
       
   323     case PROP_TARGET_G:
       
   324       alpha->target_g = g_value_get_uint (value);
       
   325       gst_alpha_init_params (alpha);
       
   326       break;
       
   327     case PROP_TARGET_B:
       
   328       alpha->target_b = g_value_get_uint (value);
       
   329       gst_alpha_init_params (alpha);
       
   330       break;
       
   331     case PROP_ANGLE:
       
   332       alpha->angle = g_value_get_float (value);
       
   333       gst_alpha_init_params (alpha);
       
   334       break;
       
   335     case PROP_NOISE_LEVEL:
       
   336       alpha->noise_level = g_value_get_float (value);
       
   337       gst_alpha_init_params (alpha);
       
   338       break;
       
   339     case PROP_BLACK_SENSITIVITY:
       
   340       alpha->black_sensitivity = g_value_get_uint (value);
       
   341       break;
       
   342     case PROP_WHITE_SENSITIVITY:
       
   343       alpha->white_sensitivity = g_value_get_uint (value);
       
   344       break;
       
   345     default:
       
   346       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   347       break;
       
   348   }
       
   349 }
       
   350 static void
       
   351 gst_alpha_get_property (GObject * object, guint prop_id, GValue * value,
       
   352     GParamSpec * pspec)
       
   353 {
       
   354   GstAlpha *alpha;
       
   355 
       
   356   g_return_if_fail (GST_IS_ALPHA (object));
       
   357 
       
   358   alpha = GST_ALPHA (object);
       
   359 
       
   360   switch (prop_id) {
       
   361     case PROP_METHOD:
       
   362       g_value_set_enum (value, alpha->method);
       
   363       break;
       
   364     case PROP_ALPHA:
       
   365       g_value_set_double (value, alpha->alpha);
       
   366       break;
       
   367     case PROP_TARGET_R:
       
   368       g_value_set_uint (value, alpha->target_r);
       
   369       break;
       
   370     case PROP_TARGET_G:
       
   371       g_value_set_uint (value, alpha->target_g);
       
   372       break;
       
   373     case PROP_TARGET_B:
       
   374       g_value_set_uint (value, alpha->target_b);
       
   375       break;
       
   376     case PROP_ANGLE:
       
   377       g_value_set_float (value, alpha->angle);
       
   378       break;
       
   379     case PROP_NOISE_LEVEL:
       
   380       g_value_set_float (value, alpha->noise_level);
       
   381       break;
       
   382     case PROP_BLACK_SENSITIVITY:
       
   383       g_value_set_uint (value, alpha->black_sensitivity);
       
   384       break;
       
   385     case PROP_WHITE_SENSITIVITY:
       
   386       g_value_set_uint (value, alpha->white_sensitivity);
       
   387       break;
       
   388     default:
       
   389       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   390       break;
       
   391   }
       
   392 }
       
   393 
       
   394 static gboolean
       
   395 gst_alpha_get_unit_size (GstBaseTransform * btrans,
       
   396     GstCaps * caps, guint * size)
       
   397 {
       
   398   GstVideoFormat format;
       
   399   gint width, height;
       
   400 
       
   401   if (!gst_video_format_parse_caps (caps, &format, &width, &height))
       
   402     return FALSE;
       
   403 
       
   404   *size = gst_video_format_get_size (format, width, height);
       
   405 
       
   406   GST_DEBUG_OBJECT (btrans, "unit size = %d for format %d w %d height %d",
       
   407       *size, format, width, height);
       
   408 
       
   409   return TRUE;
       
   410 }
       
   411 
       
   412 static GstCaps *
       
   413 gst_alpha_transform_caps (GstBaseTransform * btrans,
       
   414     GstPadDirection direction, GstCaps * caps)
       
   415 {
       
   416   GstCaps *ret;
       
   417   GstStructure *structure;
       
   418   gint i;
       
   419 
       
   420   ret = gst_caps_copy (caps);
       
   421 
       
   422   /* When going from the SINK pad to the src, we just need to make sure the
       
   423    * format is AYUV */
       
   424   if (direction == GST_PAD_SINK) {
       
   425     for (i = 0; i < gst_caps_get_size (ret); i++) {
       
   426       structure = gst_caps_get_structure (ret, i);
       
   427       gst_structure_set (structure, "format",
       
   428           GST_TYPE_FOURCC, GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'), NULL);
       
   429     }
       
   430   } else {
       
   431     GstCaps *ayuv_caps;
       
   432 
       
   433     /* In the other direction, prepend a copy of the caps with format AYUV, 
       
   434      * and set the first to I420 */
       
   435     ayuv_caps = gst_caps_copy (ret);
       
   436 
       
   437     for (i = 0; i < gst_caps_get_size (ret); i++) {
       
   438       structure = gst_caps_get_structure (ret, i);
       
   439       gst_structure_set (structure, "format",
       
   440           GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'), NULL);
       
   441     }
       
   442 
       
   443     gst_caps_append (ret, ayuv_caps);
       
   444   }
       
   445 
       
   446   gst_caps_do_simplify (ret);
       
   447 
       
   448   return ret;
       
   449 }
       
   450 
       
   451 static gboolean
       
   452 gst_alpha_set_caps (GstBaseTransform * btrans,
       
   453     GstCaps * incaps, GstCaps * outcaps)
       
   454 {
       
   455   GstAlpha *alpha = GST_ALPHA (btrans);
       
   456 
       
   457   if (!gst_video_format_parse_caps (incaps, &alpha->format,
       
   458           &alpha->width, &alpha->height))
       
   459     return FALSE;
       
   460 
       
   461   if (alpha->format == GST_VIDEO_FORMAT_AYUV)
       
   462     alpha->ayuv = TRUE;
       
   463   else
       
   464     alpha->ayuv = FALSE;
       
   465 
       
   466   return TRUE;
       
   467 }
       
   468 
       
   469 static void
       
   470 gst_alpha_set_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
       
   471     gdouble alpha)
       
   472 {
       
   473   gint b_alpha = (gint) (alpha * 255);
       
   474   gint y, x;
       
   475 
       
   476   for (y = 0; y < height; y++) {
       
   477     for (x = 0; x < width; x++) {
       
   478       *dest++ = (*src++ * b_alpha) >> 8;
       
   479       *dest++ = *src++;
       
   480       *dest++ = *src++;
       
   481       *dest++ = *src++;
       
   482     }
       
   483   }
       
   484 }
       
   485 
       
   486 static void
       
   487 gst_alpha_set_i420 (guint8 * src, guint8 * dest, gint width, gint height,
       
   488     gdouble alpha)
       
   489 {
       
   490   gint b_alpha = (gint) (alpha * 255);
       
   491   guint8 *srcY;
       
   492   guint8 *srcU;
       
   493   guint8 *srcV;
       
   494   gint i, j;
       
   495   gint src_wrap, src_uv_wrap;
       
   496   gint y_stride, uv_stride;
       
   497   gboolean odd_width;
       
   498 
       
   499   y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, width);
       
   500   uv_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, width);
       
   501 
       
   502   src_wrap = y_stride - width;
       
   503   src_uv_wrap = uv_stride - (width / 2);
       
   504 
       
   505   srcY = src;
       
   506   srcU = src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420,
       
   507       1, width, height);
       
   508   srcV = src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420,
       
   509       2, width, height);
       
   510 
       
   511   odd_width = (width % 2 != 0);
       
   512 
       
   513   for (i = 0; i < height; i++) {
       
   514     for (j = 0; j < width / 2; j++) {
       
   515       *dest++ = b_alpha;
       
   516       *dest++ = *srcY++;
       
   517       *dest++ = *srcU;
       
   518       *dest++ = *srcV;
       
   519       *dest++ = b_alpha;
       
   520       *dest++ = *srcY++;
       
   521       *dest++ = *srcU++;
       
   522       *dest++ = *srcV++;
       
   523     }
       
   524     /* Might have one odd column left to do */
       
   525     if (odd_width) {
       
   526       *dest++ = b_alpha;
       
   527       *dest++ = *srcY++;
       
   528       *dest++ = *srcU;
       
   529       *dest++ = *srcV;
       
   530     }
       
   531     if (i % 2 == 0) {
       
   532       srcU -= width / 2;
       
   533       srcV -= width / 2;
       
   534     } else {
       
   535       srcU += src_uv_wrap;
       
   536       srcV += src_uv_wrap;
       
   537     }
       
   538     srcY += src_wrap;
       
   539   }
       
   540 }
       
   541 
       
   542 static void
       
   543 gst_alpha_chroma_key_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
       
   544     GstAlpha * alpha)
       
   545 {
       
   546   gint b_alpha;
       
   547   guint8 *src1;
       
   548   guint8 *dest1;
       
   549   gint i, j;
       
   550   gint x, z, u, v, y, a;
       
   551   gint tmp, tmp1;
       
   552   gint x1, y1;
       
   553   gint smin, smax;
       
   554 
       
   555   smin = 128 - alpha->black_sensitivity;
       
   556   smax = 128 + alpha->white_sensitivity;
       
   557 
       
   558   src1 = src;
       
   559   dest1 = dest;
       
   560 
       
   561   for (i = 0; i < height; i++) {
       
   562     for (j = 0; j < width; j++) {
       
   563       a = *src1++ * (alpha->alpha);
       
   564       y = *src1++;
       
   565       u = *src1++ - 128;
       
   566       v = *src1++ - 128;
       
   567 
       
   568       if (y < smin || y > smax) {
       
   569         /* too dark or too bright, keep alpha */
       
   570         b_alpha = a;
       
   571       } else {
       
   572         /* Convert foreground to XZ coords where X direction is defined by
       
   573            the key color */
       
   574         tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
       
   575         x = CLAMP (tmp, -128, 127);
       
   576         tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
       
   577         z = CLAMP (tmp, -128, 127);
       
   578 
       
   579         /* WARNING: accept angle should never be set greater than "somewhat less
       
   580            than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
       
   581            80 degrees should be enough if foreground is reasonable. If this seems
       
   582            to be a problem, go to alternative ways of checking point position
       
   583            (scalar product or line equations). This angle should not be too small
       
   584            either to avoid infinite ctg (used to suppress foreground without use of
       
   585            division) */
       
   586 
       
   587         tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
       
   588         tmp = MIN (tmp, 127);
       
   589 
       
   590         if (abs (z) > tmp) {
       
   591           /* keep foreground Kfg = 0 */
       
   592           b_alpha = a;
       
   593         } else {
       
   594           /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
       
   595              according to Kfg */
       
   596           tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
       
   597           tmp = CLAMP (tmp, -128, 127);
       
   598           x1 = abs (tmp);
       
   599           y1 = z;
       
   600 
       
   601           tmp1 = x - x1;
       
   602           tmp1 = MAX (tmp1, 0);
       
   603           b_alpha = (((unsigned char) (tmp1) *
       
   604                   (unsigned short) (alpha->one_over_kc)) / 2);
       
   605           b_alpha = 255 - CLAMP (b_alpha, 0, 255);
       
   606           b_alpha = (a * b_alpha) >> 8;
       
   607 
       
   608           tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
       
   609           tmp1 = MIN (tmp, 255);
       
   610 
       
   611           tmp = y - tmp1;
       
   612           y = MAX (tmp, 0);
       
   613 
       
   614           /* Convert suppressed foreground back to CbCr */
       
   615           tmp = ((char) (x1) * (short) (alpha->cb) -
       
   616               (char) (y1) * (short) (alpha->cr)) >> 7;
       
   617           u = CLAMP (tmp, -128, 127);
       
   618 
       
   619           tmp = ((char) (x1) * (short) (alpha->cr) +
       
   620               (char) (y1) * (short) (alpha->cb)) >> 7;
       
   621           v = CLAMP (tmp, -128, 127);
       
   622 
       
   623           /* Deal with noise. For now, a circle around the key color with
       
   624              radius of noise_level treated as exact key color. Introduces
       
   625              sharp transitions.
       
   626            */
       
   627           tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
       
   628           tmp = MIN (tmp, 0xffff);
       
   629 
       
   630           if (tmp < alpha->noise_level * alpha->noise_level) {
       
   631             b_alpha = 0;
       
   632           }
       
   633         }
       
   634       }
       
   635 
       
   636       u += 128;
       
   637       v += 128;
       
   638 
       
   639       *dest1++ = b_alpha;
       
   640       *dest1++ = y;
       
   641       *dest1++ = u;
       
   642       *dest1++ = v;
       
   643     }
       
   644   }
       
   645 }
       
   646 
       
   647 static void
       
   648 gst_alpha_chromakey_row_i420 (GstAlpha * alpha, guint8 * dest1, guint8 * dest2,
       
   649     guint8 * srcY1, guint8 * srcY2, guint8 * srcU, guint8 * srcV, gint width)
       
   650 {
       
   651   gint xpos;
       
   652   gint b_alpha;
       
   653   gint x, z, u, v, y11, y12, y21, y22, a;
       
   654   gint tmp, tmp1;
       
   655   gint x1, y1;
       
   656   gint smin, smax;
       
   657 
       
   658   a = 255 * alpha->alpha;
       
   659   smin = 128 - alpha->black_sensitivity;
       
   660   smax = 128 + alpha->white_sensitivity;
       
   661 
       
   662   for (xpos = 0; xpos < width / 2; xpos++) {
       
   663     y11 = *srcY1++;
       
   664     y12 = *srcY1++;
       
   665     y21 = *srcY2++;
       
   666     y22 = *srcY2++;
       
   667     u = *srcU++ - 128;
       
   668     v = *srcV++ - 128;
       
   669 
       
   670     if (y11 < smin || y11 > smax ||
       
   671         y12 < smin || y12 > smax ||
       
   672         y21 < smin || y21 > smax || y22 < smin || y22 > smax) {
       
   673       /* too dark or too bright, make opaque */
       
   674       b_alpha = 255;
       
   675     } else {
       
   676       /* Convert foreground to XZ coords where X direction is defined by
       
   677          the key color */
       
   678       tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
       
   679       x = CLAMP (tmp, -128, 127);
       
   680       tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
       
   681       z = CLAMP (tmp, -128, 127);
       
   682 
       
   683       /* WARNING: accept angle should never be set greater than "somewhat less
       
   684          than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
       
   685          80 degrees should be enough if foreground is reasonable. If this seems
       
   686          to be a problem, go to alternative ways of checking point position
       
   687          (scalar product or line equations). This angle should not be too small
       
   688          either to avoid infinite ctg (used to suppress foreground without use of
       
   689          division) */
       
   690 
       
   691       tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
       
   692       tmp = MIN (tmp, 127);
       
   693 
       
   694       if (abs (z) > tmp) {
       
   695         /* keep foreground Kfg = 0 */
       
   696         b_alpha = 255;
       
   697       } else {
       
   698         /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
       
   699            according to Kfg */
       
   700         tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
       
   701         tmp = CLAMP (tmp, -128, 127);
       
   702         x1 = abs (tmp);
       
   703         y1 = z;
       
   704 
       
   705         tmp1 = x - x1;
       
   706         tmp1 = MAX (tmp1, 0);
       
   707         b_alpha = (((unsigned char) (tmp1) *
       
   708                 (unsigned short) (alpha->one_over_kc)) / 2);
       
   709         b_alpha = 255 - CLAMP (b_alpha, 0, 255);
       
   710         b_alpha = (a * b_alpha) >> 8;
       
   711 
       
   712         tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
       
   713         tmp1 = MIN (tmp, 255);
       
   714 
       
   715         tmp = y11 - tmp1;
       
   716         y11 = MAX (tmp, 0);
       
   717         tmp = y12 - tmp1;
       
   718         y12 = MAX (tmp, 0);
       
   719         tmp = y21 - tmp1;
       
   720         y21 = MAX (tmp, 0);
       
   721         tmp = y22 - tmp1;
       
   722         y22 = MAX (tmp, 0);
       
   723 
       
   724         /* Convert suppressed foreground back to CbCr */
       
   725         tmp = ((char) (x1) * (short) (alpha->cb) -
       
   726             (char) (y1) * (short) (alpha->cr)) >> 7;
       
   727         u = CLAMP (tmp, -128, 127);
       
   728 
       
   729         tmp = ((char) (x1) * (short) (alpha->cr) +
       
   730             (char) (y1) * (short) (alpha->cb)) >> 7;
       
   731         v = CLAMP (tmp, -128, 127);
       
   732 
       
   733         /* Deal with noise. For now, a circle around the key color with
       
   734            radius of noise_level treated as exact key color. Introduces
       
   735            sharp transitions.
       
   736          */
       
   737         tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
       
   738         tmp = MIN (tmp, 0xffff);
       
   739 
       
   740         if (tmp < alpha->noise_level * alpha->noise_level) {
       
   741           /* Uncomment this if you want total suppression within the noise circle */
       
   742           b_alpha = 0;
       
   743         }
       
   744       }
       
   745     }
       
   746 
       
   747     u += 128;
       
   748     v += 128;
       
   749 
       
   750     *dest1++ = b_alpha;
       
   751     *dest1++ = y11;
       
   752     *dest1++ = u;
       
   753     *dest1++ = v;
       
   754     *dest1++ = b_alpha;
       
   755     *dest1++ = y12;
       
   756     *dest1++ = u;
       
   757     *dest1++ = v;
       
   758 
       
   759     *dest2++ = b_alpha;
       
   760     *dest2++ = y21;
       
   761     *dest2++ = u;
       
   762     *dest2++ = v;
       
   763     *dest2++ = b_alpha;
       
   764     *dest2++ = y22;
       
   765     *dest2++ = u;
       
   766     *dest2++ = v;
       
   767   }
       
   768 }
       
   769 
       
   770 /* based on http://www.cs.utah.edu/~michael/chroma/
       
   771  */
       
   772 static void
       
   773 gst_alpha_chroma_key_i420 (guint8 * src, guint8 * dest, gint width, gint height,
       
   774     GstAlpha * alpha)
       
   775 {
       
   776   guint8 *srcY1, *srcY2, *srcU, *srcV;
       
   777   guint8 *dest1, *dest2;
       
   778   gint ypos;
       
   779   gint dest_stride, src_y_stride, src_uv_stride;
       
   780 
       
   781   dest_stride =
       
   782       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_AYUV, 0, width);
       
   783   src_y_stride =
       
   784       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, width);
       
   785   src_uv_stride =
       
   786       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, width);
       
   787 
       
   788   srcY1 = src;
       
   789   srcY2 = src + src_y_stride;
       
   790 
       
   791   srcU = src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420,
       
   792       1, width, height);
       
   793   srcV = src + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420,
       
   794       2, width, height);
       
   795 
       
   796   dest1 = dest;
       
   797   dest2 = dest + dest_stride;
       
   798 
       
   799   /* Redefine Y strides to skip 2 lines at a time ... */
       
   800   dest_stride *= 2;
       
   801   src_y_stride *= 2;
       
   802 
       
   803   for (ypos = 0; ypos < height / 2; ypos++) {
       
   804 
       
   805     gst_alpha_chromakey_row_i420 (alpha, dest1, dest2,
       
   806         srcY1, srcY2, srcU, srcV, width);
       
   807 
       
   808     dest1 += dest_stride;
       
   809     dest2 += dest_stride;
       
   810     srcY1 += src_y_stride;
       
   811     srcY2 += src_y_stride;
       
   812     srcU += src_uv_stride;
       
   813     srcV += src_uv_stride;
       
   814   }
       
   815 }
       
   816 
       
   817 static void
       
   818 gst_alpha_init_params (GstAlpha * alpha)
       
   819 {
       
   820   float kgl;
       
   821   float tmp;
       
   822   float tmp1, tmp2;
       
   823 
       
   824   alpha->y =
       
   825       0.257 * alpha->target_r + 0.504 * alpha->target_g +
       
   826       0.098 * alpha->target_b;
       
   827   tmp1 =
       
   828       -0.148 * alpha->target_r - 0.291 * alpha->target_g +
       
   829       0.439 * alpha->target_b;
       
   830   tmp2 =
       
   831       0.439 * alpha->target_r - 0.368 * alpha->target_g -
       
   832       0.071 * alpha->target_b;
       
   833   kgl = sqrt (tmp1 * tmp1 + tmp2 * tmp2);
       
   834   alpha->cb = 127 * (tmp1 / kgl);
       
   835   alpha->cr = 127 * (tmp2 / kgl);
       
   836 
       
   837   alpha->accept_angle_cos = cos (M_PI * alpha->angle / 180);
       
   838   alpha->accept_angle_sin = sin (M_PI * alpha->angle / 180);
       
   839   tmp = 15 * tan (M_PI * alpha->angle / 180);
       
   840   tmp = MIN (tmp, 255);
       
   841   alpha->accept_angle_tg = tmp;
       
   842   tmp = 15 / tan (M_PI * alpha->angle / 180);
       
   843   tmp = MIN (tmp, 255);
       
   844   alpha->accept_angle_ctg = tmp;
       
   845   tmp = 1 / (kgl);
       
   846   alpha->one_over_kc = 255 * 2 * tmp - 255;
       
   847   tmp = 15 * (float) (alpha->y) / kgl;
       
   848   tmp = MIN (tmp, 255);
       
   849   alpha->kfgy_scale = tmp;
       
   850   alpha->kg = MIN (kgl, 127);
       
   851 }
       
   852 
       
   853 static gboolean
       
   854 gst_alpha_start (GstBaseTransform * btrans)
       
   855 {
       
   856   GstAlpha *alpha = GST_ALPHA (btrans);
       
   857 
       
   858   gst_alpha_init_params (alpha);
       
   859 
       
   860   return TRUE;
       
   861 }
       
   862 
       
   863 static GstFlowReturn
       
   864 gst_alpha_transform (GstBaseTransform * btrans, GstBuffer * in, GstBuffer * out)
       
   865 {
       
   866   GstAlpha *alpha = GST_ALPHA (btrans);
       
   867   gint width, height;
       
   868   GstClockTime timestamp;
       
   869 
       
   870   width = alpha->width;
       
   871   height = alpha->height;
       
   872 
       
   873   GST_BUFFER_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (in);
       
   874   GST_BUFFER_DURATION (out) = GST_BUFFER_DURATION (in);
       
   875   timestamp = gst_segment_to_stream_time (&btrans->segment, GST_FORMAT_TIME,
       
   876       GST_BUFFER_TIMESTAMP (in));
       
   877   GST_LOG ("Got stream time of %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp));
       
   878   if (GST_CLOCK_TIME_IS_VALID (timestamp))
       
   879     gst_object_sync_values (G_OBJECT (alpha), timestamp);
       
   880 
       
   881   switch (alpha->method) {
       
   882     case ALPHA_METHOD_SET:
       
   883       if (alpha->ayuv) {
       
   884         gst_alpha_set_ayuv (GST_BUFFER_DATA (in),
       
   885             GST_BUFFER_DATA (out), width, height, alpha->alpha);
       
   886       } else {
       
   887         gst_alpha_set_i420 (GST_BUFFER_DATA (in),
       
   888             GST_BUFFER_DATA (out), width, height, alpha->alpha);
       
   889       }
       
   890       break;
       
   891     case ALPHA_METHOD_GREEN:
       
   892     case ALPHA_METHOD_BLUE:
       
   893     case ALPHA_METHOD_CUSTOM:
       
   894       if (alpha->ayuv) {
       
   895         gst_alpha_chroma_key_ayuv (GST_BUFFER_DATA (in),
       
   896             GST_BUFFER_DATA (out), width, height, alpha);
       
   897       } else {
       
   898         gst_alpha_chroma_key_i420 (GST_BUFFER_DATA (in),
       
   899             GST_BUFFER_DATA (out), width, height, alpha);
       
   900       }
       
   901       break;
       
   902     default:
       
   903       break;
       
   904   }
       
   905 
       
   906   return GST_FLOW_OK;
       
   907 }
       
   908 
       
   909 static gboolean
       
   910 plugin_init (GstPlugin * plugin)
       
   911 {
       
   912   gst_controller_init (NULL, NULL);
       
   913 
       
   914   return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
       
   915 }
       
   916 
       
   917 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
       
   918     GST_VERSION_MINOR,
       
   919     "alpha",
       
   920     "adds an alpha channel to video - constant or via chroma-keying",
       
   921     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
       
   922 
       
   923 
       
   924 #ifdef __SYMBIAN32__
       
   925 EXPORT_C GstPluginDesc* _GST_PLUGIN_DESC()
       
   926 {
       
   927     return &gst_plugin_desc;
       
   928 }
       
   929 
       
   930 #endif