change NULL to (NULL) for GST_ELEMENT_ERROR
[platform/upstream/gst-plugins-good.git] / ext / aalib / gstaasink.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
24 #include <string.h>
25 #include <sys/time.h>
26
27 #include "gstaasink.h"
28 #include <gst/video/video.h>
29
30 /* elementfactory information */
31 static GstElementDetails gst_aasink_details = {
32   "AA sink",
33   "Sink/Video",
34   "An ASCII art videosink",
35   "Wim Taymans <wim.taymans@chello.be>"
36 };
37
38 /* aasink signals and args */
39 enum {
40   SIGNAL_FRAME_DISPLAYED,
41   SIGNAL_HAVE_SIZE,
42   LAST_SIGNAL
43 };
44
45
46 enum {
47   ARG_0,
48   ARG_WIDTH,
49   ARG_HEIGHT,
50   ARG_DRIVER,
51   ARG_DITHER,
52   ARG_BRIGHTNESS,
53   ARG_CONTRAST,
54   ARG_GAMMA,
55   ARG_INVERSION,
56   ARG_RANDOMVAL,
57   ARG_FRAMES_DISPLAYED,
58   ARG_FRAME_TIME,
59 };
60
61 static GstStaticPadTemplate sink_template =
62 GST_STATIC_PAD_TEMPLATE (
63   "sink",
64   GST_PAD_SINK,
65   GST_PAD_ALWAYS,
66   GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
67 );
68
69 static void     gst_aasink_base_init    (gpointer g_class);
70 static void     gst_aasink_class_init   (GstAASinkClass *klass);
71 static void     gst_aasink_init         (GstAASink *aasink);
72
73 static void     gst_aasink_set_clock    (GstElement *element, GstClock *clock);
74 static void     gst_aasink_chain        (GstPad *pad, GstData *_data);
75
76 static void     gst_aasink_set_property (GObject *object, guint prop_id, 
77                                          const GValue *value, GParamSpec *pspec);
78 static void     gst_aasink_get_property (GObject *object, guint prop_id, 
79                                          GValue *value, GParamSpec *pspec);
80
81 static GstElementStateReturn gst_aasink_change_state (GstElement *element);
82
83 static GstElementClass *parent_class = NULL;
84 static guint gst_aasink_signals[LAST_SIGNAL] = { 0 };
85
86 GType
87 gst_aasink_get_type (void)
88 {
89   static GType aasink_type = 0;
90
91   if (!aasink_type) {
92     static const GTypeInfo aasink_info = {
93       sizeof(GstAASinkClass),
94       gst_aasink_base_init,
95       NULL,
96       (GClassInitFunc) gst_aasink_class_init,
97       NULL,
98       NULL,
99       sizeof(GstAASink),
100       0,
101       (GInstanceInitFunc)gst_aasink_init,
102     };
103     aasink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstAASink", &aasink_info, 0);
104   }
105   return aasink_type;
106 }
107
108 #define GST_TYPE_AADRIVERS (gst_aasink_drivers_get_type())
109 static GType
110 gst_aasink_drivers_get_type (void)
111 {
112   static GType driver_type = 0;
113   if (!driver_type) {
114     GEnumValue *drivers;
115     const struct aa_driver *driver;
116     gint n_drivers;
117     gint i;
118
119     for (n_drivers = 0; aa_drivers[n_drivers]; n_drivers++){
120       /* count number of drivers */
121     }
122     
123     drivers = g_new0(GEnumValue, n_drivers + 1);
124
125     for (i = 0; i < n_drivers; i++){
126       driver = aa_drivers[i];
127       drivers[i].value = i;
128       drivers[i].value_name = g_strdup (driver->shortname);
129       drivers[i].value_nick = g_strdup (driver->name);
130     }
131     drivers[i].value = 0;
132     drivers[i].value_name = NULL;
133     drivers[i].value_nick = NULL;
134
135     driver_type = g_enum_register_static ("GstAASinkDrivers", drivers);
136   }
137   return driver_type;
138 }
139
140 #define GST_TYPE_AADITHER (gst_aasink_dither_get_type())
141 static GType
142 gst_aasink_dither_get_type (void)
143 {
144   static GType dither_type = 0;
145   if (!dither_type) {
146     GEnumValue *ditherers;
147     gint n_ditherers;
148     gint i;
149
150     for (n_ditherers = 0; aa_dithernames[n_ditherers]; n_ditherers++){
151       /* count number of ditherers */
152     }
153     
154     ditherers = g_new0(GEnumValue, n_ditherers + 1);
155
156     for (i = 0; i < n_ditherers; i++){
157       ditherers[i].value = i;
158       ditherers[i].value_name = g_strdup (aa_dithernames[i]);
159       ditherers[i].value_nick = g_strdup (aa_dithernames[i]);
160     }
161     ditherers[i].value = 0;
162     ditherers[i].value_name = NULL;
163     ditherers[i].value_nick = NULL;
164
165     dither_type = g_enum_register_static ("GstAASinkDitherers", ditherers);
166   }
167   return dither_type;
168 }
169
170 static void
171 gst_aasink_base_init (gpointer g_class)
172 {
173   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
174
175   gst_element_class_add_pad_template (element_class,
176       gst_static_pad_template_get (&sink_template));
177   gst_element_class_set_details (element_class, &gst_aasink_details);
178 }
179
180 static void
181 gst_aasink_class_init (GstAASinkClass *klass)
182 {
183   GObjectClass *gobject_class;
184   GstElementClass *gstelement_class;
185
186   gobject_class = (GObjectClass*)klass;
187   gstelement_class = (GstElementClass*)klass;
188
189   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
190
191   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_WIDTH,
192     g_param_spec_int("width","width","width",
193                      G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */
194   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_HEIGHT,
195     g_param_spec_int("height","height","height",
196                      G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */
197   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DRIVER,
198     g_param_spec_enum("driver","driver","driver",
199                       GST_TYPE_AADRIVERS,0,G_PARAM_READWRITE)); /* CHECKME! */
200   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DITHER,
201     g_param_spec_enum("dither","dither","dither",
202                       GST_TYPE_AADITHER,0,G_PARAM_READWRITE)); /* CHECKME! */
203   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BRIGHTNESS,
204     g_param_spec_int("brightness","brightness","brightness",
205                      G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */
206   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CONTRAST,
207     g_param_spec_int("contrast","contrast","contrast",
208                      G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */
209   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_GAMMA,
210     g_param_spec_float("gamma","gamma","gamma",
211                        0.0,5.0,1.0,G_PARAM_READWRITE)); /* CHECKME */
212   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_INVERSION,
213     g_param_spec_boolean("inversion","inversion","inversion",
214                          TRUE,G_PARAM_READWRITE)); /* CHECKME */
215   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_RANDOMVAL,
216     g_param_spec_int("randomval","randomval","randomval",
217                      G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */
218   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FRAMES_DISPLAYED,
219     g_param_spec_int("frames_displayed","frames_displayed","frames_displayed",
220                      G_MININT,G_MAXINT,0,G_PARAM_READABLE)); /* CHECKME */
221   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FRAME_TIME,
222     g_param_spec_int("frame_time","frame_time","frame_time",
223                      G_MININT,G_MAXINT,0,G_PARAM_READABLE)); /* CHECKME */
224
225   gobject_class->set_property = gst_aasink_set_property;
226   gobject_class->get_property = gst_aasink_get_property;
227
228   gst_aasink_signals[SIGNAL_FRAME_DISPLAYED] =
229     g_signal_new ("frame_displayed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
230                    G_STRUCT_OFFSET (GstAASinkClass, frame_displayed), NULL, NULL,
231                    g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
232   gst_aasink_signals[SIGNAL_HAVE_SIZE] =
233     g_signal_new ("have_size", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
234                    G_STRUCT_OFFSET (GstAASinkClass, have_size), NULL, NULL,
235                    gst_marshal_VOID__INT_INT, G_TYPE_NONE, 2,
236                    G_TYPE_UINT, G_TYPE_UINT);
237
238   gstelement_class->change_state = gst_aasink_change_state;
239   gstelement_class->set_clock    = gst_aasink_set_clock;
240 }
241
242 static GstPadLinkReturn
243 gst_aasink_sinkconnect (GstPad *pad, const GstCaps *caps)
244 {
245   GstAASink *aasink;
246   GstStructure *structure;
247
248   aasink = GST_AASINK (gst_pad_get_parent (pad));
249
250   structure = gst_caps_get_structure (caps, 0);
251   gst_structure_get_int (structure, "width", &aasink->width);
252   gst_structure_get_int (structure, "height", &aasink->height);
253
254   /* FIXME aasink->format is never set */
255
256   GST_DEBUG ("aasink: setting %08lx (" GST_FOURCC_FORMAT ")",
257                  aasink->format, GST_FOURCC_ARGS(aasink->format));
258   
259   g_signal_emit( G_OBJECT (aasink), gst_aasink_signals[SIGNAL_HAVE_SIZE], 0,
260                  aasink->width, aasink->height);
261
262   return GST_PAD_LINK_OK;
263 }
264
265 static void
266 gst_aasink_set_clock (GstElement *element, GstClock *clock)
267 {
268   GstAASink *aasink = GST_AASINK (element);
269
270   aasink->clock = clock;
271 }
272
273 static void
274 gst_aasink_init (GstAASink *aasink)
275 {
276   aasink->sinkpad = gst_pad_new_from_template (
277       gst_element_get_pad_template (GST_ELEMENT (aasink), "sink"), "sink");
278   gst_element_add_pad (GST_ELEMENT (aasink), aasink->sinkpad);
279   gst_pad_set_chain_function (aasink->sinkpad, gst_aasink_chain);
280   gst_pad_set_link_function (aasink->sinkpad, gst_aasink_sinkconnect);
281
282   memcpy(&aasink->ascii_surf, &aa_defparams, sizeof (struct aa_hardware_params));
283   aasink->ascii_parms.bright = 0;
284   aasink->ascii_parms.contrast = 16;
285   aasink->ascii_parms.gamma = 1.0;
286   aasink->ascii_parms.dither = 0;
287   aasink->ascii_parms.inversion = 0;
288   aasink->ascii_parms.randomval = 0;
289   aasink->aa_driver = 0;
290
291   aasink->width = -1;
292   aasink->height = -1;
293
294   aasink->clock = NULL;
295
296   GST_FLAG_SET(aasink, GST_ELEMENT_THREAD_SUGGESTED);
297 }
298
299 static void
300 gst_aasink_scale (GstAASink *aasink, gchar *src, gchar *dest,
301                   gint sw, gint sh, 
302                   gint dw, gint dh)
303 {
304   gint ypos, yinc, y;
305   gint xpos, xinc, x;
306
307   g_return_if_fail ((dw != 0) && (dh != 0));
308
309   ypos = 0x10000;
310   yinc = (sh << 16) / dh;
311   xinc = (sw << 16) / dw;
312
313   for (y = dh; y; y--) {
314     while (ypos > 0x10000) {
315       ypos -= 0x10000;
316       src  += sw;
317     }
318     xpos = 0x10000;
319     {
320       guchar *destp = dest;
321       guchar *srcp = src;
322
323       for ( x=dw; x; x-- ) {
324         while (xpos >= 0x10000L) {
325           srcp++;
326           xpos -= 0x10000L;
327         }
328         *destp++ = *srcp;
329         xpos     += xinc;
330       }
331     }
332     dest += dw;
333     ypos += yinc;
334   }
335 }
336
337 static void
338 gst_aasink_chain (GstPad *pad, GstData *_data)
339 {
340   GstBuffer *buf = GST_BUFFER (_data);
341   GstAASink *aasink;
342
343   g_return_if_fail (pad != NULL);
344   g_return_if_fail (GST_IS_PAD (pad));
345   g_return_if_fail (buf != NULL);
346
347   aasink = GST_AASINK (gst_pad_get_parent (pad));
348
349   gst_aasink_scale (aasink,
350                     GST_BUFFER_DATA (buf),              /* src */
351                     aa_image (aasink->context),         /* dest */
352                     aasink->width,                      /* sw */
353                     aasink->height,                     /* sh */
354                     aa_imgwidth (aasink->context),      /* dw */
355                     aa_imgheight (aasink->context));    /* dh */
356
357   GST_DEBUG ("videosink: clock wait: %" G_GUINT64_FORMAT, GST_BUFFER_TIMESTAMP(buf));
358
359   if (aasink->clock && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
360     gst_element_wait (GST_ELEMENT (aasink), GST_BUFFER_TIMESTAMP (buf));
361   }
362
363   aa_render (aasink->context, &aasink->ascii_parms, 
364                   0, 0, aa_imgwidth (aasink->context), aa_imgheight (aasink->context));
365   aa_flush (aasink->context);
366   aa_getevent (aasink->context, FALSE);
367
368   g_signal_emit(G_OBJECT(aasink),gst_aasink_signals[SIGNAL_FRAME_DISPLAYED], 0);
369
370   gst_buffer_unref(buf);
371 }
372
373
374 static void
375 gst_aasink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
376 {
377   GstAASink *aasink;
378
379   /* it's not null if we got it, but it might not be ours */
380   g_return_if_fail (GST_IS_AASINK (object));
381
382   aasink = GST_AASINK (object);
383
384   switch (prop_id) {
385     case ARG_WIDTH:
386       aasink->ascii_surf.width = g_value_get_int (value);
387       break;
388     case ARG_HEIGHT:
389       aasink->ascii_surf.height = g_value_get_int (value);
390       break;
391     case ARG_DRIVER: {
392       aasink->aa_driver = g_value_get_enum (value);
393       break;
394     }
395     case ARG_DITHER: {
396       aasink->ascii_parms.dither = g_value_get_enum (value);
397       break;
398     }
399     case ARG_BRIGHTNESS: {
400       aasink->ascii_parms.bright = g_value_get_int (value);
401       break;
402     }
403     case ARG_CONTRAST: {
404       aasink->ascii_parms.contrast = g_value_get_int (value);
405       break;
406     }
407     case ARG_GAMMA: {
408       aasink->ascii_parms.gamma = g_value_get_float (value);
409       break;
410     }
411     case ARG_INVERSION: {
412       aasink->ascii_parms.inversion = g_value_get_boolean (value);
413       break;
414     }
415     case ARG_RANDOMVAL: {
416       aasink->ascii_parms.randomval = g_value_get_int (value);
417       break;
418     }
419     default:
420       break;
421   }
422 }
423
424 static void
425 gst_aasink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
426 {
427   GstAASink *aasink;
428
429   /* it's not null if we got it, but it might not be ours */
430   aasink = GST_AASINK(object);
431
432   switch (prop_id) {
433     case ARG_WIDTH: {
434       g_value_set_int (value, aasink->ascii_surf.width);
435       break;
436     }
437     case ARG_HEIGHT: {
438       g_value_set_int (value, aasink->ascii_surf.height);
439       break;
440     }
441     case ARG_DRIVER: {
442       g_value_set_enum (value, aasink->aa_driver);
443       break;
444     }
445     case ARG_DITHER: {
446       g_value_set_enum (value, aasink->ascii_parms.dither);
447       break;
448     }
449     case ARG_BRIGHTNESS: {
450       g_value_set_int (value, aasink->ascii_parms.bright);
451       break;
452     }
453     case ARG_CONTRAST: {
454       g_value_set_int (value, aasink->ascii_parms.contrast);
455       break;
456     }
457     case ARG_GAMMA: {
458       g_value_set_float (value, aasink->ascii_parms.gamma);
459       break;
460     }
461     case ARG_INVERSION: {
462       g_value_set_boolean (value, aasink->ascii_parms.inversion);
463       break;
464     }
465     case ARG_RANDOMVAL: {
466       g_value_set_int (value, aasink->ascii_parms.randomval);
467       break;
468     }
469     case ARG_FRAMES_DISPLAYED: {
470       g_value_set_int (value, aasink->frames_displayed);
471       break;
472     }
473     case ARG_FRAME_TIME: {
474       g_value_set_int (value, aasink->frame_time/1000000);
475       break;
476     }
477     default: {
478       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
479       break;
480     }
481   }
482 }
483
484 static gboolean
485 gst_aasink_open (GstAASink *aasink)
486 {
487   g_return_val_if_fail (!GST_FLAG_IS_SET (aasink ,GST_AASINK_OPEN), FALSE);
488
489   aa_recommendhidisplay(aa_drivers[aasink->aa_driver]->shortname);
490
491   aasink->context = aa_autoinit (&aasink->ascii_surf);
492   if (aasink->context == NULL) {
493     GST_ELEMENT_ERROR (GST_ELEMENT (aasink), LIBRARY, TOO_LAZY, (NULL),
494                          ("error opening aalib context"));
495     return FALSE;
496   }
497   aa_autoinitkbd(aasink->context, 0);
498   aa_resizehandler(aasink->context, (void *)aa_resize);
499
500   GST_FLAG_SET (aasink, GST_AASINK_OPEN);
501
502   return TRUE;
503 }
504
505 static void
506 gst_aasink_close (GstAASink *aasink)
507 {
508   g_return_if_fail (GST_FLAG_IS_SET (aasink ,GST_AASINK_OPEN));
509
510   aa_close (aasink->context);
511
512   GST_FLAG_UNSET (aasink, GST_AASINK_OPEN);
513 }
514
515 static GstElementStateReturn
516 gst_aasink_change_state (GstElement *element)
517 {
518   g_return_val_if_fail (GST_IS_AASINK (element), GST_STATE_FAILURE);
519
520   if (GST_STATE_PENDING (element) == GST_STATE_NULL) {
521     if (GST_FLAG_IS_SET (element, GST_AASINK_OPEN))
522       gst_aasink_close (GST_AASINK (element));
523   } else {
524     if (!GST_FLAG_IS_SET (element, GST_AASINK_OPEN)) {
525       if (!gst_aasink_open (GST_AASINK (element)))
526         return GST_STATE_FAILURE;
527     }
528   }
529
530   if (GST_ELEMENT_CLASS (parent_class)->change_state)
531     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
532
533   return GST_STATE_SUCCESS;
534 }
535
536 static gboolean
537 plugin_init (GstPlugin *plugin)
538 {
539   if (!gst_element_register (plugin, "aasink", GST_RANK_NONE, GST_TYPE_AASINK))
540     return FALSE;
541
542   return TRUE;
543 }
544
545 GST_PLUGIN_DEFINE (
546   GST_VERSION_MAJOR,
547   GST_VERSION_MINOR,
548   "aasink",
549   "ASCII Art video sink",
550   plugin_init,
551   VERSION,
552   "GPL",
553   GST_PACKAGE,
554   GST_ORIGIN
555 )