Tizen 2.1 base
[framework/multimedia/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 "gstvaapivalue.h"
32 #include "gstvaapidisplay.h"
33 #include "gstvaapidisplay_priv.h"
34 #include "gstvaapiworkarounds.h"
35
36 #define DEBUG 1
37 #include "gstvaapidebug.h"
38
39 GST_DEBUG_CATEGORY(gst_debug_vaapi);
40
41 G_DEFINE_TYPE(GstVaapiDisplay, gst_vaapi_display, G_TYPE_OBJECT);
42
43 typedef struct _GstVaapiConfig GstVaapiConfig;
44 struct _GstVaapiConfig {
45     GstVaapiProfile     profile;
46     GstVaapiEntrypoint  entrypoint;
47 };
48
49 typedef struct _GstVaapiProperty GstVaapiProperty;
50 struct _GstVaapiProperty {
51     const gchar        *name;
52     VADisplayAttribute  attribute;
53     gint                old_value;
54 };
55
56 #define DEFAULT_RENDER_MODE     GST_VAAPI_RENDER_MODE_TEXTURE
57 #define DEFAULT_ROTATION        GST_VAAPI_ROTATION_0
58
59 enum {
60     PROP_0,
61
62     PROP_DISPLAY,
63     PROP_DISPLAY_TYPE,
64     PROP_WIDTH,
65     PROP_HEIGHT,
66     PROP_RENDER_MODE,
67     PROP_ROTATION,
68     PROP_HUE,
69     PROP_SATURATION,
70     PROP_BRIGHTNESS,
71     PROP_CONTRAST,
72
73     N_PROPERTIES
74 };
75
76 static GstVaapiDisplayCache *g_display_cache = NULL;
77
78 static GParamSpec *g_properties[N_PROPERTIES] = { NULL, };
79
80 static gboolean
81 get_attribute(GstVaapiDisplay *display, VADisplayAttribType type, gint *value);
82
83 static gboolean
84 set_attribute(GstVaapiDisplay *display, VADisplayAttribType type, gint value);
85
86 static gboolean
87 get_color_balance(GstVaapiDisplay *display, guint prop_id, gfloat *v);
88
89 static gboolean
90 set_color_balance(GstVaapiDisplay *display, guint prop_id, gfloat v);
91
92 static inline GstVaapiDisplayCache *
93 get_display_cache(void)
94 {
95     if (!g_display_cache)
96         g_display_cache = gst_vaapi_display_cache_new();
97     return g_display_cache;
98 }
99
100 GstVaapiDisplayCache *
101 gst_vaapi_display_get_cache(void)
102 {
103     return get_display_cache();
104 }
105
106 static void
107 free_display_cache(void)
108 {
109     if (!g_display_cache)
110         return;
111     if (gst_vaapi_display_cache_get_size(g_display_cache) > 0)
112         return;
113     gst_vaapi_display_cache_free(g_display_cache);
114     g_display_cache = NULL;
115 }
116
117 /* GstVaapiDisplayType enumerations */
118 GType
119 gst_vaapi_display_type_get_type(void)
120 {
121     static GType g_type = 0;
122
123     static const GEnumValue display_types[] = {
124         { GST_VAAPI_DISPLAY_TYPE_ANY,
125           "Auto detection", "any" },
126 #if USE_X11
127         { GST_VAAPI_DISPLAY_TYPE_X11,
128           "VA/X11 display", "x11" },
129 #endif
130 #if USE_GLX
131         { GST_VAAPI_DISPLAY_TYPE_GLX,
132           "VA/GLX display", "glx" },
133 #endif
134 #if USE_WAYLAND
135         { GST_VAAPI_DISPLAY_TYPE_WAYLAND,
136           "VA/Wayland display", "wayland" },
137 #endif
138 #if USE_DRM
139         { GST_VAAPI_DISPLAY_TYPE_DRM,
140           "VA/DRM display", "drm" },
141 #endif
142         { 0, NULL, NULL },
143     };
144
145     if (!g_type)
146         g_type = g_enum_register_static("GstVaapiDisplayType", display_types);
147     return g_type;
148 }
149
150 /* Append GstVaapiImageFormat to formats array */
151 static inline void
152 append_format(GArray *formats, GstVaapiImageFormat format)
153 {
154     g_array_append_val(formats, format);
155 }
156
157 /* Append VAImageFormats to formats array */
158 static void
159 append_formats(GArray *formats, const VAImageFormat *va_formats, guint n)
160 {
161     GstVaapiImageFormat format;
162     gboolean has_YV12 = FALSE;
163     gboolean has_I420 = FALSE;
164     guint i;
165
166     for (i = 0; i < n; i++) {
167         const VAImageFormat * const va_format = &va_formats[i];
168
169         format = gst_vaapi_image_format(va_format);
170         if (!format) {
171             GST_DEBUG("unsupported format %" GST_FOURCC_FORMAT,
172                       GST_FOURCC_ARGS(va_format->fourcc));
173             continue;
174         }
175
176         switch (format) {
177         case GST_VAAPI_IMAGE_YV12:
178             has_YV12 = TRUE;
179             break;
180         case GST_VAAPI_IMAGE_I420:
181             has_I420 = TRUE;
182             break;
183         default:
184             break;
185         }
186         append_format(formats, format);
187     }
188
189     /* Append I420 (resp. YV12) format if YV12 (resp. I420) is not
190        supported by the underlying driver */
191     if (has_YV12 && !has_I420)
192         append_format(formats, GST_VAAPI_IMAGE_I420);
193     else if (has_I420 && !has_YV12)
194         append_format(formats, GST_VAAPI_IMAGE_YV12);
195 }
196
197 /* Sort image formats. Prefer YUV formats first */
198 static gint
199 compare_yuv_formats(gconstpointer a, gconstpointer b)
200 {
201     const GstVaapiImageFormat fmt1 = *(GstVaapiImageFormat *)a;
202     const GstVaapiImageFormat fmt2 = *(GstVaapiImageFormat *)b;
203
204     const gboolean is_fmt1_yuv = gst_vaapi_image_format_is_yuv(fmt1);
205     const gboolean is_fmt2_yuv = gst_vaapi_image_format_is_yuv(fmt2);
206
207     if (is_fmt1_yuv != is_fmt2_yuv)
208         return is_fmt1_yuv ? -1 : 1;
209
210     return ((gint)gst_vaapi_image_format_get_score(fmt1) -
211             (gint)gst_vaapi_image_format_get_score(fmt2));
212 }
213
214 /* Sort subpicture formats. Prefer RGB formats first */
215 static gint
216 compare_rgb_formats(gconstpointer a, gconstpointer b)
217 {
218     const GstVaapiImageFormat fmt1 = *(GstVaapiImageFormat *)a;
219     const GstVaapiImageFormat fmt2 = *(GstVaapiImageFormat *)b;
220
221     const gboolean is_fmt1_rgb = gst_vaapi_image_format_is_rgb(fmt1);
222     const gboolean is_fmt2_rgb = gst_vaapi_image_format_is_rgb(fmt2);
223
224     if (is_fmt1_rgb != is_fmt2_rgb)
225         return is_fmt1_rgb ? -1 : 1;
226
227     return ((gint)gst_vaapi_image_format_get_score(fmt1) -
228             (gint)gst_vaapi_image_format_get_score(fmt2));
229 }
230
231 /* Check if configs array contains profile at entrypoint */
232 static inline gboolean
233 find_config(
234     GArray             *configs,
235     GstVaapiProfile     profile,
236     GstVaapiEntrypoint  entrypoint
237 )
238 {
239     GstVaapiConfig *config;
240     guint i;
241
242     if (!configs)
243         return FALSE;
244
245     for (i = 0; i < configs->len; i++) {
246         config = &g_array_index(configs, GstVaapiConfig, i);
247         if (config->profile == profile && config->entrypoint == entrypoint)
248             return TRUE;
249     }
250     return FALSE;
251 }
252
253 /* HACK: append H.263 Baseline profile if MPEG-4:2 Simple profile is supported */
254 static void
255 append_h263_config(GArray *configs)
256 {
257     GstVaapiConfig *config, tmp_config;
258     GstVaapiConfig *mpeg4_simple_config = NULL;
259     GstVaapiConfig *h263_baseline_config = NULL;
260     guint i;
261
262     if (!WORKAROUND_H263_BASELINE_DECODE_PROFILE)
263         return;
264
265     if (!configs)
266         return;
267
268     for (i = 0; i < configs->len; i++) {
269         config = &g_array_index(configs, GstVaapiConfig, i);
270         if (config->profile == GST_VAAPI_PROFILE_MPEG4_SIMPLE)
271             mpeg4_simple_config = config;
272         else if (config->profile == GST_VAAPI_PROFILE_H263_BASELINE)
273             h263_baseline_config = config;
274     }
275
276     if (mpeg4_simple_config && !h263_baseline_config) {
277         tmp_config = *mpeg4_simple_config;
278         tmp_config.profile = GST_VAAPI_PROFILE_H263_BASELINE;
279         g_array_append_val(configs, tmp_config);
280     }
281 }
282
283 /* Convert configs array to profiles as GstCaps */
284 static GstCaps *
285 get_profile_caps(GArray *configs)
286 {
287     GstVaapiConfig *config;
288     GstCaps *out_caps, *caps;
289     guint i;
290
291     if (!configs)
292         return NULL;
293
294     out_caps = gst_caps_new_empty();
295     if (!out_caps)
296         return NULL;
297
298     for (i = 0; i < configs->len; i++) {
299         config = &g_array_index(configs, GstVaapiConfig, i);
300         caps   = gst_vaapi_profile_get_caps(config->profile);
301         if (caps)
302             gst_caps_merge(out_caps, caps);
303     }
304     return out_caps;
305 }
306
307 /* Check if formats array contains format */
308 static inline gboolean
309 find_format(GArray *formats, GstVaapiImageFormat format)
310 {
311     guint i;
312
313     for (i = 0; i < formats->len; i++)
314         if (g_array_index(formats, GstVaapiImageFormat, i) == format)
315             return TRUE;
316     return FALSE;
317 }
318
319 /* Convert formats array to GstCaps */
320 static GstCaps *
321 get_format_caps(GArray *formats)
322 {
323     GstVaapiImageFormat format;
324     GstCaps *out_caps, *caps;
325     guint i;
326
327     out_caps = gst_caps_new_empty();
328     if (!out_caps)
329         return NULL;
330
331     for (i = 0; i < formats->len; i++) {
332         format = g_array_index(formats, GstVaapiImageFormat, i);
333         caps   = gst_vaapi_image_format_get_caps(format);
334         if (caps)
335             gst_caps_append(out_caps, caps);
336     }
337     return out_caps;
338 }
339
340 /* Find display attribute */
341 static const GstVaapiProperty *
342 find_property(GArray *properties, const gchar *name)
343 {
344     GstVaapiProperty *prop;
345     guint i;
346
347     if (!name)
348         return NULL;
349
350     for (i = 0; i < properties->len; i++) {
351         prop = &g_array_index(properties, GstVaapiProperty, i);
352         if (strcmp(prop->name, name) == 0)
353             return prop;
354     }
355     return NULL;
356 }
357
358 #if 0
359 static const GstVaapiProperty *
360 find_property_by_type(GArray *properties, VADisplayAttribType type)
361 {
362     GstVaapiProperty *prop;
363     guint i;
364
365     for (i = 0; i < properties->len; i++) {
366         prop = &g_array_index(properties, GstVaapiProperty, i);
367         if (prop->attribute.type == type)
368             return prop;
369     }
370     return NULL;
371 }
372 #endif
373
374 static inline const GstVaapiProperty *
375 find_property_by_pspec(GstVaapiDisplay *display, GParamSpec *pspec)
376 {
377     return find_property(display->priv->properties, pspec->name);
378 }
379
380 static void
381 gst_vaapi_display_calculate_pixel_aspect_ratio(GstVaapiDisplay *display)
382 {
383     GstVaapiDisplayPrivate * const priv = display->priv;
384     gdouble ratio, delta;
385     gint i, j, index, windex;
386
387     static const gint par[][2] = {
388         {1, 1},         /* regular screen            */
389         {16, 15},       /* PAL TV                    */
390         {11, 10},       /* 525 line Rec.601 video    */
391         {54, 59},       /* 625 line Rec.601 video    */
392         {64, 45},       /* 1280x1024 on 16:9 display */
393         {5, 3},         /* 1280x1024 on  4:3 display */
394         {4, 3}          /*  800x600  on 16:9 display */
395     };
396
397     /* First, calculate the "real" ratio based on the X values;
398      * which is the "physical" w/h divided by the w/h in pixels of the
399      * display */
400     if (!priv->width || !priv->height || !priv->width_mm || !priv->height_mm)
401         ratio = 1.0;
402     else
403         ratio = (gdouble)(priv->width_mm * priv->height) /
404             (priv->height_mm * priv->width);
405     GST_DEBUG("calculated pixel aspect ratio: %f", ratio);
406
407     /* Now, find the one from par[][2] with the lowest delta to the real one */
408 #define DELTA(idx, w) (ABS(ratio - ((gdouble)par[idx][w] / par[idx][!(w)])))
409     delta  = DELTA(0, 0);
410     index  = 0;
411     windex = 0;
412
413     for (i = 1; i < G_N_ELEMENTS(par); i++) {
414         for (j = 0; j < 2; j++) {
415             const gdouble this_delta = DELTA(i, j);
416             if (this_delta < delta) {
417                 index  = i;
418                 windex = j;
419                 delta  = this_delta;
420             }
421         }
422     }
423 #undef DELTA
424
425     priv->par_n = par[index][windex];
426     priv->par_d = par[index][windex ^ 1];
427 }
428
429 static void
430 gst_vaapi_display_destroy(GstVaapiDisplay *display)
431 {
432     GstVaapiDisplayPrivate * const priv = display->priv;
433
434     if (priv->decoders) {
435         g_array_free(priv->decoders, TRUE);
436         priv->decoders = NULL;
437     }
438
439     if (priv->encoders) {
440         g_array_free(priv->encoders, TRUE);
441         priv->encoders = NULL;
442     }
443
444     if (priv->image_formats) {
445         g_array_free(priv->image_formats, TRUE);
446         priv->image_formats = NULL;
447     }
448
449     if (priv->subpicture_formats) {
450         g_array_free(priv->subpicture_formats, TRUE);
451         priv->subpicture_formats = NULL;
452     }
453
454     if (priv->properties) {
455         g_array_free(priv->properties, TRUE);
456         priv->properties = NULL;
457     }
458
459     if (priv->display) {
460         if (!priv->parent)
461             vaTerminate(priv->display);
462         priv->display = NULL;
463     }
464
465     if (priv->create_display) {
466         GstVaapiDisplayClass *klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
467         if (klass->close_display)
468             klass->close_display(display);
469     }
470
471     g_clear_object(&priv->parent);
472
473     if (g_display_cache) {
474         gst_vaapi_display_cache_remove(get_display_cache(), display);
475         free_display_cache();
476     }
477 }
478
479 static gboolean
480 gst_vaapi_display_create(GstVaapiDisplay *display)
481 {
482     GstVaapiDisplayPrivate * const priv = display->priv;
483     GstVaapiDisplayCache *cache;
484     gboolean            has_errors      = TRUE;
485     VADisplayAttribute *display_attrs   = NULL;
486     VAProfile          *profiles        = NULL;
487     VAEntrypoint       *entrypoints     = NULL;
488     VAImageFormat      *formats         = NULL;
489     unsigned int       *flags           = NULL;
490     gint                i, j, n, num_entrypoints, major_version, minor_version;
491     VAStatus            status;
492     GstVaapiDisplayInfo info;
493     const GstVaapiDisplayInfo *cached_info = NULL;
494
495     memset(&info, 0, sizeof(info));
496     info.display = display;
497     info.display_type = priv->display_type;
498
499     if (priv->display)
500         info.va_display = priv->display;
501     else if (priv->create_display) {
502         GstVaapiDisplayClass *klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
503         if (klass->open_display && !klass->open_display(display))
504             return FALSE;
505         if (!klass->get_display || !klass->get_display(display, &info))
506             return FALSE;
507         priv->display = info.va_display;
508         priv->display_type = info.display_type;
509         if (klass->get_size)
510             klass->get_size(display, &priv->width, &priv->height);
511         if (klass->get_size_mm)
512             klass->get_size_mm(display, &priv->width_mm, &priv->height_mm);
513         gst_vaapi_display_calculate_pixel_aspect_ratio(display);
514     }
515     if (!priv->display)
516         return FALSE;
517
518     cache = get_display_cache();
519     if (!cache)
520         return FALSE;
521     cached_info = gst_vaapi_display_cache_lookup_by_va_display(
522         cache,
523         info.va_display
524     );
525     if (cached_info) {
526         g_clear_object(&priv->parent);
527         priv->parent = g_object_ref(cached_info->display);
528         priv->display_type = cached_info->display_type;
529     }
530
531     if (!priv->parent) {
532         status = vaInitialize(priv->display, &major_version, &minor_version);
533         if (!vaapi_check_status(status, "vaInitialize()"))
534             goto end;
535         GST_DEBUG("VA-API version %d.%d", major_version, minor_version);
536     }
537
538     /* VA profiles */
539     profiles = g_new(VAProfile, vaMaxNumProfiles(priv->display));
540     if (!profiles)
541         goto end;
542     entrypoints = g_new(VAEntrypoint, vaMaxNumEntrypoints(priv->display));
543     if (!entrypoints)
544         goto end;
545     status = vaQueryConfigProfiles(priv->display, profiles, &n);
546     if (!vaapi_check_status(status, "vaQueryConfigProfiles()"))
547         goto end;
548
549     GST_DEBUG("%d profiles", n);
550     for (i = 0; i < n; i++) {
551 #if VA_CHECK_VERSION(0,34,0)
552         /* Introduced in VA/VPP API */
553         if (profiles[i] == VAProfileNone)
554             continue;
555 #endif
556         GST_DEBUG("  %s", string_of_VAProfile(profiles[i]));
557     }
558
559     priv->decoders = g_array_new(FALSE, FALSE, sizeof(GstVaapiConfig));
560     if (!priv->decoders)
561         goto end;
562     priv->encoders = g_array_new(FALSE, FALSE, sizeof(GstVaapiConfig));
563     if (!priv->encoders)
564         goto end;
565
566     for (i = 0; i < n; i++) {
567         GstVaapiConfig config;
568
569         config.profile = gst_vaapi_profile(profiles[i]);
570         if (!config.profile)
571             continue;
572
573         status = vaQueryConfigEntrypoints(
574             priv->display,
575             profiles[i],
576             entrypoints, &num_entrypoints
577         );
578         if (!vaapi_check_status(status, "vaQueryConfigEntrypoints()"))
579             continue;
580
581         for (j = 0; j < num_entrypoints; j++) {
582             config.entrypoint = gst_vaapi_entrypoint(entrypoints[j]);
583             switch (config.entrypoint) {
584             case GST_VAAPI_ENTRYPOINT_VLD:
585             case GST_VAAPI_ENTRYPOINT_IDCT:
586             case GST_VAAPI_ENTRYPOINT_MOCO:
587                 g_array_append_val(priv->decoders, config);
588                 break;
589             case GST_VAAPI_ENTRYPOINT_SLICE_ENCODE:
590                 g_array_append_val(priv->encoders, config);
591                 break;
592             }
593         }
594     }
595     append_h263_config(priv->decoders);
596
597     /* VA display attributes */
598     display_attrs =
599         g_new(VADisplayAttribute, vaMaxNumDisplayAttributes(priv->display));
600     if (!display_attrs)
601         goto end;
602
603     n = 0; /* XXX: workaround old GMA500 bug */
604     status = vaQueryDisplayAttributes(priv->display, display_attrs, &n);
605     if (!vaapi_check_status(status, "vaQueryDisplayAttributes()"))
606         goto end;
607
608     priv->properties = g_array_new(FALSE, FALSE, sizeof(GstVaapiProperty));
609     if (!priv->properties)
610         goto end;
611
612     GST_DEBUG("%d display attributes", n);
613     for (i = 0; i < n; i++) {
614         VADisplayAttribute * const attr = &display_attrs[i];
615         GstVaapiProperty prop;
616         gint value;
617
618         GST_DEBUG("  %s", string_of_VADisplayAttributeType(attr->type));
619
620         switch (attr->type) {
621 #if !VA_CHECK_VERSION(0,34,0)
622         case VADisplayAttribDirectSurface:
623             prop.name = GST_VAAPI_DISPLAY_PROP_RENDER_MODE;
624             break;
625 #endif
626         case VADisplayAttribRenderMode:
627             prop.name = GST_VAAPI_DISPLAY_PROP_RENDER_MODE;
628             break;
629         case VADisplayAttribRotation:
630             prop.name = GST_VAAPI_DISPLAY_PROP_ROTATION;
631             break;
632         case VADisplayAttribHue:
633             prop.name = GST_VAAPI_DISPLAY_PROP_HUE;
634             break;
635         case VADisplayAttribSaturation:
636             prop.name = GST_VAAPI_DISPLAY_PROP_SATURATION;
637             break;
638         case VADisplayAttribBrightness:
639             prop.name = GST_VAAPI_DISPLAY_PROP_BRIGHTNESS;
640             break;
641         case VADisplayAttribContrast:
642             prop.name = GST_VAAPI_DISPLAY_PROP_CONTRAST;
643             break;
644         default:
645             prop.name = NULL;
646             break;
647         }
648         if (!prop.name)
649             continue;
650
651         /* Assume the attribute is really supported if we can get the
652          * actual and current value */
653         if (!get_attribute(display, attr->type, &value))
654             continue;
655
656         /* Some drivers (e.g. EMGD) have completely random initial
657          * values */
658         if (value < attr->min_value || value > attr->max_value)
659             continue;
660
661         prop.attribute = *attr;
662         prop.old_value = value;
663         g_array_append_val(priv->properties, prop);
664     }
665
666     /* VA image formats */
667     formats = g_new(VAImageFormat, vaMaxNumImageFormats(priv->display));
668     if (!formats)
669         goto end;
670     status = vaQueryImageFormats(priv->display, formats, &n);
671     if (!vaapi_check_status(status, "vaQueryImageFormats()"))
672         goto end;
673
674     GST_DEBUG("%d image formats", n);
675     for (i = 0; i < n; i++)
676         GST_DEBUG("  %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS(formats[i].fourcc));
677
678     priv->image_formats =
679         g_array_new(FALSE, FALSE, sizeof(GstVaapiImageFormat));
680     if (!priv->image_formats)
681         goto end;
682     append_formats(priv->image_formats, formats, n);
683     g_array_sort(priv->image_formats, compare_yuv_formats);
684
685     /* VA subpicture formats */
686     n = vaMaxNumSubpictureFormats(priv->display);
687     formats = g_renew(VAImageFormat, formats, n);
688     flags   = g_new(guint, n);
689     if (!formats || !flags)
690         goto end;
691     status = vaQuerySubpictureFormats(priv->display, formats, flags, (guint *)&n);
692     if (!vaapi_check_status(status, "vaQuerySubpictureFormats()"))
693         goto end;
694
695     GST_DEBUG("%d subpicture formats", n);
696     for (i = 0; i < n; i++)
697         GST_DEBUG("  %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS(formats[i].fourcc));
698
699     priv->subpicture_formats =
700         g_array_new(FALSE, FALSE, sizeof(GstVaapiImageFormat));
701     if (!priv->subpicture_formats)
702         goto end;
703     append_formats(priv->subpicture_formats, formats, n);
704     g_array_sort(priv->subpicture_formats, compare_rgb_formats);
705
706     if (!cached_info) {
707         if (!gst_vaapi_display_cache_add(cache, &info))
708             goto end;
709     }
710
711     has_errors = FALSE;
712 end:
713     g_free(display_attrs);
714     g_free(profiles);
715     g_free(entrypoints);
716     g_free(formats);
717     g_free(flags);
718     return !has_errors;
719 }
720
721 static void
722 gst_vaapi_display_lock_default(GstVaapiDisplay *display)
723 {
724     GstVaapiDisplayPrivate *priv = display->priv;
725
726     if (priv->parent)
727         priv = priv->parent->priv;
728     g_static_rec_mutex_lock(&priv->mutex);
729 }
730
731 static void
732 gst_vaapi_display_unlock_default(GstVaapiDisplay *display)
733 {
734     GstVaapiDisplayPrivate *priv = display->priv;
735
736     if (priv->parent)
737         priv = priv->parent->priv;
738     g_static_rec_mutex_unlock(&priv->mutex);
739 }
740
741 static void
742 gst_vaapi_display_finalize(GObject *object)
743 {
744     GstVaapiDisplay * const display = GST_VAAPI_DISPLAY(object);
745
746     gst_vaapi_display_destroy(display);
747
748     g_static_rec_mutex_free(&display->priv->mutex);
749
750     G_OBJECT_CLASS(gst_vaapi_display_parent_class)->finalize(object);
751 }
752
753 static void
754 gst_vaapi_display_set_property(
755     GObject      *object,
756     guint         prop_id,
757     const GValue *value,
758     GParamSpec   *pspec
759 )
760 {
761     GstVaapiDisplay * const display = GST_VAAPI_DISPLAY(object);
762
763     switch (prop_id) {
764     case PROP_DISPLAY:
765         display->priv->display = g_value_get_pointer(value);
766         break;
767     case PROP_DISPLAY_TYPE:
768         display->priv->display_type = g_value_get_enum(value);
769         break;
770     case PROP_RENDER_MODE:
771         gst_vaapi_display_set_render_mode(display, g_value_get_enum(value));
772         break;
773     case PROP_ROTATION:
774         gst_vaapi_display_set_rotation(display, g_value_get_enum(value));
775         break;
776     case PROP_HUE:
777     case PROP_SATURATION:
778     case PROP_BRIGHTNESS:
779     case PROP_CONTRAST:
780         set_color_balance(display, prop_id, g_value_get_float(value));
781         break;
782     default:
783         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
784         break;
785     }
786 }
787
788 static void
789 gst_vaapi_display_get_property(
790     GObject    *object,
791     guint       prop_id,
792     GValue     *value,
793     GParamSpec *pspec
794 )
795 {
796     GstVaapiDisplay * const display = GST_VAAPI_DISPLAY(object);
797
798     switch (prop_id) {
799     case PROP_DISPLAY:
800         g_value_set_pointer(value, gst_vaapi_display_get_display(display));
801         break;
802     case PROP_DISPLAY_TYPE:
803         g_value_set_enum(value, gst_vaapi_display_get_display_type(display));
804         break;
805     case PROP_WIDTH:
806         g_value_set_uint(value, gst_vaapi_display_get_width(display));
807         break;
808     case PROP_HEIGHT:
809         g_value_set_uint(value, gst_vaapi_display_get_height(display));
810         break;
811     case PROP_RENDER_MODE: {
812         GstVaapiRenderMode mode;
813         if (!gst_vaapi_display_get_render_mode(display, &mode))
814             mode = DEFAULT_RENDER_MODE;
815         g_value_set_enum(value, mode);
816         break;
817     }
818     case PROP_ROTATION:
819         g_value_set_enum(value, gst_vaapi_display_get_rotation(display));
820         break;
821     case PROP_HUE:
822     case PROP_SATURATION:
823     case PROP_BRIGHTNESS:
824     case PROP_CONTRAST: {
825         gfloat v;
826         if (!get_color_balance(display, prop_id, &v))
827             v = G_PARAM_SPEC_FLOAT(pspec)->default_value;
828         g_value_set_float(value, v);
829         break;
830     }
831     default:
832         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
833         break;
834     }
835 }
836
837 static void
838 gst_vaapi_display_constructed(GObject *object)
839 {
840     GstVaapiDisplay * const display = GST_VAAPI_DISPLAY(object);
841     GObjectClass *parent_class;
842
843     display->priv->create_display = display->priv->display == NULL;
844     if (!gst_vaapi_display_create(display))
845         gst_vaapi_display_destroy(display);
846
847     parent_class = G_OBJECT_CLASS(gst_vaapi_display_parent_class);
848     if (parent_class->constructed)
849         parent_class->constructed(object);
850 }
851
852 static void
853 gst_vaapi_display_class_init(GstVaapiDisplayClass *klass)
854 {
855     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
856     GstVaapiDisplayClass * const dpy_class = GST_VAAPI_DISPLAY_CLASS(klass);
857
858     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapi, "vaapi", 0, "VA-API helper");
859
860     g_type_class_add_private(klass, sizeof(GstVaapiDisplayPrivate));
861
862     object_class->finalize      = gst_vaapi_display_finalize;
863     object_class->set_property  = gst_vaapi_display_set_property;
864     object_class->get_property  = gst_vaapi_display_get_property;
865     object_class->constructed   = gst_vaapi_display_constructed;
866
867     dpy_class->lock             = gst_vaapi_display_lock_default;
868     dpy_class->unlock           = gst_vaapi_display_unlock_default;
869
870     g_properties[PROP_DISPLAY] =
871         g_param_spec_pointer("display",
872                              "VA display",
873                              "VA display",
874                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
875
876     g_properties[PROP_DISPLAY_TYPE] =
877         g_param_spec_enum("display-type",
878                           "VA display type",
879                           "VA display type",
880                           GST_VAAPI_TYPE_DISPLAY_TYPE,
881                           GST_VAAPI_DISPLAY_TYPE_ANY,
882                           G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
883
884     g_properties[PROP_WIDTH] =
885         g_param_spec_uint("width",
886                           "Width",
887                           "The display width",
888                           1, G_MAXUINT32, 1,
889                           G_PARAM_READABLE);
890
891     g_properties[PROP_HEIGHT] =
892         g_param_spec_uint("height",
893                           "height",
894                           "The display height",
895                           1, G_MAXUINT32, 1,
896                           G_PARAM_READABLE);
897
898     /**
899      * GstVaapiDisplay:render-mode:
900      *
901      * The VA display rendering mode, expressed as a #GstVaapiRenderMode.
902      */
903     g_properties[PROP_RENDER_MODE] =
904         g_param_spec_enum(GST_VAAPI_DISPLAY_PROP_RENDER_MODE,
905                           "render mode",
906                           "The display rendering mode",
907                           GST_VAAPI_TYPE_RENDER_MODE,
908                           DEFAULT_RENDER_MODE,
909                           G_PARAM_READWRITE);
910
911     /**
912      * GstVaapiDisplay:rotation:
913      *
914      * The VA display rotation mode, expressed as a #GstVaapiRotation.
915      */
916     g_properties[PROP_ROTATION] =
917         g_param_spec_enum(GST_VAAPI_DISPLAY_PROP_ROTATION,
918                           "rotation",
919                           "The display rotation mode",
920                           GST_VAAPI_TYPE_ROTATION,
921                           DEFAULT_ROTATION,
922                           G_PARAM_READWRITE);
923
924     /**
925      * GstVaapiDisplay:hue:
926      *
927      * The VA display hue, expressed as a float value. Range is -180.0
928      * to 180.0. Default value is 0.0 and represents no modification.
929      */
930     g_properties[PROP_HUE] =
931         g_param_spec_float(GST_VAAPI_DISPLAY_PROP_HUE,
932                            "hue",
933                            "The display hue value",
934                            -180.0, 180.0, 0.0,
935                            G_PARAM_READWRITE);
936
937     /**
938      * GstVaapiDisplay:saturation:
939      *
940      * The VA display saturation, expressed as a float value. Range is
941      * 0.0 to 2.0. Default value is 1.0 and represents no modification.
942      */
943     g_properties[PROP_SATURATION] =
944         g_param_spec_float(GST_VAAPI_DISPLAY_PROP_SATURATION,
945                            "saturation",
946                            "The display saturation value",
947                            0.0, 2.0, 1.0,
948                            G_PARAM_READWRITE);
949
950     /**
951      * GstVaapiDisplay:brightness:
952      *
953      * The VA display brightness, expressed as a float value. Range is
954      * -1.0 to 1.0. Default value is 0.0 and represents no modification.
955      */
956     g_properties[PROP_BRIGHTNESS] =
957         g_param_spec_float(GST_VAAPI_DISPLAY_PROP_BRIGHTNESS,
958                            "brightness",
959                            "The display brightness value",
960                            -1.0, 1.0, 0.0,
961                            G_PARAM_READWRITE);
962
963     /**
964      * GstVaapiDisplay:contrast:
965      *
966      * The VA display contrast, expressed as a float value. Range is
967      * 0.0 to 2.0. Default value is 1.0 and represents no modification.
968      */
969     g_properties[PROP_CONTRAST] =
970         g_param_spec_float(GST_VAAPI_DISPLAY_PROP_CONTRAST,
971                            "contrast",
972                            "The display contrast value",
973                            0.0, 2.0, 1.0,
974                            G_PARAM_READWRITE);
975
976     g_object_class_install_properties(object_class, N_PROPERTIES, g_properties);
977 }
978
979 static void
980 gst_vaapi_display_init(GstVaapiDisplay *display)
981 {
982     GstVaapiDisplayPrivate *priv = GST_VAAPI_DISPLAY_GET_PRIVATE(display);
983
984     display->priv               = priv;
985     priv->parent                = NULL;
986     priv->display_type          = GST_VAAPI_DISPLAY_TYPE_ANY;
987     priv->display               = NULL;
988     priv->width                 = 0;
989     priv->height                = 0;
990     priv->width_mm              = 0;
991     priv->height_mm             = 0;
992     priv->par_n                 = 1;
993     priv->par_d                 = 1;
994     priv->decoders              = NULL;
995     priv->encoders              = NULL;
996     priv->image_formats         = NULL;
997     priv->subpicture_formats    = NULL;
998     priv->properties            = NULL;
999     priv->create_display        = TRUE;
1000
1001     g_static_rec_mutex_init(&priv->mutex);
1002 }
1003
1004 /**
1005  * gst_vaapi_display_new_with_display:
1006  * @va_display: a #VADisplay
1007  *
1008  * Creates a new #GstVaapiDisplay, using @va_display as the VA
1009  * display.
1010  *
1011  * Return value: the newly created #GstVaapiDisplay object
1012  */
1013 GstVaapiDisplay *
1014 gst_vaapi_display_new_with_display(VADisplay va_display)
1015 {
1016     GstVaapiDisplayCache * const cache = get_display_cache();
1017     const GstVaapiDisplayInfo *info;
1018
1019     g_return_val_if_fail(va_display != NULL, NULL);
1020     g_return_val_if_fail(cache != NULL, NULL);
1021
1022     info = gst_vaapi_display_cache_lookup_by_va_display(cache, va_display);
1023     if (info)
1024         return g_object_ref(info->display);
1025
1026     return g_object_new(GST_VAAPI_TYPE_DISPLAY,
1027                         "display", va_display,
1028                         NULL);
1029 }
1030
1031 /**
1032  * gst_vaapi_display_lock:
1033  * @display: a #GstVaapiDisplay
1034  *
1035  * Locks @display. If @display is already locked by another thread,
1036  * the current thread will block until @display is unlocked by the
1037  * other thread.
1038  */
1039 void
1040 gst_vaapi_display_lock(GstVaapiDisplay *display)
1041 {
1042     GstVaapiDisplayClass *klass;
1043
1044     g_return_if_fail(GST_VAAPI_IS_DISPLAY(display));
1045
1046     klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
1047     if (klass->lock)
1048         klass->lock(display);
1049 }
1050
1051 /**
1052  * gst_vaapi_display_unlock:
1053  * @display: a #GstVaapiDisplay
1054  *
1055  * Unlocks @display. If another thread is blocked in a
1056  * gst_vaapi_display_lock() call for @display, it will be woken and
1057  * can lock @display itself.
1058  */
1059 void
1060 gst_vaapi_display_unlock(GstVaapiDisplay *display)
1061 {
1062     GstVaapiDisplayClass *klass;
1063
1064     g_return_if_fail(GST_VAAPI_IS_DISPLAY(display));
1065
1066     klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
1067     if (klass->unlock)
1068         klass->unlock(display);
1069 }
1070
1071 /**
1072  * gst_vaapi_display_sync:
1073  * @display: a #GstVaapiDisplay
1074  *
1075  * Flushes any requests queued for the windowing system and waits until
1076  * all requests have been handled. This is often used for making sure
1077  * that the display is synchronized with the current state of the program.
1078  *
1079  * This is most useful for X11. On windowing systems where requests are
1080  * handled synchronously, this function will do nothing.
1081  */
1082 void
1083 gst_vaapi_display_sync(GstVaapiDisplay *display)
1084 {
1085     GstVaapiDisplayClass *klass;
1086
1087     g_return_if_fail(GST_VAAPI_IS_DISPLAY(display));
1088
1089     klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
1090     if (klass->sync)
1091         klass->sync(display);
1092     else if (klass->flush)
1093         klass->flush(display);
1094 }
1095
1096 /**
1097  * gst_vaapi_display_flush:
1098  * @display: a #GstVaapiDisplay
1099  *
1100  * Flushes any requests queued for the windowing system.
1101  *
1102  * This is most useful for X11. On windowing systems where requests
1103  * are handled synchronously, this function will do nothing.
1104  */
1105 void
1106 gst_vaapi_display_flush(GstVaapiDisplay *display)
1107 {
1108     GstVaapiDisplayClass *klass;
1109
1110     g_return_if_fail(GST_VAAPI_IS_DISPLAY(display));
1111
1112     klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
1113     if (klass->flush)
1114         klass->flush(display);
1115 }
1116
1117 /**
1118  * gst_vaapi_display_get_display:
1119  * @display: a #GstVaapiDisplay
1120  *
1121  * Returns the #GstVaapiDisplayType bound to @display.
1122  *
1123  * Return value: the #GstVaapiDisplayType
1124  */
1125 GstVaapiDisplayType
1126 gst_vaapi_display_get_display_type(GstVaapiDisplay *display)
1127 {
1128     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display),
1129                          GST_VAAPI_DISPLAY_TYPE_ANY);
1130
1131     return display->priv->display_type;
1132 }
1133
1134 /**
1135  * gst_vaapi_display_get_display:
1136  * @display: a #GstVaapiDisplay
1137  *
1138  * Returns the #VADisplay bound to @display.
1139  *
1140  * Return value: the #VADisplay
1141  */
1142 VADisplay
1143 gst_vaapi_display_get_display(GstVaapiDisplay *display)
1144 {
1145     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
1146
1147     return display->priv->display;
1148 }
1149
1150 /**
1151  * gst_vaapi_display_get_width:
1152  * @display: a #GstVaapiDisplay
1153  *
1154  * Retrieves the width of a #GstVaapiDisplay.
1155  *
1156  * Return value: the width of the @display, in pixels
1157  */
1158 guint
1159 gst_vaapi_display_get_width(GstVaapiDisplay *display)
1160 {
1161     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), 0);
1162
1163     return display->priv->width;
1164 }
1165
1166 /**
1167  * gst_vaapi_display_get_height:
1168  * @display: a #GstVaapiDisplay
1169  *
1170  * Retrieves the height of a #GstVaapiDisplay
1171  *
1172  * Return value: the height of the @display, in pixels
1173  */
1174 guint
1175 gst_vaapi_display_get_height(GstVaapiDisplay *display)
1176 {
1177     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), 0);
1178
1179     return display->priv->height;
1180 }
1181
1182 /**
1183  * gst_vaapi_display_get_size:
1184  * @display: a #GstVaapiDisplay
1185  * @pwidth: return location for the width, or %NULL
1186  * @pheight: return location for the height, or %NULL
1187  *
1188  * Retrieves the dimensions of a #GstVaapiDisplay.
1189  */
1190 void
1191 gst_vaapi_display_get_size(GstVaapiDisplay *display, guint *pwidth, guint *pheight)
1192 {
1193     g_return_if_fail(GST_VAAPI_DISPLAY(display));
1194
1195     if (pwidth)
1196         *pwidth = display->priv->width;
1197
1198     if (pheight)
1199         *pheight = display->priv->height;
1200 }
1201
1202 /**
1203  * gst_vaapi_display_get_pixel_aspect_ratio:
1204  * @display: a #GstVaapiDisplay
1205  * @par_n: return location for the numerator of pixel aspect ratio, or %NULL
1206  * @par_d: return location for the denominator of pixel aspect ratio, or %NULL
1207  *
1208  * Retrieves the pixel aspect ratio of a #GstVaapiDisplay.
1209  */
1210 void
1211 gst_vaapi_display_get_pixel_aspect_ratio(
1212     GstVaapiDisplay *display,
1213     guint           *par_n,
1214     guint           *par_d
1215 )
1216 {
1217     g_return_if_fail(GST_VAAPI_IS_DISPLAY(display));
1218
1219     if (par_n)
1220         *par_n = display->priv->par_n;
1221
1222     if (par_d)
1223         *par_d = display->priv->par_d;
1224 }
1225
1226 /**
1227  * gst_vaapi_display_get_decode_caps:
1228  * @display: a #GstVaapiDisplay
1229  *
1230  * Gets the supported profiles for decoding as #GstCaps capabilities.
1231  *
1232  * Return value: a newly allocated #GstCaps object, possibly empty
1233  */
1234 GstCaps *
1235 gst_vaapi_display_get_decode_caps(GstVaapiDisplay *display)
1236 {
1237     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
1238
1239     return get_profile_caps(display->priv->decoders);
1240 }
1241
1242 /**
1243  * gst_vaapi_display_has_decoder:
1244  * @display: a #GstVaapiDisplay
1245  * @profile: a #VAProfile
1246  * @entrypoint: a #GstVaaiEntrypoint
1247  *
1248  * Returns whether VA @display supports @profile for decoding at the
1249  * specified @entrypoint.
1250  *
1251  * Return value: %TRUE if VA @display supports @profile for decoding.
1252  */
1253 gboolean
1254 gst_vaapi_display_has_decoder(
1255     GstVaapiDisplay    *display,
1256     GstVaapiProfile     profile,
1257     GstVaapiEntrypoint  entrypoint
1258 )
1259 {
1260     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1261
1262     return find_config(display->priv->decoders, profile, entrypoint);
1263 }
1264
1265 /**
1266  * gst_vaapi_display_get_encode_caps:
1267  * @display: a #GstVaapiDisplay
1268  *
1269  * Gets the supported profiles for decoding as #GstCaps capabilities.
1270  *
1271  * Return value: a newly allocated #GstCaps object, possibly empty
1272  */
1273 GstCaps *
1274 gst_vaapi_display_get_encode_caps(GstVaapiDisplay *display)
1275 {
1276     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
1277
1278     return get_profile_caps(display->priv->encoders);
1279 }
1280
1281 /**
1282  * gst_vaapi_display_has_encoder:
1283  * @display: a #GstVaapiDisplay
1284  * @profile: a #VAProfile
1285  * @entrypoint: a #GstVaapiEntrypoint
1286  *
1287  * Returns whether VA @display supports @profile for encoding at the
1288  * specified @entrypoint.
1289  *
1290  * Return value: %TRUE if VA @display supports @profile for encoding.
1291  */
1292 gboolean
1293 gst_vaapi_display_has_encoder(
1294     GstVaapiDisplay    *display,
1295     GstVaapiProfile     profile,
1296     GstVaapiEntrypoint  entrypoint
1297 )
1298 {
1299     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1300
1301     return find_config(display->priv->encoders, profile, entrypoint);
1302 }
1303
1304 /**
1305  * gst_vaapi_display_get_image_caps:
1306  * @display: a #GstVaapiDisplay
1307  *
1308  * Gets the supported image formats for gst_vaapi_surface_get_image()
1309  * or gst_vaapi_surface_put_image() as #GstCaps capabilities.
1310  *
1311  * Note that this method does not necessarily map image formats
1312  * returned by vaQueryImageFormats(). The set of capabilities can be
1313  * stripped down, if gstreamer-vaapi does not support the format, or
1314  * expanded to cover compatible formats not exposed by the underlying
1315  * driver. e.g. I420 can be supported even if the driver only exposes
1316  * YV12.
1317  *
1318  * Return value: a newly allocated #GstCaps object, possibly empty
1319  */
1320 GstCaps *
1321 gst_vaapi_display_get_image_caps(GstVaapiDisplay *display)
1322 {
1323     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
1324
1325     return get_format_caps(display->priv->image_formats);
1326 }
1327
1328 /**
1329  * gst_vaapi_display_has_image_format:
1330  * @display: a #GstVaapiDisplay
1331  * @format: a #GstVaapiFormat
1332  *
1333  * Returns whether VA @display supports @format image format.
1334  *
1335  * Return value: %TRUE if VA @display supports @format image format
1336  */
1337 gboolean
1338 gst_vaapi_display_has_image_format(
1339     GstVaapiDisplay    *display,
1340     GstVaapiImageFormat format
1341 )
1342 {
1343     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1344     g_return_val_if_fail(format, FALSE);
1345
1346     if (find_format(display->priv->image_formats, format))
1347         return TRUE;
1348
1349     /* XXX: try subpicture formats since some drivers could report a
1350      * set of VA image formats that is not a superset of the set of VA
1351      * subpicture formats
1352      */
1353     return find_format(display->priv->subpicture_formats, format);
1354 }
1355
1356 /**
1357  * gst_vaapi_display_get_subpicture_caps:
1358  * @display: a #GstVaapiDisplay
1359  *
1360  * Gets the supported subpicture formats as #GstCaps capabilities.
1361  *
1362  * Note that this method does not necessarily map subpicture formats
1363  * returned by vaQuerySubpictureFormats(). The set of capabilities can
1364  * be stripped down if gstreamer-vaapi does not support the
1365  * format. e.g. this is the case for paletted formats like IA44.
1366  *
1367  * Return value: a newly allocated #GstCaps object, possibly empty
1368  */
1369 GstCaps *
1370 gst_vaapi_display_get_subpicture_caps(GstVaapiDisplay *display)
1371 {
1372     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
1373
1374     return get_format_caps(display->priv->subpicture_formats);
1375 }
1376
1377 /**
1378  * gst_vaapi_display_has_subpicture_format:
1379  * @display: a #GstVaapiDisplay
1380  * @format: a #GstVaapiFormat
1381  *
1382  * Returns whether VA @display supports @format subpicture format.
1383  *
1384  * Return value: %TRUE if VA @display supports @format subpicture format
1385  */
1386 gboolean
1387 gst_vaapi_display_has_subpicture_format(
1388     GstVaapiDisplay    *display,
1389     GstVaapiImageFormat format
1390 )
1391 {
1392     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1393     g_return_val_if_fail(format, FALSE);
1394
1395     return find_format(display->priv->subpicture_formats, format);
1396 }
1397
1398 /**
1399  * gst_vaapi_display_has_property:
1400  * @display: a #GstVaapiDisplay
1401  * @name: the property name to check
1402  *
1403  * Returns whether VA @display supports the requested property. The
1404  * check is performed against the property @name. So, the client
1405  * application may perform this check only once and cache this
1406  * information.
1407  *
1408  * Return value: %TRUE if VA @display supports property @name
1409  */
1410 gboolean
1411 gst_vaapi_display_has_property(GstVaapiDisplay *display, const gchar *name)
1412 {
1413     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1414     g_return_val_if_fail(name, FALSE);
1415
1416     return find_property(display->priv->properties, name) != NULL;
1417 }
1418
1419 static gboolean
1420 get_attribute(GstVaapiDisplay *display, VADisplayAttribType type, gint *value)
1421 {
1422     VADisplayAttribute attr;
1423     VAStatus status;
1424
1425     attr.type  = type;
1426     attr.flags = VA_DISPLAY_ATTRIB_GETTABLE;
1427     status = vaGetDisplayAttributes(display->priv->display, &attr, 1);
1428     if (!vaapi_check_status(status, "vaGetDisplayAttributes()"))
1429         return FALSE;
1430     *value = attr.value;
1431     return TRUE;
1432 }
1433
1434 static gboolean
1435 set_attribute(GstVaapiDisplay *display, VADisplayAttribType type, gint value)
1436 {
1437     VADisplayAttribute attr;
1438     VAStatus status;
1439
1440     attr.type  = type;
1441     attr.value = value;
1442     attr.flags = VA_DISPLAY_ATTRIB_SETTABLE;
1443     status = vaSetDisplayAttributes(display->priv->display, &attr, 1);
1444     if (!vaapi_check_status(status, "vaSetDisplayAttributes()"))
1445         return FALSE;
1446     return TRUE;
1447 }
1448
1449 static gboolean
1450 get_render_mode_VADisplayAttribRenderMode(
1451     GstVaapiDisplay    *display,
1452     GstVaapiRenderMode *pmode
1453 )
1454 {
1455     gint modes, devices;
1456
1457     if (!get_attribute(display, VADisplayAttribRenderDevice, &devices))
1458         return FALSE;
1459     if (!devices)
1460         return FALSE;
1461     if (!get_attribute(display, VADisplayAttribRenderMode, &modes))
1462         return FALSE;
1463
1464     /* Favor "overlay" mode since it is the most restrictive one */
1465     if (modes & (VA_RENDER_MODE_LOCAL_OVERLAY|VA_RENDER_MODE_EXTERNAL_OVERLAY))
1466         *pmode = GST_VAAPI_RENDER_MODE_OVERLAY;
1467     else
1468         *pmode = GST_VAAPI_RENDER_MODE_TEXTURE;
1469     return TRUE;
1470 }
1471
1472 static gboolean
1473 get_render_mode_VADisplayAttribDirectSurface(
1474     GstVaapiDisplay    *display,
1475     GstVaapiRenderMode *pmode
1476 )
1477 {
1478 #if VA_CHECK_VERSION(0,34,0)
1479     /* VADisplayAttribDirectsurface was removed in VA-API >= 0.34.0 */
1480     return FALSE;
1481 #else
1482     gint direct_surface;
1483
1484     if (!get_attribute(display, VADisplayAttribDirectSurface, &direct_surface))
1485         return FALSE;
1486     if (direct_surface)
1487         *pmode = GST_VAAPI_RENDER_MODE_OVERLAY;
1488     else
1489         *pmode = GST_VAAPI_RENDER_MODE_TEXTURE;
1490     return TRUE;
1491 #endif
1492 }
1493
1494 static gboolean
1495 get_render_mode_default(
1496     GstVaapiDisplay    *display,
1497     GstVaapiRenderMode *pmode
1498 )
1499 {
1500     switch (display->priv->display_type) {
1501 #if USE_WAYLAND
1502     case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
1503         /* wl_buffer mapped from VA surface through vaGetSurfaceBufferWl() */
1504         *pmode = GST_VAAPI_RENDER_MODE_OVERLAY;
1505         break;
1506 #endif
1507 #if USE_DRM
1508     case GST_VAAPI_DISPLAY_TYPE_DRM:
1509         /* vaGetSurfaceBufferDRM() returns the underlying DRM buffer handle */
1510         *pmode = GST_VAAPI_RENDER_MODE_OVERLAY;
1511         break;
1512 #endif
1513     default:
1514         /* This includes VA/X11 and VA/GLX modes */
1515         *pmode = DEFAULT_RENDER_MODE;
1516         break;
1517     }
1518     return TRUE;
1519 }
1520
1521 /**
1522  * gst_vaapi_display_get_render_mode:
1523  * @display: a #GstVaapiDisplay
1524  * @pmode: return location for the VA @display rendering mode
1525  *
1526  * Returns the current VA @display rendering mode.
1527  *
1528  * Return value: %TRUE if VA @display rendering mode could be determined
1529  */
1530 gboolean
1531 gst_vaapi_display_get_render_mode(
1532     GstVaapiDisplay    *display,
1533     GstVaapiRenderMode *pmode
1534 )
1535 {
1536     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1537
1538     /* Try with render-mode attribute */
1539     if (get_render_mode_VADisplayAttribRenderMode(display, pmode))
1540         return TRUE;
1541
1542     /* Try with direct-surface attribute */
1543     if (get_render_mode_VADisplayAttribDirectSurface(display, pmode))
1544         return TRUE;
1545
1546     /* Default: determine from the display type */
1547     return get_render_mode_default(display, pmode);
1548 }
1549
1550 /**
1551  * gst_vaapi_display_set_render_mode:
1552  * @display: a #GstVaapiDisplay
1553  * @mode: the #GstVaapiRenderMode to set
1554  *
1555  * Sets the VA @display rendering mode to the supplied @mode. This
1556  * function returns %FALSE if the rendering mode could not be set,
1557  * e.g. run-time switching rendering mode is not supported.
1558  *
1559  * Return value: %TRUE if VA @display rendering @mode could be changed
1560  *   to the requested value
1561  */
1562 gboolean
1563 gst_vaapi_display_set_render_mode(
1564     GstVaapiDisplay   *display,
1565     GstVaapiRenderMode mode
1566 )
1567 {
1568     gint modes, devices;
1569
1570     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1571
1572     if (!get_attribute(display, VADisplayAttribRenderDevice, &devices))
1573         return FALSE;
1574
1575     modes = 0;
1576     switch (mode) {
1577     case GST_VAAPI_RENDER_MODE_OVERLAY:
1578         if (devices & VA_RENDER_DEVICE_LOCAL)
1579             modes |= VA_RENDER_MODE_LOCAL_OVERLAY;
1580         if (devices & VA_RENDER_DEVICE_EXTERNAL)
1581             modes |= VA_RENDER_MODE_EXTERNAL_OVERLAY;
1582         break;
1583     case GST_VAAPI_RENDER_MODE_TEXTURE:
1584         if (devices & VA_RENDER_DEVICE_LOCAL)
1585             modes |= VA_RENDER_MODE_LOCAL_GPU;
1586         if (devices & VA_RENDER_DEVICE_EXTERNAL)
1587             modes |= VA_RENDER_MODE_EXTERNAL_GPU;
1588         break;
1589     }
1590     if (!modes)
1591         return FALSE;
1592     if (!set_attribute(display, VADisplayAttribRenderMode, modes))
1593         return FALSE;
1594
1595     g_object_notify_by_pspec(G_OBJECT(display), g_properties[PROP_RENDER_MODE]);
1596     return TRUE;
1597 }
1598
1599 /**
1600  * gst_vaapi_display_get_rotation:
1601  * @display: a #GstVaapiDisplay
1602  *
1603  * Returns the current VA @display rotation angle. If the VA driver
1604  * does not support "rotation" display attribute, then the display is
1605  * assumed to be un-rotated.
1606  *
1607  * Return value: the current #GstVaapiRotation value
1608  */
1609 GstVaapiRotation
1610 gst_vaapi_display_get_rotation(GstVaapiDisplay *display)
1611 {
1612     gint value;
1613
1614     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), DEFAULT_ROTATION);
1615
1616     if (!get_attribute(display, VADisplayAttribRotation, &value))
1617         value = VA_ROTATION_NONE;
1618     return to_GstVaapiRotation(value);
1619 }
1620
1621 /**
1622  * gst_vaapi_display_set_rotation:
1623  * @display: a #GstVaapiDisplay
1624  * @rotation: the #GstVaapiRotation value to set
1625  *
1626  * Sets the VA @display rotation angle to the supplied @rotation
1627  * value. This function returns %FALSE if the rotation angle could not
1628  * be set, e.g. the VA driver does not allow to change the display
1629  * rotation angle.
1630  *
1631  * Return value: %TRUE if VA @display rotation angle could be changed
1632  *   to the requested value
1633  */
1634 gboolean
1635 gst_vaapi_display_set_rotation(
1636     GstVaapiDisplay *display,
1637     GstVaapiRotation rotation
1638 )
1639 {
1640     guint value;
1641
1642     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
1643
1644     value = from_GstVaapiRotation(rotation);
1645     if (!set_attribute(display, VADisplayAttribRotation, value))
1646         return FALSE;
1647
1648     g_object_notify_by_pspec(G_OBJECT(display), g_properties[PROP_ROTATION]);
1649     return TRUE;
1650 }
1651
1652 /* Get color balance attributes */
1653 static gboolean
1654 get_color_balance(GstVaapiDisplay *display, guint prop_id, gfloat *v)
1655 {
1656     GParamSpecFloat * const pspec = G_PARAM_SPEC_FLOAT(g_properties[prop_id]);
1657     const GstVaapiProperty *prop;
1658     const VADisplayAttribute *attr;
1659     gfloat out_value;
1660     gint value;
1661
1662     if (!pspec)
1663         return FALSE;
1664
1665     prop = find_property_by_pspec(display, &pspec->parent_instance);
1666     if (!prop)
1667         return FALSE;
1668     attr = &prop->attribute;
1669
1670     if (!get_attribute(display, attr->type, &value))
1671         return FALSE;
1672
1673     /* Scale wrt. the medium ("default") value */
1674     out_value = pspec->default_value;
1675     if (value > attr->value)
1676         out_value += ((gfloat)(value - attr->value) /
1677                       (attr->max_value - attr->value) *
1678                       (pspec->maximum - pspec->default_value));
1679     else if (value < attr->value)
1680         out_value -= ((gfloat)(attr->value - value) /
1681                       (attr->value - attr->min_value) *
1682                       (pspec->default_value - pspec->minimum));
1683     *v = out_value;
1684     return TRUE;
1685 }
1686
1687 /* Set color balance attribute */
1688 static gboolean
1689 set_color_balance(GstVaapiDisplay *display, guint prop_id, gfloat v)
1690 {
1691     GParamSpecFloat * const pspec = G_PARAM_SPEC_FLOAT(g_properties[prop_id]);
1692     const GstVaapiProperty *prop;
1693     const VADisplayAttribute *attr;
1694     gint value;
1695
1696     if (!pspec)
1697         return FALSE;
1698
1699     prop = find_property_by_pspec(display, &pspec->parent_instance);
1700     if (!prop)
1701         return FALSE;
1702     attr = &prop->attribute;
1703
1704     /* Scale wrt. the medium ("default") value */
1705     value = attr->value;
1706     if (v > pspec->default_value)
1707         value += ((v - pspec->default_value) /
1708                   (pspec->maximum - pspec->default_value) *
1709                   (attr->max_value - attr->value));
1710     else if (v < pspec->default_value)
1711         value -= ((pspec->default_value - v) /
1712                   (pspec->default_value - pspec->minimum) *
1713                   (attr->value - attr->min_value));
1714     if (!set_attribute(display, attr->type, value))
1715         return FALSE;
1716
1717     g_object_notify_by_pspec(G_OBJECT(display), g_properties[prop_id]);
1718     return TRUE;
1719 }