2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
21 /* for developers: there are two useful tools : xvinfo and xvattr */
28 #include "xvcontext.h"
30 /* Debugging category */
31 #include <gst/gstinfo.h>
33 /* for XkbKeycodeToKeysym */
34 #include <X11/XKBlib.h>
36 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvcontext);
37 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
38 #define GST_CAT_DEFAULT gst_debug_xvcontext
41 gst_xvcontext_config_clear (GstXvContextConfig * config)
43 if (config->display_name) {
44 g_free (config->display_name);
45 config->display_name = NULL;
47 config->adaptor_nr = -1;
50 GST_DEFINE_MINI_OBJECT_TYPE (GstXvContext, gst_xvcontext);
55 unsigned long functions;
56 unsigned long decorations;
60 MotifWmHints, MwmHints;
62 #define MWM_HINTS_DECORATIONS (1L << 1)
66 gst_lookup_xv_port_from_adaptor (GstXvContext * context,
67 XvAdaptorInfo * adaptors, guint adaptor_nr)
72 /* Do we support XvImageMask ? */
73 if (!(adaptors[adaptor_nr].type & XvImageMask)) {
74 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
75 adaptors[adaptor_nr].name);
79 /* We found such an adaptor, looking for an available port */
80 for (j = 0; j < adaptors[adaptor_nr].num_ports && !context->xv_port_id; j++) {
81 /* We try to grab the port */
82 res = XvGrabPort (context->disp, adaptors[adaptor_nr].base_id + j, 0);
84 context->xv_port_id = adaptors[adaptor_nr].base_id + j;
85 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_nr].name,
86 adaptors[adaptor_nr].num_ports);
88 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
89 adaptors[adaptor_nr].name, res);
94 /* This function generates a caps with all supported format by the first
95 Xv grabable port we find. We store each one of the supported formats in a
96 format list and append the format to a newly created caps that we return
97 If this function does not return NULL because of an error, it also grabs
98 the port via XvGrabPort */
100 gst_xvcontext_get_xv_support (GstXvContext * context,
101 const GstXvContextConfig * config, GError ** error)
104 XvAdaptorInfo *adaptors;
106 XvImageFormatValues *formats = NULL;
108 XvEncodingInfo *encodings = NULL;
109 gulong max_w = G_MAXINT, max_h = G_MAXINT;
110 GstCaps *caps = NULL;
111 GstCaps *rgb_caps = NULL;
113 g_return_val_if_fail (context != NULL, NULL);
115 /* First let's check that XVideo extension is available */
116 if (!XQueryExtension (context->disp, "XVideo", &i, &i, &i))
119 /* Then we get adaptors list */
120 if (Success != XvQueryAdaptors (context->disp, context->root,
121 &context->nb_adaptors, &adaptors))
124 context->xv_port_id = 0;
126 GST_DEBUG ("Found %u XV adaptor(s)", context->nb_adaptors);
129 (gchar **) g_malloc0 (context->nb_adaptors * sizeof (gchar *));
131 /* Now fill up our adaptor name array */
132 for (i = 0; i < context->nb_adaptors; i++) {
133 context->adaptors[i] = g_strdup (adaptors[i].name);
136 if (config->adaptor_nr != -1 && config->adaptor_nr < context->nb_adaptors) {
137 /* Find xv port from user defined adaptor */
138 gst_lookup_xv_port_from_adaptor (context, adaptors, config->adaptor_nr);
141 if (!context->xv_port_id) {
142 /* Now search for an adaptor that supports XvImageMask */
143 for (i = 0; i < context->nb_adaptors && !context->xv_port_id; i++) {
144 gst_lookup_xv_port_from_adaptor (context, adaptors, i);
145 context->adaptor_nr = i;
149 XvFreeAdaptorInfo (adaptors);
151 if (!context->xv_port_id)
154 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
157 XvAttribute *const attr = XvQueryPortAttributes (context->disp,
158 context->xv_port_id, &count);
159 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
160 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
161 static const char colorkey[] = "XV_COLORKEY";
163 GST_DEBUG ("Checking %d Xv port attributes", count);
165 context->have_autopaint_colorkey = FALSE;
166 context->have_double_buffer = FALSE;
167 context->have_colorkey = FALSE;
169 for (i = 0; ((i < count) && todo); i++) {
170 GST_DEBUG ("Got attribute %s", attr[i].name);
172 if (!strcmp (attr[i].name, autopaint)) {
173 const Atom atom = XInternAtom (context->disp, autopaint, False);
175 /* turn on autopaint colorkey */
176 XvSetPortAttribute (context->disp, context->xv_port_id, atom,
177 (config->autopaint_colorkey ? 1 : 0));
179 context->have_autopaint_colorkey = TRUE;
180 } else if (!strcmp (attr[i].name, dbl_buffer)) {
181 const Atom atom = XInternAtom (context->disp, dbl_buffer, False);
183 XvSetPortAttribute (context->disp, context->xv_port_id, atom,
184 (config->double_buffer ? 1 : 0));
186 context->have_double_buffer = TRUE;
187 } else if (!strcmp (attr[i].name, colorkey)) {
188 /* Set the colorkey, default is something that is dark but hopefully
189 * won't randomly appear on the screen elsewhere (ie not black or greys)
190 * can be overridden by setting "colorkey" property
192 const Atom atom = XInternAtom (context->disp, colorkey, False);
194 gboolean set_attr = TRUE;
197 /* set a colorkey in the right format RGB565/RGB888
198 * We only handle these 2 cases, because they're the only types of
199 * devices we've encountered. If we don't recognise it, leave it alone
201 cr = (config->colorkey >> 16);
202 cg = (config->colorkey >> 8) & 0xFF;
203 cb = (config->colorkey) & 0xFF;
204 switch (context->depth) {
205 case 16: /* RGB 565 */
209 ckey = (cr << 11) | (cg << 5) | cb;
212 case 32: /* RGB 888 / ARGB 8888 */
213 ckey = (cr << 16) | (cg << 8) | cb;
216 GST_DEBUG ("Unknown bit depth %d for Xv Colorkey - not adjusting",
223 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
224 (guint32) attr[i].max_value);
225 GST_LOG ("Setting color key for display depth %d to 0x%x",
226 context->depth, ckey);
228 XvSetPortAttribute (context->disp, context->xv_port_id, atom,
232 context->have_colorkey = TRUE;
239 /* Get the list of encodings supported by the adapter and look for the
240 * XV_IMAGE encoding so we can determine the maximum width and height
242 XvQueryEncodings (context->disp, context->xv_port_id, &nb_encodings,
245 for (i = 0; i < nb_encodings; i++) {
246 GST_LOG ("Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
247 i, encodings[i].name, encodings[i].width, encodings[i].height,
248 encodings[i].rate.numerator, encodings[i].rate.denominator);
249 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
250 max_w = encodings[i].width;
251 max_h = encodings[i].height;
255 XvFreeEncodingInfo (encodings);
257 /* We get all image formats supported by our port */
258 formats = XvListImageFormats (context->disp,
259 context->xv_port_id, &nb_formats);
260 caps = gst_caps_new_empty ();
261 for (i = 0; i < nb_formats; i++) {
262 GstCaps *format_caps = NULL;
263 gboolean is_rgb_format = FALSE;
264 GstVideoFormat vformat;
266 /* We set the image format of the context to an existing one. This
267 is just some valid image format for making our xshm calls check before
268 caps negotiation really happens. */
269 context->im_format = formats[i].id;
271 switch (formats[i].type) {
274 XvImageFormatValues *fmt = &(formats[i]);
278 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
280 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
281 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
282 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
285 format_caps = gst_caps_new_simple ("video/x-raw",
286 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
287 "width", GST_TYPE_INT_RANGE, 1, max_w,
288 "height", GST_TYPE_INT_RANGE, 1, max_h,
289 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
291 is_rgb_format = TRUE;
296 vformat = gst_video_format_from_fourcc (formats[i].id);
297 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
300 format_caps = gst_caps_new_simple ("video/x-raw",
301 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
302 "width", GST_TYPE_INT_RANGE, 1, max_w,
303 "height", GST_TYPE_INT_RANGE, 1, max_h,
304 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
308 vformat = GST_VIDEO_FORMAT_UNKNOWN;
309 g_assert_not_reached ();
314 GstXvImageFormat *format = NULL;
316 format = g_new0 (GstXvImageFormat, 1);
318 format->format = formats[i].id;
319 format->vformat = vformat;
320 format->caps = gst_caps_copy (format_caps);
321 context->formats_list = g_list_append (context->formats_list, format);
325 if (rgb_caps == NULL)
326 rgb_caps = format_caps;
328 gst_caps_append (rgb_caps, format_caps);
330 gst_caps_append (caps, format_caps);
334 /* Collected all caps into either the caps or rgb_caps structures.
335 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
337 gst_caps_append (caps, rgb_caps);
342 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
344 if (gst_caps_is_empty (caps))
352 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
353 ("XVideo extension is not available"));
358 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
359 ("Failed getting XV adaptors list"));
364 context->adaptor_nr = -1;
365 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_BUSY,
366 ("No Xv Port available"));
371 gst_caps_unref (caps);
372 g_set_error (error, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE,
373 ("No supported format found"));
378 /* This function calculates the pixel aspect ratio based on the properties
379 * in the context structure and stores it there. */
381 gst_xvcontext_calculate_pixel_aspect_ratio (GstXvContext * context)
383 static const gint par[][2] = {
384 {1, 1}, /* regular screen */
385 {16, 15}, /* PAL TV */
386 {11, 10}, /* 525 line Rec.601 video */
387 {54, 59}, /* 625 line Rec.601 video */
388 {64, 45}, /* 1280x1024 on 16:9 display */
389 {5, 3}, /* 1280x1024 on 4:3 display */
390 {4, 3} /* 800x600 on 16:9 display */
397 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
399 /* first calculate the "real" ratio based on the X values;
400 * which is the "physical" w/h divided by the w/h in pixels of the display */
401 ratio = (gdouble) (context->widthmm * context->height)
402 / (context->heightmm * context->width);
404 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
406 if (context->width == 720 && context->height == 576) {
407 ratio = 4.0 * 576 / (3.0 * 720);
409 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
411 /* now find the one from par[][2] with the lowest delta to the real one */
415 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
416 gdouble this_delta = DELTA (i);
418 if (this_delta < delta) {
424 GST_DEBUG ("Decided on index %d (%d/%d)", index,
425 par[index][0], par[index][1]);
427 g_free (context->par);
428 context->par = g_new0 (GValue, 1);
429 g_value_init (context->par, GST_TYPE_FRACTION);
430 gst_value_set_fraction (context->par, par[index][0], par[index][1]);
431 GST_DEBUG ("set context PAR to %d/%d",
432 gst_value_get_fraction_numerator (context->par),
433 gst_value_get_fraction_denominator (context->par));
438 static gboolean error_caught = FALSE;
441 gst_xvimage_handle_xerror (Display * display, XErrorEvent * xevent)
443 char error_msg[1024];
445 XGetErrorText (display, xevent->error_code, error_msg, 1024);
446 GST_DEBUG ("xvimage triggered an XError. error: %s", error_msg);
451 /* This function checks that it is actually really possible to create an image
454 gst_xvcontext_check_xshm_calls (GstXvContext * context)
457 XShmSegmentInfo SHMInfo;
459 int (*handler) (Display *, XErrorEvent *);
460 gboolean result = FALSE;
461 gboolean did_attach = FALSE;
463 g_return_val_if_fail (context != NULL, FALSE);
465 /* Sync to ensure any older errors are already processed */
466 XSync (context->disp, FALSE);
468 /* Set defaults so we don't free these later unnecessarily */
469 SHMInfo.shmaddr = ((void *) -1);
472 /* Setting an error handler to catch failure */
473 error_caught = FALSE;
474 handler = XSetErrorHandler (gst_xvimage_handle_xerror);
476 /* Trying to create a 1x1 picture */
477 GST_DEBUG ("XvShmCreateImage of 1x1");
478 xvimage = XvShmCreateImage (context->disp, context->xv_port_id,
479 context->im_format, NULL, 1, 1, &SHMInfo);
481 /* Might cause an error, sync to ensure it is noticed */
482 XSync (context->disp, FALSE);
483 if (!xvimage || error_caught) {
484 GST_WARNING ("could not XvShmCreateImage a 1x1 image");
487 size = xvimage->data_size;
488 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
489 if (SHMInfo.shmid == -1) {
490 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
495 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
496 if (SHMInfo.shmaddr == ((void *) -1)) {
497 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
498 /* Clean up the shared memory segment */
499 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
503 xvimage->data = SHMInfo.shmaddr;
504 SHMInfo.readOnly = FALSE;
506 if (XShmAttach (context->disp, &SHMInfo) == 0) {
507 GST_WARNING ("Failed to XShmAttach");
508 /* Clean up the shared memory segment */
509 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
513 /* Sync to ensure we see any errors we caused */
514 XSync (context->disp, FALSE);
516 /* Delete the shared memory segment as soon as everyone is attached.
517 * This way, it will be deleted as soon as we detach later, and not
518 * leaked if we crash. */
519 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
522 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
526 /* store whether we succeeded in result */
529 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
530 "Not using shared memory.");
534 /* Sync to ensure we swallow any errors we caused and reset error_caught */
535 XSync (context->disp, FALSE);
537 error_caught = FALSE;
538 XSetErrorHandler (handler);
541 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
542 SHMInfo.shmid, SHMInfo.shmseg);
543 XShmDetach (context->disp, &SHMInfo);
544 XSync (context->disp, FALSE);
546 if (SHMInfo.shmaddr != ((void *) -1))
547 shmdt (SHMInfo.shmaddr);
552 #endif /* HAVE_XSHM */
554 static GstXvContext *
555 gst_xvcontext_copy (GstXvContext * context)
561 gst_xvcontext_free (GstXvContext * context)
563 GList *formats_list, *channels_list;
566 GST_LOG ("free %p", context);
568 formats_list = context->formats_list;
570 while (formats_list) {
571 GstXvImageFormat *format = formats_list->data;
573 gst_caps_unref (format->caps);
575 formats_list = g_list_next (formats_list);
578 if (context->formats_list)
579 g_list_free (context->formats_list);
581 channels_list = context->channels_list;
583 while (channels_list) {
584 GstColorBalanceChannel *channel = channels_list->data;
586 g_object_unref (channel);
587 channels_list = g_list_next (channels_list);
590 if (context->channels_list)
591 g_list_free (context->channels_list);
594 gst_caps_unref (context->caps);
595 if (context->last_caps)
596 gst_caps_unref (context->last_caps);
598 for (i = 0; i < context->nb_adaptors; i++) {
599 g_free (context->adaptors[i]);
602 g_free (context->adaptors);
604 g_free (context->par);
606 GST_DEBUG ("Closing display and freeing X Context");
608 if (context->xv_port_id)
609 XvUngrabPort (context->disp, context->xv_port_id, 0);
612 XCloseDisplay (context->disp);
614 g_mutex_clear (&context->lock);
616 g_slice_free1 (sizeof (GstXvContext), context);
620 /* This function gets the X Display and global info about it. Everything is
621 stored in our object and will be cleaned when the object is disposed. Note
622 here that caps for supported format are generated without any window or
625 gst_xvcontext_new (GstXvContextConfig * config, GError ** error)
627 GstXvContext *context = NULL;
628 XPixmapFormatValues *px_formats = NULL;
629 gint nb_formats = 0, i, j, N_attr;
630 XvAttribute *xv_attr;
632 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
633 "XV_BRIGHTNESS", "XV_CONTRAST"
636 g_return_val_if_fail (config != NULL, NULL);
638 context = g_slice_new0 (GstXvContext);
640 gst_mini_object_init (GST_MINI_OBJECT_CAST (context), 0,
641 gst_xvcontext_get_type (),
642 (GstMiniObjectCopyFunction) gst_xvcontext_copy,
643 (GstMiniObjectDisposeFunction) NULL,
644 (GstMiniObjectFreeFunction) gst_xvcontext_free);
646 g_mutex_init (&context->lock);
647 context->im_format = 0;
648 context->adaptor_nr = -1;
650 if (!(context->disp = XOpenDisplay (config->display_name)))
653 context->screen = DefaultScreenOfDisplay (context->disp);
654 context->screen_num = DefaultScreen (context->disp);
655 context->visual = DefaultVisual (context->disp, context->screen_num);
656 context->root = DefaultRootWindow (context->disp);
657 context->white = XWhitePixel (context->disp, context->screen_num);
658 context->black = XBlackPixel (context->disp, context->screen_num);
659 context->depth = DefaultDepthOfScreen (context->screen);
661 context->width = DisplayWidth (context->disp, context->screen_num);
662 context->height = DisplayHeight (context->disp, context->screen_num);
663 context->widthmm = DisplayWidthMM (context->disp, context->screen_num);
664 context->heightmm = DisplayHeightMM (context->disp, context->screen_num);
666 GST_DEBUG ("X reports %dx%d pixels and %d mm x %d mm",
667 context->width, context->height, context->widthmm, context->heightmm);
669 gst_xvcontext_calculate_pixel_aspect_ratio (context);
670 /* We get supported pixmap formats at supported depth */
671 px_formats = XListPixmapFormats (context->disp, &nb_formats);
674 goto no_pixel_formats;
676 /* We get bpp value corresponding to our running depth */
677 for (i = 0; i < nb_formats; i++) {
678 if (px_formats[i].depth == context->depth)
679 context->bpp = px_formats[i].bits_per_pixel;
684 context->endianness =
685 (ImageByteOrder (context->disp) ==
686 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
688 /* our caps system handles 24/32bpp RGB as big-endian. */
689 if ((context->bpp == 24 || context->bpp == 32) &&
690 context->endianness == G_LITTLE_ENDIAN) {
691 context->endianness = G_BIG_ENDIAN;
692 context->visual->red_mask = GUINT32_TO_BE (context->visual->red_mask);
693 context->visual->green_mask = GUINT32_TO_BE (context->visual->green_mask);
694 context->visual->blue_mask = GUINT32_TO_BE (context->visual->blue_mask);
695 if (context->bpp == 24) {
696 context->visual->red_mask >>= 8;
697 context->visual->green_mask >>= 8;
698 context->visual->blue_mask >>= 8;
702 if (!(context->caps = gst_xvcontext_get_xv_support (context, config, error)))
705 /* Search for XShm extension support */
707 if (XShmQueryExtension (context->disp) &&
708 gst_xvcontext_check_xshm_calls (context)) {
709 context->use_xshm = TRUE;
710 GST_DEBUG ("xvimagesink is using XShm extension");
712 #endif /* HAVE_XSHM */
714 context->use_xshm = FALSE;
715 GST_DEBUG ("xvimagesink is not using XShm extension");
718 xv_attr = XvQueryPortAttributes (context->disp, context->xv_port_id, &N_attr);
720 /* Generate the channels list */
721 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
722 XvAttribute *matching_attr = NULL;
724 /* Retrieve the property atom if it exists. If it doesn't exist,
725 * the attribute itself must not either, so we can skip */
726 prop_atom = XInternAtom (context->disp, channels[i], True);
727 if (prop_atom == None)
730 if (xv_attr != NULL) {
731 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
732 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
733 matching_attr = xv_attr + j;
737 GstColorBalanceChannel *channel;
739 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
740 channel->label = g_strdup (channels[i]);
741 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
742 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
744 context->channels_list = g_list_append (context->channels_list, channel);
746 /* If the colorbalance settings have not been touched we get Xv values
747 as defaults and update our internal variables */
748 if (!config->cb_changed) {
751 XvGetPortAttribute (context->disp, context->xv_port_id,
753 /* Normalize val to [-1000, 1000] */
754 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
755 (double) (channel->max_value - channel->min_value));
757 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
759 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
760 config->saturation = val;
761 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
762 config->brightness = val;
763 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
764 config->contrast = val;
777 gst_xvcontext_unref (context);
778 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_WRITE,
779 "Could not open display %s", config->display_name);
784 gst_xvcontext_unref (context);
785 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
786 ("Could not get pixel formats"));
791 gst_xvcontext_unref (context);
797 gst_xvcontext_set_synchronous (GstXvContext * context, gboolean synchronous)
799 /* call XSynchronize with the current value of synchronous */
800 GST_DEBUG ("XSynchronize called with %s", synchronous ? "TRUE" : "FALSE");
801 g_mutex_lock (&context->lock);
802 XSynchronize (context->disp, synchronous);
803 g_mutex_unlock (&context->lock);
807 gst_xvcontext_update_colorbalance (GstXvContext * context,
808 GstXvContextConfig * config)
810 GList *channels = NULL;
812 /* Don't set the attributes if they haven't been changed, to avoid
813 * rounding errors changing the values */
814 if (!config->cb_changed)
817 /* For each channel of the colorbalance we calculate the correct value
818 doing range conversion and then set the Xv port attribute to match our
820 channels = context->channels_list;
823 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
824 GstColorBalanceChannel *channel = NULL;
827 gdouble convert_coef;
829 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
830 g_object_ref (channel);
832 /* Our range conversion coef */
833 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
835 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
837 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
838 value = config->saturation;
839 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
840 value = config->contrast;
841 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
842 value = config->brightness;
844 g_warning ("got an unknown channel %s", channel->label);
845 g_object_unref (channel);
849 /* Committing to Xv port */
850 g_mutex_lock (&context->lock);
851 prop_atom = XInternAtom (context->disp, channel->label, True);
852 if (prop_atom != None) {
855 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
856 XvSetPortAttribute (context->disp,
857 context->xv_port_id, prop_atom, xv_value);
859 g_mutex_unlock (&context->lock);
861 g_object_unref (channel);
863 channels = g_list_next (channels);
867 /* This function tries to get a format matching with a given caps in the
868 supported list of formats we generated in gst_xvimagesink_get_xv_support */
870 gst_xvcontext_get_format_from_info (GstXvContext * context, GstVideoInfo * info)
874 list = context->formats_list;
877 GstXvImageFormat *format = list->data;
879 if (format && format->vformat == GST_VIDEO_INFO_FORMAT (info))
880 return format->format;
882 list = g_list_next (list);
888 gst_xvcontext_create_xwindow (GstXvContext * context, gint width, gint height)
892 Atom hints_atom = None;
894 g_return_val_if_fail (GST_IS_XVCONTEXT (context), NULL);
896 window = g_slice_new0 (GstXWindow);
898 window->context = gst_xvcontext_ref (context);
899 window->render_rect.x = window->render_rect.y = 0;
900 window->render_rect.w = width;
901 window->render_rect.h = height;
902 window->have_render_rect = FALSE;
904 window->width = width;
905 window->height = height;
906 window->internal = TRUE;
908 g_mutex_lock (&context->lock);
910 window->win = XCreateSimpleWindow (context->disp,
911 context->root, 0, 0, width, height, 0, 0, context->black);
913 /* We have to do that to prevent X from redrawing the background on
914 * ConfigureNotify. This takes away flickering of video when resizing. */
915 XSetWindowBackgroundPixmap (context->disp, window->win, None);
917 /* Tell the window manager we'd like delete client messages instead of
919 wm_delete = XInternAtom (context->disp, "WM_DELETE_WINDOW", True);
920 if (wm_delete != None) {
921 (void) XSetWMProtocols (context->disp, window->win, &wm_delete, 1);
924 hints_atom = XInternAtom (context->disp, "_MOTIF_WM_HINTS", True);
925 if (hints_atom != None) {
928 hints = g_malloc0 (sizeof (MotifWmHints));
930 hints->flags |= MWM_HINTS_DECORATIONS;
931 hints->decorations = 1 << 0;
933 XChangeProperty (context->disp, window->win,
934 hints_atom, hints_atom, 32, PropModeReplace,
935 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
937 XSync (context->disp, FALSE);
942 window->gc = XCreateGC (context->disp, window->win, 0, NULL);
944 XMapRaised (context->disp, window->win);
946 XSync (context->disp, FALSE);
948 g_mutex_unlock (&context->lock);
954 gst_xvcontext_create_xwindow_from_xid (GstXvContext * context, XID xid)
957 XWindowAttributes attr;
959 window = g_slice_new0 (GstXWindow);
961 window->context = gst_xvcontext_ref (context);
963 /* Set the event we want to receive and create a GC */
964 g_mutex_lock (&context->lock);
966 XGetWindowAttributes (context->disp, window->win, &attr);
968 window->width = attr.width;
969 window->height = attr.height;
970 window->internal = FALSE;
972 window->have_render_rect = FALSE;
973 window->render_rect.x = window->render_rect.y = 0;
974 window->render_rect.w = attr.width;
975 window->render_rect.h = attr.height;
977 window->gc = XCreateGC (context->disp, window->win, 0, NULL);
978 g_mutex_unlock (&context->lock);
984 gst_xwindow_destroy (GstXWindow * window)
986 GstXvContext *context;
988 g_return_if_fail (window != NULL);
990 context = window->context;
992 g_mutex_lock (&context->lock);
994 /* If we did not create that window we just free the GC and let it live */
995 if (window->internal)
996 XDestroyWindow (context->disp, window->win);
998 XSelectInput (context->disp, window->win, 0);
1000 XFreeGC (context->disp, window->gc);
1002 XSync (context->disp, FALSE);
1004 g_mutex_unlock (&context->lock);
1006 gst_xvcontext_unref (context);
1008 g_slice_free1 (sizeof (GstXWindow), window);
1012 gst_xwindow_set_event_handling (GstXWindow * window, gboolean handle_events)
1014 GstXvContext *context;
1016 g_return_if_fail (window != NULL);
1018 context = window->context;
1020 g_mutex_lock (&context->lock);
1021 if (handle_events) {
1022 if (window->internal) {
1023 XSelectInput (context->disp, window->win,
1024 ExposureMask | StructureNotifyMask | PointerMotionMask |
1025 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1027 XSelectInput (context->disp, window->win,
1028 ExposureMask | StructureNotifyMask | PointerMotionMask |
1029 KeyPressMask | KeyReleaseMask);
1032 XSelectInput (context->disp, window->win, 0);
1034 g_mutex_unlock (&context->lock);
1038 gst_xwindow_set_title (GstXWindow * window, const gchar * title)
1040 GstXvContext *context;
1042 g_return_if_fail (window != NULL);
1044 context = window->context;
1046 /* we have a window */
1047 if (window->internal && title) {
1048 XTextProperty xproperty;
1050 if ((XStringListToTextProperty (((char **) &title), 1, &xproperty)) != 0) {
1051 XSetWMName (context->disp, window->win, &xproperty);
1052 XFree (xproperty.value);
1058 gst_xwindow_update_geometry (GstXWindow * window)
1060 XWindowAttributes attr;
1061 GstXvContext *context;
1063 g_return_if_fail (window != NULL);
1065 context = window->context;
1067 /* Update the window geometry */
1068 g_mutex_lock (&context->lock);
1069 XGetWindowAttributes (context->disp, window->win, &attr);
1071 window->width = attr.width;
1072 window->height = attr.height;
1074 if (!window->have_render_rect) {
1075 window->render_rect.x = window->render_rect.y = 0;
1076 window->render_rect.w = attr.width;
1077 window->render_rect.h = attr.height;
1080 g_mutex_unlock (&context->lock);
1085 gst_xwindow_clear (GstXWindow * window)
1087 GstXvContext *context;
1089 g_return_if_fail (window != NULL);
1091 context = window->context;
1093 g_mutex_lock (&context->lock);
1095 XvStopVideo (context->disp, context->xv_port_id, window->win);
1097 XSync (context->disp, FALSE);
1099 g_mutex_unlock (&context->lock);
1103 gst_xwindow_set_render_rectangle (GstXWindow * window,
1104 gint x, gint y, gint width, gint height)
1106 g_return_if_fail (window != NULL);
1108 if (width >= 0 && height >= 0) {
1109 window->render_rect.x = x;
1110 window->render_rect.y = y;
1111 window->render_rect.w = width;
1112 window->render_rect.h = height;
1113 window->have_render_rect = TRUE;
1115 window->render_rect.x = 0;
1116 window->render_rect.y = 0;
1117 window->render_rect.w = window->width;
1118 window->render_rect.h = window->height;
1119 window->have_render_rect = FALSE;