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