Remove unused variables in _class_init
[platform/upstream/gstreamer.git] / gst / smpte / gstsmptealpha.c
1 /* GStreamer
2  * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-smptealpha
22  *
23  * smptealpha can accept an I420 or AYUV video stream. An alpha channel is added
24  * using an effect specific SMPTE mask in the I420 input case. In the AYUV case,
25  * the alpha channel is modified using the effect specific SMPTE mask.
26  *
27  * The #GstSmpteAlpha:position property is a controllabe double between 0.0 and
28  * 1.0 that specifies the position in the transition. 0.0 is the start of the
29  * transition with the alpha channel to complete opaque where 1.0 has the alpha
30  * channel set to completely transparent.
31  *
32  * The #GstSmpteAlpha:depth property defines the precision in bits of the mask.
33  * A higher presision will create a mask with smoother gradients in order to
34  * avoid banding.
35  *
36  * <refsect2>
37  * <title>Sample pipelines</title>
38  * <para>
39  * Here is a pipeline to demonstrate the smpte transition :
40  * <programlisting>
41  * gst-launch -v videotestsrc ! smptealpha border=20000 type=44
42  * position=0.5 ! videomixer ! ffmpegcolorspace ! ximagesink 
43  * </programlisting>
44  * This shows a midway bowtie-h transition a from a videotestsrc to a
45  * transparent image. The edges of the transition are smoothed with a
46  * 20000 big border.
47  * </para>
48  * </refsect2>
49  */
50
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
54 #include <string.h>
55
56 #include <gst/controller/gstcontroller.h>
57
58 #include "gstsmptealpha.h"
59 #include "paint.h"
60
61 GST_DEBUG_CATEGORY_STATIC (gst_smpte_alpha_debug);
62 #define GST_CAT_DEFAULT gst_smpte_alpha_debug
63
64 /* elementfactory information */
65 static const GstElementDetails smpte_details =
66 GST_ELEMENT_DETAILS ("SMPTE transitions",
67     "Filter/Editor/Video",
68     "Apply the standard SMPTE transitions as alpha on video images",
69     "Wim Taymans <wim.taymans@gmail.com>");
70
71 static GstStaticPadTemplate gst_smpte_alpha_src_template =
72 GST_STATIC_PAD_TEMPLATE ("src",
73     GST_PAD_SRC,
74     GST_PAD_ALWAYS,
75     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")
76     )
77     );
78
79 static GstStaticPadTemplate gst_smpte_alpha_sink_template =
80     GST_STATIC_PAD_TEMPLATE ("sink",
81     GST_PAD_SINK,
82     GST_PAD_ALWAYS,
83     GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("AYUV")
84     )
85     );
86
87 /* SMPTE signals and args */
88 enum
89 {
90   /* FILL ME */
91   LAST_SIGNAL
92 };
93
94 #define DEFAULT_PROP_TYPE       1
95 #define DEFAULT_PROP_BORDER     0
96 #define DEFAULT_PROP_DEPTH      16
97 #define DEFAULT_PROP_POSITION   0.0
98
99 enum
100 {
101   PROP_0,
102   PROP_TYPE,
103   PROP_BORDER,
104   PROP_DEPTH,
105   PROP_POSITION,
106   PROP_LAST,
107 };
108
109 #define AYUV_SIZE(w,h)     ((w) * (h) * 4)
110
111 #define GST_TYPE_SMPTE_TRANSITION_TYPE (gst_smpte_alpha_transition_type_get_type())
112 static GType
113 gst_smpte_alpha_transition_type_get_type (void)
114 {
115   static GType smpte_transition_type = 0;
116   GEnumValue *smpte_transitions;
117
118   if (!smpte_transition_type) {
119     const GList *definitions;
120     gint i = 0;
121
122     definitions = gst_mask_get_definitions ();
123     smpte_transitions =
124         g_new0 (GEnumValue, g_list_length ((GList *) definitions) + 1);
125
126     while (definitions) {
127       GstMaskDefinition *definition = (GstMaskDefinition *) definitions->data;
128
129       definitions = g_list_next (definitions);
130
131       smpte_transitions[i].value = definition->type;
132       /* older GLib versions have the two fields as non-const, hence the cast */
133       smpte_transitions[i].value_nick = (gchar *) definition->short_name;
134       smpte_transitions[i].value_name = (gchar *) definition->long_name;
135
136       i++;
137     }
138
139     smpte_transition_type =
140         g_enum_register_static ("GstSMPTEAlphaTransitionType",
141         smpte_transitions);
142   }
143   return smpte_transition_type;
144 }
145
146
147 static void gst_smpte_alpha_class_init (GstSMPTEAlphaClass * klass);
148 static void gst_smpte_alpha_base_init (GstSMPTEAlphaClass * klass);
149 static void gst_smpte_alpha_init (GstSMPTEAlpha * smpte);
150 static void gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte);
151
152 static void gst_smpte_alpha_set_property (GObject * object, guint prop_id,
153     const GValue * value, GParamSpec * pspec);
154 static void gst_smpte_alpha_get_property (GObject * object, guint prop_id,
155     GValue * value, GParamSpec * pspec);
156
157 static gboolean gst_smpte_alpha_setcaps (GstBaseTransform * btrans,
158     GstCaps * incaps, GstCaps * outcaps);
159 static gboolean gst_smpte_alpha_get_unit_size (GstBaseTransform * btrans,
160     GstCaps * caps, guint * size);
161 static GstFlowReturn gst_smpte_alpha_transform (GstBaseTransform * trans,
162     GstBuffer * in, GstBuffer * out);
163
164 static GstElementClass *parent_class = NULL;
165
166 /*static guint gst_smpte_alpha_signals[LAST_SIGNAL] = { 0 }; */
167
168 static GType
169 gst_smpte_alpha_get_type (void)
170 {
171   static GType smpte_type = 0;
172
173   if (!smpte_type) {
174     static const GTypeInfo smpte_info = {
175       sizeof (GstSMPTEAlphaClass),
176       (GBaseInitFunc) gst_smpte_alpha_base_init,
177       NULL,
178       (GClassInitFunc) gst_smpte_alpha_class_init,
179       NULL,
180       NULL,
181       sizeof (GstSMPTEAlpha),
182       0,
183       (GInstanceInitFunc) gst_smpte_alpha_init,
184     };
185
186     smpte_type =
187         g_type_register_static (GST_TYPE_VIDEO_FILTER, "GstSMPTEAlpha",
188         &smpte_info, 0);
189   }
190   return smpte_type;
191 }
192
193 static void
194 gst_smpte_alpha_base_init (GstSMPTEAlphaClass * klass)
195 {
196   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
197
198   gst_element_class_add_pad_template (element_class,
199       gst_static_pad_template_get (&gst_smpte_alpha_sink_template));
200   gst_element_class_add_pad_template (element_class,
201       gst_static_pad_template_get (&gst_smpte_alpha_src_template));
202   gst_element_class_set_details (element_class, &smpte_details);
203 }
204
205 static void
206 gst_smpte_alpha_class_init (GstSMPTEAlphaClass * klass)
207 {
208   GObjectClass *gobject_class;
209   GstBaseTransformClass *trans_class;
210
211   gobject_class = (GObjectClass *) klass;
212   trans_class = (GstBaseTransformClass *) klass;
213
214   parent_class = g_type_class_peek_parent (klass);
215
216   gobject_class->set_property = gst_smpte_alpha_set_property;
217   gobject_class->get_property = gst_smpte_alpha_get_property;
218
219   gobject_class->finalize = (GObjectFinalizeFunc) gst_smpte_alpha_finalize;
220
221   _gst_mask_init ();
222
223   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TYPE,
224       g_param_spec_enum ("type", "Type", "The type of transition to use",
225           GST_TYPE_SMPTE_TRANSITION_TYPE, DEFAULT_PROP_TYPE,
226           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
227   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER,
228       g_param_spec_int ("border", "Border",
229           "The border width of the transition", 0, G_MAXINT,
230           DEFAULT_PROP_BORDER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
231   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEPTH,
232       g_param_spec_int ("depth", "Depth", "Depth of the mask in bits", 1, 24,
233           DEFAULT_PROP_DEPTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
234   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_POSITION,
235       g_param_spec_double ("position", "Position",
236           "Position of the transition effect", 0.0, 1.0, DEFAULT_PROP_POSITION,
237           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
238
239   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_smpte_alpha_setcaps);
240   trans_class->get_unit_size =
241       GST_DEBUG_FUNCPTR (gst_smpte_alpha_get_unit_size);
242   trans_class->transform = GST_DEBUG_FUNCPTR (gst_smpte_alpha_transform);
243 }
244
245 static gboolean
246 gst_smpte_alpha_update_mask (GstSMPTEAlpha * smpte, gint type, gint depth,
247     gint width, gint height)
248 {
249   GstMask *newmask;
250
251   /* try to avoid regenerating the mask if we already have one that is
252    * correct */
253   if (smpte->mask) {
254     if (smpte->type == type &&
255         smpte->depth == depth &&
256         smpte->width == width && smpte->height == height)
257       return TRUE;
258   }
259
260   newmask = gst_mask_factory_new (type, depth, width, height);
261   if (!newmask)
262     goto mask_failed;
263
264   if (smpte->mask)
265     gst_mask_destroy (smpte->mask);
266
267   smpte->mask = newmask;
268   smpte->type = type;
269   smpte->depth = depth;
270   smpte->width = width;
271   smpte->height = height;
272
273   return TRUE;
274
275   /* ERRORS */
276 mask_failed:
277   {
278     GST_ERROR_OBJECT (smpte, "failed to create a mask");
279     return FALSE;
280   }
281 }
282
283 static gboolean
284 gst_smpte_alpha_setcaps (GstBaseTransform * btrans, GstCaps * incaps,
285     GstCaps * outcaps)
286 {
287   GstSMPTEAlpha *smpte;
288   GstStructure *structure;
289   gboolean ret;
290   gint width, height;
291   guint32 fourcc;
292
293   smpte = GST_SMPTE_ALPHA (btrans);
294
295   structure = gst_caps_get_structure (incaps, 0);
296
297   /* see if we can get essential info */
298   ret = gst_structure_get_int (structure, "width", &width);
299   ret &= gst_structure_get_int (structure, "height", &height);
300   ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
301   if (!ret)
302     goto no_dimensions;
303
304   /* try to update the mask now, this will also adjust the width/height on
305    * success */
306   GST_OBJECT_LOCK (smpte);
307   ret = gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->depth,
308       width, height);
309   GST_OBJECT_UNLOCK (smpte);
310   if (!ret)
311     goto mask_failed;
312
313   switch (fourcc) {
314     case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
315       smpte->format = GST_VIDEO_FORMAT_AYUV;
316       break;
317     case GST_MAKE_FOURCC ('I', '4', '2', '0'):
318       smpte->format = GST_VIDEO_FORMAT_I420;
319       break;
320     default:
321       goto unsupported_fourcc;
322   }
323
324   return ret;
325
326   /* ERRORS */
327 no_dimensions:
328   {
329     GST_ERROR_OBJECT (smpte, "no width, height and fourcc given");
330     return FALSE;
331   }
332 mask_failed:
333   {
334     GST_ERROR_OBJECT (smpte, "failed creating the mask");
335     return FALSE;
336   }
337 unsupported_fourcc:
338   {
339     GST_ERROR_OBJECT (smpte, "unsupported fourcc %" GST_FOURCC_FORMAT,
340         GST_FOURCC_ARGS (fourcc));
341     return FALSE;
342   }
343 }
344
345 static gboolean
346 gst_smpte_alpha_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
347     guint * size)
348 {
349   GstSMPTEAlpha *smpte;
350   GstStructure *structure;
351   gboolean ret;
352   gint width, height;
353   guint32 fourcc;
354
355   smpte = GST_SMPTE_ALPHA (btrans);
356
357   structure = gst_caps_get_structure (caps, 0);
358
359   ret = gst_structure_get_int (structure, "width", &width);
360   ret &= gst_structure_get_int (structure, "height", &height);
361   ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
362   if (ret) {
363     switch (fourcc) {
364       case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
365         *size =
366             gst_video_format_get_size (GST_VIDEO_FORMAT_AYUV, width, height);
367         break;
368       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
369         *size =
370             gst_video_format_get_size (GST_VIDEO_FORMAT_I420, width, height);
371         break;
372       default:
373         ret = FALSE;
374         break;
375     }
376   }
377   return ret;
378 }
379
380 static void
381 gst_smpte_alpha_init (GstSMPTEAlpha * smpte)
382 {
383   smpte->type = DEFAULT_PROP_TYPE;
384   smpte->border = DEFAULT_PROP_BORDER;
385   smpte->depth = DEFAULT_PROP_DEPTH;
386 }
387
388 static void
389 gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte)
390 {
391   if (smpte->mask)
392     gst_mask_destroy (smpte->mask);
393
394   G_OBJECT_CLASS (parent_class)->finalize ((GObject *) smpte);
395 }
396
397 static void
398 gst_smpte_alpha_do_ayuv (GstSMPTEAlpha * smpte, guint8 * in, guint8 * out,
399     GstMask * mask, gint width, gint height, gint border, gint pos)
400 {
401   gint i, j;
402   guint32 *maskp;
403   gint value;
404   gint min, max;
405
406   if (border == 0)
407     border++;
408
409   min = pos - border;
410   max = pos;
411   GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
412       border);
413
414   maskp = mask->data;
415
416   /* we basically copy the source to dest but we scale the alpha channel with
417    * the mask */
418   for (i = 0; i < height; i++) {
419     for (j = 0; j < width; j++) {
420       value = *maskp++;
421       *out++ = (*in++ * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
422       *out++ = *in++;
423       *out++ = *in++;
424       *out++ = *in++;
425     }
426   }
427 }
428
429 static void
430 gst_smpte_alpha_do_i420 (GstSMPTEAlpha * smpte, guint8 * in, guint8 * out,
431     GstMask * mask, gint width, gint height, gint border, gint pos)
432 {
433   guint8 *srcY;
434   guint8 *srcU;
435   guint8 *srcV;
436   gint i, j;
437   gint src_wrap, src_uv_wrap;
438   gint y_stride, uv_stride;
439   gboolean odd_width;
440   guint32 *maskp;
441   gint value;
442   gint min, max;
443
444   if (border == 0)
445     border++;
446
447   min = pos - border;
448   max = pos;
449   GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
450       border);
451
452   maskp = mask->data;
453
454   y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, width);
455   uv_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, width);
456
457   src_wrap = y_stride - width;
458   src_uv_wrap = uv_stride - (width / 2);
459
460   srcY = in;
461   srcU = in + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420,
462       1, width, height);
463   srcV = in + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420,
464       2, width, height);
465
466   odd_width = (width % 2 != 0);
467
468   for (i = 0; i < height; i++) {
469     for (j = 0; j < width / 2; j++) {
470       value = *maskp++;
471       *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
472       *out++ = *srcY++;
473       *out++ = *srcU;
474       *out++ = *srcV;
475       value = *maskp++;
476       *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
477       *out++ = *srcY++;
478       *out++ = *srcU++;
479       *out++ = *srcV++;
480     }
481     /* Might have one odd column left to do */
482     if (odd_width) {
483       value = *maskp++;
484       *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
485       *out++ = *srcY++;
486       *out++ = *srcU;
487       *out++ = *srcV;
488     }
489     if (i % 2 == 0) {
490       srcU -= width / 2;
491       srcV -= width / 2;
492     } else {
493       srcU += src_uv_wrap;
494       srcV += src_uv_wrap;
495     }
496     srcY += src_wrap;
497   }
498 }
499
500 static GstFlowReturn
501 gst_smpte_alpha_transform (GstBaseTransform * trans, GstBuffer * in,
502     GstBuffer * out)
503 {
504   GstSMPTEAlpha *smpte;
505   GstClockTime timestamp, stream_time;
506   gdouble position;
507   gint border;
508
509   smpte = GST_SMPTE_ALPHA (trans);
510
511   /* first sync the controller to the current stream_time of the buffer */
512   timestamp = GST_BUFFER_TIMESTAMP (in);
513   stream_time =
514       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
515
516   GST_DEBUG_OBJECT (smpte, "sync to %" GST_TIME_FORMAT,
517       GST_TIME_ARGS (timestamp));
518
519   if (GST_CLOCK_TIME_IS_VALID (stream_time))
520     gst_object_sync_values (G_OBJECT (smpte), stream_time);
521
522   /* these are the propertis we update with only the object lock, others are
523    * only updated with the TRANSFORM_LOCK. */
524   GST_OBJECT_LOCK (smpte);
525   position = smpte->position;
526   border = smpte->border;
527   GST_OBJECT_UNLOCK (smpte);
528
529   /* run the type specific filter code */
530   switch (smpte->format) {
531     case GST_VIDEO_FORMAT_I420:
532       gst_smpte_alpha_do_i420 (smpte, GST_BUFFER_DATA (in),
533           GST_BUFFER_DATA (out),
534           smpte->mask, smpte->width, smpte->height,
535           border, ((1 << smpte->depth) + border) * position);
536       break;
537     case GST_VIDEO_FORMAT_AYUV:
538       gst_smpte_alpha_do_ayuv (smpte, GST_BUFFER_DATA (in),
539           GST_BUFFER_DATA (out),
540           smpte->mask, smpte->width, smpte->height,
541           border, ((1 << smpte->depth) + border) * position);
542       break;
543     default:
544       goto not_negotiated;
545   }
546
547   return GST_FLOW_OK;
548
549   /* ERRORS */
550 not_negotiated:
551   {
552     GST_ELEMENT_ERROR (smpte, CORE, NEGOTIATION, (NULL),
553         ("No input format negotiated"));
554     return GST_FLOW_NOT_NEGOTIATED;
555   }
556 }
557
558 static void
559 gst_smpte_alpha_set_property (GObject * object, guint prop_id,
560     const GValue * value, GParamSpec * pspec)
561 {
562   GstSMPTEAlpha *smpte;
563
564   smpte = GST_SMPTE_ALPHA (object);
565
566   switch (prop_id) {
567     case PROP_TYPE:
568       GST_BASE_TRANSFORM_LOCK (smpte);
569       /* also lock with the object lock so that reading the property doesn't
570        * have to wait for the transform lock */
571       GST_OBJECT_LOCK (smpte);
572       smpte->type = g_value_get_enum (value);
573       GST_OBJECT_UNLOCK (smpte);
574       gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->depth,
575           smpte->width, smpte->height);
576       GST_BASE_TRANSFORM_UNLOCK (smpte);
577       break;
578     case PROP_BORDER:
579       GST_OBJECT_LOCK (smpte);
580       smpte->border = g_value_get_int (value);
581       GST_OBJECT_UNLOCK (smpte);
582       break;
583     case PROP_DEPTH:
584       GST_BASE_TRANSFORM_LOCK (smpte);
585       /* also lock with the object lock so that reading the property doesn't
586        * have to wait for the transform lock */
587       GST_OBJECT_LOCK (smpte);
588       smpte->depth = g_value_get_int (value);
589       GST_OBJECT_UNLOCK (smpte);
590       gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->depth,
591           smpte->width, smpte->height);
592       GST_BASE_TRANSFORM_UNLOCK (smpte);
593       break;
594     case PROP_POSITION:
595       GST_OBJECT_LOCK (smpte);
596       smpte->position = g_value_get_double (value);
597       GST_OBJECT_UNLOCK (smpte);
598       break;
599     default:
600       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
601       break;
602   }
603 }
604
605 static void
606 gst_smpte_alpha_get_property (GObject * object, guint prop_id,
607     GValue * value, GParamSpec * pspec)
608 {
609   GstSMPTEAlpha *smpte;
610
611   smpte = GST_SMPTE_ALPHA (object);
612
613   switch (prop_id) {
614     case PROP_TYPE:
615       GST_OBJECT_LOCK (smpte);
616       g_value_set_enum (value, smpte->type);
617       GST_OBJECT_UNLOCK (smpte);
618       break;
619     case PROP_BORDER:
620       GST_OBJECT_LOCK (smpte);
621       g_value_set_int (value, smpte->border);
622       GST_OBJECT_UNLOCK (smpte);
623       break;
624     case PROP_DEPTH:
625       GST_OBJECT_LOCK (smpte);
626       g_value_set_int (value, smpte->depth);
627       GST_OBJECT_UNLOCK (smpte);
628       break;
629     case PROP_POSITION:
630       GST_OBJECT_LOCK (smpte);
631       g_value_set_double (value, smpte->position);
632       GST_OBJECT_UNLOCK (smpte);
633       break;
634     default:
635       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
636       break;
637   }
638 }
639
640 gboolean
641 gst_smpte_alpha_plugin_init (GstPlugin * plugin)
642 {
643   GST_DEBUG_CATEGORY_INIT (gst_smpte_alpha_debug, "smptealpha", 0,
644       "SMPTE alpha effect");
645
646   /* initialize gst controller library */
647   gst_controller_init (NULL, NULL);
648
649   return gst_element_register (plugin, "smptealpha", GST_RANK_NONE,
650       GST_TYPE_SMPTE_ALPHA);
651 }