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