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