gst-libs/gst/video/video.h: Fix caps template names to be understandable.
[platform/upstream/gst-plugins-good.git] / ext / libcaca / gstcacasink.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 #include <gst/navigation/navigation.h>
27
28 #include "gstcacasink.h"
29
30 /* elementfactory information */
31 static GstElementDetails gst_cacasink_details = {
32   "CACA sink",
33   "Sink/Video",
34   "A colored ASCII art videosink",
35   "Zeeshan Ali <zak147@yahoo.com>"
36 };
37
38 /* cacasink signals and args */
39 enum {
40   LAST_SIGNAL
41 };
42
43
44 enum {
45   ARG_0,
46   ARG_SCREEN_WIDTH,
47   ARG_SCREEN_HEIGHT,
48   ARG_DITHER,
49 };
50
51 static GstStaticPadTemplate sink_template =
52 GST_STATIC_PAD_TEMPLATE (
53   "sink",
54   GST_PAD_SINK,
55   GST_PAD_ALWAYS,
56   GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB));
57
58 static void     gst_cacasink_base_init  (gpointer g_class);
59 static void     gst_cacasink_class_init (GstCACASinkClass *klass);
60 static void     gst_cacasink_init               (GstCACASink *cacasink);
61 static void     gst_cacasink_interface_init     (GstImplementsInterfaceClass *klass);
62 static gboolean gst_cacasink_interface_supported (GstImplementsInterface *iface, GType type);
63 static void     gst_cacasink_navigation_init  (GstNavigationInterface *iface);
64 static void     gst_cacasink_navigation_send_event (GstNavigation *navigation, GstStructure *structure);
65
66 static void     gst_cacasink_chain      (GstPad *pad, GstData *_data);
67
68 static void     gst_cacasink_set_property       (GObject *object, guint prop_id, 
69                                          const GValue *value, GParamSpec *pspec);
70 static void     gst_cacasink_get_property       (GObject *object, guint prop_id, 
71                                          GValue *value, GParamSpec *pspec);
72
73 static GstElementStateReturn gst_cacasink_change_state (GstElement *element);
74
75 static GstElementClass *parent_class = NULL;
76
77 GType
78 gst_cacasink_get_type (void)
79 {
80   static GType cacasink_type = 0;
81
82   if (!cacasink_type) {
83     static const GTypeInfo cacasink_info = {
84       sizeof(GstCACASinkClass),
85       gst_cacasink_base_init,
86       NULL,
87       (GClassInitFunc) gst_cacasink_class_init,
88       NULL,
89       NULL,
90       sizeof(GstCACASink),
91       0,
92       (GInstanceInitFunc) gst_cacasink_init,
93     };
94     
95     static const GInterfaceInfo iface_info = {
96       (GInterfaceInitFunc) gst_cacasink_interface_init,
97       NULL,
98       NULL,
99     };
100
101     static const GInterfaceInfo navigation_info = {
102       (GInterfaceInitFunc) gst_cacasink_navigation_init,
103       NULL,
104       NULL,
105     };
106
107     cacasink_type = g_type_register_static (GST_TYPE_VIDEOSINK, "GstCACASink", &cacasink_info, 0);
108     
109     g_type_add_interface_static (cacasink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
110         &iface_info);
111     g_type_add_interface_static (cacasink_type, GST_TYPE_NAVIGATION,
112         &navigation_info);
113   }
114   return cacasink_type;
115 }
116
117 #define GST_TYPE_CACADITHER (gst_cacasink_dither_get_type())
118 static GType
119 gst_cacasink_dither_get_type (void)
120 {
121   static GType dither_type = 0;
122   if (!dither_type) {
123     GEnumValue *dithers;
124     gint n_dithers;
125     gint i;
126     gchar *caca_dithernames[] = {
127         "NONE", "ORDERED2", "ORDERED4", "ORDERED8", "RANDOM", NULL};
128
129     n_dithers = 5;
130     
131     dithers = g_new0(GEnumValue, n_dithers + 1);
132
133     for (i = 0; i < n_dithers; i++){
134       dithers[i].value = i;
135       dithers[i].value_name = g_strdup (caca_dithernames[i]);
136       dithers[i].value_nick = g_strdup (caca_dithernames[i]);
137     }
138     dithers[i].value = 0;
139     dithers[i].value_name = NULL;
140     dithers[i].value_nick = NULL;
141
142     dither_type = g_enum_register_static ("GstCACASinkDithers", dithers);
143   }
144   return dither_type;
145 }
146
147 static void
148 gst_cacasink_base_init (gpointer g_class)
149 {
150   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
151
152   gst_element_class_set_details (element_class, &gst_cacasink_details);
153   gst_element_class_add_pad_template (element_class, 
154     gst_static_pad_template_get (&sink_template));
155 }
156
157 static void
158 gst_cacasink_class_init (GstCACASinkClass *klass)
159 {
160   GObjectClass *gobject_class;
161   GstElementClass *gstelement_class;
162   GstVideoSinkClass *gstvs_class;
163
164   gobject_class = (GObjectClass*)klass;
165   gstelement_class = (GstElementClass*)klass;
166   gstvs_class = (GstVideoSinkClass*) klass;
167
168   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
169
170   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SCREEN_WIDTH,
171     g_param_spec_int("screen_width","screen_width","screen_width",
172                      G_MININT,G_MAXINT,0,G_PARAM_READABLE)); /* CHECKME */
173   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SCREEN_HEIGHT,
174     g_param_spec_int("screen_height","screen_height","screen_height",
175                      G_MININT,G_MAXINT,0,G_PARAM_READABLE)); /* CHECKME */
176   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DITHER,
177     g_param_spec_enum("dither","dither","dither",
178                       GST_TYPE_CACADITHER, 0, G_PARAM_READWRITE));
179
180   gobject_class->set_property = gst_cacasink_set_property;
181   gobject_class->get_property = gst_cacasink_get_property;
182
183   gstelement_class->change_state = gst_cacasink_change_state;
184 }
185
186 static void
187 gst_cacasink_interface_init (GstImplementsInterfaceClass *klass)
188 {
189   klass->supported = gst_cacasink_interface_supported;
190 }
191
192 static gboolean
193 gst_cacasink_interface_supported (GstImplementsInterface *iface, GType type)
194 {
195   g_assert (type == GST_TYPE_NAVIGATION);
196
197   return (GST_STATE (iface) != GST_STATE_NULL);
198 }
199
200 static void
201 gst_cacasink_navigation_init (GstNavigationInterface *iface)
202 {
203   iface->send_event = gst_cacasink_navigation_send_event;
204 }
205
206 static void
207 gst_cacasink_navigation_send_event (GstNavigation *navigation,
208     GstStructure *structure)
209 {
210   GstCACASink *cacasink = GST_CACASINK (navigation);
211   GstEvent *event;
212
213   event = gst_event_new (GST_EVENT_NAVIGATION);
214   /*GST_EVENT_TIMESTAMP (event) = 0;*/
215   event->event_data.structure.structure = structure;
216
217   /* FIXME 
218    * Obviously, the pointer x,y coordinates need to be adjusted by the
219    * window size and relation to the bounding window. */
220
221   gst_pad_send_event (gst_pad_get_peer (GST_VIDEOSINK_PAD(cacasink)),
222       event);
223 }
224 static GstPadLinkReturn
225 gst_cacasink_sinkconnect (GstPad *pad, const GstCaps *caps)
226 {
227   GstCACASink *cacasink;
228   GstStructure *structure;
229
230   cacasink = GST_CACASINK (gst_pad_get_parent (pad));
231
232   /*if (!GST_CAPS_IS_FIXED (caps))
233     return GST_PAD_LINK_DELAYED;*/
234   
235   structure = gst_caps_get_structure (caps, 0);
236   gst_structure_get_int (structure, "width",
237                     &(GST_VIDEOSINK_WIDTH (cacasink)));
238   gst_structure_get_int (structure, "height",
239                     &(GST_VIDEOSINK_HEIGHT (cacasink)));
240   gst_structure_get_int (structure, "bpp", &cacasink->bpp);
241   gst_structure_get_int (structure, "red_mask", &cacasink->red_mask);
242   gst_structure_get_int (structure, "green_mask", &cacasink->green_mask);
243   gst_structure_get_int (structure, "blue_mask", &cacasink->blue_mask);
244
245   gst_video_sink_got_video_size (GST_VIDEOSINK (cacasink), GST_VIDEOSINK_WIDTH (cacasink), GST_VIDEOSINK_HEIGHT (cacasink));
246
247   /*if (cacasink->bitmap != NULL) {
248     caca_free_bitmap (cacasink->bitmap);
249   }
250
251   caca->bitmap = caca_create_bitmap (cacasink->bpp, cacasink->image_width, cacasink->image_height, cacasink->image_width * cacasink->bpp/8, cacasink->red_mask, cacasink->green_mask, cacasink->blue_mask);*/
252
253   return GST_PAD_LINK_OK;
254 }
255
256 static void
257 gst_cacasink_init (GstCACASink *cacasink)
258 {
259   GST_VIDEOSINK_PAD (cacasink) = gst_pad_new_from_template (
260                   gst_static_pad_template_get (&sink_template), "sink");
261   gst_element_add_pad (GST_ELEMENT (cacasink), GST_VIDEOSINK_PAD (cacasink));
262   gst_pad_set_chain_function (GST_VIDEOSINK_PAD (cacasink), 
263                               gst_cacasink_chain);
264   gst_pad_set_link_function (GST_VIDEOSINK_PAD (cacasink), 
265                              gst_cacasink_sinkconnect);
266
267   cacasink->screen_width = GST_CACA_DEFAULT_SCREEN_WIDTH;
268   cacasink->screen_height = GST_CACA_DEFAULT_SCREEN_HEIGHT;
269   cacasink->bpp = GST_CACA_DEFAULT_BPP;
270   cacasink->red_mask = GST_CACA_DEFAULT_RED_MASK;
271   cacasink->green_mask = GST_CACA_DEFAULT_GREEN_MASK;
272   cacasink->blue_mask = GST_CACA_DEFAULT_BLUE_MASK;
273
274   cacasink->bitmap = NULL;
275
276   GST_FLAG_SET(cacasink, GST_ELEMENT_THREAD_SUGGESTED);
277 }
278
279 static void
280 gst_cacasink_chain (GstPad *pad, GstData *_data)
281 {
282   GstBuffer *buf = GST_BUFFER (_data);
283   GstCACASink *cacasink;
284   GstClockTime time = GST_BUFFER_TIMESTAMP (buf);
285   gint64 jitter;
286
287   g_return_if_fail (pad != NULL);
288   g_return_if_fail (GST_IS_PAD (pad));
289   g_return_if_fail (buf != NULL);
290
291   cacasink = GST_CACASINK (gst_pad_get_parent (pad));
292
293   if (GST_VIDEOSINK_CLOCK (cacasink) && time != -1) {
294     GstClockReturn ret;
295
296     cacasink->id = gst_clock_new_single_shot_id (
297                        GST_VIDEOSINK_CLOCK (cacasink), time);
298
299     GST_DEBUG ("videosink: clock %s wait: %" G_GUINT64_FORMAT " %u", 
300                GST_OBJECT_NAME (GST_VIDEOSINK_CLOCK (cacasink)),
301                time, GST_BUFFER_SIZE (buf));
302
303     ret = gst_clock_id_wait (cacasink->id, &jitter);
304     gst_clock_id_free (cacasink->id);
305     cacasink->id = NULL;
306   }
307
308   //caca_clear ();
309   caca_draw_bitmap (0, 0, cacasink->screen_width-1, cacasink->screen_height-1, cacasink->bitmap, GST_BUFFER_DATA (buf));
310   caca_refresh ();
311
312   if (GST_VIDEOSINK_CLOCK (cacasink)) {
313     jitter = gst_clock_get_time (GST_VIDEOSINK_CLOCK (cacasink)) - time;
314
315     cacasink->correction = (cacasink->correction + jitter) >> 1;
316     cacasink->correction = 0;
317   }
318
319
320   gst_buffer_unref(buf);
321 }
322
323
324 static void
325 gst_cacasink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
326 {
327   GstCACASink *cacasink;
328
329   /* it's not null if we got it, but it might not be ours */
330   g_return_if_fail (GST_IS_CACASINK (object));
331
332   cacasink = GST_CACASINK (object);
333
334   switch (prop_id) {
335     case ARG_DITHER: {
336       cacasink->dither = g_value_get_enum (value);
337       break;
338     }
339     default:
340       break;
341   }
342 }
343
344 static void
345 gst_cacasink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
346 {
347   GstCACASink *cacasink;
348
349   /* it's not null if we got it, but it might not be ours */
350   cacasink = GST_CACASINK(object);
351
352   switch (prop_id) {
353     case ARG_SCREEN_WIDTH: {
354       g_value_set_int (value, cacasink->screen_width);
355       break;
356     }
357     case ARG_SCREEN_HEIGHT: {
358       g_value_set_int (value, cacasink->screen_height);
359       break;
360     }
361     case ARG_DITHER: {
362       g_value_set_enum (value, cacasink->dither);
363       break;
364     }
365     default: {
366       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
367       break;
368     }
369   }
370 }
371
372 static gboolean
373 gst_cacasink_open (GstCACASink *cacasink)
374 {
375   g_return_val_if_fail (!GST_FLAG_IS_SET (cacasink ,GST_CACASINK_OPEN), FALSE);
376
377   caca_init ();
378
379   cacasink->screen_width = caca_get_width ();
380   cacasink->screen_height = caca_get_height ();
381   caca_set_dithering (cacasink->dither + CACA_DITHERING_NONE);
382
383   cacasink->bitmap = caca_create_bitmap (
384                         cacasink->bpp, 
385                         GST_VIDEOSINK_WIDTH (cacasink), 
386                         GST_VIDEOSINK_HEIGHT (cacasink), 
387                         GST_VIDEOSINK_WIDTH (cacasink) * cacasink->bpp/8, 
388                         cacasink->red_mask, 
389                         cacasink->green_mask, 
390                         cacasink->blue_mask,
391                         0);
392
393   GST_FLAG_SET (cacasink, GST_CACASINK_OPEN);
394
395   return TRUE;
396 }
397
398 static void
399 gst_cacasink_close (GstCACASink *cacasink)
400 {
401   g_return_if_fail (GST_FLAG_IS_SET (cacasink ,GST_CACASINK_OPEN));
402
403   caca_free_bitmap (cacasink->bitmap);
404   cacasink->bitmap = NULL;
405   caca_end ();
406
407   GST_FLAG_UNSET (cacasink, GST_CACASINK_OPEN);
408 }
409
410 static GstElementStateReturn
411 gst_cacasink_change_state (GstElement *element)
412 {
413   g_return_val_if_fail (GST_IS_CACASINK (element), GST_STATE_FAILURE);
414
415   if (GST_STATE_PENDING (element) == GST_STATE_NULL) {
416     if (GST_FLAG_IS_SET (element, GST_CACASINK_OPEN))
417       gst_cacasink_close (GST_CACASINK (element));
418   } else {
419     if (!GST_FLAG_IS_SET (element, GST_CACASINK_OPEN)) {
420       if (!gst_cacasink_open (GST_CACASINK (element)))
421         return GST_STATE_FAILURE;
422     }
423   }
424
425   if (GST_ELEMENT_CLASS (parent_class)->change_state)
426     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
427
428   return GST_STATE_SUCCESS;
429 }
430
431 static gboolean
432 plugin_init (GstPlugin *plugin)
433 {
434   /* Loading the library containing GstVideoSink, our parent object */
435   if (!gst_library_load ("gstvideo"))
436     return FALSE;
437   
438   if (!gst_element_register (plugin, "cacasink", GST_RANK_NONE, GST_TYPE_CACASINK))
439     return FALSE;
440
441   return TRUE;
442 }
443
444 GST_PLUGIN_DEFINE (
445   GST_VERSION_MAJOR,
446   GST_VERSION_MINOR,
447   "cacasink",
448   "Colored ASCII Art video sink",
449   plugin_init,
450   VERSION,
451   GST_LICENSE,
452   GST_PACKAGE,
453   GST_ORIGIN
454 )