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 <gst/controller/gstcontroller.h>
58 #include "gstsmptealpha.h"
61 GST_DEBUG_CATEGORY_STATIC (gst_smpte_alpha_debug);
62 #define GST_CAT_DEFAULT gst_smpte_alpha_debug
64 static GstStaticPadTemplate gst_smpte_alpha_src_template =
65 GST_STATIC_PAD_TEMPLATE ("src",
68 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV") ";"
69 GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";"
70 GST_VIDEO_CAPS_RGBA ";" GST_VIDEO_CAPS_ARGB)
73 static GstStaticPadTemplate gst_smpte_alpha_sink_template =
74 GST_STATIC_PAD_TEMPLATE ("sink",
77 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("YV12")
78 ";" GST_VIDEO_CAPS_YUV ("AYUV")
79 ";" GST_VIDEO_CAPS_ARGB ";" GST_VIDEO_CAPS_BGRA ";" GST_VIDEO_CAPS_RGBA
80 ";" GST_VIDEO_CAPS_ARGB)
83 /* SMPTE signals and properties */
85 #define DEFAULT_PROP_TYPE 1
86 #define DEFAULT_PROP_BORDER 0
87 #define DEFAULT_PROP_DEPTH 16
88 #define DEFAULT_PROP_POSITION 0.0
89 #define DEFAULT_PROP_INVERT FALSE
102 #define AYUV_SIZE(w,h) ((w) * (h) * 4)
104 #define GST_TYPE_SMPTE_TRANSITION_TYPE (gst_smpte_alpha_transition_type_get_type())
106 gst_smpte_alpha_transition_type_get_type (void)
108 static GType smpte_transition_type = 0;
109 GEnumValue *smpte_transitions;
111 if (!smpte_transition_type) {
112 const GList *definitions;
115 definitions = gst_mask_get_definitions ();
117 g_new0 (GEnumValue, g_list_length ((GList *) definitions) + 1);
119 while (definitions) {
120 GstMaskDefinition *definition = (GstMaskDefinition *) definitions->data;
122 definitions = g_list_next (definitions);
124 smpte_transitions[i].value = definition->type;
125 /* older GLib versions have the two fields as non-const, hence the cast */
126 smpte_transitions[i].value_nick = (gchar *) definition->short_name;
127 smpte_transitions[i].value_name = (gchar *) definition->long_name;
132 smpte_transition_type =
133 g_enum_register_static ("GstSMPTEAlphaTransitionType",
136 return smpte_transition_type;
140 static void gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte);
142 static void gst_smpte_alpha_set_property (GObject * object, guint prop_id,
143 const GValue * value, GParamSpec * pspec);
144 static void gst_smpte_alpha_get_property (GObject * object, guint prop_id,
145 GValue * value, GParamSpec * pspec);
147 static gboolean gst_smpte_alpha_setcaps (GstBaseTransform * btrans,
148 GstCaps * incaps, GstCaps * outcaps);
149 static gboolean gst_smpte_alpha_get_unit_size (GstBaseTransform * btrans,
150 GstCaps * caps, guint * size);
151 static GstFlowReturn gst_smpte_alpha_transform (GstBaseTransform * trans,
152 GstBuffer * in, GstBuffer * out);
153 static void gst_smpte_alpha_before_transform (GstBaseTransform * trans,
155 static GstCaps *gst_smpte_alpha_transform_caps (GstBaseTransform * trans,
156 GstPadDirection direction, GstCaps * from);
158 GST_BOILERPLATE (GstSMPTEAlpha, gst_smpte_alpha, GstVideoFilter,
159 GST_TYPE_VIDEO_FILTER);
162 gst_smpte_alpha_base_init (gpointer klass)
164 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
166 gst_element_class_add_static_pad_template (element_class,
167 &gst_smpte_alpha_sink_template);
168 gst_element_class_add_static_pad_template (element_class,
169 &gst_smpte_alpha_src_template);
170 gst_element_class_set_details_simple (element_class, "SMPTE transitions",
171 "Filter/Editor/Video",
172 "Apply the standard SMPTE transitions as alpha on video images",
173 "Wim Taymans <wim.taymans@gmail.com>");
177 gst_smpte_alpha_class_init (GstSMPTEAlphaClass * klass)
179 GObjectClass *gobject_class = (GObjectClass *) klass;
180 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
182 gobject_class->set_property = gst_smpte_alpha_set_property;
183 gobject_class->get_property = gst_smpte_alpha_get_property;
185 gobject_class->finalize = (GObjectFinalizeFunc) gst_smpte_alpha_finalize;
189 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TYPE,
190 g_param_spec_enum ("type", "Type", "The type of transition to use",
191 GST_TYPE_SMPTE_TRANSITION_TYPE, DEFAULT_PROP_TYPE,
192 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
193 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER,
194 g_param_spec_int ("border", "Border",
195 "The border width of the transition", 0, G_MAXINT,
197 GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
198 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEPTH,
199 g_param_spec_int ("depth", "Depth", "Depth of the mask in bits", 1, 24,
200 DEFAULT_PROP_DEPTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
201 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_POSITION,
202 g_param_spec_double ("position", "Position",
203 "Position of the transition effect", 0.0, 1.0, DEFAULT_PROP_POSITION,
204 GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
206 * GstSMPTEAlpha:invert:
208 * Set to TRUE to invert the transition mask (ie. flip it horizontally).
212 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INVERT,
213 g_param_spec_boolean ("invert", "Invert",
214 "Invert transition mask", DEFAULT_PROP_POSITION,
215 GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
217 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_smpte_alpha_setcaps);
218 trans_class->get_unit_size =
219 GST_DEBUG_FUNCPTR (gst_smpte_alpha_get_unit_size);
220 trans_class->transform = GST_DEBUG_FUNCPTR (gst_smpte_alpha_transform);
221 trans_class->before_transform =
222 GST_DEBUG_FUNCPTR (gst_smpte_alpha_before_transform);
223 trans_class->transform_caps =
224 GST_DEBUG_FUNCPTR (gst_smpte_alpha_transform_caps);
228 gst_smpte_alpha_update_mask (GstSMPTEAlpha * smpte, gint type,
229 gboolean invert, gint depth, gint width, gint height)
233 /* try to avoid regenerating the mask if we already have one that is
236 if (smpte->type == type &&
237 smpte->invert == invert &&
238 smpte->depth == depth &&
239 smpte->width == width && smpte->height == height)
244 smpte->invert = invert;
245 smpte->depth = depth;
246 smpte->width = width;
247 smpte->height = height;
249 /* Not negotiated yet */
250 if (width == 0 || height == 0) {
254 newmask = gst_mask_factory_new (type, invert, depth, width, height);
259 gst_mask_destroy (smpte->mask);
261 smpte->mask = newmask;
268 GST_ERROR_OBJECT (smpte, "failed to create a mask");
274 gst_smpte_alpha_init (GstSMPTEAlpha * smpte, GstSMPTEAlphaClass * klass)
276 smpte->type = DEFAULT_PROP_TYPE;
277 smpte->border = DEFAULT_PROP_BORDER;
278 smpte->depth = DEFAULT_PROP_DEPTH;
279 smpte->position = DEFAULT_PROP_POSITION;
280 smpte->invert = DEFAULT_PROP_INVERT;
283 #define CREATE_ARGB_FUNC(name, A, R, G, B) \
285 gst_smpte_alpha_process_##name##_##name (GstSMPTEAlpha * smpte, const guint8 * in, \
286 guint8 * out, GstMask * mask, gint width, gint height, gint border, \
290 const guint32 *maskp; \
297 min = pos - border; \
299 GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max, \
302 maskp = mask->data; \
304 /* we basically copy the source to dest but we scale the alpha channel with \
306 for (i = 0; i < height; i++) { \
307 for (j = 0; j < width; j++) { \
309 out[A] = (in[A] * ((CLAMP (value, min, max) - min) << 8) / border) >> 8; \
319 CREATE_ARGB_FUNC (argb, 0, 1, 2, 3);
320 CREATE_ARGB_FUNC (bgra, 3, 2, 1, 0);
321 CREATE_ARGB_FUNC (abgr, 0, 3, 2, 1);
322 CREATE_ARGB_FUNC (rgba, 3, 0, 1, 2);
325 gst_smpte_alpha_process_ayuv_ayuv (GstSMPTEAlpha * smpte, const guint8 * in,
326 guint8 * out, GstMask * mask, gint width, gint height, gint border,
330 const guint32 *maskp;
339 GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
344 /* we basically copy the source to dest but we scale the alpha channel with
346 for (i = 0; i < height; i++) {
347 for (j = 0; j < width; j++) {
349 *out++ = (*in++ * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
358 gst_smpte_alpha_process_i420_ayuv (GstSMPTEAlpha * smpte, const guint8 * in,
359 guint8 * out, GstMask * mask, gint width, gint height, gint border,
366 gint src_wrap, src_uv_wrap;
367 gint y_stride, uv_stride;
369 const guint32 *maskp;
378 GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
383 y_stride = gst_video_format_get_row_stride (smpte->in_format, 0, width);
384 uv_stride = gst_video_format_get_row_stride (smpte->in_format, 1, width);
386 src_wrap = y_stride - width;
387 src_uv_wrap = uv_stride - (width / 2);
390 srcU = in + gst_video_format_get_component_offset (smpte->in_format,
392 srcV = in + gst_video_format_get_component_offset (smpte->in_format,
395 odd_width = (width % 2 != 0);
397 for (i = 0; i < height; i++) {
398 for (j = 0; j < width / 2; j++) {
400 *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
405 *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
410 /* Might have one odd column left to do */
413 *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
430 gst_smpte_alpha_before_transform (GstBaseTransform * trans, GstBuffer * buf)
432 GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (trans);
433 GstClockTime timestamp, stream_time;
435 /* first sync the controller to the current stream_time of the buffer */
436 timestamp = GST_BUFFER_TIMESTAMP (buf);
438 gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
440 GST_DEBUG_OBJECT (smpte, "sync to %" GST_TIME_FORMAT,
441 GST_TIME_ARGS (timestamp));
443 if (GST_CLOCK_TIME_IS_VALID (stream_time))
444 gst_object_sync_values (G_OBJECT (smpte), stream_time);
448 gst_smpte_alpha_transform (GstBaseTransform * trans, GstBuffer * in,
451 GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (trans);
455 if (G_UNLIKELY (!smpte->process))
458 /* these are the propertis we update with only the object lock, others are
459 * only updated with the TRANSFORM_LOCK. */
460 GST_OBJECT_LOCK (smpte);
461 position = smpte->position;
462 border = smpte->border;
463 GST_OBJECT_UNLOCK (smpte);
465 /* run the type specific filter code */
466 smpte->process (smpte, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out),
467 smpte->mask, smpte->width, smpte->height, border,
468 ((1 << smpte->depth) + border) * position);
475 GST_ELEMENT_ERROR (smpte, CORE, NEGOTIATION, (NULL),
476 ("No input format negotiated"));
477 return GST_FLOW_NOT_NEGOTIATED;
482 gst_smpte_alpha_transform_caps (GstBaseTransform * trans,
483 GstPadDirection direction, GstCaps * from)
485 GstCaps *to = gst_caps_copy (from);
488 gst_caps_truncate (to);
489 s = gst_caps_get_structure (to, 0);
491 if (gst_structure_has_name (s, "video/x-raw-yuv")) {
492 GValue list = { 0, };
495 gst_structure_remove_field (s, "format");
497 g_value_init (&list, GST_TYPE_LIST);
498 g_value_init (&val, GST_TYPE_FOURCC);
499 gst_value_set_fourcc (&val, GST_STR_FOURCC ("AYUV"));
500 gst_value_list_append_value (&list, &val);
501 g_value_reset (&val);
502 gst_value_set_fourcc (&val, GST_STR_FOURCC ("I420"));
503 gst_value_list_append_value (&list, &val);
504 g_value_reset (&val);
505 gst_value_set_fourcc (&val, GST_STR_FOURCC ("YV12"));
506 gst_value_list_append_value (&list, &val);
507 g_value_unset (&val);
508 gst_structure_set_value (s, "format", &list);
509 g_value_unset (&list);
510 } else if (!gst_structure_has_name (s, "video/x-raw-rgb")) {
512 to = gst_caps_new_empty ();
519 gst_smpte_alpha_setcaps (GstBaseTransform * btrans, GstCaps * incaps,
522 GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (btrans);
526 smpte->process = NULL;
528 if (!gst_video_format_parse_caps (incaps, &smpte->in_format, &width, &height))
530 if (!gst_video_format_parse_caps (outcaps, &smpte->out_format, &width,
534 /* try to update the mask now, this will also adjust the width/height on
536 GST_OBJECT_LOCK (smpte);
538 gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->invert,
539 smpte->depth, width, height);
540 GST_OBJECT_UNLOCK (smpte);
544 switch (smpte->out_format) {
545 case GST_VIDEO_FORMAT_AYUV:
546 switch (smpte->in_format) {
547 case GST_VIDEO_FORMAT_AYUV:
548 smpte->process = gst_smpte_alpha_process_ayuv_ayuv;
550 case GST_VIDEO_FORMAT_I420:
551 smpte->process = gst_smpte_alpha_process_i420_ayuv;
557 case GST_VIDEO_FORMAT_ARGB:
558 switch (smpte->in_format) {
559 case GST_VIDEO_FORMAT_ARGB:
560 smpte->process = gst_smpte_alpha_process_argb_argb;
566 case GST_VIDEO_FORMAT_RGBA:
567 switch (smpte->in_format) {
568 case GST_VIDEO_FORMAT_RGBA:
569 smpte->process = gst_smpte_alpha_process_rgba_rgba;
575 case GST_VIDEO_FORMAT_ABGR:
576 switch (smpte->in_format) {
577 case GST_VIDEO_FORMAT_ABGR:
578 smpte->process = gst_smpte_alpha_process_abgr_abgr;
584 case GST_VIDEO_FORMAT_BGRA:
585 switch (smpte->in_format) {
586 case GST_VIDEO_FORMAT_BGRA:
587 smpte->process = gst_smpte_alpha_process_bgra_bgra;
602 GST_ERROR_OBJECT (smpte, "Invalid caps: %" GST_PTR_FORMAT, incaps);
607 GST_ERROR_OBJECT (smpte, "failed creating the mask");
613 gst_smpte_alpha_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
617 GstVideoFormat format;
619 if (!gst_video_format_parse_caps (caps, &format, &width, &height))
622 *size = gst_video_format_get_size (format, width, height);
628 gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte)
631 gst_mask_destroy (smpte->mask);
634 G_OBJECT_CLASS (parent_class)->finalize ((GObject *) smpte);
638 gst_smpte_alpha_set_property (GObject * object, guint prop_id,
639 const GValue * value, GParamSpec * pspec)
641 GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (object);
647 type = g_value_get_enum (value);
649 GST_BASE_TRANSFORM_LOCK (smpte);
650 GST_OBJECT_LOCK (smpte);
651 gst_smpte_alpha_update_mask (smpte, type, smpte->invert,
652 smpte->depth, smpte->width, smpte->height);
653 GST_OBJECT_UNLOCK (smpte);
654 GST_BASE_TRANSFORM_UNLOCK (smpte);
658 GST_OBJECT_LOCK (smpte);
659 smpte->border = g_value_get_int (value);
660 GST_OBJECT_UNLOCK (smpte);
665 depth = g_value_get_int (value);
667 GST_BASE_TRANSFORM_LOCK (smpte);
668 /* also lock with the object lock so that reading the property doesn't
669 * have to wait for the transform lock */
670 GST_OBJECT_LOCK (smpte);
671 gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->invert,
672 depth, smpte->width, smpte->height);
673 GST_OBJECT_UNLOCK (smpte);
674 GST_BASE_TRANSFORM_UNLOCK (smpte);
678 GST_OBJECT_LOCK (smpte);
679 smpte->position = g_value_get_double (value);
680 GST_OBJECT_UNLOCK (smpte);
685 invert = g_value_get_boolean (value);
686 GST_BASE_TRANSFORM_LOCK (smpte);
687 /* also lock with the object lock so that reading the property doesn't
688 * have to wait for the transform lock */
689 GST_OBJECT_LOCK (smpte);
690 gst_smpte_alpha_update_mask (smpte, smpte->type, invert,
691 smpte->depth, smpte->width, smpte->height);
692 GST_OBJECT_UNLOCK (smpte);
693 GST_BASE_TRANSFORM_UNLOCK (smpte);
697 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
703 gst_smpte_alpha_get_property (GObject * object, guint prop_id,
704 GValue * value, GParamSpec * pspec)
706 GstSMPTEAlpha *smpte;
708 smpte = GST_SMPTE_ALPHA (object);
712 GST_OBJECT_LOCK (smpte);
713 g_value_set_enum (value, smpte->type);
714 GST_OBJECT_UNLOCK (smpte);
717 GST_OBJECT_LOCK (smpte);
718 g_value_set_int (value, smpte->border);
719 GST_OBJECT_UNLOCK (smpte);
722 GST_OBJECT_LOCK (smpte);
723 g_value_set_int (value, smpte->depth);
724 GST_OBJECT_UNLOCK (smpte);
727 GST_OBJECT_LOCK (smpte);
728 g_value_set_double (value, smpte->position);
729 GST_OBJECT_UNLOCK (smpte);
732 GST_OBJECT_LOCK (smpte);
733 g_value_set_boolean (value, smpte->invert);
734 GST_OBJECT_UNLOCK (smpte);
737 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
743 gst_smpte_alpha_plugin_init (GstPlugin * plugin)
745 GST_DEBUG_CATEGORY_INIT (gst_smpte_alpha_debug, "smptealpha", 0,
746 "SMPTE alpha effect");
748 /* initialize gst controller library */
749 gst_controller_init (NULL, NULL);
751 return gst_element_register (plugin, "smptealpha", GST_RANK_NONE,
752 GST_TYPE_SMPTE_ALPHA);