Tizen 2.1 base
[framework/multimedia/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 G_DEFINE_TYPE(GstVaapiDisplayX11,
43               gst_vaapi_display_x11,
44               GST_VAAPI_TYPE_DISPLAY);
45
46 enum {
47     PROP_0,
48
49     PROP_SYNCHRONOUS,
50     PROP_DISPLAY_NAME,
51     PROP_X11_DISPLAY,
52     PROP_X11_SCREEN
53 };
54
55 #define NAME_PREFIX "X11:"
56 #define NAME_PREFIX_LENGTH 4
57
58 static inline gboolean
59 is_display_name(const gchar *display_name)
60 {
61     return strncmp(display_name, NAME_PREFIX, NAME_PREFIX_LENGTH) == 0;
62 }
63
64 static inline const gchar *
65 get_default_display_name(void)
66 {
67     static const gchar *g_display_name;
68
69     if (!g_display_name)
70         g_display_name = getenv("DISPLAY");
71     return g_display_name;
72 }
73
74 static gboolean
75 compare_display_name(gconstpointer a, gconstpointer b, gpointer user_data)
76 {
77     const gchar *cached_name = a, *cached_name_end;
78     const gchar *tested_name = b, *tested_name_end;
79     guint cached_name_length, tested_name_length;
80
81     if (!cached_name || !is_display_name(cached_name))
82         return FALSE;
83     g_return_val_if_fail(tested_name && is_display_name(tested_name), FALSE);
84
85     cached_name += NAME_PREFIX_LENGTH;
86     cached_name_end = strchr(cached_name, ':');
87     if (cached_name_end)
88         cached_name_length = cached_name_end - cached_name;
89     else
90         cached_name_length = strlen(cached_name);
91
92     tested_name += NAME_PREFIX_LENGTH;
93     tested_name_end = strchr(tested_name, ':');
94     if (tested_name_end)
95         tested_name_length = tested_name_end - tested_name;
96     else
97         tested_name_length = strlen(tested_name);
98
99     if (cached_name_length != tested_name_length)
100         return FALSE;
101     if (strncmp(cached_name, tested_name, cached_name_length) != 0)
102         return FALSE;
103
104     /* XXX: handle screen number? */
105     return TRUE;
106 }
107
108 static void
109 gst_vaapi_display_x11_finalize(GObject *object)
110 {
111     G_OBJECT_CLASS(gst_vaapi_display_x11_parent_class)->finalize(object);
112 }
113
114 /* Reconstruct a display name without our prefix */
115 static const gchar *
116 get_display_name(gpointer ptr)
117 {
118     GstVaapiDisplayX11 * const display = GST_VAAPI_DISPLAY_X11(ptr);
119     const gchar *display_name = display->priv->display_name;
120
121     if (!display_name)
122         return NULL;
123
124     if (is_display_name(display_name)) {
125         display_name += NAME_PREFIX_LENGTH;
126         if (*display_name == '\0')
127             return NULL;
128         return display_name;
129     }
130
131     /* XXX: this should not happen */
132     g_assert(0 && "display name without prefix");
133     return display_name;
134 }
135
136 /* Mangle display name with our prefix */
137 static void
138 set_display_name(GstVaapiDisplayX11 *display, const gchar *display_name)
139 {
140     GstVaapiDisplayX11Private * const priv = display->priv;
141
142     g_free(priv->display_name);
143
144     if (!display_name) {
145         display_name = get_default_display_name();
146         if (!display_name)
147             display_name = "";
148     }
149     priv->display_name = g_strdup_printf("%s%s", NAME_PREFIX, display_name);
150 }
151
152 static void
153 set_synchronous(GstVaapiDisplayX11 *display, gboolean synchronous)
154 {
155     GstVaapiDisplayX11Private * const priv = display->priv;
156
157     if (priv->synchronous != synchronous) {
158         priv->synchronous = synchronous;
159         if (priv->x11_display)
160             XSynchronize(priv->x11_display, synchronous);
161     }
162 }
163
164 static void
165 gst_vaapi_display_x11_set_property(
166     GObject      *object,
167     guint         prop_id,
168     const GValue *value,
169     GParamSpec   *pspec
170 )
171 {
172     GstVaapiDisplayX11 * const display = GST_VAAPI_DISPLAY_X11(object);
173
174     switch (prop_id) {
175     case PROP_SYNCHRONOUS:
176         set_synchronous(display, g_value_get_boolean(value));
177         break;
178     case PROP_DISPLAY_NAME:
179         set_display_name(display, g_value_get_string(value));
180         break;
181     case PROP_X11_DISPLAY:
182         display->priv->x11_display = g_value_get_pointer(value);
183         break;
184     case PROP_X11_SCREEN:
185         display->priv->x11_screen = g_value_get_int(value);
186         break;
187     default:
188         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
189         break;
190     }
191 }
192
193 static void
194 gst_vaapi_display_x11_get_property(
195     GObject    *object,
196     guint       prop_id,
197     GValue     *value,
198     GParamSpec *pspec
199 )
200 {
201     GstVaapiDisplayX11 * const display = GST_VAAPI_DISPLAY_X11(object);
202
203     switch (prop_id) {
204     case PROP_SYNCHRONOUS:
205         g_value_set_boolean(value, display->priv->synchronous);
206         break;
207     case PROP_DISPLAY_NAME:
208         g_value_set_string(value, get_display_name(display));
209         break;
210     case PROP_X11_DISPLAY:
211         g_value_set_pointer(value, gst_vaapi_display_x11_get_display(display));
212         break;
213     case PROP_X11_SCREEN:
214         g_value_set_int(value, gst_vaapi_display_x11_get_screen(display));
215         break;
216     default:
217         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
218         break;
219     }
220 }
221
222 static void
223 gst_vaapi_display_x11_constructed(GObject *object)
224 {
225     GstVaapiDisplayX11 * const display = GST_VAAPI_DISPLAY_X11(object);
226     GstVaapiDisplayX11Private * const priv = display->priv;
227     GstVaapiDisplayCache * const cache = gst_vaapi_display_get_cache();
228     const GstVaapiDisplayInfo *info;
229     GObjectClass *parent_class;
230
231     priv->create_display = priv->x11_display == NULL;
232
233     /* Don't create X11 display if there is one in the cache already */
234     if (priv->create_display) {
235         info = gst_vaapi_display_cache_lookup_by_name(
236             cache,
237             priv->display_name,
238             compare_display_name, NULL
239         );
240         if (info) {
241             priv->x11_display    = info->native_display;
242             priv->create_display = FALSE;
243         }
244     }
245
246     /* Reset display-name if the user provided his own X11 display */
247     if (!priv->create_display)
248         set_display_name(display, XDisplayString(priv->x11_display));
249
250     parent_class = G_OBJECT_CLASS(gst_vaapi_display_x11_parent_class);
251     if (parent_class->constructed)
252         parent_class->constructed(object);
253 }
254
255 static gboolean
256 gst_vaapi_display_x11_open_display(GstVaapiDisplay *display)
257 {
258     GstVaapiDisplayX11Private * const priv =
259         GST_VAAPI_DISPLAY_X11(display)->priv;
260
261     if (priv->create_display) {
262         priv->x11_display = XOpenDisplay(get_display_name(display));
263         if (!priv->x11_display)
264             return FALSE;
265         priv->x11_screen = DefaultScreen(priv->x11_display);
266     }
267     if (!priv->x11_display)
268         return FALSE;
269
270     if (priv->synchronous)
271         XSynchronize(priv->x11_display, True);
272
273 #ifdef HAVE_XRANDR
274     {
275         int evt_base, err_base;
276         priv->use_xrandr = XRRQueryExtension(
277             priv->x11_display, &evt_base, &err_base);
278     }
279 #endif
280     return TRUE;
281 }
282
283 static void
284 gst_vaapi_display_x11_close_display(GstVaapiDisplay *display)
285 {
286     GstVaapiDisplayX11Private * const priv =
287         GST_VAAPI_DISPLAY_X11(display)->priv;
288
289     if (priv->x11_display) {
290         if (priv->create_display)
291             XCloseDisplay(priv->x11_display);
292         priv->x11_display = NULL;
293     }
294
295     if (priv->display_name) {
296         g_free(priv->display_name);
297         priv->display_name = NULL;
298     }
299 }
300
301 static void
302 gst_vaapi_display_x11_sync(GstVaapiDisplay *display)
303 {
304     GstVaapiDisplayX11Private * const priv =
305         GST_VAAPI_DISPLAY_X11(display)->priv;
306
307     if (priv->x11_display) {
308         GST_VAAPI_DISPLAY_LOCK(display);
309         XSync(priv->x11_display, False);
310         GST_VAAPI_DISPLAY_UNLOCK(display);
311     }
312 }
313
314 static void
315 gst_vaapi_display_x11_flush(GstVaapiDisplay *display)
316 {
317     GstVaapiDisplayX11Private * const priv =
318         GST_VAAPI_DISPLAY_X11(display)->priv;
319
320     if (priv->x11_display) {
321         GST_VAAPI_DISPLAY_LOCK(display);
322         XFlush(priv->x11_display);
323         GST_VAAPI_DISPLAY_UNLOCK(display);
324     }
325 }
326
327 static gboolean
328 gst_vaapi_display_x11_get_display_info(
329     GstVaapiDisplay     *display,
330     GstVaapiDisplayInfo *info
331 )
332 {
333     GstVaapiDisplayX11Private * const priv =
334         GST_VAAPI_DISPLAY_X11(display)->priv;
335     GstVaapiDisplayCache *cache;
336     const GstVaapiDisplayInfo *cached_info;
337
338     /* Return any cached info even if child has its own VA display */
339     cache = gst_vaapi_display_get_cache();
340     if (!cache)
341         return FALSE;
342     cached_info = gst_vaapi_display_cache_lookup_by_native_display(
343         cache, priv->x11_display);
344     if (cached_info) {
345         *info = *cached_info;
346         return TRUE;
347     }
348
349     /* Otherwise, create VA display if there is none already */
350     info->native_display = priv->x11_display;
351     info->display_name   = priv->display_name;
352     if (!info->va_display) {
353         info->va_display = vaGetDisplay(priv->x11_display);
354         if (!info->va_display)
355             return FALSE;
356         info->display_type = GST_VAAPI_DISPLAY_TYPE_X11;
357     }
358     return TRUE;
359 }
360
361 static void
362 gst_vaapi_display_x11_get_size(
363     GstVaapiDisplay *display,
364     guint           *pwidth,
365     guint           *pheight
366 )
367 {
368     GstVaapiDisplayX11Private * const priv =
369         GST_VAAPI_DISPLAY_X11(display)->priv;
370
371     if (!priv->x11_display)
372         return;
373
374     if (pwidth)
375         *pwidth = DisplayWidth(priv->x11_display, priv->x11_screen);
376
377     if (pheight)
378         *pheight = DisplayHeight(priv->x11_display, priv->x11_screen);
379 }
380
381 static void
382 gst_vaapi_display_x11_get_size_mm(
383     GstVaapiDisplay *display,
384     guint           *pwidth,
385     guint           *pheight
386 )
387 {
388     GstVaapiDisplayX11Private * const priv =
389         GST_VAAPI_DISPLAY_X11(display)->priv;
390     guint width_mm, height_mm;
391
392     if (!priv->x11_display)
393         return;
394
395     width_mm  = DisplayWidthMM(priv->x11_display, priv->x11_screen);
396     height_mm = DisplayHeightMM(priv->x11_display, priv->x11_screen);
397
398 #ifdef HAVE_XRANDR
399     /* XXX: fix up physical size if the display is rotated */
400     if (priv->use_xrandr) {
401         XRRScreenConfiguration *xrr_config = NULL;
402         XRRScreenSize *xrr_sizes;
403         Window win;
404         int num_xrr_sizes, size_id, screen;
405         Rotation rotation;
406
407         do {
408             win    = DefaultRootWindow(priv->x11_display);
409             screen = XRRRootToScreen(priv->x11_display, win);
410
411             xrr_config = XRRGetScreenInfo(priv->x11_display, win);
412             if (!xrr_config)
413                 break;
414
415             size_id = XRRConfigCurrentConfiguration(xrr_config, &rotation);
416             if (rotation == RR_Rotate_0 || rotation == RR_Rotate_180)
417                 break;
418
419             xrr_sizes = XRRSizes(priv->x11_display, screen, &num_xrr_sizes);
420             if (!xrr_sizes || size_id >= num_xrr_sizes)
421                 break;
422
423             width_mm  = xrr_sizes[size_id].mheight;
424             height_mm = xrr_sizes[size_id].mwidth;
425         } while (0);
426         if (xrr_config)
427             XRRFreeScreenConfigInfo(xrr_config);
428     }
429 #endif
430
431     if (pwidth)
432         *pwidth = width_mm;
433
434     if (pheight)
435         *pheight = height_mm;
436 }
437
438 static void
439 gst_vaapi_display_x11_class_init(GstVaapiDisplayX11Class *klass)
440 {
441     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
442     GstVaapiDisplayClass * const dpy_class = GST_VAAPI_DISPLAY_CLASS(klass);
443
444     g_type_class_add_private(klass, sizeof(GstVaapiDisplayX11Private));
445
446     object_class->finalize      = gst_vaapi_display_x11_finalize;
447     object_class->set_property  = gst_vaapi_display_x11_set_property;
448     object_class->get_property  = gst_vaapi_display_x11_get_property;
449     object_class->constructed   = gst_vaapi_display_x11_constructed;
450
451     dpy_class->open_display     = gst_vaapi_display_x11_open_display;
452     dpy_class->close_display    = gst_vaapi_display_x11_close_display;
453     dpy_class->sync             = gst_vaapi_display_x11_sync;
454     dpy_class->flush            = gst_vaapi_display_x11_flush;
455     dpy_class->get_display      = gst_vaapi_display_x11_get_display_info;
456     dpy_class->get_size         = gst_vaapi_display_x11_get_size;
457     dpy_class->get_size_mm      = gst_vaapi_display_x11_get_size_mm;
458
459     /**
460      * GstVaapiDisplayX11:synchronous:
461      *
462      * When enabled, runs the X display in synchronous mode. Note that
463      * this is used only for debugging.
464      */
465     g_object_class_install_property
466         (object_class,
467          PROP_SYNCHRONOUS,
468          g_param_spec_boolean("synchronous",
469                               "Synchronous mode",
470                               "Toggles X display synchronous mode",
471                               FALSE,
472                               G_PARAM_READWRITE));
473
474     /**
475      * GstVaapiDisplayX11:x11-display:
476      *
477      * The X11 #Display that was created by gst_vaapi_display_x11_new()
478      * or that was bound from gst_vaapi_display_x11_new_with_display().
479      */
480     g_object_class_install_property
481         (object_class,
482          PROP_X11_DISPLAY,
483          g_param_spec_pointer("x11-display",
484                               "X11 display",
485                               "X11 display",
486                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
487
488     /**
489      * GstVaapiDisplayX11:x11-screen:
490      *
491      * The X11 screen that was created by gst_vaapi_display_x11_new()
492      * or that was bound from gst_vaapi_display_x11_new_with_display().
493      */
494     g_object_class_install_property
495         (object_class,
496          PROP_X11_SCREEN,
497          g_param_spec_int("x11-screen",
498                           "X11 screen",
499                           "X11 screen",
500                           0, G_MAXINT32, 0,
501                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
502
503     /**
504      * GstVaapiDisplayX11:display-name:
505      *
506      * The X11 display name.
507      */
508     g_object_class_install_property
509         (object_class,
510          PROP_DISPLAY_NAME,
511          g_param_spec_string("display-name",
512                              "X11 display name",
513                              "X11 display name",
514                              NULL,
515                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
516 }
517
518 static void
519 gst_vaapi_display_x11_init(GstVaapiDisplayX11 *display)
520 {
521     GstVaapiDisplayX11Private *priv = GST_VAAPI_DISPLAY_X11_GET_PRIVATE(display);
522
523     display->priv        = priv;
524     priv->create_display = TRUE;
525     priv->x11_display    = NULL;
526     priv->x11_screen     = 0;
527     priv->display_name   = NULL;
528     priv->use_xrandr     = FALSE;
529 }
530
531 /**
532  * gst_vaapi_display_x11_new:
533  * @display_name: the X11 display name
534  *
535  * Opens an X11 #Display using @display_name and returns a newly
536  * allocated #GstVaapiDisplay object. The X11 display will be cloed
537  * when the reference count of the object reaches zero.
538  *
539  * Return value: a newly allocated #GstVaapiDisplay object
540  */
541 GstVaapiDisplay *
542 gst_vaapi_display_x11_new(const gchar *display_name)
543 {
544     return g_object_new(GST_VAAPI_TYPE_DISPLAY_X11,
545                         "display-name", display_name,
546                         NULL);
547 }
548
549 /**
550  * gst_vaapi_display_x11_new_with_display:
551  * @x11_display: an X11 #Display
552  *
553  * Creates a #GstVaapiDisplay based on the X11 @x11_display
554  * display. The caller still owns the display and must call
555  * XCloseDisplay() when all #GstVaapiDisplay references are
556  * released. Doing so too early can yield undefined behaviour.
557  *
558  * Return value: a newly allocated #GstVaapiDisplay object
559  */
560 GstVaapiDisplay *
561 gst_vaapi_display_x11_new_with_display(Display *x11_display)
562 {
563     g_return_val_if_fail(x11_display, NULL);
564
565     return g_object_new(GST_VAAPI_TYPE_DISPLAY_X11,
566                         "x11-display", x11_display,
567                         "x11-screen",  DefaultScreen(x11_display),
568                         NULL);
569 }
570
571 /**
572  * gst_vaapi_display_x11_get_display:
573  * @display: a #GstVaapiDisplayX11
574  *
575  * Returns the underlying X11 #Display that was created by
576  * gst_vaapi_display_x11_new() or that was bound from
577  * gst_vaapi_display_x11_new_with_display().
578  *
579  * Return value: the X11 #Display attached to @display
580  */
581 Display *
582 gst_vaapi_display_x11_get_display(GstVaapiDisplayX11 *display)
583 {
584     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY_X11(display), NULL);
585
586     return display->priv->x11_display;
587 }
588
589 /**
590  * gst_vaapi_display_x11_get_screen:
591  * @display: a #GstVaapiDisplayX11
592  *
593  * Returns the default X11 screen that was created by
594  * gst_vaapi_display_x11_new() or that was bound from
595  * gst_vaapi_display_x11_new_with_display().
596  *
597  * Return value: the X11 #Display attached to @display
598  */
599 int
600 gst_vaapi_display_x11_get_screen(GstVaapiDisplayX11 *display)
601 {
602     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY_X11(display), -1);
603
604     return display->priv->x11_screen;
605 }