compatibility fix for new GST_DEBUG stuff.
[platform/upstream/gstreamer.git] / gst / smpte / gstsmpte.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include <string.h>
24 #include <gstsmpte.h>
25 #include "paint.h"
26
27 /* elementfactory information */
28 static GstElementDetails smpte_details = {
29   "SMPTE transitions",
30   "Filter/Video",
31   "LGPL",
32   "Apply the standard SMPTE transitions on video images",
33   VERSION,
34   "Wim Taymans <wim.taymans@chello.be>",
35   "(C) 2002",
36 };
37
38 GST_PAD_TEMPLATE_FACTORY (smpte_src_factory,
39   "src",
40   GST_PAD_SRC,
41   GST_PAD_ALWAYS,
42   GST_CAPS_NEW (
43    "smpte_src",
44    "video/raw",
45      "format",   GST_PROPS_FOURCC (GST_STR_FOURCC ("I420"))
46   )
47 )
48
49 GST_PAD_TEMPLATE_FACTORY (smpte_sink1_factory,
50   "sink1",
51   GST_PAD_SINK,
52   GST_PAD_ALWAYS,
53   GST_CAPS_NEW (
54    "smpte_sink1",
55    "video/raw",
56      "format",   GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")),
57      "width",    GST_PROPS_INT_RANGE (0, 4096),
58      "height",   GST_PROPS_INT_RANGE (0, 4096) 
59   )
60 )
61
62 GST_PAD_TEMPLATE_FACTORY (smpte_sink2_factory,
63   "sink2",
64   GST_PAD_SINK,
65   GST_PAD_ALWAYS,
66   GST_CAPS_NEW (
67    "smpte_sink2",
68    "video/raw",
69      "format",   GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")),
70      "width",    GST_PROPS_INT_RANGE (0, 4096),
71      "height",   GST_PROPS_INT_RANGE (0, 4096)
72   )
73 )
74
75
76 /* SMPTE signals and args */
77 enum {
78   /* FILL ME */
79   LAST_SIGNAL
80 };
81
82 enum {
83   ARG_0,
84   ARG_TYPE,
85   ARG_BORDER,
86   ARG_DEPTH,
87   ARG_FPS,
88 };
89
90 #define GST_TYPE_SMPTE_TRANSITION_TYPE (gst_smpte_transition_type_get_type())
91 static GType
92 gst_smpte_transition_type_get_type (void) 
93 {
94   static GType smpte_transition_type = 0;
95   GEnumValue *smpte_transitions;
96
97   if (!smpte_transition_type) {
98     const GList *definitions;
99     gint i=0;
100
101     definitions = gst_mask_get_definitions ();
102     smpte_transitions = g_new0 (GEnumValue, g_list_length ((GList *)definitions)+1);
103
104     while (definitions) {
105       GstMaskDefinition *definition = (GstMaskDefinition *) definitions->data;
106       definitions = g_list_next (definitions);
107
108       smpte_transitions[i].value = definition->type;
109       smpte_transitions[i].value_name = definition->short_name;
110       smpte_transitions[i].value_nick = definition->long_name;
111       
112       i++;
113     }
114
115     smpte_transition_type = 
116             g_enum_register_static ("GstSMPTETransitionType", smpte_transitions);
117   }
118   return smpte_transition_type;
119 }   
120
121
122 static void     gst_smpte_class_init            (GstSMPTEClass *klass);
123 static void     gst_smpte_init                  (GstSMPTE *smpte);
124
125 static void     gst_smpte_loop                  (GstElement *element);
126
127 static void     gst_smpte_set_property          (GObject *object, guint prop_id, 
128                                                  const GValue *value, GParamSpec *pspec);
129 static void     gst_smpte_get_property          (GObject *object, guint prop_id, 
130                                                  GValue *value, GParamSpec *pspec);
131
132 static GstElementClass *parent_class = NULL;
133 /*static guint gst_smpte_signals[LAST_SIGNAL] = { 0 }; */
134
135 static GType
136 gst_smpte_get_type (void)
137 {
138   static GType smpte_type = 0;
139
140   if (!smpte_type) {
141     static const GTypeInfo smpte_info = {
142       sizeof(GstSMPTEClass),      
143       NULL,
144       NULL,
145       (GClassInitFunc)gst_smpte_class_init,
146       NULL,
147       NULL,
148       sizeof(GstSMPTE),
149       0,
150       (GInstanceInitFunc)gst_smpte_init,
151     };
152     smpte_type = g_type_register_static(GST_TYPE_ELEMENT, "GstSMPTE", &smpte_info, 0);
153   }
154   return smpte_type;
155 }
156
157 static void
158 gst_smpte_class_init (GstSMPTEClass *klass)
159 {
160   GObjectClass *gobject_class;
161   GstElementClass *gstelement_class;
162
163   gobject_class = (GObjectClass*)klass;
164   gstelement_class = (GstElementClass*)klass;
165
166   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
167
168   gobject_class->set_property = gst_smpte_set_property;
169   gobject_class->get_property = gst_smpte_get_property;
170
171   _gst_mask_init ();
172
173   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TYPE,
174     g_param_spec_enum ("type", "Type", "The type of transition to use",
175                        GST_TYPE_SMPTE_TRANSITION_TYPE, 1, G_PARAM_READWRITE));
176   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FPS,
177     g_param_spec_int ("fps", "FPS", "Frames per second if no input files are given",
178                       1, 255, 1, G_PARAM_READWRITE));
179   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BORDER,
180     g_param_spec_int ("border", "Border", "The border width of the transition",
181                       0, G_MAXINT, 0, G_PARAM_READWRITE));
182   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEPTH,
183     g_param_spec_int ("depth", "Depth", "Depth of the mask in bits",
184                       1, 24, 16, G_PARAM_READWRITE));
185 }
186
187 /*                        wht  yel  cya  grn  mag  red  blu  blk   -I    Q */
188 static int y_colors[] = { 255, 226, 179, 150, 105,  76,  29,  16,  16,   0 };
189 static int u_colors[] = { 128,   0, 170,  46, 212,  85, 255, 128,   0, 128 };
190 static int v_colors[] = { 128, 155,   0,  21, 235, 255, 107, 128, 128, 255 };
191
192 static void
193 fill_i420 (guint8 *data, gint width, gint height, gint color)
194 {
195   gint size = width * height, size4 = size >> 2;
196   guint8 *yp = data;
197   guint8 *up = data + size;
198   guint8 *vp = data + size + size4;
199   
200   memset (yp, y_colors[color], size);
201   memset (up, u_colors[color], size4);
202   memset (vp, v_colors[color], size4);
203 }
204
205 static gboolean
206 gst_smpte_update_mask (GstSMPTE *smpte, gint type, gint depth, gint width, gint height)
207 {
208   GstMask *newmask;
209
210   newmask = gst_mask_factory_new (type, depth, width, height);
211   if (newmask) {
212     if (smpte->mask) {
213       gst_mask_destroy (smpte->mask);
214     }
215     smpte->mask = newmask;
216     smpte->type = type;
217     smpte->depth = depth;
218     smpte->width = width;
219     smpte->height = height;
220
221     return TRUE;
222   }
223   return FALSE;
224 }
225
226 static gboolean
227 gst_smpte_sinkconnect (GstPad *pad, GstCaps *caps)
228 {
229   GstSMPTE *smpte;
230
231   smpte = GST_SMPTE (gst_pad_get_parent (pad));
232
233   if (!GST_CAPS_IS_FIXED (caps))
234     return GST_PAD_LINK_DELAYED;
235
236   gst_caps_get_int (caps, "width", &smpte->width);
237   gst_caps_get_int (caps, "height", &smpte->height);
238
239   gst_smpte_update_mask (smpte, smpte->type, smpte->depth, smpte->width, smpte->height);
240
241   /* forward to the next plugin */
242   return gst_pad_try_set_caps(smpte->srcpad, gst_caps_copy_1(caps));
243 }
244
245 static void 
246 gst_smpte_init (GstSMPTE *smpte)
247 {
248   smpte->sinkpad1 = gst_pad_new_from_template (
249                   GST_PAD_TEMPLATE_GET (smpte_sink1_factory), "sink1");
250   gst_pad_set_link_function (smpte->sinkpad1, gst_smpte_sinkconnect);
251   gst_element_add_pad (GST_ELEMENT (smpte), smpte->sinkpad1);
252
253   smpte->sinkpad2 = gst_pad_new_from_template (
254                   GST_PAD_TEMPLATE_GET (smpte_sink2_factory), "sink2");
255   gst_pad_set_link_function (smpte->sinkpad2, gst_smpte_sinkconnect);
256   gst_element_add_pad (GST_ELEMENT (smpte), smpte->sinkpad2);
257
258   smpte->srcpad = gst_pad_new_from_template (
259                   GST_PAD_TEMPLATE_GET (smpte_src_factory), "src");
260   gst_element_add_pad (GST_ELEMENT (smpte), smpte->srcpad);
261
262   gst_element_set_loop_function (GST_ELEMENT (smpte), gst_smpte_loop);
263
264   smpte->width = 320;
265   smpte->height = 200;
266   smpte->duration = 64;
267   smpte->position = 0;
268   smpte->type = 1;
269   smpte->fps = 1;
270   smpte->border = 0;
271   smpte->depth = 16;
272   gst_smpte_update_mask (smpte, smpte->type, smpte->depth, smpte->width, smpte->height);
273 }
274
275 static void
276 gst_smpte_blend_i420 (guint8 *in1, guint8 *in2, guint8 *out, GstMask *mask,
277                       gint width, gint height, gint border, gint pos)
278 {
279   guint32 *maskp;
280   gint value;
281   gint i, j;
282   gint min, max;
283   guint8 *in1u, *in1v, *in2u, *in2v, *outu, *outv; 
284   gint lumsize = width * height;
285   gint chromsize = lumsize >> 2;
286
287   if (border == 0) border++;
288
289   min = pos - border; 
290   max = pos;
291
292   in1u = in1 + lumsize; in1v = in1u + chromsize;
293   in2u = in2 + lumsize; in2v = in2u + chromsize;
294   outu = out + lumsize; outv = outu + chromsize;
295   
296   maskp = mask->data;
297
298   for (i = 0; i < height; i++) {
299     for (j = 0; j < width; j++) {
300       value = *maskp++;
301       value = ((CLAMP (value, min, max) - min) << 8) / border;
302     
303       *out++ = ((*in1++ * value) + (*in2++ * (256 - value))) >> 8;
304       if (!(i & 1) && !(j & 1)) {
305         *outu++ = ((*in1u++ * value) + (*in2u++ * (256 - value))) >> 8;
306         *outv++ = ((*in1v++ * value) + (*in2v++ * (256 - value))) >> 8;
307       }
308     }
309   }
310 }
311
312 static void
313 gst_smpte_loop (GstElement *element)
314 {
315   GstSMPTE *smpte;
316   GstBuffer *outbuf;
317   GstClockTime ts;
318   GstBuffer *in1 = NULL, *in2 = NULL;
319
320   smpte = GST_SMPTE (element);
321
322   ts = smpte->position * GST_SECOND / smpte->fps;
323
324   while (GST_PAD_IS_USABLE (smpte->sinkpad1) && in1 == NULL) {
325     in1 = gst_pad_pull (smpte->sinkpad1);
326     if (GST_IS_EVENT (in1)) {
327       gst_pad_push (smpte->srcpad, in1);
328       in1 = NULL;
329     }
330     else 
331       ts = GST_BUFFER_TIMESTAMP (in1);
332   }
333   if (GST_PAD_IS_USABLE (smpte->sinkpad2) && in2 == NULL) {
334     in2 = gst_pad_pull (smpte->sinkpad2);
335     if (GST_IS_EVENT (in2)) {
336       gst_pad_push (smpte->srcpad, in2);
337       in2 = NULL;
338     }
339     else 
340       ts = GST_BUFFER_TIMESTAMP (in2);
341   }
342
343   if (in1 == NULL) {
344     in1 = gst_buffer_new_and_alloc (smpte->width * smpte->height * 3);
345     fill_i420 (GST_BUFFER_DATA (in1), smpte->width, smpte->height, 7);
346   }
347   if (in2 == NULL) {
348     in2 = gst_buffer_new_and_alloc (smpte->width * smpte->height * 3);
349     fill_i420 (GST_BUFFER_DATA (in2), smpte->width, smpte->height, 0);
350   }
351
352   if (smpte->position < smpte->duration) { 
353     outbuf = gst_buffer_new_and_alloc (smpte->width * smpte->height * 3);
354
355     if (!GST_PAD_CAPS (smpte->srcpad)) {
356       if (!gst_pad_try_set_caps (smpte->srcpad,
357             GST_CAPS_NEW (
358                     "smpte_srccaps",
359                     "video/raw",
360                       "format",   GST_PROPS_FOURCC (GST_MAKE_FOURCC ('I','4','2','0')),
361                       "width",    GST_PROPS_INT (smpte->width),
362                       "height",   GST_PROPS_INT (smpte->height)
363                     )))
364       {
365         gst_element_error (element, "cannot set caps");
366         return;
367       }
368     }
369
370     gst_smpte_blend_i420 (GST_BUFFER_DATA (in1), 
371                           GST_BUFFER_DATA (in2), 
372                           GST_BUFFER_DATA (outbuf),
373                           smpte->mask, smpte->width, smpte->height, 
374                           smpte->border,
375                           ((1 << smpte->depth) + smpte->border) * 
376                             smpte->position / smpte->duration);
377   }
378   else {
379     outbuf = in2;
380     gst_buffer_ref (in2);
381   }
382
383   smpte->position++;
384
385   if (in1)
386     gst_buffer_unref (in1);
387   if (in2)
388     gst_buffer_unref (in2);
389
390   GST_BUFFER_TIMESTAMP (outbuf) = ts;
391   gst_pad_push (smpte->srcpad, outbuf);
392 }
393
394 static void
395 gst_smpte_set_property (GObject *object, guint prop_id, 
396                         const GValue *value, GParamSpec *pspec)
397 {
398   GstSMPTE *smpte;
399
400   smpte = GST_SMPTE(object);
401
402   switch (prop_id) {
403     case ARG_TYPE:
404     {
405       gint type = g_value_get_enum (value);
406
407       gst_smpte_update_mask (smpte, type, smpte->depth, 
408                              smpte->width, smpte->height);
409       break;
410     }
411     case ARG_FPS:
412       smpte->fps = g_value_get_int (value);
413       break;
414     case ARG_BORDER:
415       smpte->border = g_value_get_int (value);
416       break;
417     case ARG_DEPTH:
418     {
419       gint depth = g_value_get_int (value);
420
421       gst_smpte_update_mask (smpte, smpte->type, depth, 
422                              smpte->width, smpte->height);
423       break;
424     }
425     default:
426       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
427       break;
428   }
429 }
430
431 static void
432 gst_smpte_get_property (GObject *object, guint prop_id, 
433                         GValue *value, GParamSpec *pspec)
434 {
435   GstSMPTE *smpte;
436
437   smpte = GST_SMPTE(object);
438
439   switch (prop_id) {
440     case ARG_TYPE:
441       if (smpte->mask) {
442         g_value_set_enum (value, smpte->mask->type);
443       }
444       break;
445     case ARG_FPS:
446       g_value_set_int (value, smpte->fps);
447       break;
448     case ARG_BORDER:
449       g_value_set_int (value, smpte->border);
450       break;
451     case ARG_DEPTH:
452       g_value_set_int (value, smpte->depth);
453       break;
454     default:
455       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
456       break;
457   }
458 }
459
460
461 static gboolean
462 plugin_init (GModule *module, GstPlugin *plugin)
463 {
464   GstElementFactory *factory;
465
466   factory = gst_element_factory_new("smpte",GST_TYPE_SMPTE,
467                                    &smpte_details);
468   g_return_val_if_fail(factory != NULL, FALSE);
469
470   gst_element_factory_add_pad_template (factory, 
471                   GST_PAD_TEMPLATE_GET (smpte_sink1_factory));
472   gst_element_factory_add_pad_template (factory, 
473                   GST_PAD_TEMPLATE_GET (smpte_sink2_factory));
474   gst_element_factory_add_pad_template (factory, 
475                   GST_PAD_TEMPLATE_GET (smpte_src_factory));
476
477   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
478
479   return TRUE;
480 }
481
482 GstPluginDesc plugin_desc = {
483   GST_VERSION_MAJOR,
484   GST_VERSION_MINOR,
485   "smpte",
486   plugin_init
487 };
488