2 * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com>
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.
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.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-smptealpha
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.
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.
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
37 * <title>Sample pipelines</title>
39 * Here is a pipeline to demonstrate the smpte transition :
41 * gst-launch -v videotestsrc ! smptealpha border=20000 type=44
42 * position=0.5 ! videomixer ! ffmpegcolorspace ! ximagesink
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
56 #include "gstsmptealpha.h"
59 GST_DEBUG_CATEGORY_STATIC (gst_smpte_alpha_debug);
60 #define GST_CAT_DEFAULT gst_smpte_alpha_debug
62 static GstStaticPadTemplate gst_smpte_alpha_src_template =
63 GST_STATIC_PAD_TEMPLATE ("src",
66 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
67 GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
68 GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_ARGB)
71 static GstStaticPadTemplate gst_smpte_alpha_sink_template =
72 GST_STATIC_PAD_TEMPLATE ("sink",
75 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("YV12")
76 ";" GST_VIDEO_CAPS_YUV ("AYUV")
77 ";" GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";" GST_VIDEO_CAPS_RGBA
78 ";" GST_VIDEO_CAPS_ARGB)
81 /* SMPTE signals and properties */
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
100 #define AYUV_SIZE(w,h) ((w) * (h) * 4)
102 #define GST_TYPE_SMPTE_TRANSITION_TYPE (gst_smpte_alpha_transition_type_get_type())
104 gst_smpte_alpha_transition_type_get_type (void)
106 static GType smpte_transition_type = 0;
107 GEnumValue *smpte_transitions;
109 if (!smpte_transition_type) {
110 const GList *definitions;
113 definitions = gst_mask_get_definitions ();
115 g_new0 (GEnumValue, g_list_length ((GList *) definitions) + 1);
117 while (definitions) {
118 GstMaskDefinition *definition = (GstMaskDefinition *) definitions->data;
120 definitions = g_list_next (definitions);
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;
130 smpte_transition_type =
131 g_enum_register_static ("GstSMPTEAlphaTransitionType",
134 return smpte_transition_type;
138 static void gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte);
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);
145 static gboolean gst_smpte_alpha_setcaps (GstBaseTransform * btrans,
146 GstCaps * incaps, GstCaps * outcaps);
147 static gboolean gst_smpte_alpha_get_unit_size (GstBaseTransform * btrans,
148 GstCaps * caps, guint * size);
149 static GstFlowReturn gst_smpte_alpha_transform (GstBaseTransform * trans,
150 GstBuffer * in, GstBuffer * out);
151 static void gst_smpte_alpha_before_transform (GstBaseTransform * trans,
153 static GstCaps *gst_smpte_alpha_transform_caps (GstBaseTransform * trans,
154 GstPadDirection direction, GstCaps * from);
156 GST_BOILERPLATE (GstSMPTEAlpha, gst_smpte_alpha, GstVideoFilter,
157 GST_TYPE_VIDEO_FILTER);
160 gst_smpte_alpha_base_init (gpointer klass)
162 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
164 gst_element_class_add_pad_template (element_class,
165 gst_static_pad_template_get (&gst_smpte_alpha_sink_template));
166 gst_element_class_add_pad_template (element_class,
167 gst_static_pad_template_get (&gst_smpte_alpha_src_template));
168 gst_element_class_set_details_simple (element_class, "SMPTE transitions",
169 "Filter/Editor/Video",
170 "Apply the standard SMPTE transitions as alpha on video images",
171 "Wim Taymans <wim.taymans@gmail.com>");
175 gst_smpte_alpha_class_init (GstSMPTEAlphaClass * klass)
177 GObjectClass *gobject_class = (GObjectClass *) klass;
178 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
180 gobject_class->set_property = gst_smpte_alpha_set_property;
181 gobject_class->get_property = gst_smpte_alpha_get_property;
183 gobject_class->finalize = (GObjectFinalizeFunc) gst_smpte_alpha_finalize;
187 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TYPE,
188 g_param_spec_enum ("type", "Type", "The type of transition to use",
189 GST_TYPE_SMPTE_TRANSITION_TYPE, DEFAULT_PROP_TYPE,
190 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
191 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER,
192 g_param_spec_int ("border", "Border",
193 "The border width of the transition", 0, G_MAXINT,
195 GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
196 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEPTH,
197 g_param_spec_int ("depth", "Depth", "Depth of the mask in bits", 1, 24,
198 DEFAULT_PROP_DEPTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
199 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_POSITION,
200 g_param_spec_double ("position", "Position",
201 "Position of the transition effect", 0.0, 1.0, DEFAULT_PROP_POSITION,
202 GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
204 * GstSMPTEAlpha:invert:
206 * Set to TRUE to invert the transition mask (ie. flip it horizontally).
210 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INVERT,
211 g_param_spec_boolean ("invert", "Invert",
212 "Invert transition mask", DEFAULT_PROP_POSITION,
213 GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
215 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_smpte_alpha_setcaps);
216 trans_class->get_unit_size =
217 GST_DEBUG_FUNCPTR (gst_smpte_alpha_get_unit_size);
218 trans_class->transform = GST_DEBUG_FUNCPTR (gst_smpte_alpha_transform);
219 trans_class->before_transform =
220 GST_DEBUG_FUNCPTR (gst_smpte_alpha_before_transform);
221 trans_class->transform_caps =
222 GST_DEBUG_FUNCPTR (gst_smpte_alpha_transform_caps);
226 gst_smpte_alpha_update_mask (GstSMPTEAlpha * smpte, gint type,
227 gboolean invert, gint depth, gint width, gint height)
231 /* try to avoid regenerating the mask if we already have one that is
234 if (smpte->type == type &&
235 smpte->invert == invert &&
236 smpte->depth == depth &&
237 smpte->width == width && smpte->height == height)
242 smpte->invert = invert;
243 smpte->depth = depth;
244 smpte->width = width;
245 smpte->height = height;
247 /* Not negotiated yet */
248 if (width == 0 || height == 0) {
252 newmask = gst_mask_factory_new (type, invert, depth, width, height);
257 gst_mask_destroy (smpte->mask);
259 smpte->mask = newmask;
266 GST_ERROR_OBJECT (smpte, "failed to create a mask");
272 gst_smpte_alpha_init (GstSMPTEAlpha * smpte, GstSMPTEAlphaClass * klass)
274 smpte->type = DEFAULT_PROP_TYPE;
275 smpte->border = DEFAULT_PROP_BORDER;
276 smpte->depth = DEFAULT_PROP_DEPTH;
277 smpte->position = DEFAULT_PROP_POSITION;
278 smpte->invert = DEFAULT_PROP_INVERT;
281 #define CREATE_ARGB_FUNC(name, A, R, G, B) \
283 gst_smpte_alpha_process_##name##_##name (GstSMPTEAlpha * smpte, const guint8 * in, \
284 guint8 * out, GstMask * mask, gint width, gint height, gint border, \
288 const guint32 *maskp; \
295 min = pos - border; \
297 GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max, \
300 maskp = mask->data; \
302 /* we basically copy the source to dest but we scale the alpha channel with \
304 for (i = 0; i < height; i++) { \
305 for (j = 0; j < width; j++) { \
307 out[A] = (in[A] * ((CLAMP (value, min, max) - min) << 8) / border) >> 8; \
317 CREATE_ARGB_FUNC (argb, 0, 1, 2, 3);
318 CREATE_ARGB_FUNC (bgra, 3, 2, 1, 0);
319 CREATE_ARGB_FUNC (abgr, 0, 3, 2, 1);
320 CREATE_ARGB_FUNC (rgba, 3, 0, 1, 2);
323 gst_smpte_alpha_process_ayuv_ayuv (GstSMPTEAlpha * smpte, const guint8 * in,
324 guint8 * out, GstMask * mask, gint width, gint height, gint border,
328 const guint32 *maskp;
337 GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
342 /* we basically copy the source to dest but we scale the alpha channel with
344 for (i = 0; i < height; i++) {
345 for (j = 0; j < width; j++) {
347 *out++ = (*in++ * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
356 gst_smpte_alpha_process_i420_ayuv (GstSMPTEAlpha * smpte, const guint8 * in,
357 guint8 * out, GstMask * mask, gint width, gint height, gint border,
364 gint src_wrap, src_uv_wrap;
365 gint y_stride, uv_stride;
367 const guint32 *maskp;
376 GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
381 y_stride = gst_video_format_get_row_stride (smpte->in_format, 0, width);
382 uv_stride = gst_video_format_get_row_stride (smpte->in_format, 1, width);
384 src_wrap = y_stride - width;
385 src_uv_wrap = uv_stride - (width / 2);
388 srcU = in + gst_video_format_get_component_offset (smpte->in_format,
390 srcV = in + gst_video_format_get_component_offset (smpte->in_format,
393 odd_width = (width % 2 != 0);
395 for (i = 0; i < height; i++) {
396 for (j = 0; j < width / 2; j++) {
398 *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
403 *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
408 /* Might have one odd column left to do */
411 *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
428 gst_smpte_alpha_before_transform (GstBaseTransform * trans, GstBuffer * buf)
430 GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (trans);
431 GstClockTime timestamp, stream_time;
433 /* first sync the controller to the current stream_time of the buffer */
434 timestamp = GST_BUFFER_TIMESTAMP (buf);
436 gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
438 GST_DEBUG_OBJECT (smpte, "sync to %" GST_TIME_FORMAT,
439 GST_TIME_ARGS (timestamp));
441 if (GST_CLOCK_TIME_IS_VALID (stream_time))
442 gst_object_sync_values (GST_OBJECT (smpte), stream_time);
446 gst_smpte_alpha_transform (GstBaseTransform * trans, GstBuffer * in,
449 GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (trans);
453 if (G_UNLIKELY (!smpte->process))
456 /* these are the propertis we update with only the object lock, others are
457 * only updated with the TRANSFORM_LOCK. */
458 GST_OBJECT_LOCK (smpte);
459 position = smpte->position;
460 border = smpte->border;
461 GST_OBJECT_UNLOCK (smpte);
463 /* run the type specific filter code */
464 smpte->process (smpte, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out),
465 smpte->mask, smpte->width, smpte->height, border,
466 ((1 << smpte->depth) + border) * position);
473 GST_ELEMENT_ERROR (smpte, CORE, NEGOTIATION, (NULL),
474 ("No input format negotiated"));
475 return GST_FLOW_NOT_NEGOTIATED;
480 gst_smpte_alpha_transform_caps (GstBaseTransform * trans,
481 GstPadDirection direction, GstCaps * from)
483 GstCaps *to = gst_caps_copy (from);
486 gst_caps_truncate (to);
487 s = gst_caps_get_structure (to, 0);
489 if (gst_structure_has_name (s, "video/x-raw-yuv")) {
490 GValue list = { 0, };
493 gst_structure_remove_field (s, "format");
495 g_value_init (&list, GST_TYPE_LIST);
496 g_value_init (&val, GST_TYPE_FOURCC);
497 gst_value_set_fourcc (&val, GST_STR_FOURCC ("AYUV"));
498 gst_value_list_append_value (&list, &val);
499 g_value_reset (&val);
500 gst_value_set_fourcc (&val, GST_STR_FOURCC ("I420"));
501 gst_value_list_append_value (&list, &val);
502 g_value_reset (&val);
503 gst_value_set_fourcc (&val, GST_STR_FOURCC ("YV12"));
504 gst_value_list_append_value (&list, &val);
505 g_value_unset (&val);
506 gst_structure_set_value (s, "format", &list);
507 g_value_unset (&list);
508 } else if (!gst_structure_has_name (s, "video/x-raw-rgb")) {
510 to = gst_caps_new_empty ();
517 gst_smpte_alpha_setcaps (GstBaseTransform * btrans, GstCaps * incaps,
520 GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (btrans);
524 smpte->process = NULL;
526 if (!gst_video_format_parse_caps (incaps, &smpte->in_format, &width, &height))
528 if (!gst_video_format_parse_caps (outcaps, &smpte->out_format, &width,
532 /* try to update the mask now, this will also adjust the width/height on
534 GST_OBJECT_LOCK (smpte);
536 gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->invert,
537 smpte->depth, width, height);
538 GST_OBJECT_UNLOCK (smpte);
542 switch (smpte->out_format) {
543 case GST_VIDEO_FORMAT_AYUV:
544 switch (smpte->in_format) {
545 case GST_VIDEO_FORMAT_AYUV:
546 smpte->process = gst_smpte_alpha_process_ayuv_ayuv;
548 case GST_VIDEO_FORMAT_I420:
549 smpte->process = gst_smpte_alpha_process_i420_ayuv;
555 case GST_VIDEO_FORMAT_ARGB:
556 switch (smpte->in_format) {
557 case GST_VIDEO_FORMAT_ARGB:
558 smpte->process = gst_smpte_alpha_process_argb_argb;
564 case GST_VIDEO_FORMAT_RGBA:
565 switch (smpte->in_format) {
566 case GST_VIDEO_FORMAT_RGBA:
567 smpte->process = gst_smpte_alpha_process_rgba_rgba;
573 case GST_VIDEO_FORMAT_ABGR:
574 switch (smpte->in_format) {
575 case GST_VIDEO_FORMAT_ABGR:
576 smpte->process = gst_smpte_alpha_process_abgr_abgr;
582 case GST_VIDEO_FORMAT_BGRA:
583 switch (smpte->in_format) {
584 case GST_VIDEO_FORMAT_BGRA:
585 smpte->process = gst_smpte_alpha_process_bgra_bgra;
600 GST_ERROR_OBJECT (smpte, "Invalid caps: %" GST_PTR_FORMAT, incaps);
605 GST_ERROR_OBJECT (smpte, "failed creating the mask");
611 gst_smpte_alpha_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
615 GstVideoFormat format;
617 if (!gst_video_format_parse_caps (caps, &format, &width, &height))
620 *size = gst_video_format_get_size (format, width, height);
626 gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte)
629 gst_mask_destroy (smpte->mask);
632 G_OBJECT_CLASS (parent_class)->finalize ((GObject *) smpte);
636 gst_smpte_alpha_set_property (GObject * object, guint prop_id,
637 const GValue * value, GParamSpec * pspec)
639 GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (object);
645 type = g_value_get_enum (value);
647 GST_BASE_TRANSFORM_LOCK (smpte);
648 GST_OBJECT_LOCK (smpte);
649 gst_smpte_alpha_update_mask (smpte, type, smpte->invert,
650 smpte->depth, smpte->width, smpte->height);
651 GST_OBJECT_UNLOCK (smpte);
652 GST_BASE_TRANSFORM_UNLOCK (smpte);
656 GST_OBJECT_LOCK (smpte);
657 smpte->border = g_value_get_int (value);
658 GST_OBJECT_UNLOCK (smpte);
663 depth = g_value_get_int (value);
665 GST_BASE_TRANSFORM_LOCK (smpte);
666 /* also lock with the object lock so that reading the property doesn't
667 * have to wait for the transform lock */
668 GST_OBJECT_LOCK (smpte);
669 gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->invert,
670 depth, smpte->width, smpte->height);
671 GST_OBJECT_UNLOCK (smpte);
672 GST_BASE_TRANSFORM_UNLOCK (smpte);
676 GST_OBJECT_LOCK (smpte);
677 smpte->position = g_value_get_double (value);
678 GST_OBJECT_UNLOCK (smpte);
683 invert = g_value_get_boolean (value);
684 GST_BASE_TRANSFORM_LOCK (smpte);
685 /* also lock with the object lock so that reading the property doesn't
686 * have to wait for the transform lock */
687 GST_OBJECT_LOCK (smpte);
688 gst_smpte_alpha_update_mask (smpte, smpte->type, invert,
689 smpte->depth, smpte->width, smpte->height);
690 GST_OBJECT_UNLOCK (smpte);
691 GST_BASE_TRANSFORM_UNLOCK (smpte);
695 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
701 gst_smpte_alpha_get_property (GObject * object, guint prop_id,
702 GValue * value, GParamSpec * pspec)
704 GstSMPTEAlpha *smpte;
706 smpte = GST_SMPTE_ALPHA (object);
710 GST_OBJECT_LOCK (smpte);
711 g_value_set_enum (value, smpte->type);
712 GST_OBJECT_UNLOCK (smpte);
715 GST_OBJECT_LOCK (smpte);
716 g_value_set_int (value, smpte->border);
717 GST_OBJECT_UNLOCK (smpte);
720 GST_OBJECT_LOCK (smpte);
721 g_value_set_int (value, smpte->depth);
722 GST_OBJECT_UNLOCK (smpte);
725 GST_OBJECT_LOCK (smpte);
726 g_value_set_double (value, smpte->position);
727 GST_OBJECT_UNLOCK (smpte);
730 GST_OBJECT_LOCK (smpte);
731 g_value_set_boolean (value, smpte->invert);
732 GST_OBJECT_UNLOCK (smpte);
735 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
741 gst_smpte_alpha_plugin_init (GstPlugin * plugin)
743 GST_DEBUG_CATEGORY_INIT (gst_smpte_alpha_debug, "smptealpha", 0,
744 "SMPTE alpha effect");
746 return gst_element_register (plugin, "smptealpha", GST_RANK_NONE,
747 GST_TYPE_SMPTE_ALPHA);