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