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