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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, 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-1.0 -v videotestsrc ! smptealpha border=20000 type=44
42  * position=0.5 ! videomixer ! videoconvert ! 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 "gstsmptealpha.h"
57 #include "paint.h"
58 
59 GST_DEBUG_CATEGORY_STATIC (gst_smpte_alpha_debug);
60 #define GST_CAT_DEFAULT gst_smpte_alpha_debug
61 
62 static GstStaticPadTemplate gst_smpte_alpha_src_template =
63     GST_STATIC_PAD_TEMPLATE ("src",
64     GST_PAD_SRC,
65     GST_PAD_ALWAYS,
66     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("AYUV") ";"
67         GST_VIDEO_CAPS_MAKE ("ARGB") ";" GST_VIDEO_CAPS_MAKE ("BGRA") ";"
68         GST_VIDEO_CAPS_MAKE ("RGBA") ";" GST_VIDEO_CAPS_MAKE ("ARGB"))
69     );
70 
71 static GstStaticPadTemplate gst_smpte_alpha_sink_template =
72     GST_STATIC_PAD_TEMPLATE ("sink",
73     GST_PAD_SINK,
74     GST_PAD_ALWAYS,
75     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("I420") ";"
76         GST_VIDEO_CAPS_MAKE ("YV12")
77         ";" GST_VIDEO_CAPS_MAKE ("AYUV")
78         ";" GST_VIDEO_CAPS_MAKE ("ARGB") ";" GST_VIDEO_CAPS_MAKE ("BGRA")
79         ";" GST_VIDEO_CAPS_MAKE ("RGBA") ";" GST_VIDEO_CAPS_MAKE ("ARGB"))
80     );
81 
82 /* SMPTE signals and properties */
83 
84 #define DEFAULT_PROP_TYPE	1
85 #define DEFAULT_PROP_BORDER	0
86 #define DEFAULT_PROP_DEPTH	16
87 #define DEFAULT_PROP_POSITION	0.0
88 #define DEFAULT_PROP_INVERT   FALSE
89 
90 enum
91 {
92   PROP_0,
93   PROP_TYPE,
94   PROP_BORDER,
95   PROP_DEPTH,
96   PROP_POSITION,
97   PROP_INVERT
98 };
99 
100 #define AYUV_SIZE(w,h)     ((w) * (h) * 4)
101 
102 #define GST_TYPE_SMPTE_TRANSITION_TYPE (gst_smpte_alpha_transition_type_get_type())
103 static GType
gst_smpte_alpha_transition_type_get_type(void)104 gst_smpte_alpha_transition_type_get_type (void)
105 {
106   static GType smpte_transition_type = 0;
107   GEnumValue *smpte_transitions;
108 
109   if (!smpte_transition_type) {
110     const GList *definitions;
111     gint i = 0;
112 
113     definitions = gst_mask_get_definitions ();
114     smpte_transitions =
115         g_new0 (GEnumValue, g_list_length ((GList *) definitions) + 1);
116 
117     while (definitions) {
118       GstMaskDefinition *definition = (GstMaskDefinition *) definitions->data;
119 
120       definitions = g_list_next (definitions);
121 
122       smpte_transitions[i].value = definition->type;
123       /* older GLib versions have the two fields as non-const, hence the cast */
124       smpte_transitions[i].value_nick = (gchar *) definition->short_name;
125       smpte_transitions[i].value_name = (gchar *) definition->long_name;
126 
127       i++;
128     }
129 
130     smpte_transition_type =
131         g_enum_register_static ("GstSMPTEAlphaTransitionType",
132         smpte_transitions);
133   }
134   return smpte_transition_type;
135 }
136 
137 
138 static void gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte);
139 
140 static void gst_smpte_alpha_set_property (GObject * object, guint prop_id,
141     const GValue * value, GParamSpec * pspec);
142 static void gst_smpte_alpha_get_property (GObject * object, guint prop_id,
143     GValue * value, GParamSpec * pspec);
144 
145 static gboolean gst_smpte_alpha_set_info (GstVideoFilter * vfilter,
146     GstCaps * incaps, GstVideoInfo * in_info,
147     GstCaps * outcaps, GstVideoInfo * out_info);
148 static GstFlowReturn gst_smpte_alpha_transform_frame (GstVideoFilter * vfilter,
149     GstVideoFrame * in_frame, GstVideoFrame * out_frame);
150 static void gst_smpte_alpha_before_transform (GstBaseTransform * trans,
151     GstBuffer * buf);
152 static GstCaps *gst_smpte_alpha_transform_caps (GstBaseTransform * trans,
153     GstPadDirection direction, GstCaps * from, GstCaps * filter);
154 
155 #define gst_smpte_alpha_parent_class parent_class
156 G_DEFINE_TYPE (GstSMPTEAlpha, gst_smpte_alpha, GST_TYPE_VIDEO_FILTER);
157 
158 static void
gst_smpte_alpha_class_init(GstSMPTEAlphaClass * klass)159 gst_smpte_alpha_class_init (GstSMPTEAlphaClass * klass)
160 {
161   GObjectClass *gobject_class = (GObjectClass *) klass;
162   GstElementClass *element_class = (GstElementClass *) (klass);
163   GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
164   GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
165 
166   gobject_class->set_property = gst_smpte_alpha_set_property;
167   gobject_class->get_property = gst_smpte_alpha_get_property;
168 
169   gobject_class->finalize = (GObjectFinalizeFunc) gst_smpte_alpha_finalize;
170 
171   _gst_mask_init ();
172 
173   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TYPE,
174       g_param_spec_enum ("type", "Type", "The type of transition to use",
175           GST_TYPE_SMPTE_TRANSITION_TYPE, DEFAULT_PROP_TYPE,
176           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
177   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER,
178       g_param_spec_int ("border", "Border",
179           "The border width of the transition", 0, G_MAXINT,
180           DEFAULT_PROP_BORDER,
181           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
182   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEPTH,
183       g_param_spec_int ("depth", "Depth", "Depth of the mask in bits", 1, 24,
184           DEFAULT_PROP_DEPTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
185   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_POSITION,
186       g_param_spec_double ("position", "Position",
187           "Position of the transition effect", 0.0, 1.0, DEFAULT_PROP_POSITION,
188           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
189   /**
190    * GstSMPTEAlpha:invert:
191    *
192    * Set to TRUE to invert the transition mask (ie. flip it horizontally).
193    */
194   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INVERT,
195       g_param_spec_boolean ("invert", "Invert",
196           "Invert transition mask", DEFAULT_PROP_POSITION,
197           GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
198 
199   trans_class->before_transform =
200       GST_DEBUG_FUNCPTR (gst_smpte_alpha_before_transform);
201   trans_class->transform_caps =
202       GST_DEBUG_FUNCPTR (gst_smpte_alpha_transform_caps);
203 
204   vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_smpte_alpha_set_info);
205   vfilter_class->transform_frame =
206       GST_DEBUG_FUNCPTR (gst_smpte_alpha_transform_frame);
207 
208   gst_element_class_add_static_pad_template (element_class,
209       &gst_smpte_alpha_sink_template);
210   gst_element_class_add_static_pad_template (element_class,
211       &gst_smpte_alpha_src_template);
212   gst_element_class_set_static_metadata (element_class, "SMPTE transitions",
213       "Filter/Editor/Video",
214       "Apply the standard SMPTE transitions as alpha on video images",
215       "Wim Taymans <wim.taymans@gmail.com>");
216 }
217 
218 static gboolean
gst_smpte_alpha_update_mask(GstSMPTEAlpha * smpte,gint type,gboolean invert,gint depth,gint width,gint height)219 gst_smpte_alpha_update_mask (GstSMPTEAlpha * smpte, gint type,
220     gboolean invert, gint depth, gint width, gint height)
221 {
222   GstMask *newmask;
223 
224   /* try to avoid regenerating the mask if we already have one that is
225    * correct */
226   if (smpte->mask) {
227     if (smpte->type == type &&
228         smpte->invert == invert &&
229         smpte->depth == depth &&
230         smpte->width == width && smpte->height == height)
231       return TRUE;
232   }
233 
234   smpte->type = type;
235   smpte->invert = invert;
236   smpte->depth = depth;
237   smpte->width = width;
238   smpte->height = height;
239 
240   /* Not negotiated yet */
241   if (width == 0 || height == 0) {
242     return TRUE;
243   }
244 
245   newmask = gst_mask_factory_new (type, invert, depth, width, height);
246   if (!newmask)
247     goto mask_failed;
248 
249   if (smpte->mask)
250     gst_mask_destroy (smpte->mask);
251 
252   smpte->mask = newmask;
253 
254   return TRUE;
255 
256   /* ERRORS */
257 mask_failed:
258   {
259     GST_ERROR_OBJECT (smpte, "failed to create a mask");
260     return FALSE;
261   }
262 }
263 
264 static void
gst_smpte_alpha_init(GstSMPTEAlpha * smpte)265 gst_smpte_alpha_init (GstSMPTEAlpha * smpte)
266 {
267   smpte->type = DEFAULT_PROP_TYPE;
268   smpte->border = DEFAULT_PROP_BORDER;
269   smpte->depth = DEFAULT_PROP_DEPTH;
270   smpte->position = DEFAULT_PROP_POSITION;
271   smpte->invert = DEFAULT_PROP_INVERT;
272 }
273 
274 #define CREATE_ARGB_FUNC(name, A, R, G, B) \
275 static void \
276 gst_smpte_alpha_process_##name##_##name (GstSMPTEAlpha * smpte, \
277     const GstVideoFrame * in_frame, GstVideoFrame * out_frame, GstMask * mask, \
278     gint border, gint pos) \
279 { \
280   gint i, j; \
281   const guint32 *maskp; \
282   gint value; \
283   gint min, max; \
284   gint width, height; \
285   guint8 *in, *out; \
286   gint src_wrap, dest_wrap; \
287   \
288   if (border == 0) \
289     border++; \
290   \
291   min = pos - border; \
292   max = pos; \
293   GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max, \
294       border); \
295   \
296   maskp = mask->data; \
297   \
298   width = GST_VIDEO_FRAME_WIDTH (out_frame); \
299   height = GST_VIDEO_FRAME_HEIGHT (out_frame); \
300   \
301   in = GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0); \
302   out = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0); \
303   src_wrap = GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 0) - (width << 2); \
304   dest_wrap = GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 0) - (width << 2); \
305   \
306   /* we basically copy the source to dest but we scale the alpha channel with \
307    * the mask */ \
308   for (i = 0; i < height; i++) { \
309     for (j = 0; j < width; j++) { \
310       value = *maskp++; \
311       out[A] = (in[A] * ((CLAMP (value, min, max) - min) << 8) / border) >> 8; \
312       out[R] = in[R]; \
313       out[G] = in[G]; \
314       out[B] = in[B]; \
315       out += 4; \
316       in += 4; \
317     } \
318     in += src_wrap; \
319     out += dest_wrap; \
320   } \
321 }
322 
323 CREATE_ARGB_FUNC (argb, 0, 1, 2, 3);
324 CREATE_ARGB_FUNC (bgra, 3, 2, 1, 0);
325 CREATE_ARGB_FUNC (abgr, 0, 3, 2, 1);
326 CREATE_ARGB_FUNC (rgba, 3, 0, 1, 2);
327 
328 static void
gst_smpte_alpha_process_ayuv_ayuv(GstSMPTEAlpha * smpte,const GstVideoFrame * in_frame,GstVideoFrame * out_frame,GstMask * mask,gint border,gint pos)329 gst_smpte_alpha_process_ayuv_ayuv (GstSMPTEAlpha * smpte,
330     const GstVideoFrame * in_frame, GstVideoFrame * out_frame, GstMask * mask,
331     gint border, gint pos)
332 {
333   gint i, j;
334   const guint32 *maskp;
335   gint value;
336   gint min, max;
337   gint width, height;
338   guint8 *in, *out;
339   gint src_wrap, dest_wrap;
340 
341   if (border == 0)
342     border++;
343 
344   min = pos - border;
345   max = pos;
346   GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
347       border);
348 
349   maskp = mask->data;
350 
351   width = GST_VIDEO_FRAME_WIDTH (out_frame);
352   height = GST_VIDEO_FRAME_HEIGHT (out_frame);
353 
354   in = GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0);
355   out = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0);
356   src_wrap = GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 0) - (width << 2);
357   dest_wrap = GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 0) - (width << 2);
358 
359   /* we basically copy the source to dest but we scale the alpha channel with
360    * the mask */
361   for (i = 0; i < height; i++) {
362     for (j = 0; j < width; j++) {
363       value = *maskp++;
364       *out++ = (*in++ * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
365       *out++ = *in++;
366       *out++ = *in++;
367       *out++ = *in++;
368     }
369     in += src_wrap;
370     out += dest_wrap;
371   }
372 }
373 
374 static void
gst_smpte_alpha_process_i420_ayuv(GstSMPTEAlpha * smpte,const GstVideoFrame * in_frame,GstVideoFrame * out_frame,GstMask * mask,gint border,gint pos)375 gst_smpte_alpha_process_i420_ayuv (GstSMPTEAlpha * smpte,
376     const GstVideoFrame * in_frame, GstVideoFrame * out_frame, GstMask * mask,
377     gint border, gint pos)
378 {
379   const guint8 *srcY;
380   const guint8 *srcU;
381   const guint8 *srcV;
382   guint8 *out;
383   gint i, j;
384   gint src_wrap, src_u_wrap, src_v_wrap, dest_wrap;
385   gint y_stride, u_stride, v_stride;
386   gboolean odd_width;
387   const guint32 *maskp;
388   gint value;
389   gint min, max;
390   gint width, height;
391 
392   if (border == 0)
393     border++;
394 
395   min = pos - border;
396   max = pos;
397   GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
398       border);
399 
400   maskp = mask->data;
401 
402   width = GST_VIDEO_FRAME_WIDTH (out_frame);
403   height = GST_VIDEO_FRAME_HEIGHT (out_frame);
404 
405   y_stride = GST_VIDEO_FRAME_COMP_STRIDE (in_frame, 0);
406   u_stride = GST_VIDEO_FRAME_COMP_STRIDE (in_frame, 1);
407   v_stride = GST_VIDEO_FRAME_COMP_STRIDE (in_frame, 2);
408 
409   src_wrap = y_stride - width;
410   src_u_wrap = u_stride - (width / 2);
411   src_v_wrap = v_stride - (width / 2);
412 
413   srcY = GST_VIDEO_FRAME_COMP_DATA (in_frame, 0);
414   srcU = GST_VIDEO_FRAME_COMP_DATA (in_frame, 1);
415   srcV = GST_VIDEO_FRAME_COMP_DATA (in_frame, 2);
416 
417   out = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0);
418   dest_wrap = GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 0) - (width << 2);
419 
420   odd_width = (width % 2 != 0);
421 
422   for (i = 0; i < height; i++) {
423     for (j = 0; j < width / 2; j++) {
424       value = *maskp++;
425       *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
426       *out++ = *srcY++;
427       *out++ = *srcU;
428       *out++ = *srcV;
429       value = *maskp++;
430       *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
431       *out++ = *srcY++;
432       *out++ = *srcU++;
433       *out++ = *srcV++;
434     }
435     /* Might have one odd column left to do */
436     if (odd_width) {
437       value = *maskp++;
438       *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
439       *out++ = *srcY++;
440       *out++ = *srcU;
441       *out++ = *srcV;
442     }
443     if (i % 2 == 0) {
444       srcU -= width / 2;
445       srcV -= width / 2;
446     } else {
447       srcU += src_u_wrap;
448       srcV += src_v_wrap;
449     }
450     srcY += src_wrap;
451     out += dest_wrap;
452   }
453 }
454 
455 static void
gst_smpte_alpha_before_transform(GstBaseTransform * trans,GstBuffer * buf)456 gst_smpte_alpha_before_transform (GstBaseTransform * trans, GstBuffer * buf)
457 {
458   GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (trans);
459   GstClockTime timestamp, stream_time;
460 
461   /* first sync the controller to the current stream_time of the buffer */
462   timestamp = GST_BUFFER_TIMESTAMP (buf);
463   stream_time =
464       gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
465 
466   GST_DEBUG_OBJECT (smpte, "sync to %" GST_TIME_FORMAT,
467       GST_TIME_ARGS (timestamp));
468 
469   if (GST_CLOCK_TIME_IS_VALID (stream_time))
470     gst_object_sync_values (GST_OBJECT (smpte), stream_time);
471 }
472 
473 static GstFlowReturn
gst_smpte_alpha_transform_frame(GstVideoFilter * vfilter,GstVideoFrame * in_frame,GstVideoFrame * out_frame)474 gst_smpte_alpha_transform_frame (GstVideoFilter * vfilter,
475     GstVideoFrame * in_frame, GstVideoFrame * out_frame)
476 {
477   GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (vfilter);
478   gdouble position;
479   gint border;
480 
481   if (G_UNLIKELY (!smpte->process))
482     goto not_negotiated;
483 
484   GST_OBJECT_LOCK (smpte);
485   position = smpte->position;
486   border = smpte->border;
487 
488   /* run the type specific filter code */
489   smpte->process (smpte, in_frame, out_frame,
490       smpte->mask, border, ((1 << smpte->depth) + border) * position);
491   GST_OBJECT_UNLOCK (smpte);
492 
493   return GST_FLOW_OK;
494 
495   /* ERRORS */
496 not_negotiated:
497   {
498     GST_ELEMENT_ERROR (smpte, CORE, NEGOTIATION, (NULL),
499         ("No input format negotiated"));
500     return GST_FLOW_NOT_NEGOTIATED;
501   }
502 }
503 
504 static GstCaps *
gst_smpte_alpha_transform_caps(GstBaseTransform * trans,GstPadDirection direction,GstCaps * from,GstCaps * filter)505 gst_smpte_alpha_transform_caps (GstBaseTransform * trans,
506     GstPadDirection direction, GstCaps * from, GstCaps * filter)
507 {
508   GstCaps *result, *tmp_caps, *tmpl_caps = NULL;
509   gint i, j;
510 
511   tmp_caps = gst_caps_new_empty ();
512 
513   for (i = 0; i < gst_caps_get_size (from); i++) {
514     GstStructure *structure;
515     const GValue *val, *lval;
516     GValue list = { 0, };
517     GValue aval = { 0, };
518     const gchar *str;
519 
520     structure = gst_structure_copy (gst_caps_get_structure (from, i));
521     /* we can transform I420 to AYUV,
522      * so need to locate and substitute AYUV for the both of them */
523     val = gst_structure_get_value (structure, "format");
524     if (val && GST_VALUE_HOLDS_LIST (val)) {
525       gboolean seen_ayuv = FALSE, seen_i420 = FALSE;
526 
527       g_value_init (&list, GST_TYPE_LIST);
528       for (j = 0; j < gst_value_list_get_size (val); j++) {
529         lval = gst_value_list_get_value (val, j);
530         if ((str = g_value_get_string (lval))) {
531           if (strcmp (str, "AYUV") == 0) {
532             seen_ayuv = TRUE;
533           } else if (strcmp (str, "I420") == 0) {
534             seen_i420 = TRUE;
535           }
536         }
537       }
538       if (seen_ayuv && !seen_i420) {
539         str = "I420";
540       } else if (seen_i420 && !seen_ayuv) {
541         str = "AYUV";
542       } else
543         str = NULL;
544       if (str) {
545         g_value_copy (val, &list);
546         g_value_init (&aval, G_TYPE_STRING);
547         g_value_set_string (&aval, str);
548         gst_value_list_append_value (&list, &aval);
549         g_value_reset (&aval);
550         gst_structure_set_value (structure, "format", &list);
551         g_value_unset (&list);
552       }
553     } else if (val && G_VALUE_HOLDS_STRING (val)) {
554       if ((str = g_value_get_string (val)) &&
555           ((strcmp (str, "AYUV") == 0) || (strcmp (str, "I420") == 0))) {
556         g_value_init (&list, GST_TYPE_LIST);
557         g_value_init (&aval, G_TYPE_STRING);
558         g_value_set_string (&aval, "AYUV");
559         gst_value_list_append_value (&list, &aval);
560         g_value_reset (&aval);
561         g_value_set_string (&aval, "I420");
562         gst_value_list_append_value (&list, &aval);
563         g_value_reset (&aval);
564         gst_structure_set_value (structure, "format", &list);
565         g_value_unset (&list);
566       }
567     } else {
568       gst_structure_remove_field (structure, "format");
569     }
570 
571     gst_structure_remove_field (structure, "colorimetry");
572     gst_structure_remove_field (structure, "chroma-site");
573 
574     gst_caps_append_structure (tmp_caps, structure);
575   }
576 
577   /* Get the appropriate template */
578   if (direction == GST_PAD_SINK) {
579     tmpl_caps =
580         gst_static_pad_template_get_caps (&gst_smpte_alpha_src_template);
581   } else if (direction == GST_PAD_SRC) {
582     tmpl_caps =
583         gst_static_pad_template_get_caps (&gst_smpte_alpha_sink_template);
584   } else {
585     g_assert_not_reached ();
586   }
587 
588   /* Intersect with our template caps */
589   result = gst_caps_intersect (tmp_caps, tmpl_caps);
590   gst_caps_unref (tmpl_caps);
591   gst_caps_unref (tmp_caps);
592 
593   result = gst_caps_simplify (result);
594 
595   GST_LOG_OBJECT (trans, "transformed %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT,
596       from, result);
597 
598   if (filter) {
599     GstCaps *intersection;
600 
601     GST_DEBUG_OBJECT (trans, "Using filter caps %" GST_PTR_FORMAT, filter);
602     intersection =
603         gst_caps_intersect_full (filter, result, GST_CAPS_INTERSECT_FIRST);
604     gst_caps_unref (result);
605     result = intersection;
606     GST_DEBUG_OBJECT (trans, "Intersection %" GST_PTR_FORMAT, result);
607   }
608 
609   return result;
610 }
611 
612 static gboolean
gst_smpte_alpha_set_info(GstVideoFilter * vfilter,GstCaps * incaps,GstVideoInfo * in_info,GstCaps * outcaps,GstVideoInfo * out_info)613 gst_smpte_alpha_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
614     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
615 {
616   GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (vfilter);
617   gboolean ret;
618 
619   smpte->process = NULL;
620   smpte->in_format = GST_VIDEO_INFO_FORMAT (in_info);
621   smpte->out_format = GST_VIDEO_INFO_FORMAT (out_info);
622 
623   /* try to update the mask now, this will also adjust the width/height on
624    * success */
625   GST_OBJECT_LOCK (smpte);
626   ret =
627       gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->invert,
628       smpte->depth, GST_VIDEO_INFO_WIDTH (out_info),
629       GST_VIDEO_INFO_HEIGHT (out_info));
630   GST_OBJECT_UNLOCK (smpte);
631 
632   if (!ret)
633     goto mask_failed;
634 
635   switch (smpte->out_format) {
636     case GST_VIDEO_FORMAT_AYUV:
637       switch (smpte->in_format) {
638         case GST_VIDEO_FORMAT_AYUV:
639           smpte->process = gst_smpte_alpha_process_ayuv_ayuv;
640           break;
641         case GST_VIDEO_FORMAT_I420:
642           smpte->process = gst_smpte_alpha_process_i420_ayuv;
643           break;
644         default:
645           break;
646       }
647       break;
648     case GST_VIDEO_FORMAT_ARGB:
649       switch (smpte->in_format) {
650         case GST_VIDEO_FORMAT_ARGB:
651           smpte->process = gst_smpte_alpha_process_argb_argb;
652           break;
653         default:
654           break;
655       }
656       break;
657     case GST_VIDEO_FORMAT_RGBA:
658       switch (smpte->in_format) {
659         case GST_VIDEO_FORMAT_RGBA:
660           smpte->process = gst_smpte_alpha_process_rgba_rgba;
661           break;
662         default:
663           break;
664       }
665       break;
666     case GST_VIDEO_FORMAT_ABGR:
667       switch (smpte->in_format) {
668         case GST_VIDEO_FORMAT_ABGR:
669           smpte->process = gst_smpte_alpha_process_abgr_abgr;
670           break;
671         default:
672           break;
673       }
674       break;
675     case GST_VIDEO_FORMAT_BGRA:
676       switch (smpte->in_format) {
677         case GST_VIDEO_FORMAT_BGRA:
678           smpte->process = gst_smpte_alpha_process_bgra_bgra;
679           break;
680         default:
681           break;
682       }
683       break;
684     default:
685       break;
686   }
687 
688   return ret;
689 
690   /* ERRORS */
691 mask_failed:
692   {
693     GST_ERROR_OBJECT (smpte, "failed creating the mask");
694     return FALSE;
695   }
696 }
697 
698 static void
gst_smpte_alpha_finalize(GstSMPTEAlpha * smpte)699 gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte)
700 {
701   if (smpte->mask)
702     gst_mask_destroy (smpte->mask);
703   smpte->mask = NULL;
704 
705   G_OBJECT_CLASS (parent_class)->finalize ((GObject *) smpte);
706 }
707 
708 static void
gst_smpte_alpha_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)709 gst_smpte_alpha_set_property (GObject * object, guint prop_id,
710     const GValue * value, GParamSpec * pspec)
711 {
712   GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (object);
713 
714   switch (prop_id) {
715     case PROP_TYPE:{
716       gint type;
717 
718       type = g_value_get_enum (value);
719 
720       GST_OBJECT_LOCK (smpte);
721       gst_smpte_alpha_update_mask (smpte, type, smpte->invert,
722           smpte->depth, smpte->width, smpte->height);
723       GST_OBJECT_UNLOCK (smpte);
724       break;
725     }
726     case PROP_BORDER:
727       GST_OBJECT_LOCK (smpte);
728       smpte->border = g_value_get_int (value);
729       GST_OBJECT_UNLOCK (smpte);
730       break;
731     case PROP_DEPTH:{
732       gint depth;
733 
734       depth = g_value_get_int (value);
735 
736       GST_OBJECT_LOCK (smpte);
737       gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->invert,
738           depth, smpte->width, smpte->height);
739       GST_OBJECT_UNLOCK (smpte);
740       break;
741     }
742     case PROP_POSITION:
743       GST_OBJECT_LOCK (smpte);
744       smpte->position = g_value_get_double (value);
745       GST_OBJECT_UNLOCK (smpte);
746       break;
747     case PROP_INVERT:{
748       gboolean invert;
749 
750       invert = g_value_get_boolean (value);
751       GST_OBJECT_LOCK (smpte);
752       gst_smpte_alpha_update_mask (smpte, smpte->type, invert,
753           smpte->depth, smpte->width, smpte->height);
754       GST_OBJECT_UNLOCK (smpte);
755       break;
756     }
757     default:
758       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
759       break;
760   }
761 }
762 
763 static void
gst_smpte_alpha_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)764 gst_smpte_alpha_get_property (GObject * object, guint prop_id,
765     GValue * value, GParamSpec * pspec)
766 {
767   GstSMPTEAlpha *smpte;
768 
769   smpte = GST_SMPTE_ALPHA (object);
770 
771   switch (prop_id) {
772     case PROP_TYPE:
773       GST_OBJECT_LOCK (smpte);
774       g_value_set_enum (value, smpte->type);
775       GST_OBJECT_UNLOCK (smpte);
776       break;
777     case PROP_BORDER:
778       GST_OBJECT_LOCK (smpte);
779       g_value_set_int (value, smpte->border);
780       GST_OBJECT_UNLOCK (smpte);
781       break;
782     case PROP_DEPTH:
783       GST_OBJECT_LOCK (smpte);
784       g_value_set_int (value, smpte->depth);
785       GST_OBJECT_UNLOCK (smpte);
786       break;
787     case PROP_POSITION:
788       GST_OBJECT_LOCK (smpte);
789       g_value_set_double (value, smpte->position);
790       GST_OBJECT_UNLOCK (smpte);
791       break;
792     case PROP_INVERT:
793       GST_OBJECT_LOCK (smpte);
794       g_value_set_boolean (value, smpte->invert);
795       GST_OBJECT_UNLOCK (smpte);
796       break;
797     default:
798       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
799       break;
800   }
801 }
802 
803 gboolean
gst_smpte_alpha_plugin_init(GstPlugin * plugin)804 gst_smpte_alpha_plugin_init (GstPlugin * plugin)
805 {
806   GST_DEBUG_CATEGORY_INIT (gst_smpte_alpha_debug, "smptealpha", 0,
807       "SMPTE alpha effect");
808 
809   return gst_element_register (plugin, "smptealpha", GST_RANK_NONE,
810       GST_TYPE_SMPTE_ALPHA);
811 }
812