2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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.
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.
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.
26 #include <gst/navigation/navigation.h>
28 #include "gstcacasink.h"
30 /* elementfactory information */
31 static GstElementDetails gst_cacasink_details = {
34 "A colored ASCII art videosink",
35 "Zeeshan Ali <zak147@yahoo.com>"
38 /* cacasink signals and args */
52 static GstStaticPadTemplate sink_template =
53 GST_STATIC_PAD_TEMPLATE (
57 GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_RGB_16 ";" GST_VIDEO_CAPS_RGB_15)
60 static void gst_cacasink_base_init (gpointer g_class);
61 static void gst_cacasink_class_init (GstCACASinkClass *klass);
62 static void gst_cacasink_init (GstCACASink *cacasink);
63 static void gst_cacasink_interface_init (GstImplementsInterfaceClass *klass);
64 static gboolean gst_cacasink_interface_supported (GstImplementsInterface *iface,
66 static void gst_cacasink_navigation_init (GstNavigationInterface *iface);
67 static void gst_cacasink_navigation_send_event (GstNavigation *navigation,
68 GstStructure *structure);
70 static void gst_cacasink_chain (GstPad *pad, GstData *_data);
72 static void gst_cacasink_set_property (GObject *object, guint prop_id,
73 const GValue *value, GParamSpec *pspec);
74 static void gst_cacasink_get_property (GObject *object, guint prop_id,
75 GValue *value, GParamSpec *pspec);
76 static void gst_cacasink_dispose (GObject *object);
78 static GstElementStateReturn gst_cacasink_change_state (GstElement *element);
80 static GstElementClass *parent_class = NULL;
83 gst_cacasink_get_type (void)
85 static GType cacasink_type = 0;
88 static const GTypeInfo cacasink_info = {
89 sizeof(GstCACASinkClass),
90 gst_cacasink_base_init,
92 (GClassInitFunc) gst_cacasink_class_init,
97 (GInstanceInitFunc) gst_cacasink_init,
100 static const GInterfaceInfo iface_info = {
101 (GInterfaceInitFunc) gst_cacasink_interface_init,
106 static const GInterfaceInfo navigation_info = {
107 (GInterfaceInitFunc) gst_cacasink_navigation_init,
112 cacasink_type = g_type_register_static (GST_TYPE_VIDEOSINK, "GstCACASink", &cacasink_info, 0);
114 g_type_add_interface_static (cacasink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
116 g_type_add_interface_static (cacasink_type, GST_TYPE_NAVIGATION,
119 return cacasink_type;
122 #define GST_TYPE_CACADITHER (gst_cacasink_dither_get_type())
124 gst_cacasink_dither_get_type (void)
126 static GType dither_type = 0;
131 gchar *caca_dithernames[] = {
132 "NONE", "ORDERED2", "ORDERED4", "ORDERED8", "RANDOM", NULL};
136 dithers = g_new0(GEnumValue, n_dithers + 1);
138 for (i = 0; i < n_dithers; i++){
139 dithers[i].value = i;
140 dithers[i].value_name = g_strdup (caca_dithernames[i]);
141 dithers[i].value_nick = g_strdup (caca_dithernames[i]);
143 dithers[i].value = 0;
144 dithers[i].value_name = NULL;
145 dithers[i].value_nick = NULL;
147 dither_type = g_enum_register_static ("GstCACASinkDithers", dithers);
153 gst_cacasink_base_init (gpointer g_class)
155 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
157 gst_element_class_set_details (element_class, &gst_cacasink_details);
158 gst_element_class_add_pad_template (element_class,
159 gst_static_pad_template_get (&sink_template));
163 gst_cacasink_class_init (GstCACASinkClass *klass)
165 GObjectClass *gobject_class;
166 GstElementClass *gstelement_class;
167 GstVideoSinkClass *gstvs_class;
169 gobject_class = (GObjectClass*)klass;
170 gstelement_class = (GstElementClass*)klass;
171 gstvs_class = (GstVideoSinkClass*) klass;
173 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
175 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SCREEN_WIDTH,
176 g_param_spec_int("screen_width","screen_width","screen_width",
177 G_MININT,G_MAXINT,0,G_PARAM_READABLE)); /* CHECKME */
178 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SCREEN_HEIGHT,
179 g_param_spec_int("screen_height","screen_height","screen_height",
180 G_MININT,G_MAXINT,0,G_PARAM_READABLE)); /* CHECKME */
181 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DITHER,
182 g_param_spec_enum("dither","Dither Type","Set type of Dither",
183 GST_TYPE_CACADITHER, 0, G_PARAM_READWRITE));
184 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANTIALIASING,
185 g_param_spec_boolean ("anti_aliasing", "Anti-Aliasing",
186 "Enables Anti-Aliasing", TRUE, G_PARAM_READWRITE));
188 gobject_class->set_property = gst_cacasink_set_property;
189 gobject_class->get_property = gst_cacasink_get_property;
190 gobject_class->dispose = gst_cacasink_dispose;
192 gstelement_class->change_state = gst_cacasink_change_state;
196 gst_cacasink_interface_init (GstImplementsInterfaceClass *klass)
198 klass->supported = gst_cacasink_interface_supported;
202 gst_cacasink_interface_supported (GstImplementsInterface *iface, GType type)
204 g_assert (type == GST_TYPE_NAVIGATION);
206 return (GST_STATE (iface) != GST_STATE_NULL);
210 gst_cacasink_navigation_init (GstNavigationInterface *iface)
212 iface->send_event = gst_cacasink_navigation_send_event;
216 gst_cacasink_navigation_send_event (GstNavigation *navigation,
217 GstStructure *structure)
219 GstCACASink *cacasink = GST_CACASINK (navigation);
222 event = gst_event_new (GST_EVENT_NAVIGATION);
223 /*GST_EVENT_TIMESTAMP (event) = 0;*/
224 event->event_data.structure.structure = structure;
227 * Obviously, the pointer x,y coordinates need to be adjusted by the
228 * window size and relation to the bounding window. */
230 gst_pad_send_event (gst_pad_get_peer (GST_VIDEOSINK_PAD(cacasink)),
233 static GstPadLinkReturn
234 gst_cacasink_sinkconnect (GstPad *pad, const GstCaps *caps)
236 GstCACASink *cacasink;
237 GstStructure *structure;
239 cacasink = GST_CACASINK (gst_pad_get_parent (pad));
241 /*if (!GST_CAPS_IS_FIXED (caps))
242 return GST_PAD_LINK_DELAYED;*/
244 structure = gst_caps_get_structure (caps, 0);
245 gst_structure_get_int (structure, "width",
246 &(GST_VIDEOSINK_WIDTH (cacasink)));
247 gst_structure_get_int (structure, "height",
248 &(GST_VIDEOSINK_HEIGHT (cacasink)));
249 gst_structure_get_int (structure, "bpp", &cacasink->bpp);
250 gst_structure_get_int (structure, "red_mask", &cacasink->red_mask);
251 gst_structure_get_int (structure, "green_mask", &cacasink->green_mask);
252 gst_structure_get_int (structure, "blue_mask", &cacasink->blue_mask);
254 if (cacasink->bpp == 24) {
255 cacasink->red_mask = GUINT32_FROM_BE (cacasink->red_mask) >> 8;
256 cacasink->green_mask = GUINT32_FROM_BE (cacasink->green_mask) >> 8;
257 cacasink->blue_mask = GUINT32_FROM_BE (cacasink->blue_mask) >> 8;
260 else if (cacasink->bpp == 32) {
261 cacasink->red_mask = GUINT32_FROM_BE (cacasink->red_mask);
262 cacasink->green_mask = GUINT32_FROM_BE (cacasink->green_mask);
263 cacasink->blue_mask = GUINT32_FROM_BE (cacasink->blue_mask);
266 else if (cacasink->bpp == 16 || cacasink->bpp == 15) {
267 cacasink->red_mask = GUINT16_FROM_BE (cacasink->red_mask);
268 cacasink->green_mask = GUINT16_FROM_BE (cacasink->green_mask);
269 cacasink->blue_mask = GUINT16_FROM_BE (cacasink->blue_mask);
272 if (cacasink->bitmap) {
273 caca_free_bitmap (cacasink->bitmap);
276 cacasink->bitmap = caca_create_bitmap (
278 GST_VIDEOSINK_WIDTH (cacasink),
279 GST_VIDEOSINK_HEIGHT (cacasink),
280 GST_VIDEOSINK_WIDTH (cacasink) * cacasink->bpp/8,
282 cacasink->green_mask,
286 if (!cacasink->bitmap) {
287 return GST_PAD_LINK_DELAYED;
290 return GST_PAD_LINK_OK;
294 gst_cacasink_init (GstCACASink *cacasink)
296 GST_VIDEOSINK_PAD (cacasink) = gst_pad_new_from_template (
297 gst_static_pad_template_get (&sink_template), "sink");
298 gst_element_add_pad (GST_ELEMENT (cacasink), GST_VIDEOSINK_PAD (cacasink));
299 gst_pad_set_chain_function (GST_VIDEOSINK_PAD (cacasink),
301 gst_pad_set_link_function (GST_VIDEOSINK_PAD (cacasink),
302 gst_cacasink_sinkconnect);
304 cacasink->screen_width = GST_CACA_DEFAULT_SCREEN_WIDTH;
305 cacasink->screen_height = GST_CACA_DEFAULT_SCREEN_HEIGHT;
306 cacasink->bpp = GST_CACA_DEFAULT_BPP;
307 cacasink->red_mask = GST_CACA_DEFAULT_RED_MASK;
308 cacasink->green_mask = GST_CACA_DEFAULT_GREEN_MASK;
309 cacasink->blue_mask = GST_CACA_DEFAULT_BLUE_MASK;
311 cacasink->bitmap = NULL;
314 cacasink->screen_width = caca_get_width ();
315 cacasink->screen_height = caca_get_height ();
316 cacasink->antialiasing = TRUE;
317 caca_set_feature (CACA_ANTIALIASING_MAX);
318 cacasink->dither = 0;
319 caca_set_dithering (CACA_DITHERING_NONE);
321 GST_FLAG_SET(cacasink, GST_ELEMENT_THREAD_SUGGESTED);
325 gst_cacasink_chain (GstPad *pad, GstData *_data)
327 GstBuffer *buf = GST_BUFFER (_data);
328 GstCACASink *cacasink;
329 GstClockTime time = GST_BUFFER_TIMESTAMP (buf);
332 g_return_if_fail (pad != NULL);
333 g_return_if_fail (GST_IS_PAD (pad));
334 g_return_if_fail (buf != NULL);
336 cacasink = GST_CACASINK (gst_pad_get_parent (pad));
338 if (GST_VIDEOSINK_CLOCK (cacasink) && time != -1) {
341 cacasink->id = gst_clock_new_single_shot_id (
342 GST_VIDEOSINK_CLOCK (cacasink), time);
344 GST_DEBUG ("videosink: clock %s wait: %" G_GUINT64_FORMAT " %u",
345 GST_OBJECT_NAME (GST_VIDEOSINK_CLOCK (cacasink)),
346 time, GST_BUFFER_SIZE (buf));
348 ret = gst_clock_id_wait (cacasink->id, &jitter);
349 gst_clock_id_free (cacasink->id);
354 caca_draw_bitmap (0, 0, cacasink->screen_width-1, cacasink->screen_height-1, cacasink->bitmap, GST_BUFFER_DATA (buf));
357 if (GST_VIDEOSINK_CLOCK (cacasink)) {
358 jitter = gst_clock_get_time (GST_VIDEOSINK_CLOCK (cacasink)) - time;
360 cacasink->correction = (cacasink->correction + jitter) >> 1;
361 cacasink->correction = 0;
364 gst_buffer_unref(buf);
369 gst_cacasink_dispose (GObject *object)
371 GstCACASink *cacasink = GST_CACASINK (object);
373 if (cacasink->bitmap) {
374 caca_free_bitmap (cacasink->bitmap);
379 G_OBJECT_CLASS (parent_class)->dispose (object);
383 gst_cacasink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
385 GstCACASink *cacasink;
387 /* it's not null if we got it, but it might not be ours */
388 g_return_if_fail (GST_IS_CACASINK (object));
390 cacasink = GST_CACASINK (object);
394 cacasink->dither = g_value_get_enum (value);
395 caca_set_dithering (cacasink->dither + CACA_DITHERING_NONE);
398 case ARG_ANTIALIASING: {
399 cacasink->antialiasing = g_value_get_boolean (value);
400 if (cacasink->antialiasing) {
401 caca_set_feature (CACA_ANTIALIASING_MAX);
405 caca_set_feature (CACA_ANTIALIASING_MIN);
415 gst_cacasink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
417 GstCACASink *cacasink;
419 /* it's not null if we got it, but it might not be ours */
420 cacasink = GST_CACASINK(object);
423 case ARG_SCREEN_WIDTH: {
424 g_value_set_int (value, cacasink->screen_width);
427 case ARG_SCREEN_HEIGHT: {
428 g_value_set_int (value, cacasink->screen_height);
432 g_value_set_enum (value, cacasink->dither);
435 case ARG_ANTIALIASING: {
436 g_value_set_boolean (value, cacasink->antialiasing);
440 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
447 gst_cacasink_open (GstCACASink *cacasink)
449 g_return_val_if_fail (!GST_FLAG_IS_SET (cacasink ,GST_CACASINK_OPEN), FALSE);
451 GST_FLAG_SET (cacasink, GST_CACASINK_OPEN);
457 gst_cacasink_close (GstCACASink *cacasink)
459 g_return_if_fail (GST_FLAG_IS_SET (cacasink ,GST_CACASINK_OPEN));
461 GST_FLAG_UNSET (cacasink, GST_CACASINK_OPEN);
464 static GstElementStateReturn
465 gst_cacasink_change_state (GstElement *element)
467 g_return_val_if_fail (GST_IS_CACASINK (element), GST_STATE_FAILURE);
469 if (GST_STATE_PENDING (element) == GST_STATE_NULL) {
470 if (GST_FLAG_IS_SET (element, GST_CACASINK_OPEN))
471 gst_cacasink_close (GST_CACASINK (element));
473 if (!GST_FLAG_IS_SET (element, GST_CACASINK_OPEN)) {
474 if (!gst_cacasink_open (GST_CACASINK (element)))
475 return GST_STATE_FAILURE;
479 if (GST_ELEMENT_CLASS (parent_class)->change_state)
480 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
482 return GST_STATE_SUCCESS;
486 plugin_init (GstPlugin *plugin)
488 /* Loading the library containing GstVideoSink, our parent object */
489 if (!gst_library_load ("gstvideo"))
492 if (!gst_element_register (plugin, "cacasink", GST_RANK_NONE, GST_TYPE_CACASINK))
502 "Colored ASCII Art video sink",