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 /* elementfactory information */
65 static const GstElementDetails smpte_details =
66 GST_ELEMENT_DETAILS ("SMPTE transitions",
67 "Filter/Editor/Video",
68 "Apply the standard SMPTE transitions as alpha on video images",
69 "Wim Taymans <wim.taymans@gmail.com>");
71 static GstStaticPadTemplate gst_smpte_alpha_src_template =
72 GST_STATIC_PAD_TEMPLATE ("src",
75 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")
79 static GstStaticPadTemplate gst_smpte_alpha_sink_template =
80 GST_STATIC_PAD_TEMPLATE ("sink",
83 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420") ";" GST_VIDEO_CAPS_YUV ("AYUV")
87 /* SMPTE signals and args */
94 #define DEFAULT_PROP_TYPE 1
95 #define DEFAULT_PROP_BORDER 0
96 #define DEFAULT_PROP_DEPTH 16
97 #define DEFAULT_PROP_POSITION 0.0
109 #define AYUV_SIZE(w,h) ((w) * (h) * 4)
111 #define GST_TYPE_SMPTE_TRANSITION_TYPE (gst_smpte_alpha_transition_type_get_type())
113 gst_smpte_alpha_transition_type_get_type (void)
115 static GType smpte_transition_type = 0;
116 GEnumValue *smpte_transitions;
118 if (!smpte_transition_type) {
119 const GList *definitions;
122 definitions = gst_mask_get_definitions ();
124 g_new0 (GEnumValue, g_list_length ((GList *) definitions) + 1);
126 while (definitions) {
127 GstMaskDefinition *definition = (GstMaskDefinition *) definitions->data;
129 definitions = g_list_next (definitions);
131 smpte_transitions[i].value = definition->type;
132 /* older GLib versions have the two fields as non-const, hence the cast */
133 smpte_transitions[i].value_nick = (gchar *) definition->short_name;
134 smpte_transitions[i].value_name = (gchar *) definition->long_name;
139 smpte_transition_type =
140 g_enum_register_static ("GstSMPTEAlphaTransitionType",
143 return smpte_transition_type;
147 static void gst_smpte_alpha_class_init (GstSMPTEAlphaClass * klass);
148 static void gst_smpte_alpha_base_init (GstSMPTEAlphaClass * klass);
149 static void gst_smpte_alpha_init (GstSMPTEAlpha * smpte);
150 static void gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte);
152 static void gst_smpte_alpha_set_property (GObject * object, guint prop_id,
153 const GValue * value, GParamSpec * pspec);
154 static void gst_smpte_alpha_get_property (GObject * object, guint prop_id,
155 GValue * value, GParamSpec * pspec);
157 static gboolean gst_smpte_alpha_setcaps (GstBaseTransform * btrans,
158 GstCaps * incaps, GstCaps * outcaps);
159 static gboolean gst_smpte_alpha_get_unit_size (GstBaseTransform * btrans,
160 GstCaps * caps, guint * size);
161 static GstFlowReturn gst_smpte_alpha_transform (GstBaseTransform * trans,
162 GstBuffer * in, GstBuffer * out);
164 static GstElementClass *parent_class = NULL;
166 /*static guint gst_smpte_alpha_signals[LAST_SIGNAL] = { 0 }; */
169 gst_smpte_alpha_get_type (void)
171 static GType smpte_type = 0;
174 static const GTypeInfo smpte_info = {
175 sizeof (GstSMPTEAlphaClass),
176 (GBaseInitFunc) gst_smpte_alpha_base_init,
178 (GClassInitFunc) gst_smpte_alpha_class_init,
181 sizeof (GstSMPTEAlpha),
183 (GInstanceInitFunc) gst_smpte_alpha_init,
187 g_type_register_static (GST_TYPE_VIDEO_FILTER, "GstSMPTEAlpha",
194 gst_smpte_alpha_base_init (GstSMPTEAlphaClass * klass)
196 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
198 gst_element_class_add_pad_template (element_class,
199 gst_static_pad_template_get (&gst_smpte_alpha_sink_template));
200 gst_element_class_add_pad_template (element_class,
201 gst_static_pad_template_get (&gst_smpte_alpha_src_template));
202 gst_element_class_set_details (element_class, &smpte_details);
206 gst_smpte_alpha_class_init (GstSMPTEAlphaClass * klass)
208 GObjectClass *gobject_class;
209 GstBaseTransformClass *trans_class;
211 gobject_class = (GObjectClass *) klass;
212 trans_class = (GstBaseTransformClass *) klass;
214 parent_class = g_type_class_peek_parent (klass);
216 gobject_class->set_property = gst_smpte_alpha_set_property;
217 gobject_class->get_property = gst_smpte_alpha_get_property;
219 gobject_class->finalize = (GObjectFinalizeFunc) gst_smpte_alpha_finalize;
223 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TYPE,
224 g_param_spec_enum ("type", "Type", "The type of transition to use",
225 GST_TYPE_SMPTE_TRANSITION_TYPE, DEFAULT_PROP_TYPE,
226 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
227 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER,
228 g_param_spec_int ("border", "Border",
229 "The border width of the transition", 0, G_MAXINT,
230 DEFAULT_PROP_BORDER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
231 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEPTH,
232 g_param_spec_int ("depth", "Depth", "Depth of the mask in bits", 1, 24,
233 DEFAULT_PROP_DEPTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
234 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_POSITION,
235 g_param_spec_double ("position", "Position",
236 "Position of the transition effect", 0.0, 1.0, DEFAULT_PROP_POSITION,
237 GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
239 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_smpte_alpha_setcaps);
240 trans_class->get_unit_size =
241 GST_DEBUG_FUNCPTR (gst_smpte_alpha_get_unit_size);
242 trans_class->transform = GST_DEBUG_FUNCPTR (gst_smpte_alpha_transform);
246 gst_smpte_alpha_update_mask (GstSMPTEAlpha * smpte, gint type, gint depth,
247 gint width, gint height)
251 /* try to avoid regenerating the mask if we already have one that is
254 if (smpte->type == type &&
255 smpte->depth == depth &&
256 smpte->width == width && smpte->height == height)
260 newmask = gst_mask_factory_new (type, depth, width, height);
265 gst_mask_destroy (smpte->mask);
267 smpte->mask = newmask;
269 smpte->depth = depth;
270 smpte->width = width;
271 smpte->height = height;
278 GST_ERROR_OBJECT (smpte, "failed to create a mask");
284 gst_smpte_alpha_setcaps (GstBaseTransform * btrans, GstCaps * incaps,
287 GstSMPTEAlpha *smpte;
288 GstStructure *structure;
293 smpte = GST_SMPTE_ALPHA (btrans);
295 structure = gst_caps_get_structure (incaps, 0);
297 /* see if we can get essential info */
298 ret = gst_structure_get_int (structure, "width", &width);
299 ret &= gst_structure_get_int (structure, "height", &height);
300 ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
304 /* try to update the mask now, this will also adjust the width/height on
306 GST_OBJECT_LOCK (smpte);
307 ret = gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->depth,
309 GST_OBJECT_UNLOCK (smpte);
314 case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
315 smpte->format = GST_VIDEO_FORMAT_AYUV;
317 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
318 smpte->format = GST_VIDEO_FORMAT_I420;
321 goto unsupported_fourcc;
329 GST_ERROR_OBJECT (smpte, "no width, height and fourcc given");
334 GST_ERROR_OBJECT (smpte, "failed creating the mask");
339 GST_ERROR_OBJECT (smpte, "unsupported fourcc %" GST_FOURCC_FORMAT,
340 GST_FOURCC_ARGS (fourcc));
346 gst_smpte_alpha_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
349 GstSMPTEAlpha *smpte;
350 GstStructure *structure;
355 smpte = GST_SMPTE_ALPHA (btrans);
357 structure = gst_caps_get_structure (caps, 0);
359 ret = gst_structure_get_int (structure, "width", &width);
360 ret &= gst_structure_get_int (structure, "height", &height);
361 ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
364 case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
366 gst_video_format_get_size (GST_VIDEO_FORMAT_AYUV, width, height);
368 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
370 gst_video_format_get_size (GST_VIDEO_FORMAT_I420, width, height);
381 gst_smpte_alpha_init (GstSMPTEAlpha * smpte)
383 smpte->type = DEFAULT_PROP_TYPE;
384 smpte->border = DEFAULT_PROP_BORDER;
385 smpte->depth = DEFAULT_PROP_DEPTH;
389 gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte)
392 gst_mask_destroy (smpte->mask);
394 G_OBJECT_CLASS (parent_class)->finalize ((GObject *) smpte);
398 gst_smpte_alpha_do_ayuv (GstSMPTEAlpha * smpte, guint8 * in, guint8 * out,
399 GstMask * mask, gint width, gint height, gint border, gint pos)
411 GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
416 /* we basically copy the source to dest but we scale the alpha channel with
418 for (i = 0; i < height; i++) {
419 for (j = 0; j < width; j++) {
421 *out++ = (*in++ * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
430 gst_smpte_alpha_do_i420 (GstSMPTEAlpha * smpte, guint8 * in, guint8 * out,
431 GstMask * mask, gint width, gint height, gint border, gint pos)
437 gint src_wrap, src_uv_wrap;
438 gint y_stride, uv_stride;
449 GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
454 y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, width);
455 uv_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, width);
457 src_wrap = y_stride - width;
458 src_uv_wrap = uv_stride - (width / 2);
461 srcU = in + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420,
463 srcV = in + gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420,
466 odd_width = (width % 2 != 0);
468 for (i = 0; i < height; i++) {
469 for (j = 0; j < width / 2; j++) {
471 *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
476 *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
481 /* Might have one odd column left to do */
484 *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
501 gst_smpte_alpha_transform (GstBaseTransform * trans, GstBuffer * in,
504 GstSMPTEAlpha *smpte;
505 GstClockTime timestamp, stream_time;
509 smpte = GST_SMPTE_ALPHA (trans);
511 /* first sync the controller to the current stream_time of the buffer */
512 timestamp = GST_BUFFER_TIMESTAMP (in);
514 gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);
516 GST_DEBUG_OBJECT (smpte, "sync to %" GST_TIME_FORMAT,
517 GST_TIME_ARGS (timestamp));
519 if (GST_CLOCK_TIME_IS_VALID (stream_time))
520 gst_object_sync_values (G_OBJECT (smpte), stream_time);
522 /* these are the propertis we update with only the object lock, others are
523 * only updated with the TRANSFORM_LOCK. */
524 GST_OBJECT_LOCK (smpte);
525 position = smpte->position;
526 border = smpte->border;
527 GST_OBJECT_UNLOCK (smpte);
529 /* run the type specific filter code */
530 switch (smpte->format) {
531 case GST_VIDEO_FORMAT_I420:
532 gst_smpte_alpha_do_i420 (smpte, GST_BUFFER_DATA (in),
533 GST_BUFFER_DATA (out),
534 smpte->mask, smpte->width, smpte->height,
535 border, ((1 << smpte->depth) + border) * position);
537 case GST_VIDEO_FORMAT_AYUV:
538 gst_smpte_alpha_do_ayuv (smpte, GST_BUFFER_DATA (in),
539 GST_BUFFER_DATA (out),
540 smpte->mask, smpte->width, smpte->height,
541 border, ((1 << smpte->depth) + border) * position);
552 GST_ELEMENT_ERROR (smpte, CORE, NEGOTIATION, (NULL),
553 ("No input format negotiated"));
554 return GST_FLOW_NOT_NEGOTIATED;
559 gst_smpte_alpha_set_property (GObject * object, guint prop_id,
560 const GValue * value, GParamSpec * pspec)
562 GstSMPTEAlpha *smpte;
564 smpte = GST_SMPTE_ALPHA (object);
568 GST_BASE_TRANSFORM_LOCK (smpte);
569 /* also lock with the object lock so that reading the property doesn't
570 * have to wait for the transform lock */
571 GST_OBJECT_LOCK (smpte);
572 smpte->type = g_value_get_enum (value);
573 GST_OBJECT_UNLOCK (smpte);
574 gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->depth,
575 smpte->width, smpte->height);
576 GST_BASE_TRANSFORM_UNLOCK (smpte);
579 GST_OBJECT_LOCK (smpte);
580 smpte->border = g_value_get_int (value);
581 GST_OBJECT_UNLOCK (smpte);
584 GST_BASE_TRANSFORM_LOCK (smpte);
585 /* also lock with the object lock so that reading the property doesn't
586 * have to wait for the transform lock */
587 GST_OBJECT_LOCK (smpte);
588 smpte->depth = g_value_get_int (value);
589 GST_OBJECT_UNLOCK (smpte);
590 gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->depth,
591 smpte->width, smpte->height);
592 GST_BASE_TRANSFORM_UNLOCK (smpte);
595 GST_OBJECT_LOCK (smpte);
596 smpte->position = g_value_get_double (value);
597 GST_OBJECT_UNLOCK (smpte);
600 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
606 gst_smpte_alpha_get_property (GObject * object, guint prop_id,
607 GValue * value, GParamSpec * pspec)
609 GstSMPTEAlpha *smpte;
611 smpte = GST_SMPTE_ALPHA (object);
615 GST_OBJECT_LOCK (smpte);
616 g_value_set_enum (value, smpte->type);
617 GST_OBJECT_UNLOCK (smpte);
620 GST_OBJECT_LOCK (smpte);
621 g_value_set_int (value, smpte->border);
622 GST_OBJECT_UNLOCK (smpte);
625 GST_OBJECT_LOCK (smpte);
626 g_value_set_int (value, smpte->depth);
627 GST_OBJECT_UNLOCK (smpte);
630 GST_OBJECT_LOCK (smpte);
631 g_value_set_double (value, smpte->position);
632 GST_OBJECT_UNLOCK (smpte);
635 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
641 gst_smpte_alpha_plugin_init (GstPlugin * plugin)
643 GST_DEBUG_CATEGORY_INIT (gst_smpte_alpha_debug, "smptealpha", 0,
644 "SMPTE alpha effect");
646 /* initialize gst controller library */
647 gst_controller_init (NULL, NULL);
649 return gst_element_register (plugin, "smptealpha", GST_RANK_NONE,
650 GST_TYPE_SMPTE_ALPHA);