2 * Copyright (C) <2003> Julien Moutte <julien@moutte.net>
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.
25 #include <gst/navigation/navigation.h>
26 #include <gst/xoverlay/xoverlay.h>
27 #include <gst/colorbalance/colorbalance.h>
30 #include "xvimagesink.h"
32 static void gst_xvimagesink_buffer_free (GstBuffer *buffer);
34 /* ElementFactory information */
35 static GstElementDetails gst_xvimagesink_details = GST_ELEMENT_DETAILS (
38 "A Xv based videosink",
39 "Julien Moutte <julien@moutte.net>"
42 /* Default template - initiated with class struct to allow gst-register to work
44 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
45 GST_STATIC_PAD_TEMPLATE (
51 "framerate = (double) [ 1.0, 100.0 ], "
52 "width = (int) [ 0, MAX ], "
53 "height = (int) [ 0, MAX ]; "
55 "framerate = (double) [ 1.0, 100.0 ], "
56 "width = (int) [ 0, MAX ], "
57 "height = (int) [ 0, MAX ]"
72 static GstVideoSinkClass *parent_class = NULL;
73 static gboolean error_catched = FALSE;
75 /* ============================================================= */
79 /* ============================================================= */
84 gst_xvimagesink_handle_xerror (Display *display, XErrorEvent *xevent)
86 char error_msg [1024];
87 XGetErrorText (display, xevent->error_code, error_msg, 1024);
88 GST_DEBUG ("xvimagesink failed to use XShm calls. error: %s",
94 /* This function checks that it is actually really possible to create an image
97 gst_xvimagesink_check_xshm_calls (GstXContext *xcontext)
99 GstXvImage *xvimage = NULL;
100 int (*handler)(Display *, XErrorEvent *);
102 g_return_val_if_fail (xcontext != NULL, FALSE);
105 xvimage = g_new0 (GstXvImage, 1);
107 /* Setting an error handler to catch failure */
108 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
110 xvimage->size = (xcontext->bpp / 8);
112 /* Trying to create a 1x1 picture */
113 xvimage->xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
114 xcontext->im_format, NULL, 1, 1,
117 xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
119 xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, 0, 0);
120 xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
121 xvimage->SHMInfo.readOnly = FALSE;
123 XShmAttach (xcontext->disp, &xvimage->SHMInfo);
125 error_catched = FALSE;
127 XSync(xcontext->disp, 0);
129 XSetErrorHandler (handler);
132 { /* Failed, detaching shared memory, destroying image and telling we can't
134 error_catched = FALSE;
135 XFree (xvimage->xvimage);
136 shmdt (xvimage->SHMInfo.shmaddr);
137 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0);
139 XSync (xcontext->disp, FALSE);
144 XShmDetach (xcontext->disp, &xvimage->SHMInfo);
145 XFree (xvimage->xvimage);
146 shmdt (xvimage->SHMInfo.shmaddr);
147 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0);
149 XSync (xcontext->disp, FALSE);
151 #endif /* HAVE_XSHM */
156 /* This function handles GstXvImage creation depending on XShm availability */
158 gst_xvimagesink_xvimage_new (GstXvImageSink *xvimagesink,
159 gint width, gint height)
161 GstXvImage *xvimage = NULL;
163 g_return_val_if_fail (xvimagesink != NULL, NULL);
164 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
166 xvimage = g_new0 (GstXvImage, 1);
168 xvimage->width = width;
169 xvimage->height = height;
170 xvimage->data = NULL;
171 xvimage->xvimagesink = xvimagesink;
173 g_mutex_lock (xvimagesink->x_lock);
175 xvimage->size = (xvimagesink->xcontext->bpp / 8) * xvimage->width * xvimage->height;
178 if (xvimagesink->xcontext->use_xshm)
180 xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
181 xvimagesink->xcontext->xv_port_id,
182 xvimagesink->xcontext->im_format,
183 NULL, xvimage->width,
184 xvimage->height, &xvimage->SHMInfo);
186 xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
189 xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, 0, 0);
190 xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
192 xvimage->SHMInfo.readOnly = FALSE;
194 XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
197 #endif /* HAVE_XSHM */
199 xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
200 xvimagesink->xcontext->xv_port_id,
201 xvimagesink->xcontext->im_format,
203 xvimage->width, xvimage->height);
205 xvimage->data = g_malloc (xvimage->xvimage->data_size);
208 if (xvimage->xvimage)
210 XSync (xvimagesink->xcontext->disp, FALSE);
215 g_free (xvimage->data);
222 g_mutex_unlock (xvimagesink->x_lock);
227 /* This function destroys a GstXvImage handling XShm availability */
229 gst_xvimagesink_xvimage_destroy (GstXvImageSink *xvimagesink,
232 g_return_if_fail (xvimage != NULL);
233 g_return_if_fail (xvimagesink != NULL);
234 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
236 g_mutex_lock (xvimagesink->x_lock);
239 if (xvimagesink->xcontext->use_xshm)
241 if (xvimage->SHMInfo.shmaddr)
242 XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
244 if (xvimage->xvimage)
245 XFree (xvimage->xvimage);
247 if (xvimage->SHMInfo.shmaddr)
248 shmdt (xvimage->SHMInfo.shmaddr);
250 if (xvimage->SHMInfo.shmid > 0)
251 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, 0);
254 #endif /* HAVE_XSHM */
256 if (xvimage->xvimage)
257 XFree (xvimage->xvimage);
260 XSync (xvimagesink->xcontext->disp, FALSE);
262 g_mutex_unlock (xvimagesink->x_lock);
267 /* This function puts a GstXvImage on a GstXvImageSink's window */
269 gst_xvimagesink_xvimage_put (GstXvImageSink *xvimagesink, GstXvImage *xvimage)
271 g_return_if_fail (xvimage != NULL);
272 g_return_if_fail (xvimagesink != NULL);
273 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
275 g_mutex_lock (xvimagesink->x_lock);
277 /* We scale to the window's geometry */
279 if (xvimagesink->xcontext->use_xshm)
281 XvShmPutImage (xvimagesink->xcontext->disp,
282 xvimagesink->xcontext->xv_port_id,
283 xvimagesink->xwindow->win,
284 xvimagesink->xwindow->gc, xvimage->xvimage,
285 0, 0, xvimage->width, xvimage->height,
286 0, 0, xvimagesink->xwindow->width,
287 xvimagesink->xwindow->height, FALSE);
290 #endif /* HAVE_XSHM */
292 XvPutImage (xvimagesink->xcontext->disp,
293 xvimagesink->xcontext->xv_port_id,
294 xvimagesink->xwindow->win,
295 xvimagesink->xwindow->gc, xvimage->xvimage,
296 0, 0, xvimage->width, xvimage->height,
297 0, 0, xvimagesink->xwindow->width,
298 xvimagesink->xwindow->height);
301 XSync(xvimagesink->xcontext->disp, FALSE);
303 g_mutex_unlock (xvimagesink->x_lock);
306 /* This function handles a GstXWindow creation */
308 gst_xvimagesink_xwindow_new (GstXvImageSink *xvimagesink,
309 gint width, gint height)
311 GstXWindow *xwindow = NULL;
314 g_return_val_if_fail (xvimagesink != NULL, NULL);
315 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
317 xwindow = g_new0 (GstXWindow, 1);
319 xwindow->width = width;
320 xwindow->height = height;
321 xwindow->internal = TRUE;
323 g_mutex_lock (xvimagesink->x_lock);
325 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
326 xvimagesink->xcontext->root,
327 0, 0, xwindow->width, xwindow->height,
328 0, 0, xvimagesink->xcontext->black);
330 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
331 StructureNotifyMask | PointerMotionMask | KeyPressMask |
332 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
334 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
335 xwindow->win, 0, &values);
337 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
339 XSync(xvimagesink->xcontext->disp, FALSE);
341 g_mutex_unlock (xvimagesink->x_lock);
343 gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (xvimagesink), xwindow->win);
348 /* This function destroys a GstXWindow */
350 gst_xvimagesink_xwindow_destroy (GstXvImageSink *xvimagesink, GstXWindow *xwindow)
352 g_return_if_fail (xwindow != NULL);
353 g_return_if_fail (xvimagesink != NULL);
354 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
356 g_mutex_lock (xvimagesink->x_lock);
358 /* If we did not create that window we just free the GC and let it live */
359 if (xwindow->internal)
360 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
362 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
364 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
366 XSync(xvimagesink->xcontext->disp, FALSE);
368 g_mutex_unlock (xvimagesink->x_lock);
373 /* This function resizes a GstXWindow */
375 gst_xvimagesink_xwindow_resize (GstXvImageSink *xvimagesink,
376 GstXWindow *xwindow, guint width, guint height)
378 g_return_if_fail (xwindow != NULL);
379 g_return_if_fail (xvimagesink != NULL);
380 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
382 g_mutex_lock (xvimagesink->x_lock);
384 xwindow->width = width;
385 xwindow->height = height;
387 XResizeWindow (xvimagesink->xcontext->disp, xwindow->win,
388 xwindow->width, xwindow->height);
390 XSync(xvimagesink->xcontext->disp, FALSE);
392 g_mutex_unlock (xvimagesink->x_lock);
395 /* This function commits our internal colorbalance settings to our grabbed Xv
396 port. If the xcontext is not initialized yet it simply returns */
398 gst_xvimagesink_update_colorbalance (GstXvImageSink *xvimagesink)
400 GList *channels = NULL;
402 g_return_if_fail (xvimagesink != NULL);
403 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
405 /* If we haven't initialized the X context we can't update anything */
406 if (xvimagesink->xcontext == NULL)
409 /* For each channel of the colorbalance we calculate the correct value
410 doing range conversion and then set the Xv port attribute to match our
412 channels = xvimagesink->xcontext->channels_list;
416 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data))
418 GstColorBalanceChannel *channel = NULL;
420 gdouble convert_coef;
422 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
423 g_object_ref (channel);
425 /* Our range conversion coef */
426 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
428 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0)
430 value = (xvimagesink->hue + 1000) * convert_coef +
433 else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0)
435 value = (xvimagesink->saturation + 1000) * convert_coef +
438 else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0)
440 value = (xvimagesink->contrast + 1000) * convert_coef +
443 else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0)
445 value = (xvimagesink->brightness + 1000) * convert_coef +
450 g_warning ("got an unknown channel %s", channel->label);
451 g_object_unref (channel);
455 /* Committing to Xv port */
456 g_mutex_lock (xvimagesink->x_lock);
457 XvSetPortAttribute (xvimagesink->xcontext->disp,
458 xvimagesink->xcontext->xv_port_id,
459 XInternAtom (xvimagesink->xcontext->disp,
460 channel->label, 1), value);
461 g_mutex_unlock (xvimagesink->x_lock);
463 g_object_unref (channel);
465 channels = g_list_next (channels);
469 /* This function handles XEvents that might be in the queue. It generates
470 GstEvent that will be sent upstream in the pipeline to handle interactivity
471 and navigation. It will also listen for configure events on the window to
472 trigger caps renegotiation so on the fly software scaling can work. */
474 gst_xvimagesink_handle_xevents (GstXvImageSink *xvimagesink, GstPad *pad)
478 g_return_if_fail (xvimagesink != NULL);
479 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
481 /* We get all events on our window to throw them upstream */
482 g_mutex_lock (xvimagesink->x_lock);
483 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
484 xvimagesink->xwindow->win,
485 ExposureMask | StructureNotifyMask |
486 PointerMotionMask | KeyPressMask |
487 KeyReleaseMask | ButtonPressMask |
488 ButtonReleaseMask, &e))
492 /* We lock only for the X function call */
493 g_mutex_unlock (xvimagesink->x_lock);
497 case ConfigureNotify:
498 /* Window got resized or moved. We update our data. */
499 GST_DEBUG ("xvimagesink window is at %d, %d with geometry : %d,%d",
500 e.xconfigure.x, e.xconfigure.y,
501 e.xconfigure.width, e.xconfigure.height);
502 xvimagesink->xwindow->width = e.xconfigure.width;
503 xvimagesink->xwindow->height = e.xconfigure.height;
506 /* Mouse pointer moved over our window. We send upstream
507 events for interactivity/navigation */
508 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
509 e.xmotion.x, e.xmotion.y);
510 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
512 e.xmotion.x, e.xmotion.y);
515 /* Mouse button pressed over our window. We send upstream
516 events for interactivity/navigation */
517 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
518 e.xbutton.button, e.xbutton.x, e.xbutton.y);
519 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
520 "mouse-button-press",
522 e.xbutton.x, e.xbutton.y);
525 /* Mouse button released over our window. We send upstream
526 events for interactivity/navigation */
527 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
528 e.xbutton.button, e.xbutton.x, e.xbutton.y);
529 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
530 "mouse-button-release",
532 e.xbutton.x, e.xbutton.y);
536 /* Key pressed/released over our window. We send upstream
537 events for interactivity/navigation */
538 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
539 e.xkey.keycode, e.xkey.x, e.xkey.y);
540 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
542 if (keysym != NoSymbol) {
543 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
545 "key-press" : "key-release",
546 XKeysymToString (keysym));
549 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
551 "key-press" : "key-release",
556 GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
559 g_mutex_lock (xvimagesink->x_lock);
561 g_mutex_unlock (xvimagesink->x_lock);
564 /* This function generates a caps with all supported format by the first
565 Xv grabable port we find. We store each one of the supported formats in a
566 format list and append the format to a newly created caps that we return */
568 gst_xvimagesink_get_xv_support (GstXContext *xcontext)
571 XvAdaptorInfo *adaptors;
573 g_return_val_if_fail (xcontext != NULL, NULL);
575 /* First let's check that XVideo extension is available */
576 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i))
578 GST_DEBUG ("XVideo extension is not available");
582 /* Then we get adaptors list */
583 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
584 &nb_adaptors, &adaptors))
586 GST_DEBUG ("Failed getting XV adaptors list");
590 xcontext->xv_port_id = 0;
592 GST_DEBUG ("Found %d XV adaptor(s)", nb_adaptors);
594 /* Now search for an adaptor that supports XvImageMask */
595 for (i = 0; i < nb_adaptors && !xcontext->xv_port_id; i++)
597 if (adaptors[i].type & XvImageMask)
601 /* We found such an adaptor, looking for an available port */
602 for (j = 0; j < adaptors[i].num_ports && !xcontext->xv_port_id; j++)
604 /* We try to grab the port */
605 if (Success == XvGrabPort (xcontext->disp,
606 adaptors[i].base_id + j, 0))
608 xcontext->xv_port_id = adaptors[i].base_id + j;
613 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[i].name,
614 adaptors[i].num_ports);
617 XvFreeAdaptorInfo (adaptors);
619 if (xcontext->xv_port_id)
622 XvImageFormatValues *formats = NULL;
623 GstCaps *caps = NULL;
625 /* We get all image formats supported by our port */
626 formats = XvListImageFormats (xcontext->disp,
627 xcontext->xv_port_id, &nb_formats);
628 caps = gst_caps_new_empty ();
629 for (i = 0; i < nb_formats; i++)
631 GstCaps *format_caps = NULL;
633 /* We set the image format of the xcontext to an existing one. Sink
634 connect method will override that but we need to have at least a
635 valid image format so that we can make our xshm calls check before
636 caps negotiation really happens. */
637 xcontext->im_format = formats[i].id;
639 switch (formats[i].type)
643 format_caps = gst_caps_new_simple ("video/x-raw-rgb",
644 "endianness", G_TYPE_INT, xcontext->endianness,
645 "depth", G_TYPE_INT, xcontext->depth,
646 "bpp", G_TYPE_INT, xcontext->bpp,
647 "blue_mask", G_TYPE_INT, formats[i].red_mask,
648 "green_mask", G_TYPE_INT, formats[i].green_mask,
649 "red_mask", G_TYPE_INT, formats[i].blue_mask,
650 "width", GST_TYPE_INT_RANGE, 0, G_MAXINT,
651 "height", GST_TYPE_INT_RANGE, 0, G_MAXINT,
652 "framerate", GST_TYPE_DOUBLE_RANGE, 1.0, 100.0,
655 /* For RGB caps we store them and the image
656 format so that we can get back the format
657 when sinkconnect will give us a caps without
661 GstXvImageFormat *format = NULL;
662 format = g_new0 (GstXvImageFormat, 1);
665 format->format = formats[i].id;
666 format->caps = gst_caps_copy (format_caps);
667 xcontext->formats_list = g_list_append (
668 xcontext->formats_list, format);
674 format_caps = gst_caps_new_simple ("video/x-raw-yuv",
675 "format", GST_TYPE_FOURCC,formats[i].id,
676 "width", GST_TYPE_INT_RANGE, 0, G_MAXINT,
677 "height", GST_TYPE_INT_RANGE, 0, G_MAXINT,
678 "framerate", GST_TYPE_DOUBLE_RANGE, 1.0, 100.0,
682 g_assert_not_reached();
686 gst_caps_append (caps, format_caps);
692 GST_DEBUG_CAPS ("Generated the following caps", caps);
700 /* This function get the X Display and global infos about it. Everything is
701 stored in our object and will be cleaned when the object is disposed. Note
702 here that caps for supported format are generated without any window or
705 gst_xvimagesink_xcontext_get (GstXvImageSink *xvimagesink)
707 GstXContext *xcontext = NULL;
708 XPixmapFormatValues *px_formats = NULL;
709 gint nb_formats = 0, i, j, N_attr;
710 XvAttribute *xv_attr;
711 char *channels[4] = { "XV_HUE", "XV_SATURATION",
712 "XV_BRIGHTNESS", "XV_CONTRAST" };
714 g_return_val_if_fail (xvimagesink != NULL, NULL);
715 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
717 xcontext = g_new0 (GstXContext, 1);
719 g_mutex_lock (xvimagesink->x_lock);
721 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
725 g_mutex_unlock (xvimagesink->x_lock);
727 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, TOO_LAZY, (NULL),
728 ("Could not open display"));
732 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
733 xcontext->screen_num = DefaultScreen (xcontext->disp);
734 xcontext->visual = DefaultVisual(xcontext->disp, xcontext->screen_num);
735 xcontext->root = DefaultRootWindow (xcontext->disp);
736 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
737 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
738 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
740 /* We get supported pixmap formats at supported depth */
741 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
745 XCloseDisplay (xcontext->disp);
746 g_mutex_unlock (xvimagesink->x_lock);
751 /* We get bpp value corresponding to our running depth */
752 for (i=0; i<nb_formats; i++)
754 if (px_formats[i].depth == xcontext->depth)
755 xcontext->bpp = px_formats[i].bits_per_pixel;
760 xcontext->endianness = (ImageByteOrder (xcontext->disp) == LSBFirst) ? G_LITTLE_ENDIAN:G_BIG_ENDIAN;
762 /* our caps system handles 24/32bpp RGB as big-endian. */
763 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
764 xcontext->endianness == G_LITTLE_ENDIAN) {
765 xcontext->endianness = G_BIG_ENDIAN;
766 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
767 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
768 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
769 if (xcontext->bpp == 24) {
770 xcontext->visual->red_mask >>= 8;
771 xcontext->visual->green_mask >>= 8;
772 xcontext->visual->blue_mask >>= 8;
776 xcontext->caps = gst_xvimagesink_get_xv_support (xcontext);
780 XCloseDisplay (xcontext->disp);
781 g_mutex_unlock (xvimagesink->x_lock);
787 /* Search for XShm extension support */
788 if (XShmQueryExtension (xcontext->disp) &&
789 gst_xvimagesink_check_xshm_calls (xcontext))
791 xcontext->use_xshm = TRUE;
792 GST_DEBUG ("xvimagesink is using XShm extension");
796 xcontext->use_xshm = FALSE;
797 GST_DEBUG ("xvimagesink is not using XShm extension");
799 #endif /* HAVE_XSHM */
801 xv_attr = XvQueryPortAttributes (xcontext->disp,
802 xcontext->xv_port_id,
806 /* Generate the channels list */
807 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++)
809 XvAttribute *matching_attr = NULL;
813 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
814 if (! g_ascii_strcasecmp (channels[i], xv_attr[j].name))
815 matching_attr = xv_attr + j;
819 GstColorBalanceChannel *channel;
820 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
821 channel->label = g_strdup (channels[i]);
822 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
823 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
825 xcontext->channels_list = g_list_append (xcontext->channels_list,
828 /* If the colorbalance settings have not been touched we get Xv values
829 as defaults and update our internal variables */
830 if (!xvimagesink->cb_changed) {
832 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
833 XInternAtom (xcontext->disp, channel->label, 1),
835 /* Normalize val to [-1000, 1000] */
836 val = -1000 + 2000 * (val - channel->min_value) /
837 (channel->max_value - channel->min_value);
839 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
840 xvimagesink->hue = val;
841 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
842 xvimagesink->saturation = val;
843 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
844 xvimagesink->brightness = val;
845 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
846 xvimagesink->contrast = val;
854 g_mutex_unlock (xvimagesink->x_lock);
859 /* This function cleans the X context. Closing the Display, releasing the XV
860 port and unrefing the caps for supported formats. */
862 gst_xvimagesink_xcontext_clear (GstXvImageSink *xvimagesink)
864 GList *formats_list, *channels_list;
866 g_return_if_fail (xvimagesink != NULL);
867 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
869 formats_list = xvimagesink->xcontext->formats_list;
873 GstXvImageFormat *format = formats_list->data;
874 gst_caps_free (format->caps);
876 formats_list = g_list_next (formats_list);
879 if (xvimagesink->xcontext->formats_list)
880 g_list_free (xvimagesink->xcontext->formats_list);
882 channels_list = xvimagesink->xcontext->channels_list;
884 while (channels_list)
886 GstColorBalanceChannel *channel = channels_list->data;
887 g_object_unref (channel);
888 channels_list = g_list_next (channels_list);
891 if (xvimagesink->xcontext->channels_list)
892 g_list_free (xvimagesink->xcontext->channels_list);
894 gst_caps_free (xvimagesink->xcontext->caps);
896 g_mutex_lock (xvimagesink->x_lock);
898 XvUngrabPort (xvimagesink->xcontext->disp,
899 xvimagesink->xcontext->xv_port_id, 0);
901 XCloseDisplay (xvimagesink->xcontext->disp);
903 g_mutex_unlock (xvimagesink->x_lock);
905 xvimagesink->xcontext = NULL;
909 gst_xvimagesink_imagepool_clear (GstXvImageSink *xvimagesink)
911 g_mutex_lock(xvimagesink->pool_lock);
913 while (xvimagesink->image_pool)
915 GstXvImage *xvimage = xvimagesink->image_pool->data;
916 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
917 xvimagesink->image_pool);
918 gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage);
921 g_mutex_unlock(xvimagesink->pool_lock);
927 gst_xvimagesink_fixate (GstPad *pad, const GstCaps *caps)
929 GstStructure *structure;
932 if (gst_caps_get_size (caps) > 1) return NULL;
934 newcaps = gst_caps_copy (caps);
935 structure = gst_caps_get_structure (newcaps, 0);
937 if (gst_caps_structure_fixate_field_nearest_int (structure, "width", 320)) {
940 if (gst_caps_structure_fixate_field_nearest_int (structure, "height", 240)) {
943 if (gst_caps_structure_fixate_field_nearest_double (structure, "framerate",
948 gst_caps_free (newcaps);
952 /* This function tries to get a format matching with a given caps in the
953 supported list of formats we generated in gst_xvimagesink_get_xv_support */
955 gst_xvimagesink_get_fourcc_from_caps (GstXvImageSink *xvimagesink,
960 g_return_val_if_fail (xvimagesink != NULL, 0);
961 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
963 list = xvimagesink->xcontext->formats_list;
967 GstXvImageFormat *format = list->data;
971 GstCaps *icaps = NULL;
972 icaps = gst_caps_intersect (caps, format->caps);
973 if (!gst_caps_is_empty(icaps))
974 return format->format;
976 list = g_list_next (list);
983 gst_xvimagesink_getcaps (GstPad *pad)
985 GstXvImageSink *xvimagesink;
987 xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad));
989 if (xvimagesink->xcontext)
990 return gst_caps_copy (xvimagesink->xcontext->caps);
992 return gst_caps_from_string(
994 "framerate = (double) [ 1.0, 100.0 ], "
995 "width = (int) [ 0, MAX ], "
996 "height = (int) [ 0, MAX ]; "
998 "framerate = (double) [ 0, MAX ], "
999 "width = (int) [ 0, MAX ], "
1000 "height = (int) [ 0, MAX ]");
1003 static GstPadLinkReturn
1004 gst_xvimagesink_sink_link (GstPad *pad, const GstCaps *caps)
1006 GstXvImageSink *xvimagesink;
1007 char *caps_str1, *caps_str2;
1008 GstStructure *structure;
1011 xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad));
1013 caps_str1 = gst_caps_to_string (xvimagesink->xcontext->caps);
1014 caps_str2 = gst_caps_to_string (caps);
1016 GST_DEBUG ("sinkconnect %s with %s", caps_str1, caps_str2);
1021 structure = gst_caps_get_structure (caps, 0);
1022 ret = gst_structure_get_int (structure, "width",
1023 &(GST_VIDEOSINK_WIDTH (xvimagesink)));
1024 ret &= gst_structure_get_int (structure, "height",
1025 &(GST_VIDEOSINK_HEIGHT (xvimagesink)));
1026 ret &= gst_structure_get_double (structure, "framerate",
1027 &xvimagesink->framerate);
1028 if (!ret) return GST_PAD_LINK_REFUSED;
1030 xvimagesink->xcontext->im_format = 0;
1031 if (!gst_structure_get_fourcc (structure, "format",
1032 &xvimagesink->xcontext->im_format)) {
1033 xvimagesink->xcontext->im_format = gst_xvimagesink_get_fourcc_from_caps (
1034 xvimagesink, gst_caps_copy(caps));
1036 if (xvimagesink->xcontext->im_format == 0) {
1037 return GST_PAD_LINK_REFUSED;
1040 xvimagesink->pixel_width = 1;
1041 gst_structure_get_int (structure, "pixel_width", &xvimagesink->pixel_width);
1043 xvimagesink->pixel_height = 1;
1044 gst_structure_get_int (structure, "pixel_height",
1045 &xvimagesink->pixel_height);
1047 /* Creating our window and our image */
1048 if (!xvimagesink->xwindow)
1049 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1050 GST_VIDEOSINK_WIDTH (xvimagesink),
1051 GST_VIDEOSINK_HEIGHT (xvimagesink));
1054 if (xvimagesink->xwindow->internal)
1055 gst_xvimagesink_xwindow_resize (xvimagesink, xvimagesink->xwindow,
1056 GST_VIDEOSINK_WIDTH (xvimagesink),
1057 GST_VIDEOSINK_HEIGHT (xvimagesink));
1060 if ( (xvimagesink->xvimage) &&
1061 ( (GST_VIDEOSINK_WIDTH (xvimagesink) != xvimagesink->xvimage->width) ||
1062 (GST_VIDEOSINK_HEIGHT (xvimagesink) != xvimagesink->xvimage->height) ) )
1063 { /* We renew our xvimage only if size changed */
1064 gst_xvimagesink_xvimage_destroy (xvimagesink, xvimagesink->xvimage);
1066 xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
1067 GST_VIDEOSINK_WIDTH (xvimagesink),
1068 GST_VIDEOSINK_HEIGHT (xvimagesink));
1070 else if (!xvimagesink->xvimage) /* If no xvimage, creating one */
1071 xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
1072 GST_VIDEOSINK_WIDTH (xvimagesink),
1073 GST_VIDEOSINK_HEIGHT (xvimagesink));
1075 gst_x_overlay_got_desired_size (GST_X_OVERLAY (xvimagesink),
1076 GST_VIDEOSINK_WIDTH (xvimagesink),
1077 GST_VIDEOSINK_HEIGHT (xvimagesink));
1079 return GST_PAD_LINK_OK;
1082 static GstElementStateReturn
1083 gst_xvimagesink_change_state (GstElement *element)
1085 GstXvImageSink *xvimagesink;
1087 xvimagesink = GST_XVIMAGESINK (element);
1089 switch (GST_STATE_TRANSITION (element)) {
1090 case GST_STATE_NULL_TO_READY:
1091 /* Initializing the XContext */
1092 if (!xvimagesink->xcontext)
1093 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1094 if (!xvimagesink->xcontext)
1095 return GST_STATE_FAILURE;
1096 else /* If context initialized correctly let's commit our colorbalance */
1097 gst_xvimagesink_update_colorbalance (xvimagesink);
1099 case GST_STATE_READY_TO_PAUSED:
1100 xvimagesink->time = 0;
1102 case GST_STATE_PAUSED_TO_PLAYING:
1104 case GST_STATE_PLAYING_TO_PAUSED:
1106 case GST_STATE_PAUSED_TO_READY:
1107 xvimagesink->framerate = 0;
1108 GST_VIDEOSINK_WIDTH (xvimagesink) = 0;
1109 GST_VIDEOSINK_HEIGHT (xvimagesink) = 0;
1111 case GST_STATE_READY_TO_NULL:
1112 if (xvimagesink->xvimage)
1114 gst_xvimagesink_xvimage_destroy (xvimagesink, xvimagesink->xvimage);
1115 xvimagesink->xvimage = NULL;
1118 if (xvimagesink->image_pool)
1119 gst_xvimagesink_imagepool_clear (xvimagesink);
1121 if (xvimagesink->xwindow)
1123 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1124 xvimagesink->xwindow = NULL;
1127 if (xvimagesink->xcontext)
1129 gst_xvimagesink_xcontext_clear (xvimagesink);
1130 xvimagesink->xcontext = NULL;
1135 if (GST_ELEMENT_CLASS (parent_class)->change_state)
1136 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
1138 return GST_STATE_SUCCESS;
1142 gst_xvimagesink_chain (GstPad *pad, GstData *data)
1144 GstBuffer *buf = NULL;
1145 GstXvImageSink *xvimagesink;
1147 g_return_if_fail (pad != NULL);
1148 g_return_if_fail (GST_IS_PAD (pad));
1149 g_return_if_fail (data != NULL);
1151 xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad));
1153 if (GST_IS_EVENT (data))
1155 gst_pad_event_default (pad, GST_EVENT (data));
1159 buf = GST_BUFFER (data);
1161 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1162 xvimagesink->time = GST_BUFFER_TIMESTAMP (buf);
1164 GST_DEBUG ("videosink: clock wait: %" G_GUINT64_FORMAT, xvimagesink->time);
1166 if (GST_VIDEOSINK_CLOCK (xvimagesink)) {
1167 gst_element_wait (GST_ELEMENT (xvimagesink), xvimagesink->time);
1170 /* If this buffer has been allocated using our buffer management we simply
1171 put the ximage which is in the PRIVATE pointer */
1172 if (GST_BUFFER_FREE_DATA_FUNC (buf) == gst_xvimagesink_buffer_free)
1174 gst_xvimagesink_xvimage_put (xvimagesink, GST_BUFFER_PRIVATE (buf));
1176 else /* Else we have to copy the data into our private image, */
1177 { /* if we have one... */
1178 if (xvimagesink->xvimage)
1180 memcpy (xvimagesink->xvimage->xvimage->data,
1181 GST_BUFFER_DATA (buf),
1182 MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
1183 gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage);
1185 else /* No image available. Something went wrong during capsnego ! */
1187 gst_buffer_unref (buf);
1188 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL), ("no format defined before chain function"));
1193 /* set correct time for next buffer */
1194 if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf) && xvimagesink->framerate > 0) {
1195 xvimagesink->time += GST_SECOND / xvimagesink->framerate;
1198 gst_buffer_unref (buf);
1200 gst_xvimagesink_handle_xevents (xvimagesink, pad);
1203 /* Buffer management */
1206 gst_xvimagesink_buffer_free (GstBuffer *buffer)
1208 GstXvImageSink *xvimagesink;
1209 GstXvImage *xvimage;
1211 xvimage = GST_BUFFER_PRIVATE (buffer);
1213 g_assert (GST_IS_XVIMAGESINK (xvimage->xvimagesink));
1214 xvimagesink = xvimage->xvimagesink;
1216 /* If our geometry changed we can't reuse that image. */
1217 if ( (xvimage->width != GST_VIDEOSINK_WIDTH (xvimagesink)) ||
1218 (xvimage->height != GST_VIDEOSINK_HEIGHT (xvimagesink)) )
1219 gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage);
1220 else /* In that case we can reuse the image and add it to our image pool. */
1222 g_mutex_lock (xvimagesink->pool_lock);
1223 xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
1225 g_mutex_unlock (xvimagesink->pool_lock);
1230 gst_xvimagesink_buffer_alloc (GstPad *pad, guint64 offset, guint size)
1232 GstXvImageSink *xvimagesink;
1234 GstXvImage *xvimage = NULL;
1235 gboolean not_found = TRUE;
1237 xvimagesink = GST_XVIMAGESINK (gst_pad_get_parent (pad));
1239 g_mutex_lock (xvimagesink->pool_lock);
1241 /* Walking through the pool cleaning unsuable images and searching for a
1243 while (not_found && xvimagesink->image_pool)
1245 xvimage = xvimagesink->image_pool->data;
1249 /* Removing from the pool */
1250 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
1251 xvimagesink->image_pool);
1253 if ( (xvimage->width != GST_VIDEOSINK_WIDTH (xvimagesink)) ||
1254 (xvimage->height != GST_VIDEOSINK_HEIGHT (xvimagesink)) )
1255 { /* This image is unusable. Destroying... */
1256 gst_xvimagesink_xvimage_destroy (xvimagesink, xvimage);
1259 else /* We found a suitable image */
1266 g_mutex_unlock (xvimagesink->pool_lock);
1268 if (!xvimage) /* We found no suitable image in the pool. Creating... */
1270 xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
1271 GST_VIDEOSINK_WIDTH (xvimagesink),
1272 GST_VIDEOSINK_HEIGHT (xvimagesink));
1277 buffer = gst_buffer_new ();
1279 /* Storing some pointers in the buffer */
1280 GST_BUFFER_PRIVATE (buffer) = xvimage;
1282 GST_BUFFER_DATA (buffer) = xvimage->xvimage->data;
1283 GST_BUFFER_FREE_DATA_FUNC (buffer) = gst_xvimagesink_buffer_free;
1284 GST_BUFFER_SIZE (buffer) = xvimage->size;
1291 /* Interfaces stuff */
1294 gst_xvimagesink_interface_supported (GstImplementsInterface *iface, GType type)
1296 g_assert (type == GST_TYPE_NAVIGATION ||
1297 type == GST_TYPE_X_OVERLAY ||
1298 type == GST_TYPE_COLOR_BALANCE);
1303 gst_xvimagesink_interface_init (GstImplementsInterfaceClass *klass)
1305 klass->supported = gst_xvimagesink_interface_supported;
1309 gst_xvimagesink_navigation_send_event (GstNavigation *navigation,
1310 GstStructure *structure)
1312 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
1316 event = gst_event_new (GST_EVENT_NAVIGATION);
1317 event->event_data.structure.structure = structure;
1319 /* Converting pointer coordinates to the non scaled geometry */
1320 if (gst_structure_get_double (structure, "pointer_x", &x))
1322 x *= GST_VIDEOSINK_WIDTH (xvimagesink);
1323 x /= xvimagesink->xwindow->width;
1324 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1326 if (gst_structure_get_double (structure, "pointer_y", &y))
1328 y *= GST_VIDEOSINK_HEIGHT (xvimagesink);
1329 y /= xvimagesink->xwindow->height;
1330 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1333 gst_pad_send_event (gst_pad_get_peer (GST_VIDEOSINK_PAD (xvimagesink)),
1338 gst_xvimagesink_navigation_init (GstNavigationInterface *iface)
1340 iface->send_event = gst_xvimagesink_navigation_send_event;
1344 gst_xvimagesink_set_xwindow_id (GstXOverlay *overlay, XID xwindow_id)
1346 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1347 GstXWindow *xwindow = NULL;
1348 XWindowAttributes attr;
1350 g_return_if_fail (xvimagesink != NULL);
1351 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1353 /* If we already use that window return */
1354 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win))
1357 /* If the element has not initialized the X11 context try to do so */
1358 if (!xvimagesink->xcontext)
1359 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1361 if (!xvimagesink->xcontext)
1363 g_warning ("xvimagesink was unable to obtain the X11 context.");
1366 else /* If context initialized correctly let's commit our colorbalance */
1367 gst_xvimagesink_update_colorbalance (xvimagesink);
1369 /* Clear image pool as the images are unusable anyway */
1370 gst_xvimagesink_imagepool_clear (xvimagesink);
1372 /* Clear the xvimage */
1373 if (xvimagesink->xvimage)
1375 gst_xvimagesink_xvimage_destroy (xvimagesink, xvimagesink->xvimage);
1376 xvimagesink->xvimage = NULL;
1379 /* If a window is there already we destroy it */
1380 if (xvimagesink->xwindow)
1382 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1383 xvimagesink->xwindow = NULL;
1386 /* If the xid is 0 we go back to an internal window */
1387 if (xwindow_id == 0)
1389 /* If no width/height caps nego did not happen window will be created
1390 during caps nego then */
1391 if (GST_VIDEOSINK_WIDTH (xvimagesink) &&
1392 GST_VIDEOSINK_HEIGHT (xvimagesink))
1394 xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1395 GST_VIDEOSINK_WIDTH (xvimagesink),
1396 GST_VIDEOSINK_HEIGHT (xvimagesink));
1401 xwindow = g_new0 (GstXWindow, 1);
1403 xwindow->win = xwindow_id;
1405 /* We get window geometry, set the event we want to receive,
1407 g_mutex_lock (xvimagesink->x_lock);
1408 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
1409 xwindow->width = attr.width;
1410 xwindow->height = attr.height;
1411 xwindow->internal = FALSE;
1412 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
1413 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1416 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
1417 xwindow->win, 0, NULL);
1418 g_mutex_unlock (xvimagesink->x_lock);
1421 /* Recreating our xvimage */
1422 if (!xvimagesink->xvimage &&
1423 GST_VIDEOSINK_WIDTH (xvimagesink) &&
1424 GST_VIDEOSINK_HEIGHT (xvimagesink))
1426 xvimagesink->xvimage = gst_xvimagesink_xvimage_new (
1428 GST_VIDEOSINK_WIDTH (xvimagesink),
1429 GST_VIDEOSINK_HEIGHT (xvimagesink));
1433 xvimagesink->xwindow = xwindow;
1437 gst_xvimagesink_get_desired_size (GstXOverlay *overlay,
1438 guint *width, guint *height)
1440 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1442 *width = GST_VIDEOSINK_WIDTH (xvimagesink);
1443 *height = GST_VIDEOSINK_HEIGHT (xvimagesink);
1447 gst_xvimagesink_xoverlay_init (GstXOverlayClass *iface)
1449 iface->set_xwindow_id = gst_xvimagesink_set_xwindow_id;
1450 iface->get_desired_size = gst_xvimagesink_get_desired_size;
1453 static const GList *
1454 gst_xvimagesink_colorbalance_list_channels (GstColorBalance *balance)
1456 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1458 g_return_val_if_fail (xvimagesink != NULL, NULL);
1459 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1461 if (xvimagesink->xcontext)
1462 return xvimagesink->xcontext->channels_list;
1468 gst_xvimagesink_colorbalance_set_value (GstColorBalance *balance,
1469 GstColorBalanceChannel *channel,
1472 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1474 g_return_if_fail (xvimagesink != NULL);
1475 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1476 g_return_if_fail (channel->label != NULL);
1478 xvimagesink->cb_changed = TRUE;
1480 /* Normalize val to [-1000, 1000] */
1481 value = -1000 + 2000 * (value - channel->min_value) /
1482 (channel->max_value - channel->min_value);
1484 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0)
1486 xvimagesink->hue = value;
1488 else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0)
1490 xvimagesink->saturation = value;
1492 else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0)
1494 xvimagesink->contrast = value;
1496 else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0)
1498 xvimagesink->brightness = value;
1502 g_warning ("got an unknown channel %s", channel->label);
1506 gst_xvimagesink_update_colorbalance (xvimagesink);
1510 gst_xvimagesink_colorbalance_get_value (GstColorBalance *balance,
1511 GstColorBalanceChannel *channel)
1513 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1516 g_return_val_if_fail (xvimagesink != NULL, 0);
1517 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1518 g_return_val_if_fail (channel->label != NULL, 0);
1520 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0)
1522 value = xvimagesink->hue;
1524 else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0)
1526 value = xvimagesink->saturation;
1528 else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0)
1530 value = xvimagesink->contrast;
1532 else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0)
1534 value = xvimagesink->brightness;
1538 g_warning ("got an unknown channel %s", channel->label);
1541 /* Normalize val to [channel->min_value, channel->max_value] */
1542 value = channel->min_value + (channel->max_value - channel->min_value) *
1543 (value + 1000) / 2000;
1549 gst_xvimagesink_colorbalance_init (GstColorBalanceClass *iface)
1551 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
1552 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
1553 iface->set_value = gst_xvimagesink_colorbalance_set_value;
1554 iface->get_value = gst_xvimagesink_colorbalance_get_value;
1557 /* =========================================== */
1559 /* Init & Class init */
1561 /* =========================================== */
1564 gst_xvimagesink_set_property (GObject *object, guint prop_id,
1565 const GValue *value, GParamSpec *pspec)
1567 GstXvImageSink *xvimagesink;
1569 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1571 xvimagesink = GST_XVIMAGESINK (object);
1576 xvimagesink->hue = g_value_get_int (value);
1577 xvimagesink->cb_changed = TRUE;
1578 gst_xvimagesink_update_colorbalance (xvimagesink);
1581 xvimagesink->contrast = g_value_get_int (value);
1582 xvimagesink->cb_changed = TRUE;
1583 gst_xvimagesink_update_colorbalance (xvimagesink);
1585 case ARG_BRIGHTNESS:
1586 xvimagesink->brightness = g_value_get_int (value);
1587 xvimagesink->cb_changed = TRUE;
1588 gst_xvimagesink_update_colorbalance (xvimagesink);
1590 case ARG_SATURATION:
1591 xvimagesink->saturation = g_value_get_int (value);
1592 xvimagesink->cb_changed = TRUE;
1593 gst_xvimagesink_update_colorbalance (xvimagesink);
1596 xvimagesink->display_name = g_strdup (g_value_get_string (value));
1598 case ARG_SYNCHRONOUS:
1599 xvimagesink->synchronous = g_value_get_boolean (value);
1600 if (xvimagesink->xcontext) {
1601 XSynchronize (xvimagesink->xcontext->disp,
1602 xvimagesink->synchronous);
1606 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1612 gst_xvimagesink_get_property (GObject *object, guint prop_id,
1613 GValue *value, GParamSpec *pspec)
1615 GstXvImageSink *xvimagesink;
1617 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1619 xvimagesink = GST_XVIMAGESINK (object);
1624 g_value_set_int (value, xvimagesink->hue);
1627 g_value_set_int (value, xvimagesink->contrast);
1629 case ARG_BRIGHTNESS:
1630 g_value_set_int (value, xvimagesink->brightness);
1632 case ARG_SATURATION:
1633 g_value_set_int (value, xvimagesink->saturation);
1636 g_value_set_string (value, g_strdup (xvimagesink->display_name));
1638 case ARG_SYNCHRONOUS:
1639 g_value_set_boolean (value, xvimagesink->synchronous);
1642 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1648 gst_xvimagesink_dispose (GObject *object)
1650 GstXvImageSink *xvimagesink;
1652 xvimagesink = GST_XVIMAGESINK (object);
1654 if (xvimagesink->display_name)
1656 g_free (xvimagesink->display_name);
1657 xvimagesink->display_name = NULL;
1660 g_mutex_free (xvimagesink->x_lock);
1661 g_mutex_free (xvimagesink->pool_lock);
1663 G_OBJECT_CLASS (parent_class)->dispose (object);
1667 gst_xvimagesink_init (GstXvImageSink *xvimagesink)
1669 GST_VIDEOSINK_PAD (xvimagesink) = gst_pad_new_from_template (
1670 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory),
1673 gst_element_add_pad (GST_ELEMENT (xvimagesink),
1674 GST_VIDEOSINK_PAD (xvimagesink));
1676 gst_pad_set_chain_function (GST_VIDEOSINK_PAD (xvimagesink),
1677 gst_xvimagesink_chain);
1678 gst_pad_set_link_function (GST_VIDEOSINK_PAD (xvimagesink),
1679 gst_xvimagesink_sink_link);
1680 gst_pad_set_getcaps_function (GST_VIDEOSINK_PAD (xvimagesink),
1681 gst_xvimagesink_getcaps);
1682 gst_pad_set_fixate_function (GST_VIDEOSINK_PAD (xvimagesink),
1683 gst_xvimagesink_fixate);
1684 gst_pad_set_bufferalloc_function (GST_VIDEOSINK_PAD (xvimagesink),
1685 gst_xvimagesink_buffer_alloc);
1687 xvimagesink->display_name = NULL;
1688 xvimagesink->xcontext = NULL;
1689 xvimagesink->xwindow = NULL;
1690 xvimagesink->xvimage = NULL;
1692 xvimagesink->hue = xvimagesink->saturation = 0;
1693 xvimagesink->contrast = xvimagesink->brightness = 0;
1694 xvimagesink->cb_changed = FALSE;
1696 xvimagesink->framerate = 0;
1698 xvimagesink->x_lock = g_mutex_new ();
1700 xvimagesink->pixel_width = xvimagesink->pixel_height = 1;
1702 xvimagesink->image_pool = NULL;
1703 xvimagesink->pool_lock = g_mutex_new ();
1705 GST_FLAG_SET(xvimagesink, GST_ELEMENT_THREAD_SUGGESTED);
1706 GST_FLAG_SET(xvimagesink, GST_ELEMENT_EVENT_AWARE);
1710 gst_xvimagesink_base_init (gpointer g_class)
1712 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1714 gst_element_class_set_details (element_class, &gst_xvimagesink_details);
1716 gst_element_class_add_pad_template (element_class,
1717 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
1721 gst_xvimagesink_class_init (GstXvImageSinkClass *klass)
1723 GObjectClass *gobject_class;
1724 GstElementClass *gstelement_class;
1726 gobject_class = (GObjectClass *) klass;
1727 gstelement_class = (GstElementClass *) klass;
1729 parent_class = g_type_class_ref (GST_TYPE_VIDEOSINK);
1731 g_object_class_install_property (gobject_class, ARG_CONTRAST,
1732 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1733 -1000, 1000, 0, G_PARAM_READWRITE));
1734 g_object_class_install_property(gobject_class, ARG_BRIGHTNESS,
1735 g_param_spec_int ("brightness", "Brightness", "The brightness of the video",
1736 -1000, 1000, 0, G_PARAM_READWRITE));
1737 g_object_class_install_property (gobject_class, ARG_HUE,
1738 g_param_spec_int ("hue", "Hue", "The hue of the video",
1739 -1000, 1000, 0, G_PARAM_READWRITE));
1740 g_object_class_install_property (gobject_class, ARG_SATURATION,
1741 g_param_spec_int ("saturation", "Saturation", "The saturation of the video",
1742 -1000, 1000, 0, G_PARAM_READWRITE));
1743 g_object_class_install_property (gobject_class, ARG_DISPLAY,
1744 g_param_spec_string ("display", "Display", "X Display name",
1745 NULL, G_PARAM_READWRITE));
1746 g_object_class_install_property (gobject_class, ARG_SYNCHRONOUS,
1747 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
1748 "the X display in synchronous mode. (used only for debugging)", FALSE,
1749 G_PARAM_READWRITE));
1751 gobject_class->dispose = gst_xvimagesink_dispose;
1752 gobject_class->set_property = gst_xvimagesink_set_property;
1753 gobject_class->get_property = gst_xvimagesink_get_property;
1755 gstelement_class->change_state = gst_xvimagesink_change_state;
1758 /* ============================================================= */
1760 /* Public Methods */
1762 /* ============================================================= */
1764 /* =========================================== */
1766 /* Object typing & Creation */
1768 /* =========================================== */
1771 gst_xvimagesink_get_type (void)
1773 static GType xvimagesink_type = 0;
1775 if (!xvimagesink_type)
1777 static const GTypeInfo xvimagesink_info = {
1778 sizeof(GstXvImageSinkClass),
1779 gst_xvimagesink_base_init,
1781 (GClassInitFunc) gst_xvimagesink_class_init,
1784 sizeof(GstXvImageSink),
1786 (GInstanceInitFunc) gst_xvimagesink_init,
1788 static const GInterfaceInfo iface_info = {
1789 (GInterfaceInitFunc) gst_xvimagesink_interface_init,
1793 static const GInterfaceInfo navigation_info = {
1794 (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
1798 static const GInterfaceInfo overlay_info = {
1799 (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
1803 static const GInterfaceInfo colorbalance_info = {
1804 (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
1809 xvimagesink_type = g_type_register_static (GST_TYPE_VIDEOSINK,
1811 &xvimagesink_info, 0);
1813 g_type_add_interface_static (xvimagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
1815 g_type_add_interface_static (xvimagesink_type, GST_TYPE_NAVIGATION,
1817 g_type_add_interface_static (xvimagesink_type, GST_TYPE_X_OVERLAY,
1819 g_type_add_interface_static (xvimagesink_type, GST_TYPE_COLOR_BALANCE,
1820 &colorbalance_info);
1823 return xvimagesink_type;
1827 plugin_init (GstPlugin *plugin)
1829 /* Loading the library containing GstVideoSink, our parent object */
1830 if (!gst_library_load ("gstvideo"))
1833 if (!gst_element_register (plugin, "xvimagesink",
1834 GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
1844 "XFree86 video output plugin using Xv extension",