display: add initial support for display attributes.
[vaapi:lorenzphs-gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapidisplay.c
1 /*
2  *  gstvaapidisplay.c - VA 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
25  * @short_description: VA display abstraction
26  */
27
28 #include "sysdeps.h"
29 #include <string.h>
30 #include "gstvaapiutils.h"
31 #include "gstvaapidisplay.h"
32 #include "gstvaapidisplay_priv.h"
33 #include "gstvaapiworkarounds.h"
34
35 #define DEBUG 1
36 #include "gstvaapidebug.h"
37
38 GST_DEBUG_CATEGORY(gst_debug_vaapi);
39
40 G_DEFINE_TYPE(GstVaapiDisplay, gst_vaapi_display, G_TYPE_OBJECT);
41
42 typedef struct _GstVaapiConfig GstVaapiConfig;
43 struct _GstVaapiConfig {
44     GstVaapiProfile     profile;
45     GstVaapiEntrypoint  entrypoint;
46 };
47
48 typedef struct _GstVaapiProperty GstVaapiProperty;
49 struct _GstVaapiProperty {
50     const gchar        *name;
51     VADisplayAttribute  attribute;
52 };
53
54 enum {
55     PROP_0,
56
57     PROP_DISPLAY,
58     PROP_DISPLAY_TYPE,
59     PROP_WIDTH,
60     PROP_HEIGHT
61 };
62
63 static GstVaapiDisplayCache *g_display_cache = NULL;
64
65 static inline GstVaapiDisplayCache *
66 get_display_cache(void)
67 {
68     if (!g_display_cache)
69         g_display_cache = gst_vaapi_display_cache_new();
70     return g_display_cache;
71 }
72
73 GstVaapiDisplayCache *
74 gst_vaapi_display_get_cache(void)
75 {
76     return get_display_cache();
77 }
78
79 static void
80 free_display_cache(void)
81 {
82     if (!g_display_cache)
83         return;
84     if (gst_vaapi_display_cache_get_size(g_display_cache) > 0)
85         return;
86     gst_vaapi_display_cache_free(g_display_cache);
87     g_display_cache = NULL;
88 }
89
90 /* GstVaapiDisplayType enumerations */
91 GType
92 gst_vaapi_display_type_get_type(void)
93 {
94     static GType g_type = 0;
95
96     static const GEnumValue display_types[] = {
97         { GST_VAAPI_DISPLAY_TYPE_ANY,
98           "Auto detection", "any" },
99 #if USE_X11
100         { GST_VAAPI_DISPLAY_TYPE_X11,
101           "VA/X11 display", "x11" },
102 #endif
103 #if USE_GLX
104         { GST_VAAPI_DISPLAY_TYPE_GLX,
105           "VA/GLX display", "glx" },
106 #endif
107 #if USE_WAYLAND
108         { GST_VAAPI_DISPLAY_TYPE_WAYLAND,
109           "VA/Wayland display", "wayland" },
110 #endif
111 #if USE_DRM
112         { GST_VAAPI_DISPLAY_TYPE_DRM,
113           "VA/DRM display", "drm" },
114 #endif
115         { 0, NULL, NULL },
116     };
117
118     if (!g_type)
119         g_type = g_enum_register_static("GstVaapiDisplayType", display_types);
120     return g_type;
121 }
122
123 /* Append GstVaapiImageFormat to formats array */
124 static inline void
125 append_format(GArray *formats, GstVaapiImageFormat format)
126 {
127     g_array_append_val(formats, format);
128 }
129
130 /* Append VAImageFormats to formats array */
131 static void
132 append_formats(GArray *formats, const VAImageFormat *va_formats, guint n)
133 {
134     GstVaapiImageFormat format;
135     gboolean has_YV12 = FALSE;
136     gboolean has_I420 = FALSE;
137     guint i;
138
139     for (i = 0; i < n; i++) {
140         const VAImageFormat * const va_format = &va_formats[i];
141
142         format = gst_vaapi_image_format(va_format);
143         if (!format) {
144             GST_DEBUG("unsupported format %" GST_FOURCC_FORMAT,
145                       GST_FOURCC_ARGS(va_format->fourcc));
146             continue;
147         }
148
149         switch (format) {
150         case GST_VAAPI_IMAGE_YV12:
151             has_YV12 = TRUE;
152             break;
153         case GST_VAAPI_IMAGE_I420:
154             has_I420 = TRUE;
155             break;
156         default:
157             break;
158         }
159         append_format(formats, format);
160     }
161
162     /* Append I420 (resp. YV12) format if YV12 (resp. I420) is not
163        supported by the underlying driver */
164     if (has_YV12 && !has_I420)
165         append_format(formats, GST_VAAPI_IMAGE_I420);
166     else if (has_I420 && !has_YV12)
167         append_format(formats, GST_VAAPI_IMAGE_YV12);
168 }
169
170 /* Sort image formats. Prefer YUV formats first */
171 static gint
172 compare_yuv_formats(gconstpointer a, gconstpointer b)
173 {
174     const GstVaapiImageFormat fmt1 = *(GstVaapiImageFormat *)a;
175     const GstVaapiImageFormat fmt2 = *(GstVaapiImageFormat *)b;
176
177     const gboolean is_fmt1_yuv = gst_vaapi_image_format_is_yuv(fmt1);
178     const gboolean is_fmt2_yuv = gst_vaapi_image_format_is_yuv(fmt2);
179
180     if (is_fmt1_yuv != is_fmt2_yuv)
181         return is_fmt1_yuv ? -1 : 1;
182
183     return ((gint)gst_vaapi_image_format_get_score(fmt1) -
184             (gint)gst_vaapi_image_format_get_score(fmt2));
185 }
186
187 /* Sort subpicture formats. Prefer RGB formats first */
188 static gint
189 compare_rgb_formats(gconstpointer a, gconstpointer b)
190 {
191     const GstVaapiImageFormat fmt1 = *(GstVaapiImageFormat *)a;
192     const GstVaapiImageFormat fmt2 = *(GstVaapiImageFormat *)b;
193
194     const gboolean is_fmt1_rgb = gst_vaapi_image_format_is_rgb(fmt1);
195     const gboolean is_fmt2_rgb = gst_vaapi_image_format_is_rgb(fmt2);
196
197     if (is_fmt1_rgb != is_fmt2_rgb)
198         return is_fmt1_rgb ? -1 : 1;
199
200     return ((gint)gst_vaapi_image_format_get_score(fmt1) -
201             (gint)gst_vaapi_image_format_get_score(fmt2));
202 }
203
204 /* Check if configs array contains profile at entrypoint */
205 static inline gboolean
206 find_config(
207     GArray             *configs,
208     GstVaapiProfile     profile,
209     GstVaapiEntrypoint  entrypoint
210 )
211 {
212     GstVaapiConfig *config;
213     guint i;
214
215     if (!configs)
216         return FALSE;
217
218     for (i = 0; i < configs->len; i++) {
219         config = &g_array_index(configs, GstVaapiConfig, i);
220         if (config->profile == profile && config->entrypoint == entrypoint)
221             return TRUE;
222     }
223     return FALSE;
224 }
225
226 /* HACK: append H.263 Baseline profile if MPEG-4:2 Simple profile is supported */
227 static void
228 append_h263_config(GArray *configs)
229 {
230     GstVaapiConfig *config, tmp_config;
231     GstVaapiConfig *mpeg4_simple_config = NULL;
232     GstVaapiConfig *h263_baseline_config = NULL;
233     guint i;
234
235     if (!WORKAROUND_H263_BASELINE_DECODE_PROFILE)
236         return;
237
238     if (!configs)
239         return;
240
241     for (i = 0; i < configs->len; i++) {
242         config = &g_array_index(configs, GstVaapiConfig, i);
243         if (config->profile == GST_VAAPI_PROFILE_MPEG4_SIMPLE)
244             mpeg4_simple_config = config;
245         else if (config->profile == GST_VAAPI_PROFILE_H263_BASELINE)
246             h263_baseline_config = config;
247     }
248
249     if (mpeg4_simple_config && !h263_baseline_config) {
250         tmp_config = *mpeg4_simple_config;
251         tmp_config.profile = GST_VAAPI_PROFILE_H263_BASELINE;
252         g_array_append_val(configs, tmp_config);
253     }
254 }
255
256 /* Convert configs array to profiles as GstCaps */
257 static GstCaps *
258 get_profile_caps(GArray *configs)
259 {
260     GstVaapiConfig *config;
261     GstCaps *out_caps, *caps;
262     guint i;
263
264     if (!configs)
265         return NULL;
266
267     out_caps = gst_caps_new_empty();
268     if (!out_caps)
269         return NULL;
270
271     for (i = 0; i < configs->len; i++) {
272         config = &g_array_index(configs, GstVaapiConfig, i);
273         caps   = gst_vaapi_profile_get_caps(config->profile);
274         if (caps)
275             gst_caps_merge(out_caps, caps);
276     }
277     return out_caps;
278 }
279
280 /* Check if formats array contains format */
281 static inline gboolean
282 find_format(GArray *formats, GstVaapiImageFormat format)
283 {
284     guint i;
285
286     for (i = 0; i < formats->len; i++)
287         if (g_array_index(formats, GstVaapiImageFormat, i) == format)
288             return TRUE;
289     return FALSE;
290 }
291
292 /* Convert formats array to GstCaps */
293 static GstCaps *
294 get_format_caps(GArray *formats)
295 {
296     GstVaapiImageFormat format;
297     GstCaps *out_caps, *caps;
298     guint i;
299
300     out_caps = gst_caps_new_empty();
301     if (!out_caps)
302         return NULL;
303
304     for (i = 0; i < formats->len; i++) {
305         format = g_array_index(formats, GstVaapiImageFormat, i);
306         caps   = gst_vaapi_image_format_get_caps(format);
307         if (caps)
308             gst_caps_append(out_caps, caps);
309     }
310     return out_caps;
311 }
312
313 /* Find display attribute */
314 static const GstVaapiProperty *
315 find_property(GArray *properties, const gchar *name)
316 {
317     GstVaapiProperty *prop;
318     guint i;
319
320     if (!name)
321         return NULL;
322
323     for (i = 0; i < properties->len; i++) {
324         prop = &g_array_index(properties, GstVaapiProperty, i);
325         if (strcmp(prop->name, name) == 0)
326             return prop;
327     }
328     return NULL;
329 }
330
331 #if 0
332 static const GstVaapiProperty *
333 find_property_by_type(GArray *properties, VADisplayAttribType type)
334 {
335     GstVaapiProperty *prop;
336     guint i;
337
338     for (i = 0; i < properties->len; i++) {
339         prop = &g_array_index(properties, GstVaapiProperty, i);
340         if (prop->attribute.type == type)
341             return prop;
342     }
343     return NULL;
344 }
345 #endif
346
347 static void
348 gst_vaapi_display_calculate_pixel_aspect_ratio(GstVaapiDisplay *display)
349 {
350     GstVaapiDisplayPrivate * const priv = display->priv;
351     gdouble ratio, delta;
352     gint i, index;
353
354     static const gint par[][2] = {
355         {1, 1},         /* regular screen            */
356         {16, 15},       /* PAL TV                    */
357         {11, 10},       /* 525 line Rec.601 video    */
358         {54, 59},       /* 625 line Rec.601 video    */
359         {64, 45},       /* 1280x1024 on 16:9 display */
360         {5, 3},         /* 1280x1024 on  4:3 display */
361         {4, 3}          /*  800x600  on 16:9 display */
362     };
363
364     /* First, calculate the "real" ratio based on the X values;
365      * which is the "physical" w/h divided by the w/h in pixels of the
366      * display */
367     if (!priv->width || !priv->height || !priv->width_mm || !priv->height_mm)
368         ratio = 1.0;
369     else
370         ratio = (gdouble)(priv->width_mm * priv->height) /
371             (priv->height_mm * priv->width);
372     GST_DEBUG("calculated pixel aspect ratio: %f", ratio);
373
374     /* Now, find the one from par[][2] with the lowest delta to the real one */
375 #define DELTA(idx) (ABS(ratio - ((gdouble)par[idx][0] / par[idx][1])))
376     delta = DELTA(0);
377     index = 0;
378
379     for (i = 1; i < G_N_ELEMENTS(par); i++) {
380         const gdouble this_delta = DELTA(i);
381         if (this_delta < delta) {
382             index = i;
383             delta = this_delta;
384         }
385     }
386 #undef DELTA
387
388     priv->par_n = par[index][0];
389     priv->par_d = par[index][1];
390 }
391
392 static void
393 gst_vaapi_display_destroy(GstVaapiDisplay *display)
394 {
395     GstVaapiDisplayPrivate * const priv = display->priv;
396
397     if (priv->decoders) {
398         g_array_free(priv->decoders, TRUE);
399         priv->decoders = NULL;
400     }
401
402     if (priv->encoders) {
403         g_array_free(priv->encoders, TRUE);
404         priv->encoders = NULL;
405     }
406
407     if (priv->image_formats) {
408         g_array_free(priv->image_formats, TRUE);
409         priv->image_formats = NULL;
410     }
411
412     if (priv->subpicture_formats) {
413         g_array_free(priv->subpicture_formats, TRUE);
414         priv->subpicture_formats = NULL;
415     }
416
417     if (priv->properties) {
418         g_array_free(priv->properties, TRUE);
419         priv->properties = NULL;
420     }
421
422     if (priv->display) {
423         if (!priv->parent)
424             vaTerminate(priv->display);
425         priv->display = NULL;
426     }
427
428     if (priv->create_display) {
429         GstVaapiDisplayClass *klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
430         if (klass->close_display)
431             klass->close_display(display);
432     }
433
434     g_clear_object(&priv->parent);
435
436     if (g_display_cache) {
437         gst_vaapi_display_cache_remove(get_display_cache(), display);
438         free_display_cache();
439     }
440 }
441
442 static gboolean
443 gst_vaapi_display_create(GstVaapiDisplay *display)
444 {
445     GstVaapiDisplayPrivate * const priv = display->priv;
446     GstVaapiDisplayCache *cache;
447     gboolean            has_errors      = TRUE;
448     VADisplayAttribute *display_attrs   = NULL;
449     VAProfile          *profiles        = NULL;
450     VAEntrypoint       *entrypoints     = NULL;
451     VAImageFormat      *formats         = NULL;
452     unsigned int       *flags           = NULL;
453     gint                i, j, n, num_entrypoints, major_version, minor_version;
454     VAStatus            status;
455     GstVaapiDisplayInfo info;
456     const GstVaapiDisplayInfo *cached_info = NULL;
457
458     memset(&info, 0, sizeof(info));
459     info.display = display;
460     info.display_type = priv->display_type;
461
462     if (priv->display)
463         info.va_display = priv->display;
464     else if (priv->create_display) {
465         GstVaapiDisplayClass *klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
466         if (klass->open_display && !klass->open_display(display))
467             return FALSE;
468         if (!klass->get_display || !klass->get_display(display, &info))
469             return FALSE;
470         priv->display = info.va_display;
471         priv->display_type = info.display_type;
472         if (klass->get_size)
473             klass->get_size(display, &priv->width, &priv->height);
474         if (klass->get_size_mm)
475             klass->get_size_mm(display, &priv->width_mm, &priv->height_mm);
476         gst_vaapi_display_calculate_pixel_aspect_ratio(display);
477     }
478     if (!priv->display)
479         return FALSE;
480
481     cache = get_display_cache();
482     if (!cache)
483         return FALSE;
484     cached_info = gst_vaapi_display_cache_lookup_by_va_display(
485         cache,
486         info.va_display
487     );
488     if (cached_info) {
489         g_clear_object(&priv->parent);
490         priv->parent = g_object_ref(cached_info->display);
491         priv->display_type = cached_info->display_type;
492     }
493
494     if (!priv->parent) {
495         status = vaInitialize(priv->display, &major_version, &minor_version);
496         if (!vaapi_check_status(status, "vaInitialize()"))
497             goto end;
498         GST_DEBUG("VA-API version %d.%d", major_version, minor_version);
499     }
500
501     /* VA profiles */
502     profiles = g_new(VAProfile, vaMaxNumProfiles(priv->display));
503     if (!profiles)
504         goto end;
505     entrypoints = g_new(VAEntrypoint, vaMaxNumEntrypoints(priv->display));
506     if (!entrypoints)
507         goto end;
508     status = vaQueryConfigProfiles(priv->display, profiles, &n);
509     if (!vaapi_check_status(status, "vaQueryConfigProfiles()"))
510         goto end;
511
512     GST_DEBUG("%d profiles", n);
513     for (i = 0; i < n; i++) {
514 #if VA_CHECK_VERSION(0,34,0)
515         /* Introduced in VA/VPP API */
516         if (profiles[i] == VAProfileNone)
517             continue;
518 #endif
519         GST_DEBUG("  %s", string_of_VAProfile(profiles[i]));
520     }
521
522     priv->decoders = g_array_new(FALSE, FALSE, sizeof(GstVaapiConfig));
523     if (!priv->decoders)
524         goto end;
525     priv->encoders = g_array_new(FALSE, FALSE, sizeof(GstVaapiConfig));
526     if (!priv->encoders)
527         goto end;
528
529     for (i = 0; i < n; i++) {
530         GstVaapiConfig config;
531
532         config.profile = gst_vaapi_profile(profiles[i]);
533         if (!config.profile)
534             continue;
535
536         status = vaQueryConfigEntrypoints(
537             priv->display,
538             profiles[i],
539             entrypoints, &num_entrypoints
540         );
541         if (!vaapi_check_status(status, "vaQueryConfigEntrypoints()"))
542             continue;
543
544         for (j = 0; j < num_entrypoints; j++) {
545             config.entrypoint = gst_vaapi_entrypoint(entrypoints[j]);
546             switch (config.entrypoint) {
547             case GST_VAAPI_ENTRYPOINT_VLD:
548             case GST_VAAPI_ENTRYPOINT_IDCT:
549             case GST_VAAPI_ENTRYPOINT_MOCO:
550                 g_array_append_val(priv->decoders, config);
551                 break;
552             case GST_VAAPI_ENTRYPOINT_SLICE_ENCODE:
553                 g_array_append_val(priv->encoders, config);
554                 break;
555             }
556         }
557     }
558     append_h263_config(priv->decoders);
559
560     /* VA display attributes */
561     display_attrs =
562         g_new(VADisplayAttribute, vaMaxNumDisplayAttributes(priv->display));
563     if (!display_attrs)
564         goto end;
565
566     n = 0; /* XXX: workaround old GMA500 bug */
567     status = vaQueryDisplayAttributes(priv->display, display_attrs, &n);
568     if (!vaapi_check_status(status, "vaQueryDisplayAttributes()"))
569         goto end;
570
571     priv->properties = g_array_new(FALSE, FALSE, sizeof(GstVaapiProperty));
572     if (!priv->properties)
573         goto end;
574
575     GST_DEBUG("%d display attributes", n);
576     for (i = 0; i < n; i++) {
577         VADisplayAttribute * const attr = &display_attrs[i];
578         GstVaapiProperty prop;
579
580         GST_DEBUG("  %s", string_of_VADisplayAttributeType(attr->type));
581
582         switch (attr->type) {
583         default:
584             prop.attribute.flags = 0;
585             break;
586         }
587         if (!prop.attribute.flags)
588             continue;
589         g_array_append_val(priv->properties, prop);
590     }
591
592     /* VA image formats */
593     formats = g_new(VAImageFormat, vaMaxNumImageFormats(priv->display));
594     if (!formats)
595         goto end;
596     status = vaQueryImageFormats(priv->display, formats, &n);
597     if (!vaapi_check_status(status, "vaQueryImageFormats()"))
598         goto end;
599
600     GST_DEBUG("%d image formats", n);
601     for (i = 0; i < n; i++)
602         GST_DEBUG("  %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS(formats[i].fourcc));
603
604     priv->image_formats =
605         g_array_new(FALSE, FALSE, sizeof(GstVaapiImageFormat));
606     if (!priv->image_formats)
607         goto end;
608     append_formats(priv->image_formats, formats, n);
609     g_array_sort(priv->image_formats, compare_yuv_formats);
610
611     /* VA subpicture formats */
612     n = vaMaxNumSubpictureFormats(priv->display);
613     formats = g_renew(VAImageFormat, formats, n);
614     flags   = g_new(guint, n);
615     if (!formats || !flags)
616         goto end;
617     status = vaQuerySubpictureFormats(priv->display, formats, flags, (guint *)&n);
618     if (!vaapi_check_status(status, "vaQuerySubpictureFormats()"))
619         goto end;
620
621     GST_DEBUG("%d subpicture formats", n);
622     for (i = 0; i < n; i++)
623         GST_DEBUG("  %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS(formats[i].fourcc));
624
625     priv->subpicture_formats =
626         g_array_new(FALSE, FALSE, sizeof(GstVaapiImageFormat));
627     if (!priv->subpicture_formats)
628         goto end;
629     append_formats(priv->subpicture_formats, formats, n);
630     g_array_sort(priv->subpicture_formats, compare_rgb_formats);
631
632     if (!cached_info) {
633         if (!gst_vaapi_display_cache_add(cache, &info))
634             goto end;
635     }
636
637     has_errors = FALSE;
638 end:
639     g_free(display_attrs);
640     g_free(profiles);
641     g_free(entrypoints);
642     g_free(formats);
643     g_free(flags);
644     return !has_errors;
645 }
646
647 static void
648 gst_vaapi_display_lock_default(GstVaapiDisplay *display)
649 {
650     GstVaapiDisplayPrivate *priv = display->priv;
651
652     if (priv->parent)
653         priv = priv->parent->priv;
654     g_static_rec_mutex_lock(&priv->mutex);
655 }
656
657 static void
658 gst_vaapi_display_unlock_default(GstVaapiDisplay *display)
659 {
660     GstVaapiDisplayPrivate *priv = display->priv;
661
662     if (priv->parent)
663         priv = priv->parent->priv;
664     g_static_rec_mutex_unlock(&priv->mutex);
665 }
666
667 static void
668 gst_vaapi_display_finalize(GObject *object)
669 {
670     GstVaapiDisplay * const display = GST_VAAPI_DISPLAY(object);
671
672     gst_vaapi_display_destroy(display);
673
674     g_static_rec_mutex_free(&display->priv->mutex);
675
676     G_OBJECT_CLASS(gst_vaapi_display_parent_class)->finalize(object);
677 }
678
679 static void
680 gst_vaapi_display_set_property(
681     GObject      *object,
682     guint         prop_id,
683     const GValue *value,
684     GParamSpec   *pspec
685 )
686 {
687     GstVaapiDisplay * const display = GST_VAAPI_DISPLAY(object);
688
689     switch (prop_id) {
690     case PROP_DISPLAY:
691         display->priv->display = g_value_get_pointer(value);
692         break;
693     case PROP_DISPLAY_TYPE:
694         display->priv->display_type = g_value_get_enum(value);
695         break;
696     default:
697         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
698         break;
699     }
700 }
701
702 static void
703 gst_vaapi_display_get_property(
704     GObject    *object,
705     guint       prop_id,
706     GValue     *value,
707     GParamSpec *pspec
708 )
709 {
710     GstVaapiDisplay * const display = GST_VAAPI_DISPLAY(object);
711
712     switch (prop_id) {
713     case PROP_DISPLAY:
714         g_value_set_pointer(value, gst_vaapi_display_get_display(display));
715         break;
716     case PROP_DISPLAY_TYPE:
717         g_value_set_enum(value, gst_vaapi_display_get_display_type(display));
718         break;
719     case PROP_WIDTH:
720         g_value_set_uint(value, gst_vaapi_display_get_width(display));
721         break;
722     case PROP_HEIGHT:
723         g_value_set_uint(value, gst_vaapi_display_get_height(display));
724         break;
725     default:
726         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
727         break;
728     }
729 }
730
731 static void
732 gst_vaapi_display_constructed(GObject *object)
733 {
734     GstVaapiDisplay * const display = GST_VAAPI_DISPLAY(object);
735     GObjectClass *parent_class;
736
737     display->priv->create_display = display->priv->display == NULL;
738     if (!gst_vaapi_display_create(display))
739         gst_vaapi_display_destroy(display);
740
741     parent_class = G_OBJECT_CLASS(gst_vaapi_display_parent_class);
742     if (parent_class->constructed)
743         parent_class->constructed(object);
744 }
745
746 static void
747 gst_vaapi_display_class_init(GstVaapiDisplayClass *klass)
748 {
749     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
750     GstVaapiDisplayClass * const dpy_class = GST_VAAPI_DISPLAY_CLASS(klass);
751
752     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapi, "vaapi", 0, "VA-API helper");
753
754     g_type_class_add_private(klass, sizeof(GstVaapiDisplayPrivate));
755
756     object_class->finalize      = gst_vaapi_display_finalize;
757     object_class->set_property  = gst_vaapi_display_set_property;
758     object_class->get_property  = gst_vaapi_display_get_property;
759     object_class->constructed   = gst_vaapi_display_constructed;
760
761     dpy_class->lock             = gst_vaapi_display_lock_default;
762     dpy_class->unlock           = gst_vaapi_display_unlock_default;
763
764     g_object_class_install_property
765         (object_class,
766          PROP_DISPLAY,
767          g_param_spec_pointer("display",
768                               "VA display",
769                               "VA display",
770                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
771
772     g_object_class_install_property
773         (object_class,
774          PROP_DISPLAY_TYPE,
775          g_param_spec_enum("display-type",
776                            "VA display type",
777                            "VA display type",
778                            GST_VAAPI_TYPE_DISPLAY_TYPE,
779                            GST_VAAPI_DISPLAY_TYPE_ANY,
780                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
781
782     g_object_class_install_property
783         (object_class,
784          PROP_WIDTH,
785          g_param_spec_uint("width",
786                            "Width",
787                            "The display width",
788                            1, G_MAXUINT32, 1,
789                            G_PARAM_READABLE));
790
791     g_object_class_install_property
792         (object_class,
793          PROP_HEIGHT,
794          g_param_spec_uint("height",
795                            "height",
796                            "The display height",
797                            1, G_MAXUINT32, 1,
798                            G_PARAM_READABLE));
799 }
800
801 static void
802 gst_vaapi_display_init(GstVaapiDisplay *display)
803 {
804     GstVaapiDisplayPrivate *priv = GST_VAAPI_DISPLAY_GET_PRIVATE(display);
805
806     display->priv               = priv;
807     priv->parent                = NULL;
808     priv->display_type          = GST_VAAPI_DISPLAY_TYPE_ANY;
809     priv->display               = NULL;
810     priv->width                 = 0;
811     priv->height                = 0;
812     priv->width_mm              = 0;
813     priv->height_mm             = 0;
814     priv->par_n                 = 1;
815     priv->par_d                 = 1;
816     priv->decoders              = NULL;
817     priv->encoders              = NULL;
818     priv->image_formats         = NULL;
819     priv->subpicture_formats    = NULL;
820     priv->properties            = NULL;
821     priv->create_display        = TRUE;
822
823     g_static_rec_mutex_init(&priv->mutex);
824 }
825
826 /**
827  * gst_vaapi_display_new_with_display:
828  * @va_display: a #VADisplay
829  *
830  * Creates a new #GstVaapiDisplay, using @va_display as the VA
831  * display.
832  *
833  * Return value: the newly created #GstVaapiDisplay object
834  */
835 GstVaapiDisplay *
836 gst_vaapi_display_new_with_display(VADisplay va_display)
837 {
838     GstVaapiDisplayCache * const cache = get_display_cache();
839     const GstVaapiDisplayInfo *info;
840
841     g_return_val_if_fail(va_display != NULL, NULL);
842     g_return_val_if_fail(cache != NULL, NULL);
843
844     info = gst_vaapi_display_cache_lookup_by_va_display(cache, va_display);
845     if (info)
846         return g_object_ref(info->display);
847
848     return g_object_new(GST_VAAPI_TYPE_DISPLAY,
849                         "display", va_display,
850                         NULL);
851 }
852
853 /**
854  * gst_vaapi_display_lock:
855  * @display: a #GstVaapiDisplay
856  *
857  * Locks @display. If @display is already locked by another thread,
858  * the current thread will block until @display is unlocked by the
859  * other thread.
860  */
861 void
862 gst_vaapi_display_lock(GstVaapiDisplay *display)
863 {
864     GstVaapiDisplayClass *klass;
865
866     g_return_if_fail(GST_VAAPI_IS_DISPLAY(display));
867
868     klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
869     if (klass->lock)
870         klass->lock(display);
871 }
872
873 /**
874  * gst_vaapi_display_unlock:
875  * @display: a #GstVaapiDisplay
876  *
877  * Unlocks @display. If another thread is blocked in a
878  * gst_vaapi_display_lock() call for @display, it will be woken and
879  * can lock @display itself.
880  */
881 void
882 gst_vaapi_display_unlock(GstVaapiDisplay *display)
883 {
884     GstVaapiDisplayClass *klass;
885
886     g_return_if_fail(GST_VAAPI_IS_DISPLAY(display));
887
888     klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
889     if (klass->unlock)
890         klass->unlock(display);
891 }
892
893 /**
894  * gst_vaapi_display_sync:
895  * @display: a #GstVaapiDisplay
896  *
897  * Flushes any requests queued for the windowing system and waits until
898  * all requests have been handled. This is often used for making sure
899  * that the display is synchronized with the current state of the program.
900  *
901  * This is most useful for X11. On windowing systems where requests are
902  * handled synchronously, this function will do nothing.
903  */
904 void
905 gst_vaapi_display_sync(GstVaapiDisplay *display)
906 {
907     GstVaapiDisplayClass *klass;
908
909     g_return_if_fail(GST_VAAPI_IS_DISPLAY(display));
910
911     klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
912     if (klass->sync)
913         klass->sync(display);
914     else if (klass->flush)
915         klass->flush(display);
916 }
917
918 /**
919  * gst_vaapi_display_flush:
920  * @display: a #GstVaapiDisplay
921  *
922  * Flushes any requests queued for the windowing system.
923  *
924  * This is most useful for X11. On windowing systems where requests
925  * are handled synchronously, this function will do nothing.
926  */
927 void
928 gst_vaapi_display_flush(GstVaapiDisplay *display)
929 {
930     GstVaapiDisplayClass *klass;
931
932     g_return_if_fail(GST_VAAPI_IS_DISPLAY(display));
933
934     klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
935     if (klass->flush)
936         klass->flush(display);
937 }
938
939 /**
940  * gst_vaapi_display_get_display:
941  * @display: a #GstVaapiDisplay
942  *
943  * Returns the #GstVaapiDisplayType bound to @display.
944  *
945  * Return value: the #GstVaapiDisplayType
946  */
947 GstVaapiDisplayType
948 gst_vaapi_display_get_display_type(GstVaapiDisplay *display)
949 {
950     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display),
951                          GST_VAAPI_DISPLAY_TYPE_ANY);
952
953     return display->priv->display_type;
954 }
955
956 /**
957  * gst_vaapi_display_get_display:
958  * @display: a #GstVaapiDisplay
959  *
960  * Returns the #VADisplay bound to @display.
961  *
962  * Return value: the #VADisplay
963  */
964 VADisplay
965 gst_vaapi_display_get_display(GstVaapiDisplay *display)
966 {
967     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
968
969     return display->priv->display;
970 }
971
972 /**
973  * gst_vaapi_display_get_width:
974  * @display: a #GstVaapiDisplay
975  *
976  * Retrieves the width of a #GstVaapiDisplay.
977  *
978  * Return value: the width of the @display, in pixels
979  */
980 guint
981 gst_vaapi_display_get_width(GstVaapiDisplay *display)
982 {
983     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), 0);
984
985     return display->priv->width;
986 }
987
988 /**
989  * gst_vaapi_display_get_height:
990  * @display: a #GstVaapiDisplay
991  *
992  * Retrieves the height of a #GstVaapiDisplay
993  *
994  * Return value: the height of the @display, in pixels
995  */
996 guint
997 gst_vaapi_display_get_height(GstVaapiDisplay *display)
998 {
999     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), 0);
1000
1001     return display->priv->height;
1002 }
1003
1004 /**
1005  * gst_vaapi_display_get_size:
1006  * @display: a #GstVaapiDisplay
1007  * @pwidth: return location for the width, or %NULL
1008  * @pheight: return location for the height, or %NULL
1009  *
1010  * Retrieves the dimensions of a #GstVaapiDisplay.
1011  */
1012 void
1013 gst_vaapi_display_get_size(GstVaapiDisplay *display, guint *pwidth, guint *pheight)
1014 {
1015     g_return_if_fail(GST_VAAPI_DISPLAY(display));
1016
1017     if (pwidth)
1018         *pwidth = display->priv->width;
1019
1020     if (pheight)
1021         *pheight = display->priv->height;
1022 }
1023
1024 /**
1025  * gst_vaapi_display_get_pixel_aspect_ratio:
1026  * @display: a #GstVaapiDisplay
1027  * @par_n: return location for the numerator of pixel aspect ratio, or %NULL
1028  * @par_d: return location for the denominator of pixel aspect ratio, or %NULL
1029  *
1030  * Retrieves the pixel aspect ratio of a #GstVaapiDisplay.
1031  */
1032 void
1033 gst_vaapi_display_get_pixel_aspect_ratio(
1034     GstVaapiDisplay *display,
1035     guint           *par_n,
1036     guint           *par_d
1037 )
1038 {
1039     g_return_if_fail(GST_VAAPI_IS_DISPLAY(display));
1040
1041     if (par_n)
1042         *par_n = display->priv->par_n;
1043
1044     if (par_d)
1045         *par_d = display->priv->par_d;
1046 }
1047
1048 /**
1049  * gst_vaapi_display_get_decode_caps:
1050  * @display: a #GstVaapiDisplay
1051  *
1052  * Gets the supported profiles for decoding as #GstCaps capabilities.
1053  *
1054  * Return value: a newly allocated #GstCaps object, possibly empty
1055  */
1056 GstCaps *
1057 gst_vaapi_display_get_decode_caps(GstVaapiDisplay *display)
1058 {
1059     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
1060
1061     return get_profile_caps(display->priv->decoders);
1062 }
1063
1064 /**
1065  * gst_vaapi_display_has_decoder:
1066  * @display: a #GstVaapiDisplay
1067  * @profile: a #VAProfile
1068  * @entrypoint: a #GstVaaiEntrypoint
1069  *
1070  * Returns whether VA @display supports @profile for decoding at the
1071  * specified @entrypoint.
1072  *
1073  * Return value: %TRUE if VA @display supports @profile for decoding.
1074  */
1075 gboolean
1076 gst_vaapi_display_has_decoder(
1077     GstVaapiDisplay    *display,
1078     GstVaapiProfile     profile,
1079     GstVaapiEntrypoint  entrypoint
1080 )
1081 {
1082     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1083
1084     return find_config(display->priv->decoders, profile, entrypoint);
1085 }
1086
1087 /**
1088  * gst_vaapi_display_get_encode_caps:
1089  * @display: a #GstVaapiDisplay
1090  *
1091  * Gets the supported profiles for decoding as #GstCaps capabilities.
1092  *
1093  * Return value: a newly allocated #GstCaps object, possibly empty
1094  */
1095 GstCaps *
1096 gst_vaapi_display_get_encode_caps(GstVaapiDisplay *display)
1097 {
1098     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
1099
1100     return get_profile_caps(display->priv->encoders);
1101 }
1102
1103 /**
1104  * gst_vaapi_display_has_encoder:
1105  * @display: a #GstVaapiDisplay
1106  * @profile: a #VAProfile
1107  * @entrypoint: a #GstVaapiEntrypoint
1108  *
1109  * Returns whether VA @display supports @profile for encoding at the
1110  * specified @entrypoint.
1111  *
1112  * Return value: %TRUE if VA @display supports @profile for encoding.
1113  */
1114 gboolean
1115 gst_vaapi_display_has_encoder(
1116     GstVaapiDisplay    *display,
1117     GstVaapiProfile     profile,
1118     GstVaapiEntrypoint  entrypoint
1119 )
1120 {
1121     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1122
1123     return find_config(display->priv->encoders, profile, entrypoint);
1124 }
1125
1126 /**
1127  * gst_vaapi_display_get_image_caps:
1128  * @display: a #GstVaapiDisplay
1129  *
1130  * Gets the supported image formats for gst_vaapi_surface_get_image()
1131  * or gst_vaapi_surface_put_image() as #GstCaps capabilities.
1132  *
1133  * Note that this method does not necessarily map image formats
1134  * returned by vaQueryImageFormats(). The set of capabilities can be
1135  * stripped down, if gstreamer-vaapi does not support the format, or
1136  * expanded to cover compatible formats not exposed by the underlying
1137  * driver. e.g. I420 can be supported even if the driver only exposes
1138  * YV12.
1139  *
1140  * Return value: a newly allocated #GstCaps object, possibly empty
1141  */
1142 GstCaps *
1143 gst_vaapi_display_get_image_caps(GstVaapiDisplay *display)
1144 {
1145     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
1146
1147     return get_format_caps(display->priv->image_formats);
1148 }
1149
1150 /**
1151  * gst_vaapi_display_has_image_format:
1152  * @display: a #GstVaapiDisplay
1153  * @format: a #GstVaapiFormat
1154  *
1155  * Returns whether VA @display supports @format image format.
1156  *
1157  * Return value: %TRUE if VA @display supports @format image format
1158  */
1159 gboolean
1160 gst_vaapi_display_has_image_format(
1161     GstVaapiDisplay    *display,
1162     GstVaapiImageFormat format
1163 )
1164 {
1165     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1166     g_return_val_if_fail(format, FALSE);
1167
1168     if (find_format(display->priv->image_formats, format))
1169         return TRUE;
1170
1171     /* XXX: try subpicture formats since some drivers could report a
1172      * set of VA image formats that is not a superset of the set of VA
1173      * subpicture formats
1174      */
1175     return find_format(display->priv->subpicture_formats, format);
1176 }
1177
1178 /**
1179  * gst_vaapi_display_get_subpicture_caps:
1180  * @display: a #GstVaapiDisplay
1181  *
1182  * Gets the supported subpicture formats as #GstCaps capabilities.
1183  *
1184  * Note that this method does not necessarily map subpicture formats
1185  * returned by vaQuerySubpictureFormats(). The set of capabilities can
1186  * be stripped down if gstreamer-vaapi does not support the
1187  * format. e.g. this is the case for paletted formats like IA44.
1188  *
1189  * Return value: a newly allocated #GstCaps object, possibly empty
1190  */
1191 GstCaps *
1192 gst_vaapi_display_get_subpicture_caps(GstVaapiDisplay *display)
1193 {
1194     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
1195
1196     return get_format_caps(display->priv->subpicture_formats);
1197 }
1198
1199 /**
1200  * gst_vaapi_display_has_subpicture_format:
1201  * @display: a #GstVaapiDisplay
1202  * @format: a #GstVaapiFormat
1203  *
1204  * Returns whether VA @display supports @format subpicture format.
1205  *
1206  * Return value: %TRUE if VA @display supports @format subpicture format
1207  */
1208 gboolean
1209 gst_vaapi_display_has_subpicture_format(
1210     GstVaapiDisplay    *display,
1211     GstVaapiImageFormat format
1212 )
1213 {
1214     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1215     g_return_val_if_fail(format, FALSE);
1216
1217     return find_format(display->priv->subpicture_formats, format);
1218 }
1219
1220 /**
1221  * gst_vaapi_display_has_property:
1222  * @display: a #GstVaapiDisplay
1223  * @name: the property name to check
1224  *
1225  * Returns whether VA @display supports the requested property. The
1226  * check is performed against the property @name. So, the client
1227  * application may perform this check only once and cache this
1228  * information.
1229  *
1230  * Return value: %TRUE if VA @display supports property @name
1231  */
1232 gboolean
1233 gst_vaapi_display_has_property(GstVaapiDisplay *display, const gchar *name)
1234 {
1235     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1236     g_return_val_if_fail(name, FALSE);
1237
1238     return find_property(display->priv->properties, name) != NULL;
1239 }