x11: implement pixmap API.
[platform/upstream/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapidisplay_x11.c
1 /*
2  *  gstvaapidisplay_x11.c - VA/X11 display abstraction
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011-2012 Intel Corporation
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:gstvaapidisplay_x11
25  * @short_description: VA/X11 display abstraction
26  */
27
28 #include "sysdeps.h"
29 #include <string.h>
30 #include "gstvaapiutils.h"
31 #include "gstvaapidisplay_priv.h"
32 #include "gstvaapidisplay_x11.h"
33 #include "gstvaapidisplay_x11_priv.h"
34
35 #ifdef HAVE_XRANDR
36 # include <X11/extensions/Xrandr.h>
37 #endif
38
39 #define DEBUG 1
40 #include "gstvaapidebug.h"
41
42 static const guint g_display_types =
43     (1U << GST_VAAPI_DISPLAY_TYPE_X11) |
44     (1U << GST_VAAPI_DISPLAY_TYPE_GLX);
45
46 static inline const gchar *
47 get_default_display_name(void)
48 {
49     static const gchar *g_display_name;
50
51     if (!g_display_name)
52         g_display_name = getenv("DISPLAY");
53     return g_display_name;
54 }
55
56 static gint
57 compare_display_name(gconstpointer a, gconstpointer b)
58 {
59     const GstVaapiDisplayInfo * const info = a;
60     const gchar *cached_name = info->display_name, *cached_name_end;
61     const gchar *tested_name = b, *tested_name_end;
62     guint cached_name_length, tested_name_length;
63
64     g_return_val_if_fail(cached_name, FALSE);
65     g_return_val_if_fail(tested_name, FALSE);
66
67     cached_name_end = strchr(cached_name, ':');
68     if (cached_name_end)
69         cached_name_length = cached_name_end - cached_name;
70     else
71         cached_name_length = strlen(cached_name);
72
73     tested_name_end = strchr(tested_name, ':');
74     if (tested_name_end)
75         tested_name_length = tested_name_end - tested_name;
76     else
77         tested_name_length = strlen(tested_name);
78
79     if (cached_name_length != tested_name_length)
80         return FALSE;
81     if (strncmp(cached_name, tested_name, cached_name_length) != 0)
82         return FALSE;
83
84     /* XXX: handle screen number? */
85     return TRUE;
86 }
87
88 /* Reconstruct a display name without our prefix */
89 static const gchar *
90 get_display_name(GstVaapiDisplayX11 *display)
91 {
92     GstVaapiDisplayX11Private * const priv = &display->priv;
93     const gchar *display_name = priv->display_name;
94
95     if (!display_name || *display_name == '\0')
96         return NULL;
97     return display_name;
98 }
99
100 /* Mangle display name with our prefix */
101 static gboolean
102 set_display_name(GstVaapiDisplayX11 *display, const gchar *display_name)
103 {
104     GstVaapiDisplayX11Private * const priv = &display->priv;
105
106     g_free(priv->display_name);
107
108     if (!display_name) {
109         display_name = get_default_display_name();
110         if (!display_name)
111             display_name = "";
112     }
113     priv->display_name = g_strdup(display_name);
114     return priv->display_name != NULL;
115 }
116
117 /* Set synchronous behavious on the underlying X11 display */
118 static void
119 set_synchronous(GstVaapiDisplayX11 *display, gboolean synchronous)
120 {
121     GstVaapiDisplayX11Private * const priv = &display->priv;
122
123     if (priv->synchronous != synchronous) {
124         priv->synchronous = synchronous;
125         if (priv->x11_display) {
126             GST_VAAPI_DISPLAY_LOCK(display);
127             XSynchronize(priv->x11_display, synchronous);
128             GST_VAAPI_DISPLAY_UNLOCK(display);
129         }
130     }
131 }
132
133 /* Check whether XRANDR extension is available */
134 static void
135 check_xrandr(GstVaapiDisplayX11 *display)
136 {
137 #ifdef HAVE_XRANDR
138     GstVaapiDisplayX11Private * const priv =
139         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
140     int evt_base, err_base;
141
142     priv->use_xrandr = XRRQueryExtension(priv->x11_display,
143         &evt_base, &err_base);
144 #endif
145 }
146
147 static gboolean
148 gst_vaapi_display_x11_bind_display(GstVaapiDisplay *base_display,
149     gpointer native_display)
150 {
151     GstVaapiDisplayX11 * const display =
152         GST_VAAPI_DISPLAY_X11_CAST(base_display);
153     GstVaapiDisplayX11Private * const priv = &display->priv;
154
155     priv->x11_display = native_display;
156     priv->x11_screen = DefaultScreen(native_display);
157     priv->use_foreign_display = TRUE;
158
159     check_xrandr(display);
160
161     if (!set_display_name(display, XDisplayString(priv->x11_display)))
162         return FALSE;
163     return TRUE;
164 }
165
166 static gboolean
167 gst_vaapi_display_x11_open_display(GstVaapiDisplay *base_display,
168     const gchar *name)
169 {
170     GstVaapiDisplayX11 * const display =
171         GST_VAAPI_DISPLAY_X11_CAST(base_display);
172     GstVaapiDisplayX11Private * const priv = &display->priv;
173     GstVaapiDisplayCache *cache;
174     const GstVaapiDisplayInfo *info;
175
176     cache = gst_vaapi_display_get_cache();
177     g_return_val_if_fail(cache != NULL, FALSE);
178
179     if (!set_display_name(display, name))
180         return FALSE;
181
182     info = gst_vaapi_display_cache_lookup_custom(cache, compare_display_name,
183         priv->display_name, GST_VAAPI_DISPLAY_TYPES(display));
184     if (info) {
185         priv->x11_display = info->native_display;
186         priv->use_foreign_display = TRUE;
187     }
188     else {
189         priv->x11_display = XOpenDisplay(get_display_name(display));
190         if (!priv->x11_display)
191             return FALSE;
192         priv->use_foreign_display = FALSE;
193     }
194     priv->x11_screen = DefaultScreen(priv->x11_display);
195
196     check_xrandr(display);
197     return TRUE;
198 }
199
200 static void
201 gst_vaapi_display_x11_close_display(GstVaapiDisplay *display)
202 {
203     GstVaapiDisplayX11Private * const priv =
204         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
205
206     if (priv->pixmap_formats) {
207         g_array_free(priv->pixmap_formats, TRUE);
208         priv->pixmap_formats = NULL;
209     }
210
211     if (priv->x11_display) {
212         if (!priv->use_foreign_display)
213             XCloseDisplay(priv->x11_display);
214         priv->x11_display = NULL;
215     }
216
217     if (priv->display_name) {
218         g_free(priv->display_name);
219         priv->display_name = NULL;
220     }
221 }
222
223 static void
224 gst_vaapi_display_x11_sync(GstVaapiDisplay *display)
225 {
226     GstVaapiDisplayX11Private * const priv =
227         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
228
229     if (priv->x11_display) {
230         GST_VAAPI_DISPLAY_LOCK(display);
231         XSync(priv->x11_display, False);
232         GST_VAAPI_DISPLAY_UNLOCK(display);
233     }
234 }
235
236 static void
237 gst_vaapi_display_x11_flush(GstVaapiDisplay *display)
238 {
239     GstVaapiDisplayX11Private * const priv =
240         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
241
242     if (priv->x11_display) {
243         GST_VAAPI_DISPLAY_LOCK(display);
244         XFlush(priv->x11_display);
245         GST_VAAPI_DISPLAY_UNLOCK(display);
246     }
247 }
248
249 static gboolean
250 gst_vaapi_display_x11_get_display_info(
251     GstVaapiDisplay     *display,
252     GstVaapiDisplayInfo *info
253 )
254 {
255     GstVaapiDisplayX11Private * const priv =
256         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
257     GstVaapiDisplayCache *cache;
258     const GstVaapiDisplayInfo *cached_info;
259
260     /* Return any cached info even if child has its own VA display */
261     cache = gst_vaapi_display_get_cache();
262     if (!cache)
263         return FALSE;
264     cached_info = gst_vaapi_display_cache_lookup_by_native_display(
265         cache, priv->x11_display, GST_VAAPI_DISPLAY_TYPES(display));
266     if (cached_info) {
267         *info = *cached_info;
268         return TRUE;
269     }
270
271     /* Otherwise, create VA display if there is none already */
272     info->native_display = priv->x11_display;
273     info->display_name   = priv->display_name;
274     if (!info->va_display) {
275         info->va_display = vaGetDisplay(priv->x11_display);
276         if (!info->va_display)
277             return FALSE;
278         info->display_type = GST_VAAPI_DISPLAY_TYPE_X11;
279     }
280     return TRUE;
281 }
282
283 static void
284 gst_vaapi_display_x11_get_size(
285     GstVaapiDisplay *display,
286     guint           *pwidth,
287     guint           *pheight
288 )
289 {
290     GstVaapiDisplayX11Private * const priv =
291         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
292
293     if (!priv->x11_display)
294         return;
295
296     if (pwidth)
297         *pwidth = DisplayWidth(priv->x11_display, priv->x11_screen);
298
299     if (pheight)
300         *pheight = DisplayHeight(priv->x11_display, priv->x11_screen);
301 }
302
303 static void
304 gst_vaapi_display_x11_get_size_mm(
305     GstVaapiDisplay *display,
306     guint           *pwidth,
307     guint           *pheight
308 )
309 {
310     GstVaapiDisplayX11Private * const priv =
311         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
312     guint width_mm, height_mm;
313
314     if (!priv->x11_display)
315         return;
316
317     width_mm  = DisplayWidthMM(priv->x11_display, priv->x11_screen);
318     height_mm = DisplayHeightMM(priv->x11_display, priv->x11_screen);
319
320 #ifdef HAVE_XRANDR
321     /* XXX: fix up physical size if the display is rotated */
322     if (priv->use_xrandr) {
323         XRRScreenConfiguration *xrr_config = NULL;
324         XRRScreenSize *xrr_sizes;
325         Window win;
326         int num_xrr_sizes, size_id, screen;
327         Rotation rotation;
328
329         do {
330             win    = DefaultRootWindow(priv->x11_display);
331             screen = XRRRootToScreen(priv->x11_display, win);
332
333             xrr_config = XRRGetScreenInfo(priv->x11_display, win);
334             if (!xrr_config)
335                 break;
336
337             size_id = XRRConfigCurrentConfiguration(xrr_config, &rotation);
338             if (rotation == RR_Rotate_0 || rotation == RR_Rotate_180)
339                 break;
340
341             xrr_sizes = XRRSizes(priv->x11_display, screen, &num_xrr_sizes);
342             if (!xrr_sizes || size_id >= num_xrr_sizes)
343                 break;
344
345             width_mm  = xrr_sizes[size_id].mheight;
346             height_mm = xrr_sizes[size_id].mwidth;
347         } while (0);
348         if (xrr_config)
349             XRRFreeScreenConfigInfo(xrr_config);
350     }
351 #endif
352
353     if (pwidth)
354         *pwidth = width_mm;
355
356     if (pheight)
357         *pheight = height_mm;
358 }
359
360 void
361 gst_vaapi_display_x11_class_init(GstVaapiDisplayX11Class *klass)
362 {
363     GstVaapiMiniObjectClass * const object_class =
364         GST_VAAPI_MINI_OBJECT_CLASS(klass);
365     GstVaapiDisplayClass * const dpy_class = GST_VAAPI_DISPLAY_CLASS(klass);
366
367     gst_vaapi_display_class_init(&klass->parent_class);
368
369     object_class->size          = sizeof(GstVaapiDisplayX11);
370     dpy_class->display_types    = g_display_types;
371     dpy_class->bind_display     = gst_vaapi_display_x11_bind_display;
372     dpy_class->open_display     = gst_vaapi_display_x11_open_display;
373     dpy_class->close_display    = gst_vaapi_display_x11_close_display;
374     dpy_class->sync             = gst_vaapi_display_x11_sync;
375     dpy_class->flush            = gst_vaapi_display_x11_flush;
376     dpy_class->get_display      = gst_vaapi_display_x11_get_display_info;
377     dpy_class->get_size         = gst_vaapi_display_x11_get_size;
378     dpy_class->get_size_mm      = gst_vaapi_display_x11_get_size_mm;
379 }
380
381 static inline const GstVaapiDisplayClass *
382 gst_vaapi_display_x11_class(void)
383 {
384     static GstVaapiDisplayX11Class g_class;
385     static gsize g_class_init = FALSE;
386
387     if (g_once_init_enter(&g_class_init)) {
388         gst_vaapi_display_x11_class_init(&g_class);
389         g_once_init_leave(&g_class_init, TRUE);
390     }
391     return GST_VAAPI_DISPLAY_CLASS(&g_class);
392 }
393
394 /**
395  * gst_vaapi_display_x11_new:
396  * @display_name: the X11 display name
397  *
398  * Opens an X11 #Display using @display_name and returns a newly
399  * allocated #GstVaapiDisplay object. The X11 display will be cloed
400  * when the reference count of the object reaches zero.
401  *
402  * Return value: a newly allocated #GstVaapiDisplay object
403  */
404 GstVaapiDisplay *
405 gst_vaapi_display_x11_new(const gchar *display_name)
406 {
407     return gst_vaapi_display_new(gst_vaapi_display_x11_class(),
408         GST_VAAPI_DISPLAY_INIT_FROM_DISPLAY_NAME, (gpointer)display_name);
409 }
410
411 /**
412  * gst_vaapi_display_x11_new_with_display:
413  * @x11_display: an X11 #Display
414  *
415  * Creates a #GstVaapiDisplay based on the X11 @x11_display
416  * display. The caller still owns the display and must call
417  * XCloseDisplay() when all #GstVaapiDisplay references are
418  * released. Doing so too early can yield undefined behaviour.
419  *
420  * Return value: a newly allocated #GstVaapiDisplay object
421  */
422 GstVaapiDisplay *
423 gst_vaapi_display_x11_new_with_display(Display *x11_display)
424 {
425     g_return_val_if_fail(x11_display, NULL);
426
427     return gst_vaapi_display_new(gst_vaapi_display_x11_class(),
428         GST_VAAPI_DISPLAY_INIT_FROM_NATIVE_DISPLAY, x11_display);
429 }
430
431 /**
432  * gst_vaapi_display_x11_get_display:
433  * @display: a #GstVaapiDisplayX11
434  *
435  * Returns the underlying X11 #Display that was created by
436  * gst_vaapi_display_x11_new() or that was bound from
437  * gst_vaapi_display_x11_new_with_display().
438  *
439  * Return value: the X11 #Display attached to @display
440  */
441 Display *
442 gst_vaapi_display_x11_get_display(GstVaapiDisplayX11 *display)
443 {
444     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY_X11(display), NULL);
445
446     return GST_VAAPI_DISPLAY_XDISPLAY(display);
447 }
448
449 /**
450  * gst_vaapi_display_x11_get_screen:
451  * @display: a #GstVaapiDisplayX11
452  *
453  * Returns the default X11 screen that was created by
454  * gst_vaapi_display_x11_new() or that was bound from
455  * gst_vaapi_display_x11_new_with_display().
456  *
457  * Return value: the X11 #Display attached to @display
458  */
459 int
460 gst_vaapi_display_x11_get_screen(GstVaapiDisplayX11 *display)
461 {
462     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY_X11(display), -1);
463
464     return GST_VAAPI_DISPLAY_XSCREEN(display);
465 }
466
467 /**
468  * gst_vaapi_display_x11_set_synchronous:
469  * @display: a #GstVaapiDisplayX11
470  * @synchronous: boolean value that indicates whether to enable or
471  *   disable synchronization
472  *
473  * If @synchronous is %TRUE, gst_vaapi_display_x11_set_synchronous()
474  * turns on synchronous behaviour on the underlying X11
475  * display. Otherwise, synchronous behaviour is disabled if
476  * @synchronous is %FALSE.
477  */
478 void
479 gst_vaapi_display_x11_set_synchronous(GstVaapiDisplayX11 *display,
480     gboolean synchronous)
481 {
482     g_return_if_fail(GST_VAAPI_IS_DISPLAY_X11(display));
483
484     set_synchronous(display, synchronous);
485 }
486
487 typedef struct _GstVaapiPixmapFormatX11 GstVaapiPixmapFormatX11;
488 struct _GstVaapiPixmapFormatX11 {
489     GstVideoFormat      format;
490     gint                depth;
491     gint                bpp;
492 };
493
494 static GstVideoFormat
495 pix_fmt_to_video_format(gint depth, gint bpp)
496 {
497     GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
498
499     switch (bpp) {
500     case 16:
501         if (depth == 15)
502             format = GST_VIDEO_FORMAT_RGB15;
503         else if (depth == 16)
504             format = GST_VIDEO_FORMAT_RGB16;
505         break;
506     case 24:
507         if (depth == 24)
508             format = GST_VIDEO_FORMAT_RGB;
509         break;
510     case 32:
511         if (depth == 24 || depth == 32)
512             format = GST_VIDEO_FORMAT_xRGB;
513         break;
514     }
515     return format;
516 }
517
518 static gboolean
519 ensure_pix_fmts(GstVaapiDisplayX11 *display)
520 {
521     GstVaapiDisplayX11Private * const priv =
522         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
523     XPixmapFormatValues *pix_fmts;
524     int i, n, num_pix_fmts;
525
526     if (priv->pixmap_formats)
527         return TRUE;
528
529     GST_VAAPI_DISPLAY_LOCK(display);
530     pix_fmts = XListPixmapFormats(GST_VAAPI_DISPLAY_XDISPLAY(display),
531         &num_pix_fmts);
532     GST_VAAPI_DISPLAY_UNLOCK(display);
533     if (!pix_fmts)
534         return FALSE;
535
536     priv->pixmap_formats = g_array_sized_new(FALSE, FALSE,
537         sizeof(GstVaapiPixmapFormatX11), num_pix_fmts);
538     if (!priv->pixmap_formats) {
539         XFree(pix_fmts);
540         return FALSE;
541     }
542
543     for (i = 0, n = 0; i < num_pix_fmts; i++) {
544         GstVaapiPixmapFormatX11 * const pix_fmt =
545             &g_array_index(priv->pixmap_formats, GstVaapiPixmapFormatX11, n);
546
547         pix_fmt->depth  = pix_fmts[i].depth;
548         pix_fmt->bpp    = pix_fmts[i].bits_per_pixel;
549         pix_fmt->format = pix_fmt_to_video_format(pix_fmt->depth, pix_fmt->bpp);
550         if (pix_fmt->format != GST_VIDEO_FORMAT_UNKNOWN)
551             n++;
552     }
553     priv->pixmap_formats->len = n;
554     return TRUE;
555 }
556
557 /* Determine the GstVideoFormat based on a supported Pixmap depth */
558 GstVideoFormat
559 gst_vaapi_display_x11_get_pixmap_format(GstVaapiDisplayX11 *display,
560     guint depth)
561 {
562     if (ensure_pix_fmts(display)) {
563         GstVaapiDisplayX11Private * const priv =
564             GST_VAAPI_DISPLAY_X11_PRIVATE(display);
565         guint i;
566
567         for (i = 0; i < priv->pixmap_formats->len; i++) {
568             GstVaapiPixmapFormatX11 * const pix_fmt = &g_array_index(
569                 priv->pixmap_formats, GstVaapiPixmapFormatX11, i);
570             if (pix_fmt->depth == depth)
571                 return pix_fmt->format;
572         }
573     }
574     return GST_VIDEO_FORMAT_UNKNOWN;
575 }
576
577 /* Determine the Pixmap depth based on a GstVideoFormat */
578 guint
579 gst_vaapi_display_x11_get_pixmap_depth(GstVaapiDisplayX11 *display,
580     GstVideoFormat format)
581 {
582     if (ensure_pix_fmts(display)) {
583         GstVaapiDisplayX11Private * const priv =
584             GST_VAAPI_DISPLAY_X11_PRIVATE(display);
585         guint i;
586
587         for (i = 0; i < priv->pixmap_formats->len; i++) {
588             GstVaapiPixmapFormatX11 * const pix_fmt = &g_array_index(
589                 priv->pixmap_formats, GstVaapiPixmapFormatX11, i);
590             if (pix_fmt->format == format)
591                 return pix_fmt->depth;
592         }
593     }
594     return 0;
595 }