gstsmptealpha.c 23.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* GStreamer
 * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
16 17
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
18 19 20 21 22 23
 */

/**
 * SECTION:element-smptealpha
 *
 * smptealpha can accept an I420 or AYUV video stream. An alpha channel is added
24 25 26 27 28 29 30 31 32 33 34 35 36
 * using an effect specific SMPTE mask in the I420 input case. In the AYUV case,
 * the alpha channel is modified using the effect specific SMPTE mask.
 *
 * The #GstSmpteAlpha:position property is a controllabe double between 0.0 and
 * 1.0 that specifies the position in the transition. 0.0 is the start of the
 * transition with the alpha channel to complete opaque where 1.0 has the alpha
 * channel set to completely transparent.
 *
 * The #GstSmpteAlpha:depth property defines the precision in bits of the mask.
 * A higher presision will create a mask with smoother gradients in order to
 * avoid banding.
 *
 * <refsect2>
37 38 39 40
 * <title>Sample pipelines</title>
 * <para>
 * Here is a pipeline to demonstrate the smpte transition :
 * <programlisting>
41 42
 * gst-launch-1.0 -v videotestsrc ! smptealpha border=20000 type=44
 * position=0.5 ! videomixer ! videoconvert ! ximagesink
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
 * </programlisting>
 * This shows a midway bowtie-h transition a from a videotestsrc to a
 * transparent image. The edges of the transition are smoothed with a
 * 20000 big border.
 * </para>
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>

#include "gstsmptealpha.h"
#include "paint.h"

GST_DEBUG_CATEGORY_STATIC (gst_smpte_alpha_debug);
#define GST_CAT_DEFAULT gst_smpte_alpha_debug

static GstStaticPadTemplate gst_smpte_alpha_src_template =
63
    GST_STATIC_PAD_TEMPLATE ("src",
64 65
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
66 67 68
    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("AYUV") ";"
        GST_VIDEO_CAPS_MAKE ("ARGB") ";" GST_VIDEO_CAPS_MAKE ("BGRA") ";"
        GST_VIDEO_CAPS_MAKE ("RGBA") ";" GST_VIDEO_CAPS_MAKE ("ARGB"))
69 70 71 72 73 74
    );

static GstStaticPadTemplate gst_smpte_alpha_sink_template =
    GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
75 76 77 78 79
    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("I420") ";"
        GST_VIDEO_CAPS_MAKE ("YV12")
        ";" GST_VIDEO_CAPS_MAKE ("AYUV")
        ";" GST_VIDEO_CAPS_MAKE ("ARGB") ";" GST_VIDEO_CAPS_MAKE ("BGRA")
        ";" GST_VIDEO_CAPS_MAKE ("RGBA") ";" GST_VIDEO_CAPS_MAKE ("ARGB"))
80 81
    );

82
/* SMPTE signals and properties */
83 84 85 86 87

#define DEFAULT_PROP_TYPE	1
#define DEFAULT_PROP_BORDER	0
#define DEFAULT_PROP_DEPTH	16
#define DEFAULT_PROP_POSITION	0.0
88
#define DEFAULT_PROP_INVERT   FALSE
89 90 91 92 93 94 95 96

enum
{
  PROP_0,
  PROP_TYPE,
  PROP_BORDER,
  PROP_DEPTH,
  PROP_POSITION,
97
  PROP_INVERT
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
};

#define AYUV_SIZE(w,h)     ((w) * (h) * 4)

#define GST_TYPE_SMPTE_TRANSITION_TYPE (gst_smpte_alpha_transition_type_get_type())
static GType
gst_smpte_alpha_transition_type_get_type (void)
{
  static GType smpte_transition_type = 0;
  GEnumValue *smpte_transitions;

  if (!smpte_transition_type) {
    const GList *definitions;
    gint i = 0;

    definitions = gst_mask_get_definitions ();
    smpte_transitions =
        g_new0 (GEnumValue, g_list_length ((GList *) definitions) + 1);

    while (definitions) {
      GstMaskDefinition *definition = (GstMaskDefinition *) definitions->data;

      definitions = g_list_next (definitions);

      smpte_transitions[i].value = definition->type;
      /* older GLib versions have the two fields as non-const, hence the cast */
      smpte_transitions[i].value_nick = (gchar *) definition->short_name;
      smpte_transitions[i].value_name = (gchar *) definition->long_name;

      i++;
    }

    smpte_transition_type =
        g_enum_register_static ("GstSMPTEAlphaTransitionType",
        smpte_transitions);
  }
  return smpte_transition_type;
}


static void gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte);

static void gst_smpte_alpha_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_smpte_alpha_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
145 146 147 148 149
static gboolean gst_smpte_alpha_set_info (GstVideoFilter * vfilter,
    GstCaps * incaps, GstVideoInfo * in_info,
    GstCaps * outcaps, GstVideoInfo * out_info);
static GstFlowReturn gst_smpte_alpha_transform_frame (GstVideoFilter * vfilter,
    GstVideoFrame * in_frame, GstVideoFrame * out_frame);
150 151
static void gst_smpte_alpha_before_transform (GstBaseTransform * trans,
    GstBuffer * buf);
152
static GstCaps *gst_smpte_alpha_transform_caps (GstBaseTransform * trans,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
153
    GstPadDirection direction, GstCaps * from, GstCaps * filter);
154

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
155 156
#define gst_smpte_alpha_parent_class parent_class
G_DEFINE_TYPE (GstSMPTEAlpha, gst_smpte_alpha, GST_TYPE_VIDEO_FILTER);
157 158 159 160

static void
gst_smpte_alpha_class_init (GstSMPTEAlphaClass * klass)
{
161
  GObjectClass *gobject_class = (GObjectClass *) klass;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
162
  GstElementClass *element_class = (GstElementClass *) (klass);
163
  GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
164
  GstVideoFilterClass *vfilter_class = (GstVideoFilterClass *) klass;
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179

  gobject_class->set_property = gst_smpte_alpha_set_property;
  gobject_class->get_property = gst_smpte_alpha_get_property;

  gobject_class->finalize = (GObjectFinalizeFunc) gst_smpte_alpha_finalize;

  _gst_mask_init ();

  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TYPE,
      g_param_spec_enum ("type", "Type", "The type of transition to use",
          GST_TYPE_SMPTE_TRANSITION_TYPE, DEFAULT_PROP_TYPE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER,
      g_param_spec_int ("border", "Border",
          "The border width of the transition", 0, G_MAXINT,
180 181
          DEFAULT_PROP_BORDER,
          GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
182 183 184 185 186 187 188
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEPTH,
      g_param_spec_int ("depth", "Depth", "Depth of the mask in bits", 1, 24,
          DEFAULT_PROP_DEPTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_POSITION,
      g_param_spec_double ("position", "Position",
          "Position of the transition effect", 0.0, 1.0, DEFAULT_PROP_POSITION,
          GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
189 190 191 192 193
  /**
   * GstSMPTEAlpha:invert:
   *
   * Set to TRUE to invert the transition mask (ie. flip it horizontally).
   */
194 195 196 197
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INVERT,
      g_param_spec_boolean ("invert", "Invert",
          "Invert transition mask", DEFAULT_PROP_POSITION,
          GST_PARAM_CONTROLLABLE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
198

199 200
  trans_class->before_transform =
      GST_DEBUG_FUNCPTR (gst_smpte_alpha_before_transform);
201 202
  trans_class->transform_caps =
      GST_DEBUG_FUNCPTR (gst_smpte_alpha_transform_caps);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
203 204 205 206 207

  vfilter_class->set_info = GST_DEBUG_FUNCPTR (gst_smpte_alpha_set_info);
  vfilter_class->transform_frame =
      GST_DEBUG_FUNCPTR (gst_smpte_alpha_transform_frame);

208 209 210 211
  gst_element_class_add_static_pad_template (element_class,
      &gst_smpte_alpha_sink_template);
  gst_element_class_add_static_pad_template (element_class,
      &gst_smpte_alpha_src_template);
212
  gst_element_class_set_static_metadata (element_class, "SMPTE transitions",
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
213 214 215
      "Filter/Editor/Video",
      "Apply the standard SMPTE transitions as alpha on video images",
      "Wim Taymans <wim.taymans@gmail.com>");
216 217 218
}

static gboolean
219 220
gst_smpte_alpha_update_mask (GstSMPTEAlpha * smpte, gint type,
    gboolean invert, gint depth, gint width, gint height)
221 222 223 224 225 226 227
{
  GstMask *newmask;

  /* try to avoid regenerating the mask if we already have one that is
   * correct */
  if (smpte->mask) {
    if (smpte->type == type &&
228
        smpte->invert == invert &&
229 230 231 232 233
        smpte->depth == depth &&
        smpte->width == width && smpte->height == height)
      return TRUE;
  }

234
  smpte->type = type;
235
  smpte->invert = invert;
236 237 238 239 240 241 242 243 244
  smpte->depth = depth;
  smpte->width = width;
  smpte->height = height;

  /* Not negotiated yet */
  if (width == 0 || height == 0) {
    return TRUE;
  }

245
  newmask = gst_mask_factory_new (type, invert, depth, width, height);
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
  if (!newmask)
    goto mask_failed;

  if (smpte->mask)
    gst_mask_destroy (smpte->mask);

  smpte->mask = newmask;

  return TRUE;

  /* ERRORS */
mask_failed:
  {
    GST_ERROR_OBJECT (smpte, "failed to create a mask");
    return FALSE;
  }
}

static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
265
gst_smpte_alpha_init (GstSMPTEAlpha * smpte)
266 267 268 269
{
  smpte->type = DEFAULT_PROP_TYPE;
  smpte->border = DEFAULT_PROP_BORDER;
  smpte->depth = DEFAULT_PROP_DEPTH;
270
  smpte->position = DEFAULT_PROP_POSITION;
271
  smpte->invert = DEFAULT_PROP_INVERT;
272 273
}

274 275
#define CREATE_ARGB_FUNC(name, A, R, G, B) \
static void \
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
276 277 278
gst_smpte_alpha_process_##name##_##name (GstSMPTEAlpha * smpte, \
    const GstVideoFrame * in_frame, GstVideoFrame * out_frame, GstMask * mask, \
    gint border, gint pos) \
279 280 281 282 283
{ \
  gint i, j; \
  const guint32 *maskp; \
  gint value; \
  gint min, max; \
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
284 285 286
  gint width, height; \
  guint8 *in, *out; \
  gint src_wrap, dest_wrap; \
287 288 289 290 291 292 293 294 295 296 297
  \
  if (border == 0) \
    border++; \
  \
  min = pos - border; \
  max = pos; \
  GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max, \
      border); \
  \
  maskp = mask->data; \
  \
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
298 299 300 301 302 303 304 305
  width = GST_VIDEO_FRAME_WIDTH (out_frame); \
  height = GST_VIDEO_FRAME_HEIGHT (out_frame); \
  \
  in = GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0); \
  out = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0); \
  src_wrap = GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 0) - (width << 2); \
  dest_wrap = GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 0) - (width << 2); \
  \
306 307 308 309 310 311 312 313 314 315 316 317
  /* we basically copy the source to dest but we scale the alpha channel with \
   * the mask */ \
  for (i = 0; i < height; i++) { \
    for (j = 0; j < width; j++) { \
      value = *maskp++; \
      out[A] = (in[A] * ((CLAMP (value, min, max) - min) << 8) / border) >> 8; \
      out[R] = in[R]; \
      out[G] = in[G]; \
      out[B] = in[B]; \
      out += 4; \
      in += 4; \
    } \
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
318 319
    in += src_wrap; \
    out += dest_wrap; \
320
  } \
321 322
}

323 324 325 326 327
CREATE_ARGB_FUNC (argb, 0, 1, 2, 3);
CREATE_ARGB_FUNC (bgra, 3, 2, 1, 0);
CREATE_ARGB_FUNC (abgr, 0, 3, 2, 1);
CREATE_ARGB_FUNC (rgba, 3, 0, 1, 2);

328
static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
329 330 331
gst_smpte_alpha_process_ayuv_ayuv (GstSMPTEAlpha * smpte,
    const GstVideoFrame * in_frame, GstVideoFrame * out_frame, GstMask * mask,
    gint border, gint pos)
332 333
{
  gint i, j;
334
  const guint32 *maskp;
335 336
  gint value;
  gint min, max;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
337 338 339
  gint width, height;
  guint8 *in, *out;
  gint src_wrap, dest_wrap;
340 341 342 343 344 345 346 347 348 349 350

  if (border == 0)
    border++;

  min = pos - border;
  max = pos;
  GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
      border);

  maskp = mask->data;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
351 352 353 354 355 356 357 358
  width = GST_VIDEO_FRAME_WIDTH (out_frame);
  height = GST_VIDEO_FRAME_HEIGHT (out_frame);

  in = GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0);
  out = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0);
  src_wrap = GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 0) - (width << 2);
  dest_wrap = GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 0) - (width << 2);

359 360 361 362 363 364 365 366 367 368
  /* we basically copy the source to dest but we scale the alpha channel with
   * the mask */
  for (i = 0; i < height; i++) {
    for (j = 0; j < width; j++) {
      value = *maskp++;
      *out++ = (*in++ * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
      *out++ = *in++;
      *out++ = *in++;
      *out++ = *in++;
    }
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
369 370
    in += src_wrap;
    out += dest_wrap;
371 372
  }
}
373

374
static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
375 376 377
gst_smpte_alpha_process_i420_ayuv (GstSMPTEAlpha * smpte,
    const GstVideoFrame * in_frame, GstVideoFrame * out_frame, GstMask * mask,
    gint border, gint pos)
378
{
379 380 381
  const guint8 *srcY;
  const guint8 *srcU;
  const guint8 *srcV;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
382
  guint8 *out;
383
  gint i, j;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
384 385
  gint src_wrap, src_u_wrap, src_v_wrap, dest_wrap;
  gint y_stride, u_stride, v_stride;
386
  gboolean odd_width;
387
  const guint32 *maskp;
388 389
  gint value;
  gint min, max;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
390
  gint width, height;
391 392 393 394 395 396 397 398 399 400 401

  if (border == 0)
    border++;

  min = pos - border;
  max = pos;
  GST_DEBUG_OBJECT (smpte, "pos %d, min %d, max %d, border %d", pos, min, max,
      border);

  maskp = mask->data;

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
402 403 404 405 406 407
  width = GST_VIDEO_FRAME_WIDTH (out_frame);
  height = GST_VIDEO_FRAME_HEIGHT (out_frame);

  y_stride = GST_VIDEO_FRAME_COMP_STRIDE (in_frame, 0);
  u_stride = GST_VIDEO_FRAME_COMP_STRIDE (in_frame, 1);
  v_stride = GST_VIDEO_FRAME_COMP_STRIDE (in_frame, 2);
408 409

  src_wrap = y_stride - width;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
410 411
  src_u_wrap = u_stride - (width / 2);
  src_v_wrap = v_stride - (width / 2);
412

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
413 414 415 416 417 418
  srcY = GST_VIDEO_FRAME_COMP_DATA (in_frame, 0);
  srcU = GST_VIDEO_FRAME_COMP_DATA (in_frame, 1);
  srcV = GST_VIDEO_FRAME_COMP_DATA (in_frame, 2);

  out = GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0);
  dest_wrap = GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 0) - (width << 2);
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446

  odd_width = (width % 2 != 0);

  for (i = 0; i < height; i++) {
    for (j = 0; j < width / 2; j++) {
      value = *maskp++;
      *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
      *out++ = *srcY++;
      *out++ = *srcU;
      *out++ = *srcV;
      value = *maskp++;
      *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
      *out++ = *srcY++;
      *out++ = *srcU++;
      *out++ = *srcV++;
    }
    /* Might have one odd column left to do */
    if (odd_width) {
      value = *maskp++;
      *out++ = (0xff * ((CLAMP (value, min, max) - min) << 8) / border) >> 8;
      *out++ = *srcY++;
      *out++ = *srcU;
      *out++ = *srcV;
    }
    if (i % 2 == 0) {
      srcU -= width / 2;
      srcV -= width / 2;
    } else {
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
447 448
      srcU += src_u_wrap;
      srcV += src_v_wrap;
449 450
    }
    srcY += src_wrap;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
451
    out += dest_wrap;
452 453 454
  }
}

455 456
static void
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 464 465 466 467 468 469
  stream_time =
      gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, timestamp);

  GST_DEBUG_OBJECT (smpte, "sync to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (timestamp));

  if (GST_CLOCK_TIME_IS_VALID (stream_time))
470
    gst_object_sync_values (GST_OBJECT (smpte), stream_time);
471 472 473
}

static GstFlowReturn
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
474 475
gst_smpte_alpha_transform_frame (GstVideoFilter * vfilter,
    GstVideoFrame * in_frame, GstVideoFrame * out_frame)
476
{
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
477
  GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (vfilter);
478 479 480 481 482
  gdouble position;
  gint border;

  if (G_UNLIKELY (!smpte->process))
    goto not_negotiated;
483 484 485 486 487 488

  GST_OBJECT_LOCK (smpte);
  position = smpte->position;
  border = smpte->border;

  /* run the type specific filter code */
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
489 490 491
  smpte->process (smpte, in_frame, out_frame,
      smpte->mask, border, ((1 << smpte->depth) + border) * position);
  GST_OBJECT_UNLOCK (smpte);
492 493 494 495 496 497 498 499 500 501 502 503

  return GST_FLOW_OK;

  /* ERRORS */
not_negotiated:
  {
    GST_ELEMENT_ERROR (smpte, CORE, NEGOTIATION, (NULL),
        ("No input format negotiated"));
    return GST_FLOW_NOT_NEGOTIATED;
  }
}

504 505
static GstCaps *
gst_smpte_alpha_transform_caps (GstBaseTransform * trans,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
506
    GstPadDirection direction, GstCaps * from, GstCaps * filter)
507
{
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
508 509
  GstCaps *result, *tmp_caps, *tmpl_caps = NULL;
  gint i, j;
510

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
511
  tmp_caps = gst_caps_new_empty ();
512

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
513 514 515
  for (i = 0; i < gst_caps_get_size (from); i++) {
    GstStructure *structure;
    const GValue *val, *lval;
516
    GValue list = { 0, };
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
    GValue aval = { 0, };
    const gchar *str;

    structure = gst_structure_copy (gst_caps_get_structure (from, i));
    /* we can transform I420 to AYUV,
     * so need to locate and substitute AYUV for the both of them */
    val = gst_structure_get_value (structure, "format");
    if (val && GST_VALUE_HOLDS_LIST (val)) {
      gboolean seen_ayuv = FALSE, seen_i420 = FALSE;

      g_value_init (&list, GST_TYPE_LIST);
      for (j = 0; j < gst_value_list_get_size (val); j++) {
        lval = gst_value_list_get_value (val, j);
        if ((str = g_value_get_string (lval))) {
          if (strcmp (str, "AYUV") == 0) {
            seen_ayuv = TRUE;
          } else if (strcmp (str, "I420") == 0) {
            seen_i420 = TRUE;
          }
        }
      }
      if (seen_ayuv && !seen_i420) {
        str = "I420";
      } else if (seen_i420 && !seen_ayuv) {
        str = "AYUV";
      } else
        str = NULL;
      if (str) {
        g_value_copy (val, &list);
        g_value_init (&aval, G_TYPE_STRING);
        g_value_set_string (&aval, str);
        gst_value_list_append_value (&list, &aval);
        g_value_reset (&aval);
        gst_structure_set_value (structure, "format", &list);
        g_value_unset (&list);
      }
    } else if (val && G_VALUE_HOLDS_STRING (val)) {
      if ((str = g_value_get_string (val)) &&
          ((strcmp (str, "AYUV") == 0) || (strcmp (str, "I420") == 0))) {
        g_value_init (&list, GST_TYPE_LIST);
        g_value_init (&aval, G_TYPE_STRING);
        g_value_set_string (&aval, "AYUV");
        gst_value_list_append_value (&list, &aval);
        g_value_reset (&aval);
        g_value_set_string (&aval, "I420");
        gst_value_list_append_value (&list, &aval);
        g_value_reset (&aval);
        gst_structure_set_value (structure, "format", &list);
        g_value_unset (&list);
      }
    } else {
      gst_structure_remove_field (structure, "format");
    }

571
    gst_structure_remove_field (structure, "colorimetry");
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
572 573 574 575 576 577 578 579 580 581 582 583 584 585
    gst_structure_remove_field (structure, "chroma-site");

    gst_caps_append_structure (tmp_caps, structure);
  }

  /* Get the appropriate template */
  if (direction == GST_PAD_SINK) {
    tmpl_caps =
        gst_static_pad_template_get_caps (&gst_smpte_alpha_src_template);
  } else if (direction == GST_PAD_SRC) {
    tmpl_caps =
        gst_static_pad_template_get_caps (&gst_smpte_alpha_sink_template);
  } else {
    g_assert_not_reached ();
586 587
  }

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
  /* Intersect with our template caps */
  result = gst_caps_intersect (tmp_caps, tmpl_caps);
  gst_caps_unref (tmpl_caps);
  gst_caps_unref (tmp_caps);

  result = gst_caps_simplify (result);

  GST_LOG_OBJECT (trans, "transformed %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT,
      from, result);

  if (filter) {
    GstCaps *intersection;

    GST_DEBUG_OBJECT (trans, "Using filter caps %" GST_PTR_FORMAT, filter);
    intersection =
        gst_caps_intersect_full (filter, result, GST_CAPS_INTERSECT_FIRST);
    gst_caps_unref (result);
    result = intersection;
    GST_DEBUG_OBJECT (trans, "Intersection %" GST_PTR_FORMAT, result);
  }

  return result;
610 611 612
}

static gboolean
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
613 614
gst_smpte_alpha_set_info (GstVideoFilter * vfilter, GstCaps * incaps,
    GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
615
{
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
616
  GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (vfilter);
617 618 619
  gboolean ret;

  smpte->process = NULL;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
620 621
  smpte->in_format = GST_VIDEO_INFO_FORMAT (in_info);
  smpte->out_format = GST_VIDEO_INFO_FORMAT (out_info);
622 623 624 625 626 627

  /* try to update the mask now, this will also adjust the width/height on
   * success */
  GST_OBJECT_LOCK (smpte);
  ret =
      gst_smpte_alpha_update_mask (smpte, smpte->type, smpte->invert,
628 629
      smpte->depth, GST_VIDEO_INFO_WIDTH (out_info),
      GST_VIDEO_INFO_HEIGHT (out_info));
630
  GST_OBJECT_UNLOCK (smpte);
631

632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
  if (!ret)
    goto mask_failed;

  switch (smpte->out_format) {
    case GST_VIDEO_FORMAT_AYUV:
      switch (smpte->in_format) {
        case GST_VIDEO_FORMAT_AYUV:
          smpte->process = gst_smpte_alpha_process_ayuv_ayuv;
          break;
        case GST_VIDEO_FORMAT_I420:
          smpte->process = gst_smpte_alpha_process_i420_ayuv;
          break;
        default:
          break;
      }
      break;
    case GST_VIDEO_FORMAT_ARGB:
      switch (smpte->in_format) {
        case GST_VIDEO_FORMAT_ARGB:
          smpte->process = gst_smpte_alpha_process_argb_argb;
          break;
        default:
          break;
      }
      break;
    case GST_VIDEO_FORMAT_RGBA:
      switch (smpte->in_format) {
        case GST_VIDEO_FORMAT_RGBA:
          smpte->process = gst_smpte_alpha_process_rgba_rgba;
          break;
        default:
          break;
      }
      break;
    case GST_VIDEO_FORMAT_ABGR:
      switch (smpte->in_format) {
        case GST_VIDEO_FORMAT_ABGR:
          smpte->process = gst_smpte_alpha_process_abgr_abgr;
          break;
        default:
          break;
      }
      break;
    case GST_VIDEO_FORMAT_BGRA:
      switch (smpte->in_format) {
        case GST_VIDEO_FORMAT_BGRA:
          smpte->process = gst_smpte_alpha_process_bgra_bgra;
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }

  return ret;

  /* ERRORS */
mask_failed:
  {
    GST_ERROR_OBJECT (smpte, "failed creating the mask");
    return FALSE;
  }
}

static void
gst_smpte_alpha_finalize (GstSMPTEAlpha * smpte)
{
  if (smpte->mask)
    gst_mask_destroy (smpte->mask);
  smpte->mask = NULL;

  G_OBJECT_CLASS (parent_class)->finalize ((GObject *) smpte);
}

708 709 710 711
static void
gst_smpte_alpha_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
712
  GstSMPTEAlpha *smpte = GST_SMPTE_ALPHA (object);
713 714

  switch (prop_id) {
715 716 717 718 719
    case PROP_TYPE:{
      gint type;

      type = g_value_get_enum (value);

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 727 728 729 730
    case PROP_BORDER:
      GST_OBJECT_LOCK (smpte);
      smpte->border = g_value_get_int (value);
      GST_OBJECT_UNLOCK (smpte);
      break;
731 732 733 734 735
    case PROP_DEPTH:{
      gint depth;

      depth = g_value_get_int (value);

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 743 744 745 746
    case PROP_POSITION:
      GST_OBJECT_LOCK (smpte);
      smpte->position = g_value_get_double (value);
      GST_OBJECT_UNLOCK (smpte);
      break;
747 748 749 750 751 752 753
    case PROP_INVERT:{
      gboolean invert;

      invert = g_value_get_boolean (value);
      GST_OBJECT_LOCK (smpte);
      gst_smpte_alpha_update_mask (smpte, smpte->type, invert,
          smpte->depth, smpte->width, smpte->height);
754
      GST_OBJECT_UNLOCK (smpte);
755 756
      break;
    }
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_smpte_alpha_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstSMPTEAlpha *smpte;

  smpte = GST_SMPTE_ALPHA (object);

  switch (prop_id) {
    case PROP_TYPE:
      GST_OBJECT_LOCK (smpte);
      g_value_set_enum (value, smpte->type);
      GST_OBJECT_UNLOCK (smpte);
      break;
    case PROP_BORDER:
      GST_OBJECT_LOCK (smpte);
      g_value_set_int (value, smpte->border);
      GST_OBJECT_UNLOCK (smpte);
      break;
    case PROP_DEPTH:
      GST_OBJECT_LOCK (smpte);
      g_value_set_int (value, smpte->depth);
      GST_OBJECT_UNLOCK (smpte);
      break;
    case PROP_POSITION:
      GST_OBJECT_LOCK (smpte);
      g_value_set_double (value, smpte->position);
      GST_OBJECT_UNLOCK (smpte);
      break;
792 793 794 795 796
    case PROP_INVERT:
      GST_OBJECT_LOCK (smpte);
      g_value_set_boolean (value, smpte->invert);
      GST_OBJECT_UNLOCK (smpte);
      break;
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

gboolean
gst_smpte_alpha_plugin_init (GstPlugin * plugin)
{
  GST_DEBUG_CATEGORY_INIT (gst_smpte_alpha_debug, "smptealpha", 0,
      "SMPTE alpha effect");

  return gst_element_register (plugin, "smptealpha", GST_RANK_NONE,
      GST_TYPE_SMPTE_ALPHA);
}