10f3dfbc3ee91d4f71f9eb7b54d4cc2ad9033271
[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->x11_display) {
207         if (!priv->use_foreign_display)
208             XCloseDisplay(priv->x11_display);
209         priv->x11_display = NULL;
210     }
211
212     if (priv->display_name) {
213         g_free(priv->display_name);
214         priv->display_name = NULL;
215     }
216 }
217
218 static void
219 gst_vaapi_display_x11_sync(GstVaapiDisplay *display)
220 {
221     GstVaapiDisplayX11Private * const priv =
222         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
223
224     if (priv->x11_display) {
225         GST_VAAPI_DISPLAY_LOCK(display);
226         XSync(priv->x11_display, False);
227         GST_VAAPI_DISPLAY_UNLOCK(display);
228     }
229 }
230
231 static void
232 gst_vaapi_display_x11_flush(GstVaapiDisplay *display)
233 {
234     GstVaapiDisplayX11Private * const priv =
235         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
236
237     if (priv->x11_display) {
238         GST_VAAPI_DISPLAY_LOCK(display);
239         XFlush(priv->x11_display);
240         GST_VAAPI_DISPLAY_UNLOCK(display);
241     }
242 }
243
244 static gboolean
245 gst_vaapi_display_x11_get_display_info(
246     GstVaapiDisplay     *display,
247     GstVaapiDisplayInfo *info
248 )
249 {
250     GstVaapiDisplayX11Private * const priv =
251         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
252     GstVaapiDisplayCache *cache;
253     const GstVaapiDisplayInfo *cached_info;
254
255     /* Return any cached info even if child has its own VA display */
256     cache = gst_vaapi_display_get_cache();
257     if (!cache)
258         return FALSE;
259     cached_info = gst_vaapi_display_cache_lookup_by_native_display(
260         cache, priv->x11_display, GST_VAAPI_DISPLAY_TYPES(display));
261     if (cached_info) {
262         *info = *cached_info;
263         return TRUE;
264     }
265
266     /* Otherwise, create VA display if there is none already */
267     info->native_display = priv->x11_display;
268     info->display_name   = priv->display_name;
269     if (!info->va_display) {
270         info->va_display = vaGetDisplay(priv->x11_display);
271         if (!info->va_display)
272             return FALSE;
273         info->display_type = GST_VAAPI_DISPLAY_TYPE_X11;
274     }
275     return TRUE;
276 }
277
278 static void
279 gst_vaapi_display_x11_get_size(
280     GstVaapiDisplay *display,
281     guint           *pwidth,
282     guint           *pheight
283 )
284 {
285     GstVaapiDisplayX11Private * const priv =
286         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
287
288     if (!priv->x11_display)
289         return;
290
291     if (pwidth)
292         *pwidth = DisplayWidth(priv->x11_display, priv->x11_screen);
293
294     if (pheight)
295         *pheight = DisplayHeight(priv->x11_display, priv->x11_screen);
296 }
297
298 static void
299 gst_vaapi_display_x11_get_size_mm(
300     GstVaapiDisplay *display,
301     guint           *pwidth,
302     guint           *pheight
303 )
304 {
305     GstVaapiDisplayX11Private * const priv =
306         GST_VAAPI_DISPLAY_X11_PRIVATE(display);
307     guint width_mm, height_mm;
308
309     if (!priv->x11_display)
310         return;
311
312     width_mm  = DisplayWidthMM(priv->x11_display, priv->x11_screen);
313     height_mm = DisplayHeightMM(priv->x11_display, priv->x11_screen);
314
315 #ifdef HAVE_XRANDR
316     /* XXX: fix up physical size if the display is rotated */
317     if (priv->use_xrandr) {
318         XRRScreenConfiguration *xrr_config = NULL;
319         XRRScreenSize *xrr_sizes;
320         Window win;
321         int num_xrr_sizes, size_id, screen;
322         Rotation rotation;
323
324         do {
325             win    = DefaultRootWindow(priv->x11_display);
326             screen = XRRRootToScreen(priv->x11_display, win);
327
328             xrr_config = XRRGetScreenInfo(priv->x11_display, win);
329             if (!xrr_config)
330                 break;
331
332             size_id = XRRConfigCurrentConfiguration(xrr_config, &rotation);
333             if (rotation == RR_Rotate_0 || rotation == RR_Rotate_180)
334                 break;
335
336             xrr_sizes = XRRSizes(priv->x11_display, screen, &num_xrr_sizes);
337             if (!xrr_sizes || size_id >= num_xrr_sizes)
338                 break;
339
340             width_mm  = xrr_sizes[size_id].mheight;
341             height_mm = xrr_sizes[size_id].mwidth;
342         } while (0);
343         if (xrr_config)
344             XRRFreeScreenConfigInfo(xrr_config);
345     }
346 #endif
347
348     if (pwidth)
349         *pwidth = width_mm;
350
351     if (pheight)
352         *pheight = height_mm;
353 }
354
355 void
356 gst_vaapi_display_x11_class_init(GstVaapiDisplayX11Class *klass)
357 {
358     GstVaapiMiniObjectClass * const object_class =
359         GST_VAAPI_MINI_OBJECT_CLASS(klass);
360     GstVaapiDisplayClass * const dpy_class = GST_VAAPI_DISPLAY_CLASS(klass);
361
362     gst_vaapi_display_class_init(&klass->parent_class);
363
364     object_class->size          = sizeof(GstVaapiDisplayX11);
365     dpy_class->display_types    = g_display_types;
366     dpy_class->bind_display     = gst_vaapi_display_x11_bind_display;
367     dpy_class->open_display     = gst_vaapi_display_x11_open_display;
368     dpy_class->close_display    = gst_vaapi_display_x11_close_display;
369     dpy_class->sync             = gst_vaapi_display_x11_sync;
370     dpy_class->flush            = gst_vaapi_display_x11_flush;
371     dpy_class->get_display      = gst_vaapi_display_x11_get_display_info;
372     dpy_class->get_size         = gst_vaapi_display_x11_get_size;
373     dpy_class->get_size_mm      = gst_vaapi_display_x11_get_size_mm;
374 }
375
376 static inline const GstVaapiDisplayClass *
377 gst_vaapi_display_x11_class(void)
378 {
379     static GstVaapiDisplayX11Class g_class;
380     static gsize g_class_init = FALSE;
381
382     if (g_once_init_enter(&g_class_init)) {
383         gst_vaapi_display_x11_class_init(&g_class);
384         g_once_init_leave(&g_class_init, TRUE);
385     }
386     return GST_VAAPI_DISPLAY_CLASS(&g_class);
387 }
388
389 /**
390  * gst_vaapi_display_x11_new:
391  * @display_name: the X11 display name
392  *
393  * Opens an X11 #Display using @display_name and returns a newly
394  * allocated #GstVaapiDisplay object. The X11 display will be cloed
395  * when the reference count of the object reaches zero.
396  *
397  * Return value: a newly allocated #GstVaapiDisplay object
398  */
399 GstVaapiDisplay *
400 gst_vaapi_display_x11_new(const gchar *display_name)
401 {
402     return gst_vaapi_display_new(gst_vaapi_display_x11_class(),
403         GST_VAAPI_DISPLAY_INIT_FROM_DISPLAY_NAME, (gpointer)display_name);
404 }
405
406 /**
407  * gst_vaapi_display_x11_new_with_display:
408  * @x11_display: an X11 #Display
409  *
410  * Creates a #GstVaapiDisplay based on the X11 @x11_display
411  * display. The caller still owns the display and must call
412  * XCloseDisplay() when all #GstVaapiDisplay references are
413  * released. Doing so too early can yield undefined behaviour.
414  *
415  * Return value: a newly allocated #GstVaapiDisplay object
416  */
417 GstVaapiDisplay *
418 gst_vaapi_display_x11_new_with_display(Display *x11_display)
419 {
420     g_return_val_if_fail(x11_display, NULL);
421
422     return gst_vaapi_display_new(gst_vaapi_display_x11_class(),
423         GST_VAAPI_DISPLAY_INIT_FROM_NATIVE_DISPLAY, x11_display);
424 }
425
426 /**
427  * gst_vaapi_display_x11_get_display:
428  * @display: a #GstVaapiDisplayX11
429  *
430  * Returns the underlying X11 #Display that was created by
431  * gst_vaapi_display_x11_new() or that was bound from
432  * gst_vaapi_display_x11_new_with_display().
433  *
434  * Return value: the X11 #Display attached to @display
435  */
436 Display *
437 gst_vaapi_display_x11_get_display(GstVaapiDisplayX11 *display)
438 {
439     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY_X11(display), NULL);
440
441     return GST_VAAPI_DISPLAY_XDISPLAY(display);
442 }
443
444 /**
445  * gst_vaapi_display_x11_get_screen:
446  * @display: a #GstVaapiDisplayX11
447  *
448  * Returns the default X11 screen that was created by
449  * gst_vaapi_display_x11_new() or that was bound from
450  * gst_vaapi_display_x11_new_with_display().
451  *
452  * Return value: the X11 #Display attached to @display
453  */
454 int
455 gst_vaapi_display_x11_get_screen(GstVaapiDisplayX11 *display)
456 {
457     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY_X11(display), -1);
458
459     return GST_VAAPI_DISPLAY_XSCREEN(display);
460 }
461
462 /**
463  * gst_vaapi_display_x11_set_synchronous:
464  * @display: a #GstVaapiDisplayX11
465  * @synchronous: boolean value that indicates whether to enable or
466  *   disable synchronization
467  *
468  * If @synchronous is %TRUE, gst_vaapi_display_x11_set_synchronous()
469  * turns on synchronous behaviour on the underlying X11
470  * display. Otherwise, synchronous behaviour is disabled if
471  * @synchronous is %FALSE.
472  */
473 void
474 gst_vaapi_display_x11_set_synchronous(GstVaapiDisplayX11 *display,
475     gboolean synchronous)
476 {
477     g_return_if_fail(GST_VAAPI_IS_DISPLAY_X11(display));
478
479     set_synchronous(display, synchronous);
480 }