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_xv_context);
37 #define GST_CAT_DEFAULT gst_debug_xv_context
40 gst_xvcontext_config_clear (GstXvContextConfig * config)
42 if (config->display_name) {
43 g_free (config->display_name);
44 config->display_name = NULL;
46 config->adaptor_nr = -1;
49 GST_DEFINE_MINI_OBJECT_TYPE (GstXvContext, gst_xvcontext);
54 unsigned long functions;
55 unsigned long decorations;
59 MotifWmHints, MwmHints;
61 #define MWM_HINTS_DECORATIONS (1L << 1)
65 gst_lookup_xv_port_from_adaptor (GstXvContext * context,
66 XvAdaptorInfo * adaptors, guint adaptor_nr)
71 /* Do we support XvImageMask ? */
72 if (!(adaptors[adaptor_nr].type & XvImageMask)) {
73 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
74 adaptors[adaptor_nr].name);
78 /* We found such an adaptor, looking for an available port */
79 for (j = 0; j < adaptors[adaptor_nr].num_ports && !context->xv_port_id; j++) {
80 /* We try to grab the port */
81 res = XvGrabPort (context->disp, adaptors[adaptor_nr].base_id + j, 0);
83 context->xv_port_id = adaptors[adaptor_nr].base_id + j;
84 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_nr].name,
85 adaptors[adaptor_nr].num_ports);
87 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
88 adaptors[adaptor_nr].name, res);
93 /* This function generates a caps with all supported format by the first
94 Xv grabable port we find. We store each one of the supported formats in a
95 format list and append the format to a newly created caps that we return
96 If this function does not return NULL because of an error, it also grabs
97 the port via XvGrabPort */
99 gst_xvcontext_get_xv_support (GstXvContext * context,
100 const GstXvContextConfig * config, GError ** error)
103 XvAdaptorInfo *adaptors;
105 XvImageFormatValues *formats = NULL;
107 XvEncodingInfo *encodings = NULL;
108 gulong max_w = G_MAXINT, max_h = G_MAXINT;
109 GstCaps *caps = NULL;
110 GstCaps *rgb_caps = NULL;
112 g_return_val_if_fail (context != NULL, NULL);
114 /* First let's check that XVideo extension is available */
115 if (!XQueryExtension (context->disp, "XVideo", &i, &i, &i))
118 /* Then we get adaptors list */
119 if (Success != XvQueryAdaptors (context->disp, context->root,
120 &context->nb_adaptors, &adaptors))
123 context->xv_port_id = 0;
125 GST_DEBUG ("Found %u XV adaptor(s)", context->nb_adaptors);
128 (gchar **) g_malloc0 (context->nb_adaptors * sizeof (gchar *));
130 /* Now fill up our adaptor name array */
131 for (i = 0; i < context->nb_adaptors; i++) {
132 context->adaptors[i] = g_strdup (adaptors[i].name);
135 if (config->adaptor_nr != -1 && config->adaptor_nr < context->nb_adaptors) {
136 /* Find xv port from user defined adaptor */
137 gst_lookup_xv_port_from_adaptor (context, adaptors, config->adaptor_nr);
140 if (!context->xv_port_id) {
141 /* Now search for an adaptor that supports XvImageMask */
142 for (i = 0; i < context->nb_adaptors && !context->xv_port_id; i++) {
143 gst_lookup_xv_port_from_adaptor (context, adaptors, i);
144 context->adaptor_nr = i;
148 XvFreeAdaptorInfo (adaptors);
150 if (!context->xv_port_id)
153 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
156 XvAttribute *const attr = XvQueryPortAttributes (context->disp,
157 context->xv_port_id, &count);
158 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
159 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
160 static const char colorkey[] = "XV_COLORKEY";
161 static const char iturbt709[] = "XV_ITURBT_709";
162 static const char *xv_colorspace = "XV_COLORSPACE";
164 GST_DEBUG ("Checking %d Xv port attributes", count);
166 context->have_autopaint_colorkey = FALSE;
167 context->have_double_buffer = FALSE;
168 context->have_colorkey = FALSE;
169 context->have_iturbt709 = FALSE;
170 context->have_xvcolorspace = FALSE;
172 for (i = 0; ((i < count) && todo); i++) {
173 GST_DEBUG ("Got attribute %s", attr[i].name);
175 if (!strcmp (attr[i].name, autopaint)) {
176 const Atom atom = XInternAtom (context->disp, autopaint, False);
178 /* turn on autopaint colorkey */
179 XvSetPortAttribute (context->disp, context->xv_port_id, atom,
180 (config->autopaint_colorkey ? 1 : 0));
182 context->have_autopaint_colorkey = TRUE;
183 } else if (!strcmp (attr[i].name, dbl_buffer)) {
184 const Atom atom = XInternAtom (context->disp, dbl_buffer, False);
186 XvSetPortAttribute (context->disp, context->xv_port_id, atom,
187 (config->double_buffer ? 1 : 0));
189 context->have_double_buffer = TRUE;
190 } else if (!strcmp (attr[i].name, colorkey)) {
191 /* Set the colorkey, default is something that is dark but hopefully
192 * won't randomly appear on the screen elsewhere (ie not black or greys)
193 * can be overridden by setting "colorkey" property
195 const Atom atom = XInternAtom (context->disp, colorkey, False);
197 gboolean set_attr = TRUE;
200 /* set a colorkey in the right format RGB565/RGB888
201 * We only handle these 2 cases, because they're the only types of
202 * devices we've encountered. If we don't recognise it, leave it alone
204 cr = (config->colorkey >> 16);
205 cg = (config->colorkey >> 8) & 0xFF;
206 cb = (config->colorkey) & 0xFF;
207 switch (context->depth) {
208 case 16: /* RGB 565 */
212 ckey = (cr << 11) | (cg << 5) | cb;
215 case 32: /* RGB 888 / ARGB 8888 */
216 ckey = (cr << 16) | (cg << 8) | cb;
219 GST_DEBUG ("Unknown bit depth %d for Xv Colorkey - not adjusting",
226 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
227 (guint32) attr[i].max_value);
228 GST_LOG ("Setting color key for display depth %d to 0x%x",
229 context->depth, ckey);
231 XvSetPortAttribute (context->disp, context->xv_port_id, atom,
235 context->have_colorkey = TRUE;
236 } else if (!strcmp (attr[i].name, iturbt709)) {
238 context->have_iturbt709 = TRUE;
239 } else if (!strcmp (attr[i].name, xv_colorspace)) {
240 context->have_xvcolorspace = TRUE;
248 /* Get the list of encodings supported by the adapter and look for the
249 * XV_IMAGE encoding so we can determine the maximum width and height
251 XvQueryEncodings (context->disp, context->xv_port_id, &nb_encodings,
254 for (i = 0; i < nb_encodings; i++) {
255 GST_LOG ("Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
256 i, encodings[i].name, encodings[i].width, encodings[i].height,
257 encodings[i].rate.numerator, encodings[i].rate.denominator);
258 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
259 max_w = encodings[i].width;
260 max_h = encodings[i].height;
264 XvFreeEncodingInfo (encodings);
266 /* We get all image formats supported by our port */
267 formats = XvListImageFormats (context->disp,
268 context->xv_port_id, &nb_formats);
269 caps = gst_caps_new_empty ();
270 for (i = 0; i < nb_formats; i++) {
271 GstCaps *format_caps = NULL;
272 gboolean is_rgb_format = FALSE;
273 GstVideoFormat vformat;
275 /* We set the image format of the context to an existing one. This
276 is just some valid image format for making our xshm calls check before
277 caps negotiation really happens. */
278 context->im_format = formats[i].id;
280 switch (formats[i].type) {
283 XvImageFormatValues *fmt = &(formats[i]);
287 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
289 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
290 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
291 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
294 format_caps = gst_caps_new_simple ("video/x-raw",
295 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
296 "width", GST_TYPE_INT_RANGE, 1, max_w,
297 "height", GST_TYPE_INT_RANGE, 1, max_h,
298 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
300 is_rgb_format = TRUE;
305 vformat = gst_video_format_from_fourcc (formats[i].id);
306 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
309 format_caps = gst_caps_new_simple ("video/x-raw",
310 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
311 "width", GST_TYPE_INT_RANGE, 1, max_w,
312 "height", GST_TYPE_INT_RANGE, 1, max_h,
313 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
317 vformat = GST_VIDEO_FORMAT_UNKNOWN;
318 g_assert_not_reached ();
323 GstXvImageFormat *format = NULL;
325 format = g_new0 (GstXvImageFormat, 1);
327 format->format = formats[i].id;
328 format->vformat = vformat;
329 format->caps = gst_caps_copy (format_caps);
330 context->formats_list = g_list_append (context->formats_list, format);
334 if (rgb_caps == NULL)
335 rgb_caps = format_caps;
337 gst_caps_append (rgb_caps, format_caps);
339 gst_caps_append (caps, format_caps);
343 /* Collected all caps into either the caps or rgb_caps structures.
344 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
346 gst_caps_append (caps, rgb_caps);
351 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
353 if (gst_caps_is_empty (caps))
361 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
362 ("XVideo extension is not available"));
367 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
368 ("Failed getting XV adaptors list"));
373 context->adaptor_nr = -1;
374 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_BUSY,
375 ("No Xv Port available"));
380 gst_caps_unref (caps);
381 g_set_error (error, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE,
382 ("No supported format found"));
387 /* This function calculates the pixel aspect ratio based on the properties
388 * in the context structure and stores it there. */
390 gst_xvcontext_calculate_pixel_aspect_ratio (GstXvContext * context)
392 static const gint par[][2] = {
393 {1, 1}, /* regular screen */
394 {16, 15}, /* PAL TV */
395 {11, 10}, /* 525 line Rec.601 video */
396 {54, 59}, /* 625 line Rec.601 video */
397 {64, 45}, /* 1280x1024 on 16:9 display */
398 {5, 3}, /* 1280x1024 on 4:3 display */
399 {4, 3} /* 800x600 on 16:9 display */
406 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
408 /* first calculate the "real" ratio based on the X values;
409 * which is the "physical" w/h divided by the w/h in pixels of the display */
410 ratio = (gdouble) (context->widthmm * context->height)
411 / (context->heightmm * context->width);
413 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
415 if (context->width == 720 && context->height == 576) {
416 ratio = 4.0 * 576 / (3.0 * 720);
418 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
420 /* now find the one from par[][2] with the lowest delta to the real one */
424 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
425 gdouble this_delta = DELTA (i);
427 if (this_delta < delta) {
433 GST_DEBUG ("Decided on index %d (%d/%d)", index,
434 par[index][0], par[index][1]);
436 g_free (context->par);
437 context->par = g_new0 (GValue, 1);
438 g_value_init (context->par, GST_TYPE_FRACTION);
439 gst_value_set_fraction (context->par, par[index][0], par[index][1]);
440 GST_DEBUG ("set context PAR to %d/%d",
441 gst_value_get_fraction_numerator (context->par),
442 gst_value_get_fraction_denominator (context->par));
447 static gboolean error_caught = FALSE;
450 gst_xvimage_handle_xerror (Display * display, XErrorEvent * xevent)
452 char error_msg[1024];
454 XGetErrorText (display, xevent->error_code, error_msg, 1024);
455 GST_DEBUG ("xvimage triggered an XError. error: %s", error_msg);
460 /* This function checks that it is actually really possible to create an image
463 gst_xvcontext_check_xshm_calls (GstXvContext * context)
466 XShmSegmentInfo SHMInfo;
468 int (*handler) (Display *, XErrorEvent *);
469 gboolean result = FALSE;
470 gboolean did_attach = FALSE;
472 g_return_val_if_fail (context != NULL, FALSE);
474 /* Sync to ensure any older errors are already processed */
475 XSync (context->disp, FALSE);
477 /* Set defaults so we don't free these later unnecessarily */
478 SHMInfo.shmaddr = ((void *) -1);
481 /* Setting an error handler to catch failure */
482 error_caught = FALSE;
483 handler = XSetErrorHandler (gst_xvimage_handle_xerror);
485 /* Trying to create a 1x1 picture */
486 GST_DEBUG ("XvShmCreateImage of 1x1");
487 xvimage = XvShmCreateImage (context->disp, context->xv_port_id,
488 context->im_format, NULL, 1, 1, &SHMInfo);
490 /* Might cause an error, sync to ensure it is noticed */
491 XSync (context->disp, FALSE);
492 if (!xvimage || error_caught) {
493 GST_WARNING ("could not XvShmCreateImage a 1x1 image");
496 size = xvimage->data_size;
497 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
498 if (SHMInfo.shmid == -1) {
499 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
504 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
505 if (SHMInfo.shmaddr == ((void *) -1)) {
506 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
507 /* Clean up the shared memory segment */
508 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
512 xvimage->data = SHMInfo.shmaddr;
513 SHMInfo.readOnly = FALSE;
515 if (XShmAttach (context->disp, &SHMInfo) == 0) {
516 GST_WARNING ("Failed to XShmAttach");
517 /* Clean up the shared memory segment */
518 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
522 /* Sync to ensure we see any errors we caused */
523 XSync (context->disp, FALSE);
525 /* Delete the shared memory segment as soon as everyone is attached.
526 * This way, it will be deleted as soon as we detach later, and not
527 * leaked if we crash. */
528 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
531 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
535 /* store whether we succeeded in result */
538 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
539 "Not using shared memory.");
543 /* Sync to ensure we swallow any errors we caused and reset error_caught */
544 XSync (context->disp, FALSE);
546 error_caught = FALSE;
547 XSetErrorHandler (handler);
550 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
551 SHMInfo.shmid, SHMInfo.shmseg);
552 XShmDetach (context->disp, &SHMInfo);
553 XSync (context->disp, FALSE);
555 if (SHMInfo.shmaddr != ((void *) -1))
556 shmdt (SHMInfo.shmaddr);
561 #endif /* HAVE_XSHM */
563 static GstXvContext *
564 gst_xvcontext_copy (GstXvContext * context)
570 gst_xvcontext_free (GstXvContext * context)
572 GList *formats_list, *channels_list;
575 GST_LOG ("free %p", context);
577 formats_list = context->formats_list;
579 while (formats_list) {
580 GstXvImageFormat *format = formats_list->data;
582 gst_caps_unref (format->caps);
584 formats_list = g_list_next (formats_list);
587 if (context->formats_list)
588 g_list_free (context->formats_list);
590 channels_list = context->channels_list;
592 while (channels_list) {
593 GstColorBalanceChannel *channel = channels_list->data;
595 g_object_unref (channel);
596 channels_list = g_list_next (channels_list);
599 if (context->channels_list)
600 g_list_free (context->channels_list);
603 gst_caps_unref (context->caps);
604 if (context->last_caps)
605 gst_caps_unref (context->last_caps);
607 for (i = 0; i < context->nb_adaptors; i++) {
608 g_free (context->adaptors[i]);
611 g_free (context->adaptors);
613 g_free (context->par);
615 GST_DEBUG ("Closing display and freeing X Context");
617 if (context->xv_port_id)
618 XvUngrabPort (context->disp, context->xv_port_id, 0);
621 XCloseDisplay (context->disp);
623 g_mutex_clear (&context->lock);
625 g_slice_free1 (sizeof (GstXvContext), context);
629 /* This function gets the X Display and global info about it. Everything is
630 stored in our object and will be cleaned when the object is disposed. Note
631 here that caps for supported format are generated without any window or
634 gst_xvcontext_new (GstXvContextConfig * config, GError ** error)
636 GstXvContext *context = NULL;
637 XPixmapFormatValues *px_formats = NULL;
638 gint nb_formats = 0, i, j, N_attr;
639 XvAttribute *xv_attr;
641 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
642 "XV_BRIGHTNESS", "XV_CONTRAST"
644 int opcode, event, err;
645 int major = XkbMajorVersion;
646 int minor = XkbMinorVersion;
648 g_return_val_if_fail (config != NULL, NULL);
650 context = g_slice_new0 (GstXvContext);
652 gst_mini_object_init (GST_MINI_OBJECT_CAST (context), 0,
653 gst_xvcontext_get_type (),
654 (GstMiniObjectCopyFunction) gst_xvcontext_copy,
655 (GstMiniObjectDisposeFunction) NULL,
656 (GstMiniObjectFreeFunction) gst_xvcontext_free);
658 g_mutex_init (&context->lock);
659 context->im_format = 0;
660 context->adaptor_nr = -1;
662 if (!(context->disp = XOpenDisplay (config->display_name)))
665 context->screen = DefaultScreenOfDisplay (context->disp);
666 context->screen_num = DefaultScreen (context->disp);
667 context->visual = DefaultVisual (context->disp, context->screen_num);
668 context->root = DefaultRootWindow (context->disp);
669 context->white = XWhitePixel (context->disp, context->screen_num);
670 context->black = XBlackPixel (context->disp, context->screen_num);
671 context->depth = DefaultDepthOfScreen (context->screen);
673 context->width = DisplayWidth (context->disp, context->screen_num);
674 context->height = DisplayHeight (context->disp, context->screen_num);
675 context->widthmm = DisplayWidthMM (context->disp, context->screen_num);
676 context->heightmm = DisplayHeightMM (context->disp, context->screen_num);
678 GST_DEBUG ("X reports %dx%d pixels and %d mm x %d mm",
679 context->width, context->height, context->widthmm, context->heightmm);
681 gst_xvcontext_calculate_pixel_aspect_ratio (context);
682 /* We get supported pixmap formats at supported depth */
683 px_formats = XListPixmapFormats (context->disp, &nb_formats);
686 goto no_pixel_formats;
688 /* We get bpp value corresponding to our running depth */
689 for (i = 0; i < nb_formats; i++) {
690 if (px_formats[i].depth == context->depth)
691 context->bpp = px_formats[i].bits_per_pixel;
696 context->endianness =
697 (ImageByteOrder (context->disp) ==
698 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
700 /* our caps system handles 24/32bpp RGB as big-endian. */
701 if ((context->bpp == 24 || context->bpp == 32) &&
702 context->endianness == G_LITTLE_ENDIAN) {
703 context->endianness = G_BIG_ENDIAN;
704 context->visual->red_mask = GUINT32_TO_BE (context->visual->red_mask);
705 context->visual->green_mask = GUINT32_TO_BE (context->visual->green_mask);
706 context->visual->blue_mask = GUINT32_TO_BE (context->visual->blue_mask);
707 if (context->bpp == 24) {
708 context->visual->red_mask >>= 8;
709 context->visual->green_mask >>= 8;
710 context->visual->blue_mask >>= 8;
714 if (!(context->caps = gst_xvcontext_get_xv_support (context, config, error)))
717 /* Search for XShm extension support */
719 if (XShmQueryExtension (context->disp) &&
720 gst_xvcontext_check_xshm_calls (context)) {
721 context->use_xshm = TRUE;
722 GST_DEBUG ("xvimagesink is using XShm extension");
724 #endif /* HAVE_XSHM */
726 context->use_xshm = FALSE;
727 GST_DEBUG ("xvimagesink is not using XShm extension");
729 if (XkbQueryExtension (context->disp, &opcode, &event, &err, &major, &minor)) {
730 context->use_xkb = TRUE;
731 GST_DEBUG ("xvimagesink is using Xkb extension");
733 context->use_xkb = FALSE;
734 GST_DEBUG ("xvimagesink is not using Xkb extension");
737 xv_attr = XvQueryPortAttributes (context->disp, context->xv_port_id, &N_attr);
739 /* Generate the channels list */
740 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
741 XvAttribute *matching_attr = NULL;
743 /* Retrieve the property atom if it exists. If it doesn't exist,
744 * the attribute itself must not either, so we can skip */
745 prop_atom = XInternAtom (context->disp, channels[i], True);
746 if (prop_atom == None)
749 if (xv_attr != NULL) {
750 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
751 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
752 matching_attr = xv_attr + j;
756 GstColorBalanceChannel *channel;
758 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
759 channel->label = g_strdup (channels[i]);
760 channel->min_value = matching_attr->min_value;
761 channel->max_value = matching_attr->max_value;
763 context->channels_list = g_list_append (context->channels_list, channel);
765 /* If the colorbalance settings have not been touched we get Xv values
766 as defaults and update our internal variables */
767 if (!config->cb_changed) {
770 XvGetPortAttribute (context->disp, context->xv_port_id,
772 /* Normalize val to [-1000, 1000] */
773 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
774 (double) (channel->max_value - channel->min_value));
776 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
778 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
779 config->saturation = val;
780 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
781 config->brightness = val;
782 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
783 config->contrast = val;
796 gst_xvcontext_unref (context);
797 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_WRITE,
798 "Could not open display %s", config->display_name);
803 gst_xvcontext_unref (context);
804 g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
805 ("Could not get pixel formats"));
810 gst_xvcontext_unref (context);
816 gst_xvcontext_set_synchronous (GstXvContext * context, gboolean synchronous)
818 /* call XSynchronize with the current value of synchronous */
819 GST_DEBUG ("XSynchronize called with %s", synchronous ? "TRUE" : "FALSE");
820 g_mutex_lock (&context->lock);
821 XSynchronize (context->disp, synchronous);
822 g_mutex_unlock (&context->lock);
826 gst_xvcontext_update_colorbalance (GstXvContext * context,
827 GstXvContextConfig * config)
829 GList *channels = NULL;
831 /* Don't set the attributes if they haven't been changed, to avoid
832 * rounding errors changing the values */
833 if (!config->cb_changed)
836 /* For each channel of the colorbalance we calculate the correct value
837 doing range conversion and then set the Xv port attribute to match our
839 channels = context->channels_list;
842 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
843 GstColorBalanceChannel *channel = NULL;
846 gdouble convert_coef;
848 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
849 g_object_ref (channel);
851 /* Our range conversion coef */
852 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
854 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
856 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
857 value = config->saturation;
858 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
859 value = config->contrast;
860 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
861 value = config->brightness;
863 g_warning ("got an unknown channel %s", channel->label);
864 g_object_unref (channel);
868 /* Committing to Xv port */
869 g_mutex_lock (&context->lock);
870 prop_atom = XInternAtom (context->disp, channel->label, True);
871 if (prop_atom != None) {
874 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
875 XvSetPortAttribute (context->disp,
876 context->xv_port_id, prop_atom, xv_value);
878 g_mutex_unlock (&context->lock);
880 g_object_unref (channel);
882 channels = g_list_next (channels);
886 /* This function tries to get a format matching with a given caps in the
887 supported list of formats we generated in gst_xvimagesink_get_xv_support */
889 gst_xvcontext_get_format_from_info (GstXvContext * context,
890 const GstVideoInfo * info)
894 list = context->formats_list;
897 GstXvImageFormat *format = list->data;
899 if (format && format->vformat == GST_VIDEO_INFO_FORMAT (info))
900 return format->format;
902 list = g_list_next (list);
908 gst_xvcontext_set_colorimetry (GstXvContext * context,
909 GstVideoColorimetry * colorimetry)
914 if (!context->have_iturbt709 && !context->have_xvcolorspace)
917 switch (colorimetry->matrix) {
918 case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
919 case GST_VIDEO_COLOR_MATRIX_BT709:
927 g_mutex_lock (&context->lock);
928 if (context->have_iturbt709) {
929 prop_atom = XInternAtom (context->disp, "XV_ITURBT_709", True);
930 if (prop_atom != None) {
931 XvSetPortAttribute (context->disp,
932 context->xv_port_id, prop_atom, xv_value);
936 if (context->have_xvcolorspace) {
937 prop_atom = XInternAtom (context->disp, "XV_COLORSPACE", True);
938 if (prop_atom != None) {
939 XvSetPortAttribute (context->disp,
940 context->xv_port_id, prop_atom, xv_value);
943 g_mutex_unlock (&context->lock);
947 gst_xvcontext_create_xwindow (GstXvContext * context, gint width, gint height)
951 Atom hints_atom = None;
953 g_return_val_if_fail (GST_IS_XVCONTEXT (context), NULL);
955 window = g_slice_new0 (GstXWindow);
957 window->context = gst_xvcontext_ref (context);
958 window->render_rect.x = window->render_rect.y = 0;
959 window->render_rect.w = width;
960 window->render_rect.h = height;
961 window->have_render_rect = FALSE;
963 window->width = width;
964 window->height = height;
965 window->internal = TRUE;
967 g_mutex_lock (&context->lock);
969 window->win = XCreateSimpleWindow (context->disp,
970 context->root, 0, 0, width, height, 0, 0, context->black);
972 /* We have to do that to prevent X from redrawing the background on
973 * ConfigureNotify. This takes away flickering of video when resizing. */
974 XSetWindowBackgroundPixmap (context->disp, window->win, None);
976 /* Tell the window manager we'd like delete client messages instead of
978 wm_delete = XInternAtom (context->disp, "WM_DELETE_WINDOW", True);
979 if (wm_delete != None) {
980 (void) XSetWMProtocols (context->disp, window->win, &wm_delete, 1);
983 hints_atom = XInternAtom (context->disp, "_MOTIF_WM_HINTS", True);
984 if (hints_atom != None) {
987 hints = g_malloc0 (sizeof (MotifWmHints));
989 hints->flags |= MWM_HINTS_DECORATIONS;
990 hints->decorations = 1 << 0;
992 XChangeProperty (context->disp, window->win,
993 hints_atom, hints_atom, 32, PropModeReplace,
994 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
996 XSync (context->disp, FALSE);
1001 window->gc = XCreateGC (context->disp, window->win, 0, NULL);
1003 XMapRaised (context->disp, window->win);
1005 XSync (context->disp, FALSE);
1007 g_mutex_unlock (&context->lock);
1013 gst_xvcontext_create_xwindow_from_xid (GstXvContext * context, XID xid)
1016 XWindowAttributes attr;
1018 window = g_slice_new0 (GstXWindow);
1020 window->context = gst_xvcontext_ref (context);
1022 /* Set the event we want to receive and create a GC */
1023 g_mutex_lock (&context->lock);
1025 XGetWindowAttributes (context->disp, window->win, &attr);
1027 window->width = attr.width;
1028 window->height = attr.height;
1029 window->internal = FALSE;
1031 window->have_render_rect = FALSE;
1032 window->render_rect.x = window->render_rect.y = 0;
1033 window->render_rect.w = attr.width;
1034 window->render_rect.h = attr.height;
1036 window->gc = XCreateGC (context->disp, window->win, 0, NULL);
1037 g_mutex_unlock (&context->lock);
1043 gst_xwindow_destroy (GstXWindow * window)
1045 GstXvContext *context;
1047 g_return_if_fail (window != NULL);
1049 context = window->context;
1051 g_mutex_lock (&context->lock);
1053 /* If we did not create that window we just free the GC and let it live */
1054 if (window->internal)
1055 XDestroyWindow (context->disp, window->win);
1057 XSelectInput (context->disp, window->win, 0);
1059 XFreeGC (context->disp, window->gc);
1061 XSync (context->disp, FALSE);
1063 g_mutex_unlock (&context->lock);
1065 gst_xvcontext_unref (context);
1067 g_slice_free1 (sizeof (GstXWindow), window);
1071 gst_xwindow_set_event_handling (GstXWindow * window, gboolean handle_events)
1073 GstXvContext *context;
1075 g_return_if_fail (window != NULL);
1077 context = window->context;
1079 g_mutex_lock (&context->lock);
1080 if (handle_events) {
1081 if (window->internal) {
1082 XSelectInput (context->disp, window->win,
1083 ExposureMask | StructureNotifyMask | PointerMotionMask |
1084 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1086 XSelectInput (context->disp, window->win,
1087 ExposureMask | StructureNotifyMask | PointerMotionMask |
1088 KeyPressMask | KeyReleaseMask);
1091 XSelectInput (context->disp, window->win, 0);
1093 g_mutex_unlock (&context->lock);
1097 gst_xwindow_set_title (GstXWindow * window, const gchar * title)
1099 GstXvContext *context;
1101 g_return_if_fail (window != NULL);
1103 context = window->context;
1105 /* we have a window */
1106 if (window->internal && title) {
1107 XTextProperty xproperty;
1108 XClassHint *hint = XAllocClassHint ();
1109 Atom _NET_WM_NAME = XInternAtom (context->disp, "_NET_WM_NAME", 0);
1110 Atom UTF8_STRING = XInternAtom (context->disp, "UTF8_STRING", 0);
1112 if ((XStringListToTextProperty (((char **) &title), 1, &xproperty)) != 0) {
1113 XSetWMName (context->disp, window->win, &xproperty);
1114 XFree (xproperty.value);
1116 XChangeProperty (context->disp, window->win, _NET_WM_NAME, UTF8_STRING, 8,
1117 0, (unsigned char *) title, strlen (title));
1118 XSync (context->disp, False);
1121 hint->res_name = (char *) title;
1122 hint->res_class = (char *) "GStreamer";
1123 XSetClassHint (context->disp, window->win, hint);
1131 gst_xwindow_update_geometry (GstXWindow * window)
1133 XWindowAttributes attr;
1134 GstXvContext *context;
1136 g_return_if_fail (window != NULL);
1138 context = window->context;
1140 /* Update the window geometry */
1141 g_mutex_lock (&context->lock);
1142 XGetWindowAttributes (context->disp, window->win, &attr);
1144 window->width = attr.width;
1145 window->height = attr.height;
1147 if (!window->have_render_rect) {
1148 window->render_rect.x = window->render_rect.y = 0;
1149 window->render_rect.w = attr.width;
1150 window->render_rect.h = attr.height;
1153 g_mutex_unlock (&context->lock);
1158 gst_xwindow_clear (GstXWindow * window)
1160 GstXvContext *context;
1162 g_return_if_fail (window != NULL);
1164 context = window->context;
1166 g_mutex_lock (&context->lock);
1168 XvStopVideo (context->disp, context->xv_port_id, window->win);
1170 XSync (context->disp, FALSE);
1172 g_mutex_unlock (&context->lock);
1176 gst_xwindow_set_render_rectangle (GstXWindow * window,
1177 gint x, gint y, gint width, gint height)
1179 g_return_if_fail (window != NULL);
1181 if (width >= 0 && height >= 0) {
1182 window->render_rect.x = x;
1183 window->render_rect.y = y;
1184 window->render_rect.w = width;
1185 window->render_rect.h = height;
1186 window->have_render_rect = TRUE;
1188 window->render_rect.x = 0;
1189 window->render_rect.y = 0;
1190 window->render_rect.w = window->width;
1191 window->render_rect.h = window->height;
1192 window->have_render_rect = FALSE;