• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3  *               <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20 
21 /* for developers: there are two useful tools : xvinfo and xvattr */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 /* Object header */
28 #include "xvcontext.h"
29 
30 /* Debugging category */
31 #include <gst/gstinfo.h>
32 
33 /* for XkbKeycodeToKeysym */
34 #include <X11/XKBlib.h>
35 
36 GST_DEBUG_CATEGORY (gst_debug_xv_context);
37 #define GST_CAT_DEFAULT gst_debug_xv_context
38 
39 void
gst_xvcontext_config_clear(GstXvContextConfig * config)40 gst_xvcontext_config_clear (GstXvContextConfig * config)
41 {
42   if (config->display_name) {
43     g_free (config->display_name);
44     config->display_name = NULL;
45   }
46   config->adaptor_nr = -1;
47 }
48 
49 GST_DEFINE_MINI_OBJECT_TYPE (GstXvContext, gst_xvcontext);
50 
51 typedef struct
52 {
53   unsigned long flags;
54   unsigned long functions;
55   unsigned long decorations;
56   long input_mode;
57   unsigned long status;
58 }
59 MotifWmHints, MwmHints;
60 
61 #define MWM_HINTS_DECORATIONS   (1L << 1)
62 
63 
64 static void
gst_lookup_xv_port_from_adaptor(GstXvContext * context,XvAdaptorInfo * adaptors,guint adaptor_nr)65 gst_lookup_xv_port_from_adaptor (GstXvContext * context,
66     XvAdaptorInfo * adaptors, guint adaptor_nr)
67 {
68   gint j;
69   gint res;
70 
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);
75     return;
76   }
77 
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);
82     if (Success == res) {
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);
86     } else {
87       GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
88           adaptors[adaptor_nr].name, res);
89     }
90   }
91 }
92 
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 */
98 static GstCaps *
gst_xvcontext_get_xv_support(GstXvContext * context,const GstXvContextConfig * config,GError ** error)99 gst_xvcontext_get_xv_support (GstXvContext * context,
100     const GstXvContextConfig * config, GError ** error)
101 {
102   gint i;
103   XvAdaptorInfo *adaptors;
104   gint nb_formats;
105   XvImageFormatValues *formats = NULL;
106   guint nb_encodings;
107   XvEncodingInfo *encodings = NULL;
108   gulong max_w = G_MAXINT, max_h = G_MAXINT;
109   GstCaps *caps = NULL;
110   GstCaps *rgb_caps = NULL;
111 
112   g_return_val_if_fail (context != NULL, NULL);
113 
114   /* First let's check that XVideo extension is available */
115   if (!XQueryExtension (context->disp, "XVideo", &i, &i, &i))
116     goto no_xv;
117 
118   /* Then we get adaptors list */
119   if (Success != XvQueryAdaptors (context->disp, context->root,
120           &context->nb_adaptors, &adaptors))
121     goto no_adaptors;
122 
123   context->xv_port_id = 0;
124 
125   GST_DEBUG ("Found %u XV adaptor(s)", context->nb_adaptors);
126 
127   context->adaptors =
128       (gchar **) g_malloc0 (context->nb_adaptors * sizeof (gchar *));
129 
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);
133   }
134 
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);
138   }
139 
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;
145     }
146   }
147 
148   XvFreeAdaptorInfo (adaptors);
149 
150   if (!context->xv_port_id)
151     goto no_ports;
152 
153   /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
154   {
155     int count, todo = 4;
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";
163 
164     GST_DEBUG ("Checking %d Xv port attributes", count);
165 
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;
171 
172     for (i = 0; ((i < count) && todo); i++) {
173       GST_DEBUG ("Got attribute %s", attr[i].name);
174 
175       if (!strcmp (attr[i].name, autopaint)) {
176         const Atom atom = XInternAtom (context->disp, autopaint, False);
177 
178         /* turn on autopaint colorkey */
179         XvSetPortAttribute (context->disp, context->xv_port_id, atom,
180             (config->autopaint_colorkey ? 1 : 0));
181         todo--;
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);
185 
186         XvSetPortAttribute (context->disp, context->xv_port_id, atom,
187             (config->double_buffer ? 1 : 0));
188         todo--;
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
194          */
195         const Atom atom = XInternAtom (context->disp, colorkey, False);
196         guint32 ckey = 0;
197         gboolean set_attr = TRUE;
198         guint cr, cg, cb;
199 
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
203          */
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 */
209             cr >>= 3;
210             cg >>= 2;
211             cb >>= 3;
212             ckey = (cr << 11) | (cg << 5) | cb;
213             break;
214           case 24:
215           case 32:             /* RGB 888 / ARGB 8888 */
216             ckey = (cr << 16) | (cg << 8) | cb;
217             break;
218           default:
219             GST_DEBUG ("Unknown bit depth %d for Xv Colorkey - not adjusting",
220                 context->depth);
221             set_attr = FALSE;
222             break;
223         }
224 
225         if (set_attr) {
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);
230 
231           XvSetPortAttribute (context->disp, context->xv_port_id, atom,
232               (gint) ckey);
233         }
234         todo--;
235         context->have_colorkey = TRUE;
236       } else if (!strcmp (attr[i].name, iturbt709)) {
237         todo--;
238         context->have_iturbt709 = TRUE;
239       } else if (!strcmp (attr[i].name, xv_colorspace)) {
240         context->have_xvcolorspace = TRUE;
241         todo--;
242       }
243     }
244 
245     XFree (attr);
246   }
247 
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
250    * supported */
251   XvQueryEncodings (context->disp, context->xv_port_id, &nb_encodings,
252       &encodings);
253 
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;
261     }
262   }
263 
264   XvFreeEncodingInfo (encodings);
265 
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;
274 
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;
279 
280     switch (formats[i].type) {
281       case XvRGB:
282       {
283         XvImageFormatValues *fmt = &(formats[i]);
284         gint endianness;
285 
286         endianness =
287             (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
288 
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)
292           break;
293 
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);
299 
300         is_rgb_format = TRUE;
301         break;
302       }
303       case XvYUV:
304       {
305         vformat = gst_video_format_from_fourcc (formats[i].id);
306         if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
307           break;
308 
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);
314         break;
315       }
316       default:
317         vformat = GST_VIDEO_FORMAT_UNKNOWN;
318         g_assert_not_reached ();
319         break;
320     }
321 
322     if (format_caps) {
323       GstXvImageFormat *format = NULL;
324 
325       format = g_new0 (GstXvImageFormat, 1);
326       if (format) {
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);
331       }
332 
333       if (is_rgb_format) {
334         if (rgb_caps == NULL)
335           rgb_caps = format_caps;
336         else
337           gst_caps_append (rgb_caps, format_caps);
338       } else
339         gst_caps_append (caps, format_caps);
340     }
341   }
342 
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 */
345   if (rgb_caps)
346     gst_caps_append (caps, rgb_caps);
347 
348   if (formats)
349     XFree (formats);
350 
351   GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
352 
353   if (gst_caps_is_empty (caps))
354     goto no_caps;
355 
356   return caps;
357 
358   /* ERRORS */
359 no_xv:
360   {
361     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
362         ("XVideo extension is not available"));
363     return NULL;
364   }
365 no_adaptors:
366   {
367     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
368         ("Failed getting XV adaptors list"));
369     return NULL;
370   }
371 no_ports:
372   {
373     context->adaptor_nr = -1;
374     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_BUSY,
375         ("No Xv Port available"));
376     return NULL;
377   }
378 no_caps:
379   {
380     gst_caps_unref (caps);
381     g_set_error (error, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE,
382         ("No supported format found"));
383     return NULL;
384   }
385 }
386 
387 /* This function calculates the pixel aspect ratio based on the properties
388  * in the context structure and stores it there. */
389 static void
gst_xvcontext_calculate_pixel_aspect_ratio(GstXvContext * context)390 gst_xvcontext_calculate_pixel_aspect_ratio (GstXvContext * context)
391 {
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 */
400   };
401   gint i;
402   gint index;
403   gdouble ratio;
404   gdouble delta;
405 
406 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
407 
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);
412 
413   /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
414    * override here */
415   if (context->width == 720 && context->height == 576) {
416     ratio = 4.0 * 576 / (3.0 * 720);
417   }
418   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
419 
420   /* now find the one from par[][2] with the lowest delta to the real one */
421   delta = DELTA (0);
422   index = 0;
423 
424   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
425     gdouble this_delta = DELTA (i);
426 
427     if (this_delta < delta) {
428       index = i;
429       delta = this_delta;
430     }
431   }
432 
433   GST_DEBUG ("Decided on index %d (%d/%d)", index,
434       par[index][0], par[index][1]);
435 
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));
443 }
444 
445 #ifdef HAVE_XSHM
446 /* X11 stuff */
447 static gboolean error_caught = FALSE;
448 
449 static int
gst_xvimage_handle_xerror(Display * display,XErrorEvent * xevent)450 gst_xvimage_handle_xerror (Display * display, XErrorEvent * xevent)
451 {
452   char error_msg[1024];
453 
454   XGetErrorText (display, xevent->error_code, error_msg, 1024);
455   GST_DEBUG ("xvimage triggered an XError. error: %s", error_msg);
456   error_caught = TRUE;
457   return 0;
458 }
459 
460 /* This function checks that it is actually really possible to create an image
461    using XShm */
462 static gboolean
gst_xvcontext_check_xshm_calls(GstXvContext * context)463 gst_xvcontext_check_xshm_calls (GstXvContext * context)
464 {
465   XvImage *xvimage;
466   XShmSegmentInfo SHMInfo;
467   size_t size;
468   int (*handler) (Display *, XErrorEvent *);
469   gboolean result = FALSE;
470   gboolean did_attach = FALSE;
471 
472   g_return_val_if_fail (context != NULL, FALSE);
473 
474   /* Sync to ensure any older errors are already processed */
475   XSync (context->disp, FALSE);
476 
477   /* Set defaults so we don't free these later unnecessarily */
478   SHMInfo.shmaddr = ((void *) -1);
479   SHMInfo.shmid = -1;
480 
481   /* Setting an error handler to catch failure */
482   error_caught = FALSE;
483   handler = XSetErrorHandler (gst_xvimage_handle_xerror);
484 
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);
489 
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");
494     goto beach;
495   }
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",
500         size);
501     goto beach;
502   }
503 
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);
509     goto beach;
510   }
511 
512   xvimage->data = SHMInfo.shmaddr;
513   SHMInfo.readOnly = FALSE;
514 
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);
519     goto beach;
520   }
521 
522   /* Sync to ensure we see any errors we caused */
523   XSync (context->disp, FALSE);
524 
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);
529 
530   if (!error_caught) {
531     GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
532         SHMInfo.shmseg);
533 
534     did_attach = TRUE;
535     /* store whether we succeeded in result */
536     result = TRUE;
537   } else {
538     GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
539         "Not using shared memory.");
540   }
541 
542 beach:
543   /* Sync to ensure we swallow any errors we caused and reset error_caught */
544   XSync (context->disp, FALSE);
545 
546   error_caught = FALSE;
547   XSetErrorHandler (handler);
548 
549   if (did_attach) {
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);
554   }
555   if (SHMInfo.shmaddr != ((void *) -1))
556     shmdt (SHMInfo.shmaddr);
557   if (xvimage)
558     XFree (xvimage);
559   return result;
560 }
561 #endif /* HAVE_XSHM */
562 
563 static GstXvContext *
gst_xvcontext_copy(GstXvContext * context)564 gst_xvcontext_copy (GstXvContext * context)
565 {
566   return NULL;
567 }
568 
569 static void
gst_xvcontext_free(GstXvContext * context)570 gst_xvcontext_free (GstXvContext * context)
571 {
572   GList *formats_list, *channels_list;
573   gint i = 0;
574 
575   GST_LOG ("free %p", context);
576 
577   formats_list = context->formats_list;
578 
579   while (formats_list) {
580     GstXvImageFormat *format = formats_list->data;
581 
582     gst_caps_unref (format->caps);
583     g_free (format);
584     formats_list = g_list_next (formats_list);
585   }
586 
587   if (context->formats_list)
588     g_list_free (context->formats_list);
589 
590   channels_list = context->channels_list;
591 
592   while (channels_list) {
593     GstColorBalanceChannel *channel = channels_list->data;
594 
595     g_object_unref (channel);
596     channels_list = g_list_next (channels_list);
597   }
598 
599   if (context->channels_list)
600     g_list_free (context->channels_list);
601 
602   if (context->caps)
603     gst_caps_unref (context->caps);
604   if (context->last_caps)
605     gst_caps_unref (context->last_caps);
606 
607   for (i = 0; i < context->nb_adaptors; i++) {
608     g_free (context->adaptors[i]);
609   }
610 
611   g_free (context->adaptors);
612 
613   g_free (context->par);
614 
615   GST_DEBUG ("Closing display and freeing X Context");
616 
617   if (context->xv_port_id)
618     XvUngrabPort (context->disp, context->xv_port_id, 0);
619 
620   if (context->disp)
621     XCloseDisplay (context->disp);
622 
623   g_mutex_clear (&context->lock);
624 
625   g_slice_free1 (sizeof (GstXvContext), context);
626 }
627 
628 
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
632    image creation */
633 GstXvContext *
gst_xvcontext_new(GstXvContextConfig * config,GError ** error)634 gst_xvcontext_new (GstXvContextConfig * config, GError ** error)
635 {
636   GstXvContext *context = NULL;
637   XPixmapFormatValues *px_formats = NULL;
638   gint nb_formats = 0, i, j, N_attr;
639   XvAttribute *xv_attr;
640   Atom prop_atom;
641   const char *channels[4] = { "XV_HUE", "XV_SATURATION",
642     "XV_BRIGHTNESS", "XV_CONTRAST"
643   };
644   int opcode, event, err;
645   int major = XkbMajorVersion;
646   int minor = XkbMinorVersion;
647 
648   g_return_val_if_fail (config != NULL, NULL);
649 
650   context = g_slice_new0 (GstXvContext);
651 
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);
657 
658   g_mutex_init (&context->lock);
659   context->im_format = 0;
660   context->adaptor_nr = -1;
661 
662   if (!(context->disp = XOpenDisplay (config->display_name)))
663     goto no_display;
664 
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);
672 
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);
677 
678   GST_DEBUG ("X reports %dx%d pixels and %d mm x %d mm",
679       context->width, context->height, context->widthmm, context->heightmm);
680 
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);
684 
685   if (!px_formats)
686     goto no_pixel_formats;
687 
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;
692   }
693 
694   XFree (px_formats);
695 
696   context->endianness =
697       (ImageByteOrder (context->disp) ==
698       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
699 
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;
711     }
712   }
713 
714   if (!(context->caps = gst_xvcontext_get_xv_support (context, config, error)))
715     goto no_caps;
716 
717   /* Search for XShm extension support */
718 #ifdef HAVE_XSHM
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");
723   } else
724 #endif /* HAVE_XSHM */
725   {
726     context->use_xshm = FALSE;
727     GST_DEBUG ("xvimagesink is not using XShm extension");
728   }
729   if (XkbQueryExtension (context->disp, &opcode, &event, &err, &major, &minor)) {
730     context->use_xkb = TRUE;
731     GST_DEBUG ("xvimagesink is using Xkb extension");
732   } else {
733     context->use_xkb = FALSE;
734     GST_DEBUG ("xvimagesink is not using Xkb extension");
735   }
736 
737   xv_attr = XvQueryPortAttributes (context->disp, context->xv_port_id, &N_attr);
738 
739   /* Generate the channels list */
740   for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
741     XvAttribute *matching_attr = NULL;
742 
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)
747       continue;
748 
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;
753     }
754 
755     if (matching_attr) {
756       GstColorBalanceChannel *channel;
757 
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;
762 
763       context->channels_list = g_list_append (context->channels_list, channel);
764 
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) {
768         gint val;
769 
770         XvGetPortAttribute (context->disp, context->xv_port_id,
771             prop_atom, &val);
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));
775 
776         if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
777           config->hue = val;
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;
784       }
785     }
786   }
787 
788   if (xv_attr)
789     XFree (xv_attr);
790 
791   return context;
792 
793   /* ERRORS */
794 no_display:
795   {
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);
799     return NULL;
800   }
801 no_pixel_formats:
802   {
803     gst_xvcontext_unref (context);
804     g_set_error (error, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_SETTINGS,
805         ("Could not get pixel formats"));
806     return NULL;
807   }
808 no_caps:
809   {
810     gst_xvcontext_unref (context);
811     return NULL;
812   }
813 }
814 
815 void
gst_xvcontext_set_synchronous(GstXvContext * context,gboolean synchronous)816 gst_xvcontext_set_synchronous (GstXvContext * context, gboolean synchronous)
817 {
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);
823 }
824 
825 void
gst_xvcontext_update_colorbalance(GstXvContext * context,GstXvContextConfig * config)826 gst_xvcontext_update_colorbalance (GstXvContext * context,
827     GstXvContextConfig * config)
828 {
829   GList *channels = NULL;
830 
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)
834     return;
835 
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
838      values. */
839   channels = context->channels_list;
840 
841   while (channels) {
842     if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
843       GstColorBalanceChannel *channel = NULL;
844       Atom prop_atom;
845       gint value = 0;
846       gdouble convert_coef;
847 
848       channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
849       g_object_ref (channel);
850 
851       /* Our range conversion coef */
852       convert_coef = (channel->max_value - channel->min_value) / 2000.0;
853 
854       if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
855         value = config->hue;
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;
862       } else {
863         g_warning ("got an unknown channel %s", channel->label);
864         g_object_unref (channel);
865         return;
866       }
867 
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) {
872         int xv_value;
873         xv_value =
874             floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
875         XvSetPortAttribute (context->disp,
876             context->xv_port_id, prop_atom, xv_value);
877       }
878       g_mutex_unlock (&context->lock);
879 
880       g_object_unref (channel);
881     }
882     channels = g_list_next (channels);
883   }
884 }
885 
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 */
888 gint
gst_xvcontext_get_format_from_info(GstXvContext * context,const GstVideoInfo * info)889 gst_xvcontext_get_format_from_info (GstXvContext * context,
890     const GstVideoInfo * info)
891 {
892   GList *list = NULL;
893 
894   list = context->formats_list;
895 
896   while (list) {
897     GstXvImageFormat *format = list->data;
898 
899     if (format && format->vformat == GST_VIDEO_INFO_FORMAT (info))
900       return format->format;
901 
902     list = g_list_next (list);
903   }
904   return -1;
905 }
906 
907 void
gst_xvcontext_set_colorimetry(GstXvContext * context,GstVideoColorimetry * colorimetry)908 gst_xvcontext_set_colorimetry (GstXvContext * context,
909     GstVideoColorimetry * colorimetry)
910 {
911   Atom prop_atom;
912   int xv_value;
913 
914   if (!context->have_iturbt709 && !context->have_xvcolorspace)
915     return;
916 
917   switch (colorimetry->matrix) {
918     case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
919     case GST_VIDEO_COLOR_MATRIX_BT709:
920       xv_value = 1;
921       break;
922     default:
923       xv_value = 0;
924       break;
925   }
926 
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);
933     }
934   }
935 
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);
941     }
942   }
943   g_mutex_unlock (&context->lock);
944 }
945 
946 GstXWindow *
gst_xvcontext_create_xwindow(GstXvContext * context,gint width,gint height)947 gst_xvcontext_create_xwindow (GstXvContext * context, gint width, gint height)
948 {
949   GstXWindow *window;
950   Atom wm_delete;
951   Atom hints_atom = None;
952 
953   g_return_val_if_fail (GST_IS_XVCONTEXT (context), NULL);
954 
955   window = g_slice_new0 (GstXWindow);
956 
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;
962 
963   window->width = width;
964   window->height = height;
965   window->internal = TRUE;
966 
967   g_mutex_lock (&context->lock);
968 
969   window->win = XCreateSimpleWindow (context->disp,
970       context->root, 0, 0, width, height, 0, 0, context->black);
971 
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);
975 
976   /* Tell the window manager we'd like delete client messages instead of
977    * being killed */
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);
981   }
982 
983   hints_atom = XInternAtom (context->disp, "_MOTIF_WM_HINTS", True);
984   if (hints_atom != None) {
985     MotifWmHints *hints;
986 
987     hints = g_malloc0 (sizeof (MotifWmHints));
988 
989     hints->flags |= MWM_HINTS_DECORATIONS;
990     hints->decorations = 1 << 0;
991 
992     XChangeProperty (context->disp, window->win,
993         hints_atom, hints_atom, 32, PropModeReplace,
994         (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
995 
996     XSync (context->disp, FALSE);
997 
998     g_free (hints);
999   }
1000 
1001   window->gc = XCreateGC (context->disp, window->win, 0, NULL);
1002 
1003   XMapRaised (context->disp, window->win);
1004 
1005   XSync (context->disp, FALSE);
1006 
1007   g_mutex_unlock (&context->lock);
1008 
1009   return window;
1010 }
1011 
1012 GstXWindow *
gst_xvcontext_create_xwindow_from_xid(GstXvContext * context,XID xid)1013 gst_xvcontext_create_xwindow_from_xid (GstXvContext * context, XID xid)
1014 {
1015   GstXWindow *window;
1016   XWindowAttributes attr;
1017 
1018   window = g_slice_new0 (GstXWindow);
1019   window->win = xid;
1020   window->context = gst_xvcontext_ref (context);
1021 
1022   /* Set the event we want to receive and create a GC */
1023   g_mutex_lock (&context->lock);
1024 
1025   XGetWindowAttributes (context->disp, window->win, &attr);
1026 
1027   window->width = attr.width;
1028   window->height = attr.height;
1029   window->internal = FALSE;
1030 
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;
1035 
1036   window->gc = XCreateGC (context->disp, window->win, 0, NULL);
1037   g_mutex_unlock (&context->lock);
1038 
1039   return window;
1040 }
1041 
1042 void
gst_xwindow_destroy(GstXWindow * window)1043 gst_xwindow_destroy (GstXWindow * window)
1044 {
1045   GstXvContext *context;
1046 
1047   g_return_if_fail (window != NULL);
1048 
1049   context = window->context;
1050 
1051   g_mutex_lock (&context->lock);
1052 
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);
1056   else
1057     XSelectInput (context->disp, window->win, 0);
1058 
1059   XFreeGC (context->disp, window->gc);
1060 
1061   XSync (context->disp, FALSE);
1062 
1063   g_mutex_unlock (&context->lock);
1064 
1065   gst_xvcontext_unref (context);
1066 
1067   g_slice_free1 (sizeof (GstXWindow), window);
1068 }
1069 
1070 void
gst_xwindow_set_event_handling(GstXWindow * window,gboolean handle_events)1071 gst_xwindow_set_event_handling (GstXWindow * window, gboolean handle_events)
1072 {
1073   GstXvContext *context;
1074 
1075   g_return_if_fail (window != NULL);
1076 
1077   context = window->context;
1078 
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);
1085     } else {
1086       XSelectInput (context->disp, window->win,
1087           ExposureMask | StructureNotifyMask | PointerMotionMask |
1088           KeyPressMask | KeyReleaseMask);
1089     }
1090   } else {
1091     XSelectInput (context->disp, window->win, 0);
1092   }
1093   g_mutex_unlock (&context->lock);
1094 }
1095 
1096 void
gst_xwindow_set_title(GstXWindow * window,const gchar * title)1097 gst_xwindow_set_title (GstXWindow * window, const gchar * title)
1098 {
1099   GstXvContext *context;
1100 
1101   g_return_if_fail (window != NULL);
1102 
1103   context = window->context;
1104 
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);
1111 
1112     if ((XStringListToTextProperty (((char **) &title), 1, &xproperty)) != 0) {
1113       XSetWMName (context->disp, window->win, &xproperty);
1114       XFree (xproperty.value);
1115 
1116       XChangeProperty (context->disp, window->win, _NET_WM_NAME, UTF8_STRING, 8,
1117           0, (unsigned char *) title, strlen (title));
1118       XSync (context->disp, False);
1119 
1120       if (hint) {
1121         hint->res_name = (char *) title;
1122         hint->res_class = (char *) "GStreamer";
1123         XSetClassHint (context->disp, window->win, hint);
1124       }
1125       XFree (hint);
1126     }
1127   }
1128 }
1129 
1130 void
gst_xwindow_update_geometry(GstXWindow * window)1131 gst_xwindow_update_geometry (GstXWindow * window)
1132 {
1133   XWindowAttributes attr;
1134   GstXvContext *context;
1135 
1136   g_return_if_fail (window != NULL);
1137 
1138   context = window->context;
1139 
1140   /* Update the window geometry */
1141   g_mutex_lock (&context->lock);
1142   XGetWindowAttributes (context->disp, window->win, &attr);
1143 
1144   window->width = attr.width;
1145   window->height = attr.height;
1146 
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;
1151   }
1152 
1153   g_mutex_unlock (&context->lock);
1154 }
1155 
1156 
1157 void
gst_xwindow_clear(GstXWindow * window)1158 gst_xwindow_clear (GstXWindow * window)
1159 {
1160   GstXvContext *context;
1161 
1162   g_return_if_fail (window != NULL);
1163 
1164   context = window->context;
1165 
1166   g_mutex_lock (&context->lock);
1167 
1168   XvStopVideo (context->disp, context->xv_port_id, window->win);
1169 
1170   XSync (context->disp, FALSE);
1171 
1172   g_mutex_unlock (&context->lock);
1173 }
1174 
1175 void
gst_xwindow_set_render_rectangle(GstXWindow * window,gint x,gint y,gint width,gint height)1176 gst_xwindow_set_render_rectangle (GstXWindow * window,
1177     gint x, gint y, gint width, gint height)
1178 {
1179   g_return_if_fail (window != NULL);
1180 
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;
1187   } else {
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;
1193   }
1194 }
1195