gstsmpte.c 18.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 *
 * 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
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
 * SECTION:element-smpte
 * @short_description: Takes to video frames and applies an SMPTE transition 
 * effect on them.
 *
 * <refsect2>
 * <para>
 * smpte can accept I420 video streams with the same width, height and
 * framerate. The two incomming buffers are blended together using an effect
 * specific alpha mask. 
 * </para>
 * <para>
 * The depth property defines the presision in bits of the mask. A higher
 * presision will create a mask with smoother gradients in order to avoid
 * banding.
 * </para>
 * <title>Sample pipelines</title>
 * <para>
 * Here is a pipeline to demonstrate the smpte transition :
 * <programlisting>
 * gst-launch -v videotestsrc pattern=1 ! smpte name=s border=20000 type=234
 * duration=2000000000 ! ffmpegcolorspace ! ximagesink videotestsrc ! s.
 * </programlisting>
 * This shows a pinwheel transition a from a snow videotestsrc to an smpte
 * pattern videotestsrc. The transition will take 2 seconds to complete. The
 * edges of the transition are smoothed with a 20000 big border.
 * </para>
 * </refsect2>
 */

50
51
52
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
53
#include <string.h>
54
#include "gstsmpte.h"
55
#include <gst/video/video.h>
56
57
#include "paint.h"

58
59
60
GST_DEBUG_CATEGORY_STATIC (gst_smpte_debug);
#define GST_CAT_DEFAULT gst_smpte_debug

61
/* elementfactory information */
62
static const GstElementDetails smpte_details =
Wim Taymans's avatar
Wim Taymans committed
63
64
65
66
GST_ELEMENT_DETAILS ("SMPTE transitions",
    "Filter/Editor/Video",
    "Apply the standard SMPTE transitions on video images",
    "Wim Taymans <wim.taymans@chello.be>");
67

David Schleef's avatar
David Schleef committed
68
static GstStaticPadTemplate gst_smpte_src_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
69
70
71
72
73
74
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")
    )
    );
75

David Schleef's avatar
David Schleef committed
76
static GstStaticPadTemplate gst_smpte_sink1_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
77
78
79
80
81
82
GST_STATIC_PAD_TEMPLATE ("sink1",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")
    )
    );
83

David Schleef's avatar
David Schleef committed
84
static GstStaticPadTemplate gst_smpte_sink2_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
85
86
87
88
89
90
GST_STATIC_PAD_TEMPLATE ("sink2",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")
    )
    );
91
92
93


/* SMPTE signals and args */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
94
95
enum
{
96
97
98
99
  /* FILL ME */
  LAST_SIGNAL
};

100
101
102
103
104
105
#define DEFAULT_PROP_TYPE	1
#define DEFAULT_PROP_BORDER	0
#define DEFAULT_PROP_DEPTH	16
#define DEFAULT_PROP_FPS	0.
#define DEFAULT_PROP_DURATION	GST_SECOND

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
106
107
enum
{
108
109
110
111
112
113
114
  PROP_0,
  PROP_TYPE,
  PROP_BORDER,
  PROP_DEPTH,
  PROP_FPS,
  PROP_DURATION,
  PROP_LAST,
115
116
};

117
118
119
120
121
122
123
124
125
126
127
#define I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
#define I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
#define I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(I420_Y_ROWSTRIDE(width)))/2)

#define I420_Y_OFFSET(w,h) (0)
#define I420_U_OFFSET(w,h) (I420_Y_OFFSET(w,h)+(I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
#define I420_V_OFFSET(w,h) (I420_U_OFFSET(w,h)+(I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))

#define I420_SIZE(w,h)     (I420_V_OFFSET(w,h)+(I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))


128
129
#define GST_TYPE_SMPTE_TRANSITION_TYPE (gst_smpte_transition_type_get_type())
static GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
130
gst_smpte_transition_type_get_type (void)
131
132
133
134
135
136
{
  static GType smpte_transition_type = 0;
  GEnumValue *smpte_transitions;

  if (!smpte_transition_type) {
    const GList *definitions;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
137
    gint i = 0;
138
139

    definitions = gst_mask_get_definitions ();
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
140
    smpte_transitions =
141
        g_new0 (GEnumValue, g_list_length ((GList *) definitions) + 1);
142
143
144

    while (definitions) {
      GstMaskDefinition *definition = (GstMaskDefinition *) definitions->data;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
145

146
147
148
      definitions = g_list_next (definitions);

      smpte_transitions[i].value = definition->type;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
149
150
      smpte_transitions[i].value_nick = definition->short_name;
      smpte_transitions[i].value_name = definition->long_name;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
151

152
153
154
      i++;
    }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
155
    smpte_transition_type =
156
        g_enum_register_static ("GstSMPTETransitionType", smpte_transitions);
157
158
  }
  return smpte_transition_type;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
159
}
160
161


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162
163
164
static void gst_smpte_class_init (GstSMPTEClass * klass);
static void gst_smpte_base_init (GstSMPTEClass * klass);
static void gst_smpte_init (GstSMPTE * smpte);
165

166
167
static GstFlowReturn gst_smpte_collected (GstCollectPads * pads,
    GstSMPTE * smpte);
168

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
169
170
171
172
static void gst_smpte_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_smpte_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
173

174
175
176
static GstStateChangeReturn gst_smpte_change_state (GstElement * element,
    GstStateChange transition);

177
static GstElementClass *parent_class = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
178

179
180
181
182
183
184
185
186
187
/*static guint gst_smpte_signals[LAST_SIGNAL] = { 0 }; */

static GType
gst_smpte_get_type (void)
{
  static GType smpte_type = 0;

  if (!smpte_type) {
    static const GTypeInfo smpte_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188
189
      sizeof (GstSMPTEClass),
      (GBaseInitFunc) gst_smpte_base_init,
190
      NULL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
191
      (GClassInitFunc) gst_smpte_class_init,
192
193
      NULL,
      NULL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
194
      sizeof (GstSMPTE),
195
      0,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
196
      (GInstanceInitFunc) gst_smpte_init,
197
    };
198

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
199
    smpte_type =
200
        g_type_register_static (GST_TYPE_ELEMENT, "GstSMPTE", &smpte_info, 0);
201
202
203
204
  }
  return smpte_type;
}

Ronald S. Bultje's avatar
Ronald S. Bultje committed
205
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
206
gst_smpte_base_init (GstSMPTEClass * klass)
Ronald S. Bultje's avatar
Ronald S. Bultje committed
207
208
209
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
210
211
212
213
214
215
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_smpte_sink1_template));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_smpte_sink2_template));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_smpte_src_template));
Ronald S. Bultje's avatar
Ronald S. Bultje committed
216
217
218
  gst_element_class_set_details (element_class, &smpte_details);
}

219
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
220
gst_smpte_class_init (GstSMPTEClass * klass)
221
222
223
224
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
225
226
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
227

228
  parent_class = g_type_class_peek_parent (klass);
229
230
231
232
233
234

  gobject_class->set_property = gst_smpte_set_property;
  gobject_class->get_property = gst_smpte_get_property;

  _gst_mask_init ();

235
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TYPE,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
236
      g_param_spec_enum ("type", "Type", "The type of transition to use",
237
          GST_TYPE_SMPTE_TRANSITION_TYPE, DEFAULT_PROP_TYPE,
238
          G_PARAM_READWRITE));
239
240
241
242
243
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FPS,
      g_param_spec_float ("fps", "FPS",
          "Frames per second if no input files are given (deprecated)", 0.,
          G_MAXFLOAT, DEFAULT_PROP_FPS, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BORDER,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
244
      g_param_spec_int ("border", "Border",
245
246
247
          "The border width of the transition", 0, G_MAXINT,
          DEFAULT_PROP_BORDER, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEPTH,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
248
      g_param_spec_int ("depth", "Depth", "Depth of the mask in bits", 1, 24,
249
250
251
252
253
254
255
          DEFAULT_PROP_DEPTH, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DURATION,
      g_param_spec_uint64 ("duration", "Duration",
          "Duration of the transition effect in nanoseconds", 0, G_MAXUINT64,
          DEFAULT_PROP_DURATION, G_PARAM_READWRITE));

  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_smpte_change_state);
256
257
}

258
259
260
261
/*                              wht  yel  cya  grn  mag  red  blu  blk   -I    Q */
static const int y_colors[] = { 255, 226, 179, 150, 105, 76, 29, 16, 16, 0 };
static const int u_colors[] = { 128, 0, 170, 46, 212, 85, 255, 128, 0, 128 };
static const int v_colors[] = { 128, 155, 0, 21, 235, 255, 107, 128, 128, 255 };
262
263

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
264
fill_i420 (guint8 * data, gint width, gint height, gint color)
265
{
266
267
  gint size = I420_Y_ROWSTRIDE (width) * GST_ROUND_UP_2 (height);
  gint size4 = size >> 2;
268
  guint8 *yp = data;
269
270
  guint8 *up = data + I420_U_OFFSET (width, height);
  guint8 *vp = data + I420_V_OFFSET (width, height);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
271

272
273
274
275
276
277
  memset (yp, y_colors[color], size);
  memset (up, u_colors[color], size4);
  memset (vp, v_colors[color], size4);
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
278
279
gst_smpte_update_mask (GstSMPTE * smpte, gint type, gint depth, gint width,
    gint height)
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
{
  GstMask *newmask;

  newmask = gst_mask_factory_new (type, depth, width, height);
  if (newmask) {
    if (smpte->mask) {
      gst_mask_destroy (smpte->mask);
    }
    smpte->mask = newmask;
    smpte->type = type;
    smpte->depth = depth;
    smpte->width = width;
    smpte->height = height;

    return TRUE;
  }
  return FALSE;
297
298
299
}

static gboolean
300
gst_smpte_setcaps (GstPad * pad, GstCaps * caps)
301
302
{
  GstSMPTE *smpte;
David Schleef's avatar
David Schleef committed
303
304
  GstStructure *structure;
  gboolean ret;
305

306
  smpte = GST_SMPTE (GST_PAD_PARENT (pad));
307

David Schleef's avatar
David Schleef committed
308
  structure = gst_caps_get_structure (caps, 0);
309

David Schleef's avatar
David Schleef committed
310
311
  ret = gst_structure_get_int (structure, "width", &smpte->width);
  ret &= gst_structure_get_int (structure, "height", &smpte->height);
312
313
  ret &= gst_structure_get_fraction (structure, "framerate",
      &smpte->fps_num, &smpte->fps_denom);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
314
  if (!ret)
315
    return FALSE;
316

317
318
319
320
321
322
323
324
325
326
  /* for backward compat, we store these here */
  smpte->fps = ((gdouble) smpte->fps_num) / smpte->fps_denom;

  /* figure out the duration in frames */
  smpte->end_position = gst_util_uint64_scale (smpte->duration,
      smpte->fps_num, GST_SECOND * smpte->fps_denom);

  GST_DEBUG_OBJECT (smpte, "duration: %d frames", smpte->end_position);

  ret = gst_smpte_update_mask (smpte, smpte->type, smpte->depth, smpte->width,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
327
      smpte->height);
328

329
  return ret;
330
331
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
332
333
static void
gst_smpte_init (GstSMPTE * smpte)
334
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
335
  smpte->sinkpad1 =
336
      gst_pad_new_from_static_template (&gst_smpte_sink1_template, "sink1");
337
338
339
340
  gst_pad_set_setcaps_function (smpte->sinkpad1,
      GST_DEBUG_FUNCPTR (gst_smpte_setcaps));
  gst_pad_set_getcaps_function (smpte->sinkpad1,
      GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
341
342
  gst_element_add_pad (GST_ELEMENT (smpte), smpte->sinkpad1);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
343
  smpte->sinkpad2 =
344
      gst_pad_new_from_static_template (&gst_smpte_sink2_template, "sink2");
345
346
347
348
  gst_pad_set_setcaps_function (smpte->sinkpad2,
      GST_DEBUG_FUNCPTR (gst_smpte_setcaps));
  gst_pad_set_getcaps_function (smpte->sinkpad2,
      GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
349
350
  gst_element_add_pad (GST_ELEMENT (smpte), smpte->sinkpad2);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
351
  smpte->srcpad =
352
      gst_pad_new_from_static_template (&gst_smpte_src_template, "src");
353
354
  gst_element_add_pad (GST_ELEMENT (smpte), smpte->srcpad);

355
356
  smpte->collect = gst_collect_pads_new ();
  gst_collect_pads_set_function (smpte->collect,
357
      (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_smpte_collected), smpte);
358
  gst_collect_pads_start (smpte->collect);
359

360
  gst_collect_pads_add_pad (smpte->collect, smpte->sinkpad1,
361
      sizeof (GstCollectData));
362
  gst_collect_pads_add_pad (smpte->collect, smpte->sinkpad2,
363
      sizeof (GstCollectData));
364

365
366
367
368
369
370
371
372
373
374
375
376
377
378
  smpte->fps = DEFAULT_PROP_FPS;
  smpte->type = DEFAULT_PROP_TYPE;
  smpte->border = DEFAULT_PROP_BORDER;
  smpte->depth = DEFAULT_PROP_DEPTH;
  smpte->duration = DEFAULT_PROP_DURATION;
  smpte->fps_num = 0;
  smpte->fps_denom = 1;
}

static void
gst_smpte_reset (GstSMPTE * smpte)
{
  smpte->width = -1;
  smpte->height = -1;
379
  smpte->position = 0;
380
  smpte->end_position = 0;
381
382
383
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
384
385
gst_smpte_blend_i420 (guint8 * in1, guint8 * in2, guint8 * out, GstMask * mask,
    gint width, gint height, gint border, gint pos)
386
{
387
388
  guint32 *maskp;
  gint value;
389
  gint i, j;
390
  gint min, max;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
391
  guint8 *in1u, *in1v, *in2u, *in2v, *outu, *outv;
392
393
394
  gint lumsize = width * height;
  gint chromsize = lumsize >> 2;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
395
396
  if (border == 0)
    border++;
397

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
398
  min = pos - border;
399
400
  max = pos;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
401
402
403
404
405
406
407
  in1u = in1 + lumsize;
  in1v = in1u + chromsize;
  in2u = in2 + lumsize;
  in2v = in2u + chromsize;
  outu = out + lumsize;
  outv = outu + chromsize;

408
409
  maskp = mask->data;

410
411
412
413
  for (i = 0; i < height; i++) {
    for (j = 0; j < width; j++) {
      value = *maskp++;
      value = ((CLAMP (value, min, max) - min) << 8) / border;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
414

415
416
      *out++ = ((*in1++ * value) + (*in2++ * (256 - value))) >> 8;
      if (!(i & 1) && !(j & 1)) {
417
418
        *outu++ = ((*in1u++ * value) + (*in2u++ * (256 - value))) >> 8;
        *outv++ = ((*in1v++ * value) + (*in2v++ * (256 - value))) >> 8;
419
      }
420
421
422
423
    }
  }
}

424
425
static GstFlowReturn
gst_smpte_collected (GstCollectPads * pads, GstSMPTE * smpte)
426
427
428
429
{
  GstBuffer *outbuf;
  GstClockTime ts;
  GstBuffer *in1 = NULL, *in2 = NULL;
430
  GSList *collected;
431

432
433
434
435
  if (G_UNLIKELY (smpte->fps_num == 0))
    goto not_negotiated;

  if (!GST_PAD_CAPS (smpte->sinkpad1) || !GST_PAD_CAPS (smpte->sinkpad2))
436
437
438
439
    goto not_negotiated;

  ts = gst_util_uint64_scale_int (smpte->position * GST_SECOND,
      smpte->fps_denom, smpte->fps_num);
440

441
442
443
444
445
446
  for (collected = pads->data; collected; collected = g_slist_next (collected)) {
    GstCollectData *data;

    data = (GstCollectData *) collected->data;

    if (data->pad == smpte->sinkpad1)
447
      in1 = gst_collect_pads_pop (pads, data);
448
    else if (data->pad == smpte->sinkpad2)
449
      in2 = gst_collect_pads_pop (pads, data);
450
  }
Wim Taymans's avatar
Wim Taymans committed
451
452

  if (in1 == NULL) {
453
    /* if no input, make picture black */
454
    in1 = gst_buffer_new_and_alloc (I420_SIZE (smpte->width, smpte->height));
455
456
    fill_i420 (GST_BUFFER_DATA (in1), smpte->width, smpte->height, 7);
  }
Wim Taymans's avatar
Wim Taymans committed
457
  if (in2 == NULL) {
458
    /* if no input, make picture white */
459
    in2 = gst_buffer_new_and_alloc (I420_SIZE (smpte->width, smpte->height));
460
461
462
    fill_i420 (GST_BUFFER_DATA (in2), smpte->width, smpte->height, 0);
  }

463
464
465
  if (GST_BUFFER_SIZE (in1) != GST_BUFFER_SIZE (in2))
    goto input_formats_do_not_match;

466
  if (smpte->position < smpte->end_position) {
467
    outbuf = gst_buffer_new_and_alloc (I420_SIZE (smpte->width, smpte->height));
468

469
    /* set caps if not done yet */
470
    if (!GST_PAD_CAPS (smpte->srcpad)) {
David Schleef's avatar
David Schleef committed
471
      GstCaps *caps;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
472
473

      caps =
474
475
          gst_caps_copy (gst_static_caps_get (&gst_smpte_src_template.
              static_caps));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
476
      gst_caps_set_simple (caps, "width", G_TYPE_INT, smpte->width, "height",
477
478
          G_TYPE_INT, smpte->height, "framerate", GST_TYPE_FRACTION,
          smpte->fps_num, smpte->fps_denom, NULL);
David Schleef's avatar
David Schleef committed
479

480
      gst_pad_set_caps (smpte->srcpad, caps);
481
482
483
484
485

      gst_pad_push_event (smpte->srcpad,
          gst_event_new_new_segment_full (FALSE,
              1.0, 1.0, GST_FORMAT_TIME, 0, -1, 0));

486
    }
487
    gst_buffer_set_caps (outbuf, GST_PAD_CAPS (smpte->srcpad));
488

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
489
    gst_smpte_blend_i420 (GST_BUFFER_DATA (in1),
490
491
492
493
494
        GST_BUFFER_DATA (in2),
        GST_BUFFER_DATA (outbuf),
        smpte->mask, smpte->width, smpte->height,
        smpte->border,
        ((1 << smpte->depth) + smpte->border) *
495
        smpte->position / smpte->end_position);
496

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
497
  } else {
498
499
500
501
502
503
504
505
506
507
508
509
    outbuf = in2;
    gst_buffer_ref (in2);
  }

  smpte->position++;

  if (in1)
    gst_buffer_unref (in1);
  if (in2)
    gst_buffer_unref (in2);

  GST_BUFFER_TIMESTAMP (outbuf) = ts;
510
511

  return gst_pad_push (smpte->srcpad, outbuf);
512
513
514
515
516
517
518
519

  /* ERRORS */
not_negotiated:
  {
    GST_ELEMENT_ERROR (smpte, CORE, NEGOTIATION, (NULL),
        ("No input format negotiated"));
    return GST_FLOW_NOT_NEGOTIATED;
  }
520
521
522
523
524
525
526
input_formats_do_not_match:
  {
    GST_ELEMENT_ERROR (smpte, CORE, NEGOTIATION, (NULL),
        ("input formats don't match: %" GST_PTR_FORMAT " vs. %" GST_PTR_FORMAT,
            GST_PAD_CAPS (smpte->sinkpad1), GST_PAD_CAPS (smpte->sinkpad2)));
    return GST_FLOW_ERROR;
  }
527
528
529
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
530
531
gst_smpte_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
532
533
534
{
  GstSMPTE *smpte;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
535
  smpte = GST_SMPTE (object);
536
537

  switch (prop_id) {
538
539
    case PROP_TYPE:
      smpte->type = g_value_get_enum (value);
540
      break;
541
    case PROP_BORDER:
542
543
      smpte->border = g_value_get_int (value);
      break;
544
    case PROP_FPS:
545
546
      smpte->fps = g_value_get_float (value);
      break;
547
548
549
550
551
    case PROP_DEPTH:
      smpte->depth = g_value_get_int (value);
      break;
    case PROP_DURATION:
      smpte->duration = g_value_get_uint64 (value);
552
553
554
555
556
557
558
559
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
560
561
gst_smpte_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
562
563
564
{
  GstSMPTE *smpte;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
565
  smpte = GST_SMPTE (object);
566
567

  switch (prop_id) {
568
569
    case PROP_TYPE:
      g_value_set_enum (value, smpte->type);
570
      break;
571
    case PROP_FPS:
572
      g_value_set_float (value, smpte->fps);
573
      break;
574
    case PROP_BORDER:
575
576
      g_value_set_int (value, smpte->border);
      break;
577
    case PROP_DEPTH:
578
579
      g_value_set_int (value, smpte->depth);
      break;
580
581
582
    case PROP_DURATION:
      g_value_set_uint64 (value, smpte->duration);
      break;
583
584
585
586
587
588
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
static GstStateChangeReturn
gst_smpte_change_state (GstElement * element, GstStateChange transition)
{
  GstStateChangeReturn ret;
  GstSMPTE *smpte;

  smpte = GST_SMPTE (element);

  switch (transition) {
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      gst_smpte_reset (smpte);
      GST_LOG_OBJECT (smpte, "starting collectpads");
      gst_collect_pads_start (smpte->collect);
      break;
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      GST_LOG_OBJECT (smpte, "stopping collectpads");
      gst_collect_pads_stop (smpte->collect);
      break;
    default:
      break;
  }

  ret = parent_class->change_state (element, transition);

  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      gst_smpte_reset (smpte);
      break;
    default:
      break;
  }
  return ret;
}
622
623

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
624
plugin_init (GstPlugin * plugin)
625
{
626
627
628
  GST_DEBUG_CATEGORY_INIT (gst_smpte_debug, "smpte", 0,
      "SMPTE transition effect");

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
629
  return gst_element_register (plugin, "smpte", GST_RANK_NONE, GST_TYPE_SMPTE);
630
631
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
632
633
634
635
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "smpte",
    "Apply the standard SMPTE transitions on video images",
636
    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)