64f02455d5c6148d73756f8a063c70f65935da8b
[platform/upstream/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  *    Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
6  *  Copyright (C) 2011-2013 Intel Corporation
7  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public License
11  *  as published by the Free Software Foundation; either version 2.1
12  *  of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free
21  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  *  Boston, MA 02110-1301 USA
23  */
24
25 /**
26  * SECTION:gstvaapidisplay
27  * @short_description: VA display abstraction
28  */
29
30 #include "sysdeps.h"
31 #include <string.h>
32 #include "gstvaapiutils.h"
33 #include "gstvaapivalue.h"
34 #include "gstvaapidisplay.h"
35 #include "gstvaapidisplay_priv.h"
36 #include "gstvaapiworkarounds.h"
37 #include "gstvaapiversion.h"
38
39 #define DEBUG 1
40 #include "gstvaapidebug.h"
41
42 GST_DEBUG_CATEGORY (gst_debug_vaapi);
43
44 /* Ensure those symbols are actually defined in the resulting libraries */
45 #undef gst_vaapi_display_ref
46 #undef gst_vaapi_display_unref
47 #undef gst_vaapi_display_replace
48
49 typedef struct _GstVaapiConfig GstVaapiConfig;
50 struct _GstVaapiConfig
51 {
52   GstVaapiProfile profile;
53   GstVaapiEntrypoint entrypoint;
54 };
55
56 typedef struct _GstVaapiProperty GstVaapiProperty;
57 struct _GstVaapiProperty
58 {
59   const gchar *name;
60   VADisplayAttribute attribute;
61   gint old_value;
62 };
63
64 typedef struct _GstVaapiFormatInfo GstVaapiFormatInfo;
65 struct _GstVaapiFormatInfo
66 {
67   GstVideoFormat format;
68   guint flags;
69 };
70
71 #define DEFAULT_RENDER_MODE     GST_VAAPI_RENDER_MODE_TEXTURE
72 #define DEFAULT_ROTATION        GST_VAAPI_ROTATION_0
73
74 enum
75 {
76   PROP_0,
77
78   PROP_RENDER_MODE,
79   PROP_ROTATION,
80   PROP_HUE,
81   PROP_SATURATION,
82   PROP_BRIGHTNESS,
83   PROP_CONTRAST,
84
85   N_PROPERTIES
86 };
87
88 static GstVaapiDisplayCache *g_display_cache;
89 static GMutex g_display_cache_lock;
90
91 static GParamSpec *g_properties[N_PROPERTIES] = { NULL, };
92
93 static void gst_vaapi_display_properties_init (void);
94
95 static gboolean
96 get_attribute (GstVaapiDisplay * display, VADisplayAttribType type,
97     gint * value);
98
99 static gboolean
100 set_attribute (GstVaapiDisplay * display, VADisplayAttribType type, gint value);
101
102 static gboolean
103 get_color_balance (GstVaapiDisplay * display, guint prop_id, gfloat * v);
104
105 static gboolean
106 set_color_balance (GstVaapiDisplay * display, guint prop_id, gfloat v);
107
108 static void
109 libgstvaapi_init_once (void)
110 {
111   static gsize g_once = FALSE;
112
113   if (!g_once_init_enter (&g_once))
114     return;
115
116   GST_DEBUG_CATEGORY_INIT (gst_debug_vaapi, "vaapi", 0, "VA-API helper");
117
118   /* Dump gstreamer-vaapi version for debugging purposes */
119   GST_INFO ("gstreamer-vaapi version %s", GST_VAAPI_VERSION_ID);
120
121   gst_vaapi_display_properties_init ();
122
123   g_once_init_leave (&g_once, TRUE);
124 }
125
126 static GstVaapiDisplayCache *
127 get_display_cache (void)
128 {
129   GstVaapiDisplayCache *cache = NULL;
130
131   g_mutex_lock (&g_display_cache_lock);
132   if (!g_display_cache)
133     g_display_cache = gst_vaapi_display_cache_new ();
134   if (g_display_cache)
135     cache = gst_vaapi_display_cache_ref (g_display_cache);
136   g_mutex_unlock (&g_display_cache_lock);
137   return cache;
138 }
139
140 static void
141 free_display_cache (void)
142 {
143   g_mutex_lock (&g_display_cache_lock);
144   if (g_display_cache && gst_vaapi_display_cache_is_empty (g_display_cache))
145     gst_vaapi_display_cache_replace (&g_display_cache, NULL);
146   g_mutex_unlock (&g_display_cache_lock);
147 }
148
149 /* GstVaapiDisplayType enumerations */
150 GType
151 gst_vaapi_display_type_get_type (void)
152 {
153   static GType g_type = 0;
154
155   static const GEnumValue display_types[] = {
156     {GST_VAAPI_DISPLAY_TYPE_ANY,
157         "Auto detection", "any"},
158 #if USE_X11
159     {GST_VAAPI_DISPLAY_TYPE_X11,
160         "VA/X11 display", "x11"},
161 #endif
162 #if USE_GLX
163     {GST_VAAPI_DISPLAY_TYPE_GLX,
164         "VA/GLX display", "glx"},
165 #endif
166 #if USE_WAYLAND
167     {GST_VAAPI_DISPLAY_TYPE_WAYLAND,
168         "VA/Wayland display", "wayland"},
169 #endif
170 #if USE_DRM
171     {GST_VAAPI_DISPLAY_TYPE_DRM,
172         "VA/DRM display", "drm"},
173 #endif
174     {0, NULL, NULL},
175   };
176
177   if (!g_type)
178     g_type = g_enum_register_static ("GstVaapiDisplayType", display_types);
179   return g_type;
180 }
181
182 /**
183  * gst_vaapi_display_type_is_compatible:
184  * @type1: the #GstVaapiDisplayType to test
185  * @type2: the reference #GstVaapiDisplayType
186  *
187  * Compares whether #GstVaapiDisplay @type1 is compatible with @type2.
188  * That is, if @type2 is in "any" category, or derived from @type1.
189  *
190  * Returns: %TRUE if @type1 is compatible with @type2, %FALSE otherwise.
191  */
192 gboolean
193 gst_vaapi_display_type_is_compatible (GstVaapiDisplayType type1,
194     GstVaapiDisplayType type2)
195 {
196   if (type1 == type2)
197     return TRUE;
198
199   switch (type1) {
200     case GST_VAAPI_DISPLAY_TYPE_GLX:
201       if (type2 == GST_VAAPI_DISPLAY_TYPE_X11)
202         return TRUE;
203       break;
204     default:
205       break;
206   }
207   return type2 == GST_VAAPI_DISPLAY_TYPE_ANY;
208 }
209
210 /* Append GstVideoFormat to formats array */
211 static inline void
212 append_format (GArray * formats, GstVideoFormat format, guint flags)
213 {
214   GstVaapiFormatInfo fi;
215
216   fi.format = format;
217   fi.flags = flags;
218   g_array_append_val (formats, fi);
219 }
220
221 /* Append VAImageFormats to formats array */
222 static void
223 append_formats (GArray * formats, const VAImageFormat * va_formats,
224     guint * flags, guint n)
225 {
226   GstVideoFormat format;
227   const GstVaapiFormatInfo *YV12_fip = NULL;
228   const GstVaapiFormatInfo *I420_fip = NULL;
229   guint i;
230
231   for (i = 0; i < n; i++) {
232     const VAImageFormat *const va_format = &va_formats[i];
233     const GstVaapiFormatInfo **fipp;
234
235     format = gst_vaapi_video_format_from_va_format (va_format);
236     if (format == GST_VIDEO_FORMAT_UNKNOWN) {
237       GST_DEBUG ("unsupported format %" GST_FOURCC_FORMAT,
238           GST_FOURCC_ARGS (va_format->fourcc));
239       continue;
240     }
241     append_format (formats, format, flags ? flags[i] : 0);
242
243     switch (format) {
244       case GST_VIDEO_FORMAT_YV12:
245         fipp = &YV12_fip;
246         break;
247       case GST_VIDEO_FORMAT_I420:
248         fipp = &I420_fip;
249         break;
250       default:
251         fipp = NULL;
252         break;
253     }
254     if (fipp)
255       *fipp = &g_array_index (formats, GstVaapiFormatInfo, formats->len - 1);
256   }
257
258   /* Append I420 (resp. YV12) format if YV12 (resp. I420) is not
259      supported by the underlying driver */
260   if (YV12_fip && !I420_fip)
261     append_format (formats, GST_VIDEO_FORMAT_I420, YV12_fip->flags);
262   else if (I420_fip && !YV12_fip)
263     append_format (formats, GST_VIDEO_FORMAT_YV12, I420_fip->flags);
264 }
265
266 /* Sort image formats. Prefer YUV formats first */
267 static gint
268 compare_yuv_formats (gconstpointer a, gconstpointer b)
269 {
270   const GstVideoFormat fmt1 = ((GstVaapiFormatInfo *) a)->format;
271   const GstVideoFormat fmt2 = ((GstVaapiFormatInfo *) b)->format;
272
273   const gboolean is_fmt1_yuv = gst_vaapi_video_format_is_yuv (fmt1);
274   const gboolean is_fmt2_yuv = gst_vaapi_video_format_is_yuv (fmt2);
275
276   if (is_fmt1_yuv != is_fmt2_yuv)
277     return is_fmt1_yuv ? -1 : 1;
278
279   return ((gint) gst_vaapi_video_format_get_score (fmt1) -
280       (gint) gst_vaapi_video_format_get_score (fmt2));
281 }
282
283 /* Sort subpicture formats. Prefer RGB formats first */
284 static gint
285 compare_rgb_formats (gconstpointer a, gconstpointer b)
286 {
287   const GstVideoFormat fmt1 = ((GstVaapiFormatInfo *) a)->format;
288   const GstVideoFormat fmt2 = ((GstVaapiFormatInfo *) b)->format;
289
290   const gboolean is_fmt1_rgb = gst_vaapi_video_format_is_rgb (fmt1);
291   const gboolean is_fmt2_rgb = gst_vaapi_video_format_is_rgb (fmt2);
292
293   if (is_fmt1_rgb != is_fmt2_rgb)
294     return is_fmt1_rgb ? -1 : 1;
295
296   return ((gint) gst_vaapi_video_format_get_score (fmt1) -
297       (gint) gst_vaapi_video_format_get_score (fmt2));
298 }
299
300 /* Check if configs array contains profile at entrypoint */
301 static inline gboolean
302 find_config (GArray * configs,
303     GstVaapiProfile profile, GstVaapiEntrypoint entrypoint)
304 {
305   GstVaapiConfig *config;
306   guint i;
307
308   if (!configs)
309     return FALSE;
310
311   for (i = 0; i < configs->len; i++) {
312     config = &g_array_index (configs, GstVaapiConfig, i);
313     if (config->profile == profile && config->entrypoint == entrypoint)
314       return TRUE;
315   }
316   return FALSE;
317 }
318
319 /* HACK: append H.263 Baseline profile if MPEG-4:2 Simple profile is supported */
320 static void
321 append_h263_config (GArray * configs)
322 {
323   GstVaapiConfig *config, tmp_config;
324   GstVaapiConfig *mpeg4_simple_config = NULL;
325   GstVaapiConfig *h263_baseline_config = NULL;
326   guint i;
327
328   if (!WORKAROUND_H263_BASELINE_DECODE_PROFILE)
329     return;
330
331   if (!configs)
332     return;
333
334   for (i = 0; i < configs->len; i++) {
335     config = &g_array_index (configs, GstVaapiConfig, i);
336     if (config->profile == GST_VAAPI_PROFILE_MPEG4_SIMPLE)
337       mpeg4_simple_config = config;
338     else if (config->profile == GST_VAAPI_PROFILE_H263_BASELINE)
339       h263_baseline_config = config;
340   }
341
342   if (mpeg4_simple_config && !h263_baseline_config) {
343     tmp_config = *mpeg4_simple_config;
344     tmp_config.profile = GST_VAAPI_PROFILE_H263_BASELINE;
345     g_array_append_val (configs, tmp_config);
346   }
347 }
348
349 /* Sort profiles. Group per codec */
350 static gint
351 compare_profiles (gconstpointer a, gconstpointer b)
352 {
353   const GstVaapiConfig *const config1 = (GstVaapiConfig *) a;
354   const GstVaapiConfig *const config2 = (GstVaapiConfig *) b;
355
356   if (config1->profile == config2->profile)
357     return config1->entrypoint - config2->entrypoint;
358
359   return config1->profile - config2->profile;
360 }
361
362 /* Convert configs array to profiles as GstCaps */
363 static GArray *
364 get_profiles (GArray * configs)
365 {
366   GstVaapiConfig *config;
367   GArray *out_profiles;
368   guint i;
369
370   if (!configs)
371     return NULL;
372
373   out_profiles = g_array_new (FALSE, FALSE, sizeof (GstVaapiProfile));
374   if (!out_profiles)
375     return NULL;
376
377   for (i = 0; i < configs->len; i++) {
378     config = &g_array_index (configs, GstVaapiConfig, i);
379     g_array_append_val (out_profiles, config->profile);
380   }
381   return out_profiles;
382 }
383
384 /* Find format info */
385 static const GstVaapiFormatInfo *
386 find_format_info (GArray * formats, GstVideoFormat format)
387 {
388   const GstVaapiFormatInfo *fip;
389   guint i;
390
391   for (i = 0; i < formats->len; i++) {
392     fip = &g_array_index (formats, GstVaapiFormatInfo, i);
393     if (fip->format == format)
394       return fip;
395   }
396   return NULL;
397 }
398
399 /* Check if formats array contains format */
400 static inline gboolean
401 find_format (GArray * formats, GstVideoFormat format)
402 {
403   return find_format_info (formats, format) != NULL;
404 }
405
406 /* Convert formats array to GstCaps */
407 static GArray *
408 get_formats (GArray * formats)
409 {
410   const GstVaapiFormatInfo *fip;
411   GArray *out_formats;
412   guint i;
413
414   out_formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat));
415   if (!out_formats)
416     return NULL;
417
418   for (i = 0; i < formats->len; i++) {
419     fip = &g_array_index (formats, GstVaapiFormatInfo, i);
420     g_array_append_val (out_formats, fip->format);
421   }
422   return out_formats;
423 }
424
425 /* Find display attribute */
426 static const GstVaapiProperty *
427 find_property (GArray * properties, const gchar * name)
428 {
429   GstVaapiProperty *prop;
430   guint i;
431
432   if (!name)
433     return NULL;
434
435   for (i = 0; i < properties->len; i++) {
436     prop = &g_array_index (properties, GstVaapiProperty, i);
437     if (strcmp (prop->name, name) == 0)
438       return prop;
439   }
440   return NULL;
441 }
442
443 #if 0
444 static const GstVaapiProperty *
445 find_property_by_type (GArray * properties, VADisplayAttribType type)
446 {
447   GstVaapiProperty *prop;
448   guint i;
449
450   for (i = 0; i < properties->len; i++) {
451     prop = &g_array_index (properties, GstVaapiProperty, i);
452     if (prop->attribute.type == type)
453       return prop;
454   }
455   return NULL;
456 }
457 #endif
458
459 static inline const GstVaapiProperty *
460 find_property_by_pspec (GstVaapiDisplay * display, GParamSpec * pspec)
461 {
462   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
463
464   return find_property (priv->properties, pspec->name);
465 }
466
467 static guint
468 find_property_id (const gchar * name)
469 {
470   typedef struct
471   {
472     const gchar *name;
473     guint id;
474   } property_map;
475
476   static const property_map g_property_map[] = {
477     {GST_VAAPI_DISPLAY_PROP_RENDER_MODE, PROP_RENDER_MODE},
478     {GST_VAAPI_DISPLAY_PROP_ROTATION, PROP_ROTATION},
479     {GST_VAAPI_DISPLAY_PROP_HUE, PROP_HUE},
480     {GST_VAAPI_DISPLAY_PROP_SATURATION, PROP_SATURATION},
481     {GST_VAAPI_DISPLAY_PROP_BRIGHTNESS, PROP_BRIGHTNESS},
482     {GST_VAAPI_DISPLAY_PROP_CONTRAST, PROP_CONTRAST},
483     {NULL,}
484   };
485
486   const property_map *m;
487   for (m = g_property_map; m->name != NULL; m++) {
488     if (strcmp (m->name, name) == 0)
489       return m->id;
490   }
491   return 0;
492 }
493
494 /* Initialize VA profiles (decoders, encoders) */
495 static gboolean
496 ensure_profiles (GstVaapiDisplay * display)
497 {
498   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
499   VAProfile *profiles = NULL;
500   VAEntrypoint *entrypoints = NULL;
501   gint i, j, n, num_entrypoints;
502   VAStatus status;
503   gboolean success = FALSE;
504
505   if (priv->has_profiles)
506     return TRUE;
507
508   priv->decoders = g_array_new (FALSE, FALSE, sizeof (GstVaapiConfig));
509   if (!priv->decoders)
510     goto cleanup;
511   priv->encoders = g_array_new (FALSE, FALSE, sizeof (GstVaapiConfig));
512   if (!priv->encoders)
513     goto cleanup;
514   priv->has_profiles = TRUE;
515
516   /* VA profiles */
517   profiles = g_new (VAProfile, vaMaxNumProfiles (priv->display));
518   if (!profiles)
519     goto cleanup;
520   entrypoints = g_new (VAEntrypoint, vaMaxNumEntrypoints (priv->display));
521   if (!entrypoints)
522     goto cleanup;
523
524   n = 0;
525   status = vaQueryConfigProfiles (priv->display, profiles, &n);
526   if (!vaapi_check_status (status, "vaQueryConfigProfiles()"))
527     goto cleanup;
528
529   GST_DEBUG ("%d profiles", n);
530   for (i = 0; i < n; i++) {
531 #if VA_CHECK_VERSION(0,34,0)
532     /* Introduced in VA/VPP API */
533     if (profiles[i] == VAProfileNone)
534       continue;
535 #endif
536     GST_DEBUG ("  %s", string_of_VAProfile (profiles[i]));
537   }
538
539   for (i = 0; i < n; i++) {
540     GstVaapiConfig config;
541
542     config.profile = gst_vaapi_profile (profiles[i]);
543     if (!config.profile)
544       continue;
545
546     status = vaQueryConfigEntrypoints (priv->display,
547         profiles[i], entrypoints, &num_entrypoints);
548     if (!vaapi_check_status (status, "vaQueryConfigEntrypoints()"))
549       continue;
550
551     for (j = 0; j < num_entrypoints; j++) {
552       config.entrypoint = gst_vaapi_entrypoint (entrypoints[j]);
553       switch (config.entrypoint) {
554         case GST_VAAPI_ENTRYPOINT_VLD:
555         case GST_VAAPI_ENTRYPOINT_IDCT:
556         case GST_VAAPI_ENTRYPOINT_MOCO:
557           g_array_append_val (priv->decoders, config);
558           break;
559         case GST_VAAPI_ENTRYPOINT_SLICE_ENCODE:
560           g_array_append_val (priv->encoders, config);
561           break;
562       }
563     }
564   }
565   append_h263_config (priv->decoders);
566
567   g_array_sort (priv->decoders, compare_profiles);
568   g_array_sort (priv->encoders, compare_profiles);
569
570   /* Video processing API */
571 #if USE_VA_VPP
572   status = vaQueryConfigEntrypoints (priv->display, VAProfileNone,
573       entrypoints, &num_entrypoints);
574   if (vaapi_check_status (status, "vaQueryEntrypoints() [VAProfileNone]")) {
575     for (j = 0; j < num_entrypoints; j++) {
576       if (entrypoints[j] == VAEntrypointVideoProc)
577         priv->has_vpp = TRUE;
578     }
579   }
580 #endif
581   success = TRUE;
582
583 cleanup:
584   g_free (profiles);
585   g_free (entrypoints);
586   return success;
587 }
588
589 /* Initialize VA display attributes */
590 static gboolean
591 ensure_properties (GstVaapiDisplay * display)
592 {
593   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
594   VADisplayAttribute *display_attrs = NULL;
595   VAStatus status;
596   gint i, n;
597   gboolean success = FALSE;
598
599   if (priv->properties)
600     return TRUE;
601
602   priv->properties = g_array_new (FALSE, FALSE, sizeof (GstVaapiProperty));
603   if (!priv->properties)
604     goto cleanup;
605
606   /* VA display attributes */
607   display_attrs =
608       g_new (VADisplayAttribute, vaMaxNumDisplayAttributes (priv->display));
609   if (!display_attrs)
610     goto cleanup;
611
612   n = 0;
613   status = vaQueryDisplayAttributes (priv->display, display_attrs, &n);
614   if (!vaapi_check_status (status, "vaQueryDisplayAttributes()"))
615     goto cleanup;
616
617   GST_DEBUG ("%d display attributes", n);
618   for (i = 0; i < n; i++) {
619     VADisplayAttribute *const attr = &display_attrs[i];
620     GstVaapiProperty prop;
621     gint value;
622
623     GST_DEBUG ("  %s", string_of_VADisplayAttributeType (attr->type));
624
625     switch (attr->type) {
626 #if !VA_CHECK_VERSION(0,34,0)
627       case VADisplayAttribDirectSurface:
628         prop.name = GST_VAAPI_DISPLAY_PROP_RENDER_MODE;
629         break;
630 #endif
631       case VADisplayAttribRenderMode:
632         prop.name = GST_VAAPI_DISPLAY_PROP_RENDER_MODE;
633         break;
634       case VADisplayAttribRotation:
635         prop.name = GST_VAAPI_DISPLAY_PROP_ROTATION;
636         break;
637       case VADisplayAttribHue:
638         prop.name = GST_VAAPI_DISPLAY_PROP_HUE;
639         break;
640       case VADisplayAttribSaturation:
641         prop.name = GST_VAAPI_DISPLAY_PROP_SATURATION;
642         break;
643       case VADisplayAttribBrightness:
644         prop.name = GST_VAAPI_DISPLAY_PROP_BRIGHTNESS;
645         break;
646       case VADisplayAttribContrast:
647         prop.name = GST_VAAPI_DISPLAY_PROP_CONTRAST;
648         break;
649       default:
650         prop.name = NULL;
651         break;
652     }
653     if (!prop.name)
654       continue;
655
656     /* Assume the attribute is really supported if we can get the
657      * actual and current value */
658     if (!get_attribute (display, attr->type, &value))
659       continue;
660
661     /* Some drivers (e.g. EMGD) have completely random initial
662      * values */
663     if (value < attr->min_value || value > attr->max_value)
664       continue;
665
666     prop.attribute = *attr;
667     prop.old_value = value;
668     g_array_append_val (priv->properties, prop);
669   }
670   success = TRUE;
671
672 cleanup:
673   g_free (display_attrs);
674   return success;
675 }
676
677 /* Initialize VA image formats */
678 static gboolean
679 ensure_image_formats (GstVaapiDisplay * display)
680 {
681   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
682   VAImageFormat *formats = NULL;
683   VAStatus status;
684   gint i, n;
685   gboolean success = FALSE;
686
687   if (priv->image_formats)
688     return TRUE;
689
690   priv->image_formats = g_array_new (FALSE, FALSE, sizeof (GstVaapiFormatInfo));
691   if (!priv->image_formats)
692     goto cleanup;
693
694   /* VA image formats */
695   formats = g_new (VAImageFormat, vaMaxNumImageFormats (priv->display));
696   if (!formats)
697     goto cleanup;
698
699   n = 0;
700   status = vaQueryImageFormats (priv->display, formats, &n);
701   if (!vaapi_check_status (status, "vaQueryImageFormats()"))
702     goto cleanup;
703
704   GST_DEBUG ("%d image formats", n);
705   for (i = 0; i < n; i++)
706     GST_DEBUG ("  %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (formats[i].fourcc));
707
708   append_formats (priv->image_formats, formats, NULL, n);
709   g_array_sort (priv->image_formats, compare_yuv_formats);
710   success = TRUE;
711
712 cleanup:
713   g_free (formats);
714   return success;
715 }
716
717 /* Initialize VA subpicture formats */
718 static gboolean
719 ensure_subpicture_formats (GstVaapiDisplay * display)
720 {
721   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
722   VAImageFormat *formats = NULL;
723   unsigned int *flags = NULL;
724   VAStatus status;
725   guint i, n;
726   gboolean success = FALSE;
727
728   if (priv->subpicture_formats)
729     return TRUE;
730
731   priv->subpicture_formats =
732       g_array_new (FALSE, FALSE, sizeof (GstVaapiFormatInfo));
733   if (!priv->subpicture_formats)
734     goto cleanup;
735
736   /* VA subpicture formats */
737   n = vaMaxNumSubpictureFormats (priv->display);
738   formats = g_new (VAImageFormat, n);
739   if (!formats)
740     goto cleanup;
741   flags = g_new (guint, n);
742   if (!flags)
743     goto cleanup;
744
745   n = 0;
746   status = vaQuerySubpictureFormats (priv->display, formats, flags, &n);
747   if (!vaapi_check_status (status, "vaQuerySubpictureFormats()"))
748     goto cleanup;
749
750   GST_DEBUG ("%d subpicture formats", n);
751   for (i = 0; i < n; i++) {
752     GST_DEBUG ("  %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (formats[i].fourcc));
753     flags[i] = to_GstVaapiSubpictureFlags (flags[i]);
754   }
755
756   append_formats (priv->subpicture_formats, formats, flags, n);
757   g_array_sort (priv->subpicture_formats, compare_rgb_formats);
758   success = TRUE;
759
760 cleanup:
761   g_free (formats);
762   g_free (flags);
763   return success;
764 }
765
766 static void
767 gst_vaapi_display_calculate_pixel_aspect_ratio (GstVaapiDisplay * display)
768 {
769   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
770   gdouble ratio, delta;
771   gint i, j, index, windex;
772
773   static const gint par[][2] = {
774     {1, 1},                     /* regular screen            */
775     {16, 15},                   /* PAL TV                    */
776     {11, 10},                   /* 525 line Rec.601 video    */
777     {54, 59},                   /* 625 line Rec.601 video    */
778     {64, 45},                   /* 1280x1024 on 16:9 display */
779     {5, 3},                     /* 1280x1024 on  4:3 display */
780     {4, 3}                      /*  800x600  on 16:9 display */
781   };
782
783   /* First, calculate the "real" ratio based on the X values;
784    * which is the "physical" w/h divided by the w/h in pixels of the
785    * display */
786   if (!priv->width || !priv->height || !priv->width_mm || !priv->height_mm)
787     ratio = 1.0;
788   else
789     ratio = (gdouble) (priv->width_mm * priv->height) /
790         (priv->height_mm * priv->width);
791   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
792
793   /* Now, find the one from par[][2] with the lowest delta to the real one */
794 #define DELTA(idx, w) (ABS(ratio - ((gdouble)par[idx][w] / par[idx][!(w)])))
795   delta = DELTA (0, 0);
796   index = 0;
797   windex = 0;
798
799   for (i = 1; i < G_N_ELEMENTS (par); i++) {
800     for (j = 0; j < 2; j++) {
801       const gdouble this_delta = DELTA (i, j);
802       if (this_delta < delta) {
803         index = i;
804         windex = j;
805         delta = this_delta;
806       }
807     }
808   }
809 #undef DELTA
810
811   priv->par_n = par[index][windex];
812   priv->par_d = par[index][windex ^ 1];
813 }
814
815 static void
816 gst_vaapi_display_destroy (GstVaapiDisplay * display)
817 {
818   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
819
820   if (priv->decoders) {
821     g_array_free (priv->decoders, TRUE);
822     priv->decoders = NULL;
823   }
824
825   if (priv->encoders) {
826     g_array_free (priv->encoders, TRUE);
827     priv->encoders = NULL;
828   }
829
830   if (priv->image_formats) {
831     g_array_free (priv->image_formats, TRUE);
832     priv->image_formats = NULL;
833   }
834
835   if (priv->subpicture_formats) {
836     g_array_free (priv->subpicture_formats, TRUE);
837     priv->subpicture_formats = NULL;
838   }
839
840   if (priv->properties) {
841     g_array_free (priv->properties, TRUE);
842     priv->properties = NULL;
843   }
844
845   if (priv->display) {
846     if (!priv->parent)
847       vaTerminate (priv->display);
848     priv->display = NULL;
849   }
850
851   if (!priv->use_foreign_display) {
852     GstVaapiDisplayClass *klass = GST_VAAPI_DISPLAY_GET_CLASS (display);
853     if (klass->close_display)
854       klass->close_display (display);
855   }
856
857   g_free (priv->vendor_string);
858   priv->vendor_string = NULL;
859
860   gst_vaapi_display_replace_internal (&priv->parent, NULL);
861
862   g_mutex_lock (&g_display_cache_lock);
863   if (priv->cache)
864     gst_vaapi_display_cache_remove (priv->cache, display);
865   g_mutex_unlock (&g_display_cache_lock);
866   gst_vaapi_display_cache_replace (&priv->cache, NULL);
867   free_display_cache ();
868 }
869
870 static gboolean
871 gst_vaapi_display_create_unlocked (GstVaapiDisplay * display,
872     GstVaapiDisplayInitType init_type, gpointer init_value)
873 {
874   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
875   const GstVaapiDisplayClass *const klass =
876       GST_VAAPI_DISPLAY_GET_CLASS (display);
877   gint major_version, minor_version;
878   VAStatus status;
879   GstVaapiDisplayInfo info;
880   const GstVaapiDisplayInfo *cached_info = NULL;
881
882   memset (&info, 0, sizeof (info));
883   info.display = display;
884   info.display_type = priv->display_type;
885
886   switch (init_type) {
887     case GST_VAAPI_DISPLAY_INIT_FROM_VA_DISPLAY:
888       info.va_display = init_value;
889       priv->display = init_value;
890       priv->use_foreign_display = TRUE;
891       break;
892     case GST_VAAPI_DISPLAY_INIT_FROM_DISPLAY_NAME:
893       if (klass->open_display && !klass->open_display (display, init_value))
894         return FALSE;
895       goto create_display;
896     case GST_VAAPI_DISPLAY_INIT_FROM_NATIVE_DISPLAY:
897       if (klass->bind_display && !klass->bind_display (display, init_value))
898         return FALSE;
899       // fall-through
900     create_display:
901       if (!klass->get_display || !klass->get_display (display, &info))
902         return FALSE;
903       priv->display = info.va_display;
904       priv->display_type = info.display_type;
905       if (klass->get_size)
906         klass->get_size (display, &priv->width, &priv->height);
907       if (klass->get_size_mm)
908         klass->get_size_mm (display, &priv->width_mm, &priv->height_mm);
909       gst_vaapi_display_calculate_pixel_aspect_ratio (display);
910       break;
911   }
912   if (!priv->display)
913     return FALSE;
914
915   cached_info = gst_vaapi_display_cache_lookup_by_va_display (priv->cache,
916       info.va_display);
917   if (cached_info) {
918     gst_vaapi_display_replace_internal (&priv->parent, cached_info->display);
919     priv->display_type = cached_info->display_type;
920   }
921
922   if (!priv->parent) {
923     status = vaInitialize (priv->display, &major_version, &minor_version);
924     if (!vaapi_check_status (status, "vaInitialize()"))
925       return FALSE;
926     GST_DEBUG ("VA-API version %d.%d", major_version, minor_version);
927   }
928
929   if (!cached_info) {
930     if (!gst_vaapi_display_cache_add (priv->cache, &info))
931       return FALSE;
932   }
933   return TRUE;
934 }
935
936 static gboolean
937 gst_vaapi_display_create (GstVaapiDisplay * display,
938     GstVaapiDisplayInitType init_type, gpointer init_value)
939 {
940   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
941   GstVaapiDisplayCache *cache;
942   gboolean success;
943
944   cache = get_display_cache ();
945   if (!cache)
946     return FALSE;
947   gst_vaapi_display_cache_replace (&priv->cache, cache);
948   gst_vaapi_display_cache_unref (cache);
949
950   g_mutex_lock (&g_display_cache_lock);
951   success = gst_vaapi_display_create_unlocked (display, init_type, init_value);
952   g_mutex_unlock (&g_display_cache_lock);
953   return success;
954 }
955
956 static void
957 gst_vaapi_display_lock_default (GstVaapiDisplay * display)
958 {
959   GstVaapiDisplayPrivate *priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
960
961   if (priv->parent)
962     priv = GST_VAAPI_DISPLAY_GET_PRIVATE (priv->parent);
963   g_rec_mutex_lock (&priv->mutex);
964 }
965
966 static void
967 gst_vaapi_display_unlock_default (GstVaapiDisplay * display)
968 {
969   GstVaapiDisplayPrivate *priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
970
971   if (priv->parent)
972     priv = GST_VAAPI_DISPLAY_GET_PRIVATE (priv->parent);
973   g_rec_mutex_unlock (&priv->mutex);
974 }
975
976 static void
977 gst_vaapi_display_init (GstVaapiDisplay * display)
978 {
979   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
980   const GstVaapiDisplayClass *const dpy_class =
981       GST_VAAPI_DISPLAY_GET_CLASS (display);
982
983   priv->display_type = GST_VAAPI_DISPLAY_TYPE_ANY;
984   priv->par_n = 1;
985   priv->par_d = 1;
986
987   g_rec_mutex_init (&priv->mutex);
988
989   if (dpy_class->init)
990     dpy_class->init (display);
991 }
992
993 static void
994 gst_vaapi_display_finalize (GstVaapiDisplay * display)
995 {
996   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
997
998   gst_vaapi_display_destroy (display);
999   g_rec_mutex_clear (&priv->mutex);
1000 }
1001
1002 void
1003 gst_vaapi_display_class_init (GstVaapiDisplayClass * klass)
1004 {
1005   GstVaapiMiniObjectClass *const object_class =
1006       GST_VAAPI_MINI_OBJECT_CLASS (klass);
1007   GstVaapiDisplayClass *const dpy_class = GST_VAAPI_DISPLAY_CLASS (klass);
1008
1009   libgstvaapi_init_once ();
1010
1011   object_class->size = sizeof (GstVaapiDisplay);
1012   object_class->finalize = (GDestroyNotify) gst_vaapi_display_finalize;
1013   dpy_class->lock = gst_vaapi_display_lock_default;
1014   dpy_class->unlock = gst_vaapi_display_unlock_default;
1015 }
1016
1017 static void
1018 gst_vaapi_display_properties_init (void)
1019 {
1020   /**
1021    * GstVaapiDisplay:render-mode:
1022    *
1023    * The VA display rendering mode, expressed as a #GstVaapiRenderMode.
1024    */
1025   g_properties[PROP_RENDER_MODE] =
1026       g_param_spec_enum (GST_VAAPI_DISPLAY_PROP_RENDER_MODE,
1027       "render mode",
1028       "The display rendering mode",
1029       GST_VAAPI_TYPE_RENDER_MODE, DEFAULT_RENDER_MODE, G_PARAM_READWRITE);
1030
1031   /**
1032    * GstVaapiDisplay:rotation:
1033    *
1034    * The VA display rotation mode, expressed as a #GstVaapiRotation.
1035    */
1036   g_properties[PROP_ROTATION] =
1037       g_param_spec_enum (GST_VAAPI_DISPLAY_PROP_ROTATION,
1038       "rotation",
1039       "The display rotation mode",
1040       GST_VAAPI_TYPE_ROTATION, DEFAULT_ROTATION, G_PARAM_READWRITE);
1041
1042   /**
1043    * GstVaapiDisplay:hue:
1044    *
1045    * The VA display hue, expressed as a float value. Range is -180.0
1046    * to 180.0. Default value is 0.0 and represents no modification.
1047    */
1048   g_properties[PROP_HUE] =
1049       g_param_spec_float (GST_VAAPI_DISPLAY_PROP_HUE,
1050       "hue", "The display hue value", -180.0, 180.0, 0.0, G_PARAM_READWRITE);
1051
1052   /**
1053    * GstVaapiDisplay:saturation:
1054    *
1055    * The VA display saturation, expressed as a float value. Range is
1056    * 0.0 to 2.0. Default value is 1.0 and represents no modification.
1057    */
1058   g_properties[PROP_SATURATION] =
1059       g_param_spec_float (GST_VAAPI_DISPLAY_PROP_SATURATION,
1060       "saturation",
1061       "The display saturation value", 0.0, 2.0, 1.0, G_PARAM_READWRITE);
1062
1063   /**
1064    * GstVaapiDisplay:brightness:
1065    *
1066    * The VA display brightness, expressed as a float value. Range is
1067    * -1.0 to 1.0. Default value is 0.0 and represents no modification.
1068    */
1069   g_properties[PROP_BRIGHTNESS] =
1070       g_param_spec_float (GST_VAAPI_DISPLAY_PROP_BRIGHTNESS,
1071       "brightness",
1072       "The display brightness value", -1.0, 1.0, 0.0, G_PARAM_READWRITE);
1073
1074   /**
1075    * GstVaapiDisplay:contrast:
1076    *
1077    * The VA display contrast, expressed as a float value. Range is 0.0
1078    * to 2.0. Default value is 1.0 and represents no modification.
1079    */
1080   g_properties[PROP_CONTRAST] =
1081       g_param_spec_float (GST_VAAPI_DISPLAY_PROP_CONTRAST,
1082       "contrast",
1083       "The display contrast value", 0.0, 2.0, 1.0, G_PARAM_READWRITE);
1084 }
1085
1086 static inline const GstVaapiDisplayClass *
1087 gst_vaapi_display_class (void)
1088 {
1089   static GstVaapiDisplayClass g_class;
1090   static gsize g_class_init = FALSE;
1091
1092   if (g_once_init_enter (&g_class_init)) {
1093     gst_vaapi_display_class_init (&g_class);
1094     g_once_init_leave (&g_class_init, TRUE);
1095   }
1096   return &g_class;
1097 }
1098
1099 GstVaapiDisplay *
1100 gst_vaapi_display_new (const GstVaapiDisplayClass * klass,
1101     GstVaapiDisplayInitType init_type, gpointer init_value)
1102 {
1103   GstVaapiDisplay *display;
1104
1105   display = (GstVaapiDisplay *)
1106       gst_vaapi_mini_object_new0 (GST_VAAPI_MINI_OBJECT_CLASS (klass));
1107   if (!display)
1108     return NULL;
1109
1110   gst_vaapi_display_init (display);
1111   if (!gst_vaapi_display_create (display, init_type, init_value))
1112     goto error;
1113   return display;
1114
1115 error:
1116   gst_vaapi_display_unref_internal (display);
1117   return NULL;
1118 }
1119
1120 /**
1121  * gst_vaapi_display_new_with_display:
1122  * @va_display: a #VADisplay
1123  *
1124  * Creates a new #GstVaapiDisplay, using @va_display as the VA
1125  * display.
1126  *
1127  * Return value: the newly created #GstVaapiDisplay object
1128  */
1129 GstVaapiDisplay *
1130 gst_vaapi_display_new_with_display (VADisplay va_display)
1131 {
1132   GstVaapiDisplayCache *const cache = get_display_cache ();
1133   const GstVaapiDisplayInfo *info;
1134
1135   g_return_val_if_fail (va_display != NULL, NULL);
1136   g_return_val_if_fail (cache != NULL, NULL);
1137
1138   info = gst_vaapi_display_cache_lookup_by_va_display (cache, va_display);
1139   if (info)
1140     return gst_vaapi_display_ref_internal (info->display);
1141
1142   return gst_vaapi_display_new (gst_vaapi_display_class (),
1143       GST_VAAPI_DISPLAY_INIT_FROM_VA_DISPLAY, va_display);
1144 }
1145
1146 /**
1147  * gst_vaapi_display_ref:
1148  * @display: a #GstVaapiDisplay
1149  *
1150  * Atomically increases the reference count of the given @display by one.
1151  *
1152  * Returns: The same @display argument
1153  */
1154 GstVaapiDisplay *
1155 gst_vaapi_display_ref (GstVaapiDisplay * display)
1156 {
1157   return gst_vaapi_display_ref_internal (display);
1158 }
1159
1160 /**
1161  * gst_vaapi_display_unref:
1162  * @display: a #GstVaapiDisplay
1163  *
1164  * Atomically decreases the reference count of the @display by one. If
1165  * the reference count reaches zero, the display will be free'd.
1166  */
1167 void
1168 gst_vaapi_display_unref (GstVaapiDisplay * display)
1169 {
1170   gst_vaapi_display_unref_internal (display);
1171 }
1172
1173 /**
1174  * gst_vaapi_display_replace:
1175  * @old_display_ptr: a pointer to a #GstVaapiDisplay
1176  * @new_display: a #GstVaapiDisplay
1177  *
1178  * Atomically replaces the display display held in @old_display_ptr
1179  * with @new_display. This means that @old_display_ptr shall reference
1180  * a valid display. However, @new_display can be NULL.
1181  */
1182 void
1183 gst_vaapi_display_replace (GstVaapiDisplay ** old_display_ptr,
1184     GstVaapiDisplay * new_display)
1185 {
1186   gst_vaapi_display_replace_internal (old_display_ptr, new_display);
1187 }
1188
1189 /**
1190  * gst_vaapi_display_lock:
1191  * @display: a #GstVaapiDisplay
1192  *
1193  * Locks @display. If @display is already locked by another thread,
1194  * the current thread will block until @display is unlocked by the
1195  * other thread.
1196  */
1197 void
1198 gst_vaapi_display_lock (GstVaapiDisplay * display)
1199 {
1200   GstVaapiDisplayClass *klass;
1201
1202   g_return_if_fail (display != NULL);
1203
1204   klass = GST_VAAPI_DISPLAY_GET_CLASS (display);
1205   if (klass->lock)
1206     klass->lock (display);
1207 }
1208
1209 /**
1210  * gst_vaapi_display_unlock:
1211  * @display: a #GstVaapiDisplay
1212  *
1213  * Unlocks @display. If another thread is blocked in a
1214  * gst_vaapi_display_lock() call for @display, it will be woken and
1215  * can lock @display itself.
1216  */
1217 void
1218 gst_vaapi_display_unlock (GstVaapiDisplay * display)
1219 {
1220   GstVaapiDisplayClass *klass;
1221
1222   g_return_if_fail (display != NULL);
1223
1224   klass = GST_VAAPI_DISPLAY_GET_CLASS (display);
1225   if (klass->unlock)
1226     klass->unlock (display);
1227 }
1228
1229 /**
1230  * gst_vaapi_display_sync:
1231  * @display: a #GstVaapiDisplay
1232  *
1233  * Flushes any requests queued for the windowing system and waits until
1234  * all requests have been handled. This is often used for making sure
1235  * that the display is synchronized with the current state of the program.
1236  *
1237  * This is most useful for X11. On windowing systems where requests are
1238  * handled synchronously, this function will do nothing.
1239  */
1240 void
1241 gst_vaapi_display_sync (GstVaapiDisplay * display)
1242 {
1243   GstVaapiDisplayClass *klass;
1244
1245   g_return_if_fail (display != NULL);
1246
1247   klass = GST_VAAPI_DISPLAY_GET_CLASS (display);
1248   if (klass->sync)
1249     klass->sync (display);
1250   else if (klass->flush)
1251     klass->flush (display);
1252 }
1253
1254 /**
1255  * gst_vaapi_display_flush:
1256  * @display: a #GstVaapiDisplay
1257  *
1258  * Flushes any requests queued for the windowing system.
1259  *
1260  * This is most useful for X11. On windowing systems where requests
1261  * are handled synchronously, this function will do nothing.
1262  */
1263 void
1264 gst_vaapi_display_flush (GstVaapiDisplay * display)
1265 {
1266   GstVaapiDisplayClass *klass;
1267
1268   g_return_if_fail (display != NULL);
1269
1270   klass = GST_VAAPI_DISPLAY_GET_CLASS (display);
1271   if (klass->flush)
1272     klass->flush (display);
1273 }
1274
1275 /**
1276  * gst_vaapi_display_get_display:
1277  * @display: a #GstVaapiDisplay
1278  *
1279  * Returns the #GstVaapiDisplayType bound to @display.
1280  *
1281  * Return value: the #GstVaapiDisplayType
1282  */
1283 GstVaapiDisplayType
1284 gst_vaapi_display_get_display_type (GstVaapiDisplay * display)
1285 {
1286   g_return_val_if_fail (display != NULL, GST_VAAPI_DISPLAY_TYPE_ANY);
1287
1288   return GST_VAAPI_DISPLAY_GET_PRIVATE (display)->display_type;
1289 }
1290
1291 /**
1292  * gst_vaapi_display_get_display:
1293  * @display: a #GstVaapiDisplay
1294  *
1295  * Returns the #VADisplay bound to @display.
1296  *
1297  * Return value: the #VADisplay
1298  */
1299 VADisplay
1300 gst_vaapi_display_get_display (GstVaapiDisplay * display)
1301 {
1302   g_return_val_if_fail (display != NULL, NULL);
1303
1304   return GST_VAAPI_DISPLAY_GET_PRIVATE (display)->display;
1305 }
1306
1307 /**
1308  * gst_vaapi_display_get_width:
1309  * @display: a #GstVaapiDisplay
1310  *
1311  * Retrieves the width of a #GstVaapiDisplay.
1312  *
1313  * Return value: the width of the @display, in pixels
1314  */
1315 guint
1316 gst_vaapi_display_get_width (GstVaapiDisplay * display)
1317 {
1318   g_return_val_if_fail (display != NULL, 0);
1319
1320   return GST_VAAPI_DISPLAY_GET_PRIVATE (display)->width;
1321 }
1322
1323 /**
1324  * gst_vaapi_display_get_height:
1325  * @display: a #GstVaapiDisplay
1326  *
1327  * Retrieves the height of a #GstVaapiDisplay
1328  *
1329  * Return value: the height of the @display, in pixels
1330  */
1331 guint
1332 gst_vaapi_display_get_height (GstVaapiDisplay * display)
1333 {
1334   g_return_val_if_fail (display != NULL, 0);
1335
1336   return GST_VAAPI_DISPLAY_GET_PRIVATE (display)->height;
1337 }
1338
1339 /**
1340  * gst_vaapi_display_get_size:
1341  * @display: a #GstVaapiDisplay
1342  * @pwidth: return location for the width, or %NULL
1343  * @pheight: return location for the height, or %NULL
1344  *
1345  * Retrieves the dimensions of a #GstVaapiDisplay.
1346  */
1347 void
1348 gst_vaapi_display_get_size (GstVaapiDisplay * display, guint * pwidth,
1349     guint * pheight)
1350 {
1351   g_return_if_fail (GST_VAAPI_DISPLAY (display));
1352
1353   if (pwidth)
1354     *pwidth = GST_VAAPI_DISPLAY_GET_PRIVATE (display)->width;
1355
1356   if (pheight)
1357     *pheight = GST_VAAPI_DISPLAY_GET_PRIVATE (display)->height;
1358 }
1359
1360 /**
1361  * gst_vaapi_display_get_pixel_aspect_ratio:
1362  * @display: a #GstVaapiDisplay
1363  * @par_n: return location for the numerator of pixel aspect ratio, or %NULL
1364  * @par_d: return location for the denominator of pixel aspect ratio, or %NULL
1365  *
1366  * Retrieves the pixel aspect ratio of a #GstVaapiDisplay.
1367  */
1368 void
1369 gst_vaapi_display_get_pixel_aspect_ratio (GstVaapiDisplay * display,
1370     guint * par_n, guint * par_d)
1371 {
1372   g_return_if_fail (display != NULL);
1373
1374   if (par_n)
1375     *par_n = GST_VAAPI_DISPLAY_GET_PRIVATE (display)->par_n;
1376
1377   if (par_d)
1378     *par_d = GST_VAAPI_DISPLAY_GET_PRIVATE (display)->par_d;
1379 }
1380
1381 /**
1382  * gst_vaapi_display_has_video_processing:
1383  * @display: a #GstVaapiDisplay
1384  *
1385  * Checks whether the underlying VA driver implementation supports
1386  * video processing (VPP) acceleration.
1387  *
1388  * Returns: %TRUE if some VPP features are available
1389  */
1390 gboolean
1391 gst_vaapi_display_has_video_processing (GstVaapiDisplay * display)
1392 {
1393   g_return_val_if_fail (display != NULL, FALSE);
1394
1395   if (!ensure_profiles (display))
1396     return FALSE;
1397   return GST_VAAPI_DISPLAY_GET_PRIVATE (display)->has_vpp;
1398 }
1399
1400 /**
1401  * gst_vaapi_display_get_decode_profiles:
1402  * @display: a #GstVaapiDisplay
1403  *
1404  * Gets the supported profiles for decoding. The caller owns an extra
1405  * reference to the resulting array of #GstVaapiProfile elements, so
1406  * it shall be released with g_array_unref() after usage.
1407  *
1408  * Return value: a newly allocated #GArray, or %NULL or error or if
1409  *   decoding is not supported at all
1410  */
1411 GArray *
1412 gst_vaapi_display_get_decode_profiles (GstVaapiDisplay * display)
1413 {
1414   g_return_val_if_fail (display != NULL, NULL);
1415
1416   if (!ensure_profiles (display))
1417     return NULL;
1418   return get_profiles (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->decoders);
1419 }
1420
1421 /**
1422  * gst_vaapi_display_has_decoder:
1423  * @display: a #GstVaapiDisplay
1424  * @profile: a #VAProfile
1425  * @entrypoint: a #GstVaaiEntrypoint
1426  *
1427  * Returns whether VA @display supports @profile for decoding at the
1428  * specified @entrypoint.
1429  *
1430  * Return value: %TRUE if VA @display supports @profile for decoding.
1431  */
1432 gboolean
1433 gst_vaapi_display_has_decoder (GstVaapiDisplay * display,
1434     GstVaapiProfile profile, GstVaapiEntrypoint entrypoint)
1435 {
1436   g_return_val_if_fail (display != NULL, FALSE);
1437
1438   if (!ensure_profiles (display))
1439     return FALSE;
1440   return find_config (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->decoders,
1441       profile, entrypoint);
1442 }
1443
1444 /**
1445  * gst_vaapi_display_get_encode_profiles:
1446  * @display: a #GstVaapiDisplay
1447  *
1448  * Gets the supported profiles for encoding. The caller owns an extra
1449  * reference to the resulting array of #GstVaapiProfile elements, so
1450  * it shall be released with g_array_unref() after usage.
1451  *
1452  * Return value: a newly allocated #GArray, or %NULL or error or if
1453  *   encoding is not supported at all
1454  */
1455 GArray *
1456 gst_vaapi_display_get_encode_profiles (GstVaapiDisplay * display)
1457 {
1458   g_return_val_if_fail (display != NULL, NULL);
1459
1460   if (!ensure_profiles (display))
1461     return NULL;
1462   return get_profiles (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->encoders);
1463 }
1464
1465 /**
1466  * gst_vaapi_display_has_encoder:
1467  * @display: a #GstVaapiDisplay
1468  * @profile: a #VAProfile
1469  * @entrypoint: a #GstVaapiEntrypoint
1470  *
1471  * Returns whether VA @display supports @profile for encoding at the
1472  * specified @entrypoint.
1473  *
1474  * Return value: %TRUE if VA @display supports @profile for encoding.
1475  */
1476 gboolean
1477 gst_vaapi_display_has_encoder (GstVaapiDisplay * display,
1478     GstVaapiProfile profile, GstVaapiEntrypoint entrypoint)
1479 {
1480   g_return_val_if_fail (display != NULL, FALSE);
1481
1482   if (!ensure_profiles (display))
1483     return FALSE;
1484   return find_config (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->encoders,
1485       profile, entrypoint);
1486 }
1487
1488 /**
1489  * gst_vaapi_display_get_image_formats:
1490  * @display: a #GstVaapiDisplay
1491  *
1492  * Gets the supported image formats for gst_vaapi_surface_get_image()
1493  * or gst_vaapi_surface_put_image().
1494  *
1495  * Note that this method does not necessarily map image formats
1496  * returned by vaQueryImageFormats(). The set of capabilities can be
1497  * stripped down, if gstreamer-vaapi does not support the format, or
1498  * expanded to cover compatible formats not exposed by the underlying
1499  * driver. e.g. I420 can be supported even if the driver only exposes
1500  * YV12.
1501  *
1502  * Note: the caller owns an extra reference to the resulting array of
1503  * #GstVideoFormat elements, so it shall be released with
1504  * g_array_unref() after usage.
1505  *
1506  * Return value: a newly allocated #GArray, or %NULL on error or if
1507  *   the set is empty
1508  */
1509 GArray *
1510 gst_vaapi_display_get_image_formats (GstVaapiDisplay * display)
1511 {
1512   g_return_val_if_fail (display != NULL, NULL);
1513
1514   if (!ensure_image_formats (display))
1515     return NULL;
1516   return get_formats (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->image_formats);
1517 }
1518
1519 /**
1520  * gst_vaapi_display_has_image_format:
1521  * @display: a #GstVaapiDisplay
1522  * @format: a #GstVideoFormat
1523  *
1524  * Returns whether VA @display supports @format image format.
1525  *
1526  * Return value: %TRUE if VA @display supports @format image format
1527  */
1528 gboolean
1529 gst_vaapi_display_has_image_format (GstVaapiDisplay * display,
1530     GstVideoFormat format)
1531 {
1532   GstVaapiDisplayPrivate *priv;
1533
1534   g_return_val_if_fail (display != NULL, FALSE);
1535   g_return_val_if_fail (format, FALSE);
1536
1537   priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
1538
1539   if (!ensure_image_formats (display))
1540     return FALSE;
1541   if (find_format (priv->image_formats, format))
1542     return TRUE;
1543
1544   /* XXX: try subpicture formats since some drivers could report a
1545    * set of VA image formats that is not a superset of the set of VA
1546    * subpicture formats
1547    */
1548   if (!ensure_subpicture_formats (display))
1549     return FALSE;
1550   return find_format (priv->subpicture_formats, format);
1551 }
1552
1553 /**
1554  * gst_vaapi_display_get_subpicture_formats:
1555  * @display: a #GstVaapiDisplay
1556  *
1557  * Gets the supported subpicture formats.
1558  *
1559  * Note that this method does not necessarily map subpicture formats
1560  * returned by vaQuerySubpictureFormats(). The set of capabilities can
1561  * be stripped down if gstreamer-vaapi does not support the
1562  * format. e.g. this is the case for paletted formats like IA44.
1563  *
1564  * Note: the caller owns an extra reference to the resulting array of
1565  * #GstVideoFormat elements, so it shall be released with
1566  * g_array_unref() after usage.
1567  *
1568  * Return value: a newly allocated #GArray, or %NULL on error of if
1569  *   the set is empty
1570  */
1571 GArray *
1572 gst_vaapi_display_get_subpicture_formats (GstVaapiDisplay * display)
1573 {
1574   g_return_val_if_fail (display != NULL, NULL);
1575
1576   if (!ensure_subpicture_formats (display))
1577     return NULL;
1578   return get_formats (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->
1579       subpicture_formats);
1580 }
1581
1582 /**
1583  * gst_vaapi_display_has_subpicture_format:
1584  * @display: a #GstVaapiDisplay
1585  * @format: a #GstVideoFormat
1586  * @flags_ptr: pointer to #GstVaapiSubpictureFlags, or zero
1587  *
1588  * Returns whether VA @display supports @format subpicture format with
1589  * the supplied @flags.
1590  *
1591  * Return value: %TRUE if VA @display supports @format subpicture format
1592  */
1593 gboolean
1594 gst_vaapi_display_has_subpicture_format (GstVaapiDisplay * display,
1595     GstVideoFormat format, guint * flags_ptr)
1596 {
1597   GstVaapiDisplayPrivate *priv;
1598   const GstVaapiFormatInfo *fip;
1599
1600   g_return_val_if_fail (display != NULL, FALSE);
1601   g_return_val_if_fail (format, FALSE);
1602
1603   priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
1604
1605   if (!ensure_subpicture_formats (display))
1606     return FALSE;
1607
1608   fip = find_format_info (priv->subpicture_formats, format);
1609   if (!fip)
1610     return FALSE;
1611
1612   if (flags_ptr)
1613     *flags_ptr = fip->flags;
1614   return TRUE;
1615 }
1616
1617 /**
1618  * gst_vaapi_display_has_property:
1619  * @display: a #GstVaapiDisplay
1620  * @name: the property name to check
1621  *
1622  * Returns whether VA @display supports the requested property. The
1623  * check is performed against the property @name. So, the client
1624  * application may perform this check only once and cache this
1625  * information.
1626  *
1627  * Return value: %TRUE if VA @display supports property @name
1628  */
1629 gboolean
1630 gst_vaapi_display_has_property (GstVaapiDisplay * display, const gchar * name)
1631 {
1632   g_return_val_if_fail (display != NULL, FALSE);
1633   g_return_val_if_fail (name, FALSE);
1634
1635   if (!ensure_properties (display))
1636     return FALSE;
1637   return find_property (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->properties,
1638       name) != NULL;
1639 }
1640
1641 gboolean
1642 gst_vaapi_display_get_property (GstVaapiDisplay * display, const gchar * name,
1643     GValue * out_value)
1644 {
1645   const GstVaapiProperty *prop;
1646
1647   g_return_val_if_fail (display != NULL, FALSE);
1648   g_return_val_if_fail (name != NULL, FALSE);
1649   g_return_val_if_fail (out_value != NULL, FALSE);
1650
1651   if (!ensure_properties (display))
1652     return FALSE;
1653
1654   prop =
1655       find_property (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->properties, name);
1656   if (!prop)
1657     return FALSE;
1658
1659   switch (prop->attribute.type) {
1660     case VADisplayAttribRenderMode:{
1661       GstVaapiRenderMode mode;
1662       if (!gst_vaapi_display_get_render_mode (display, &mode))
1663         return FALSE;
1664       g_value_init (out_value, GST_VAAPI_TYPE_RENDER_MODE);
1665       g_value_set_enum (out_value, mode);
1666       break;
1667     }
1668     case VADisplayAttribRotation:{
1669       GstVaapiRotation rotation;
1670       rotation = gst_vaapi_display_get_rotation (display);
1671       g_value_init (out_value, GST_VAAPI_TYPE_ROTATION);
1672       g_value_set_enum (out_value, rotation);
1673       break;
1674     }
1675     case VADisplayAttribHue:
1676     case VADisplayAttribSaturation:
1677     case VADisplayAttribBrightness:
1678     case VADisplayAttribContrast:{
1679       gfloat value;
1680       if (!get_color_balance (display, find_property_id (name), &value))
1681         return FALSE;
1682       g_value_init (out_value, G_TYPE_FLOAT);
1683       g_value_set_float (out_value, value);
1684       break;
1685     }
1686     default:
1687       GST_WARNING ("unsupported property '%s'", name);
1688       return FALSE;
1689   }
1690   return TRUE;
1691 }
1692
1693 gboolean
1694 gst_vaapi_display_set_property (GstVaapiDisplay * display, const gchar * name,
1695     const GValue * value)
1696 {
1697   const GstVaapiProperty *prop;
1698
1699   g_return_val_if_fail (display != NULL, FALSE);
1700   g_return_val_if_fail (name != NULL, FALSE);
1701   g_return_val_if_fail (value != NULL, FALSE);
1702
1703   if (!ensure_properties (display))
1704     return FALSE;
1705
1706   prop =
1707       find_property (GST_VAAPI_DISPLAY_GET_PRIVATE (display)->properties, name);
1708   if (!prop)
1709     return FALSE;
1710
1711   switch (prop->attribute.type) {
1712     case VADisplayAttribRenderMode:{
1713       GstVaapiRenderMode mode;
1714       if (!G_VALUE_HOLDS (value, GST_VAAPI_TYPE_RENDER_MODE))
1715         return FALSE;
1716       mode = g_value_get_enum (value);
1717       return gst_vaapi_display_set_render_mode (display, mode);
1718     }
1719     case VADisplayAttribRotation:{
1720       GstVaapiRotation rotation;
1721       if (!G_VALUE_HOLDS (value, GST_VAAPI_TYPE_ROTATION))
1722         return FALSE;
1723       rotation = g_value_get_enum (value);
1724       return gst_vaapi_display_set_rotation (display, rotation);
1725     }
1726     case VADisplayAttribHue:
1727     case VADisplayAttribSaturation:
1728     case VADisplayAttribBrightness:
1729     case VADisplayAttribContrast:{
1730       gfloat v;
1731       if (!G_VALUE_HOLDS (value, G_TYPE_FLOAT))
1732         return FALSE;
1733       v = g_value_get_float (value);
1734       return set_color_balance (display, find_property_id (name), v);
1735     }
1736     default:
1737       break;
1738   }
1739
1740   GST_WARNING ("unsupported property '%s'", name);
1741   return FALSE;
1742 }
1743
1744 static gboolean
1745 get_attribute (GstVaapiDisplay * display, VADisplayAttribType type,
1746     gint * value)
1747 {
1748   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
1749   VADisplayAttribute attr;
1750   VAStatus status;
1751
1752   attr.type = type;
1753   attr.flags = VA_DISPLAY_ATTRIB_GETTABLE;
1754   status = vaGetDisplayAttributes (priv->display, &attr, 1);
1755   if (!vaapi_check_status (status, "vaGetDisplayAttributes()"))
1756     return FALSE;
1757   *value = attr.value;
1758   return TRUE;
1759 }
1760
1761 static gboolean
1762 set_attribute (GstVaapiDisplay * display, VADisplayAttribType type, gint value)
1763 {
1764   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
1765   VADisplayAttribute attr;
1766   VAStatus status;
1767
1768   attr.type = type;
1769   attr.value = value;
1770   attr.flags = VA_DISPLAY_ATTRIB_SETTABLE;
1771   status = vaSetDisplayAttributes (priv->display, &attr, 1);
1772   if (!vaapi_check_status (status, "vaSetDisplayAttributes()"))
1773     return FALSE;
1774   return TRUE;
1775 }
1776
1777 static gboolean
1778 get_render_mode_VADisplayAttribRenderMode (GstVaapiDisplay * display,
1779     GstVaapiRenderMode * pmode)
1780 {
1781   gint modes, devices;
1782
1783   if (!get_attribute (display, VADisplayAttribRenderDevice, &devices))
1784     return FALSE;
1785   if (!devices)
1786     return FALSE;
1787   if (!get_attribute (display, VADisplayAttribRenderMode, &modes))
1788     return FALSE;
1789
1790   /* Favor "overlay" mode since it is the most restrictive one */
1791   if (modes & (VA_RENDER_MODE_LOCAL_OVERLAY | VA_RENDER_MODE_EXTERNAL_OVERLAY))
1792     *pmode = GST_VAAPI_RENDER_MODE_OVERLAY;
1793   else
1794     *pmode = GST_VAAPI_RENDER_MODE_TEXTURE;
1795   return TRUE;
1796 }
1797
1798 static gboolean
1799 get_render_mode_VADisplayAttribDirectSurface (GstVaapiDisplay * display,
1800     GstVaapiRenderMode * pmode)
1801 {
1802 #if VA_CHECK_VERSION(0,34,0)
1803   /* VADisplayAttribDirectsurface was removed in VA-API >= 0.34.0 */
1804   return FALSE;
1805 #else
1806   gint direct_surface;
1807
1808   if (!get_attribute (display, VADisplayAttribDirectSurface, &direct_surface))
1809     return FALSE;
1810   if (direct_surface)
1811     *pmode = GST_VAAPI_RENDER_MODE_OVERLAY;
1812   else
1813     *pmode = GST_VAAPI_RENDER_MODE_TEXTURE;
1814   return TRUE;
1815 #endif
1816 }
1817
1818 static gboolean
1819 get_render_mode_default (GstVaapiDisplay * display, GstVaapiRenderMode * pmode)
1820 {
1821   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
1822
1823   switch (priv->display_type) {
1824 #if USE_WAYLAND
1825     case GST_VAAPI_DISPLAY_TYPE_WAYLAND:
1826       /* wl_buffer mapped from VA surface through vaGetSurfaceBufferWl() */
1827       *pmode = GST_VAAPI_RENDER_MODE_OVERLAY;
1828       break;
1829 #endif
1830 #if USE_DRM
1831     case GST_VAAPI_DISPLAY_TYPE_DRM:
1832       /* vaGetSurfaceBufferDRM() returns the underlying DRM buffer handle */
1833       *pmode = GST_VAAPI_RENDER_MODE_OVERLAY;
1834       break;
1835 #endif
1836     default:
1837       /* This includes VA/X11 and VA/GLX modes */
1838       *pmode = DEFAULT_RENDER_MODE;
1839       break;
1840   }
1841   return TRUE;
1842 }
1843
1844 /**
1845  * gst_vaapi_display_get_render_mode:
1846  * @display: a #GstVaapiDisplay
1847  * @pmode: return location for the VA @display rendering mode
1848  *
1849  * Returns the current VA @display rendering mode.
1850  *
1851  * Return value: %TRUE if VA @display rendering mode could be determined
1852  */
1853 gboolean
1854 gst_vaapi_display_get_render_mode (GstVaapiDisplay * display,
1855     GstVaapiRenderMode * pmode)
1856 {
1857   g_return_val_if_fail (display != NULL, FALSE);
1858
1859   /* Try with render-mode attribute */
1860   if (get_render_mode_VADisplayAttribRenderMode (display, pmode))
1861     return TRUE;
1862
1863   /* Try with direct-surface attribute */
1864   if (get_render_mode_VADisplayAttribDirectSurface (display, pmode))
1865     return TRUE;
1866
1867   /* Default: determine from the display type */
1868   return get_render_mode_default (display, pmode);
1869 }
1870
1871 /**
1872  * gst_vaapi_display_set_render_mode:
1873  * @display: a #GstVaapiDisplay
1874  * @mode: the #GstVaapiRenderMode to set
1875  *
1876  * Sets the VA @display rendering mode to the supplied @mode. This
1877  * function returns %FALSE if the rendering mode could not be set,
1878  * e.g. run-time switching rendering mode is not supported.
1879  *
1880  * Return value: %TRUE if VA @display rendering @mode could be changed
1881  *   to the requested value
1882  */
1883 gboolean
1884 gst_vaapi_display_set_render_mode (GstVaapiDisplay * display,
1885     GstVaapiRenderMode mode)
1886 {
1887   gint modes, devices;
1888
1889   g_return_val_if_fail (display != NULL, FALSE);
1890
1891   if (!get_attribute (display, VADisplayAttribRenderDevice, &devices))
1892     return FALSE;
1893
1894   modes = 0;
1895   switch (mode) {
1896     case GST_VAAPI_RENDER_MODE_OVERLAY:
1897       if (devices & VA_RENDER_DEVICE_LOCAL)
1898         modes |= VA_RENDER_MODE_LOCAL_OVERLAY;
1899       if (devices & VA_RENDER_DEVICE_EXTERNAL)
1900         modes |= VA_RENDER_MODE_EXTERNAL_OVERLAY;
1901       break;
1902     case GST_VAAPI_RENDER_MODE_TEXTURE:
1903       if (devices & VA_RENDER_DEVICE_LOCAL)
1904         modes |= VA_RENDER_MODE_LOCAL_GPU;
1905       if (devices & VA_RENDER_DEVICE_EXTERNAL)
1906         modes |= VA_RENDER_MODE_EXTERNAL_GPU;
1907       break;
1908   }
1909   if (!modes)
1910     return FALSE;
1911   if (!set_attribute (display, VADisplayAttribRenderMode, modes))
1912     return FALSE;
1913   return TRUE;
1914 }
1915
1916 /**
1917  * gst_vaapi_display_get_rotation:
1918  * @display: a #GstVaapiDisplay
1919  *
1920  * Returns the current VA @display rotation angle. If the VA driver
1921  * does not support "rotation" display attribute, then the display is
1922  * assumed to be un-rotated.
1923  *
1924  * Return value: the current #GstVaapiRotation value
1925  */
1926 GstVaapiRotation
1927 gst_vaapi_display_get_rotation (GstVaapiDisplay * display)
1928 {
1929   gint value;
1930
1931   g_return_val_if_fail (display != NULL, DEFAULT_ROTATION);
1932
1933   if (!get_attribute (display, VADisplayAttribRotation, &value))
1934     value = VA_ROTATION_NONE;
1935   return to_GstVaapiRotation (value);
1936 }
1937
1938 /**
1939  * gst_vaapi_display_set_rotation:
1940  * @display: a #GstVaapiDisplay
1941  * @rotation: the #GstVaapiRotation value to set
1942  *
1943  * Sets the VA @display rotation angle to the supplied @rotation
1944  * value. This function returns %FALSE if the rotation angle could not
1945  * be set, e.g. the VA driver does not allow to change the display
1946  * rotation angle.
1947  *
1948  * Return value: %TRUE if VA @display rotation angle could be changed
1949  *   to the requested value
1950  */
1951 gboolean
1952 gst_vaapi_display_set_rotation (GstVaapiDisplay * display,
1953     GstVaapiRotation rotation)
1954 {
1955   guint value;
1956
1957   g_return_val_if_fail (display != NULL, FALSE);
1958
1959   value = from_GstVaapiRotation (rotation);
1960   if (!set_attribute (display, VADisplayAttribRotation, value))
1961     return FALSE;
1962   return TRUE;
1963 }
1964
1965 /* Get color balance attributes */
1966 static gboolean
1967 get_color_balance (GstVaapiDisplay * display, guint prop_id, gfloat * v)
1968 {
1969   GParamSpecFloat *const pspec = G_PARAM_SPEC_FLOAT (g_properties[prop_id]);
1970   const GstVaapiProperty *prop;
1971   const VADisplayAttribute *attr;
1972   gfloat out_value;
1973   gint value;
1974
1975   if (!ensure_properties (display))
1976     return FALSE;
1977
1978   if (!pspec)
1979     return FALSE;
1980
1981   prop = find_property_by_pspec (display, &pspec->parent_instance);
1982   if (!prop)
1983     return FALSE;
1984   attr = &prop->attribute;
1985
1986   if (!get_attribute (display, attr->type, &value))
1987     return FALSE;
1988
1989   /* Scale wrt. the medium ("default") value */
1990   out_value = pspec->default_value;
1991   if (value > attr->value)
1992     out_value += ((gfloat) (value - attr->value) /
1993         (attr->max_value - attr->value) *
1994         (pspec->maximum - pspec->default_value));
1995   else if (value < attr->value)
1996     out_value -= ((gfloat) (attr->value - value) /
1997         (attr->value - attr->min_value) *
1998         (pspec->default_value - pspec->minimum));
1999   *v = out_value;
2000   return TRUE;
2001 }
2002
2003 /* Set color balance attribute */
2004 static gboolean
2005 set_color_balance (GstVaapiDisplay * display, guint prop_id, gfloat v)
2006 {
2007   GParamSpecFloat *const pspec = G_PARAM_SPEC_FLOAT (g_properties[prop_id]);
2008   const GstVaapiProperty *prop;
2009   const VADisplayAttribute *attr;
2010   gint value;
2011
2012   if (!ensure_properties (display))
2013     return FALSE;
2014
2015   if (!pspec)
2016     return FALSE;
2017
2018   prop = find_property_by_pspec (display, &pspec->parent_instance);
2019   if (!prop)
2020     return FALSE;
2021   attr = &prop->attribute;
2022
2023   /* Scale wrt. the medium ("default") value */
2024   value = attr->value;
2025   if (v > pspec->default_value)
2026     value += ((v - pspec->default_value) /
2027         (pspec->maximum - pspec->default_value) *
2028         (attr->max_value - attr->value));
2029   else if (v < pspec->default_value)
2030     value -= ((pspec->default_value - v) /
2031         (pspec->default_value - pspec->minimum) *
2032         (attr->value - attr->min_value));
2033   if (!set_attribute (display, attr->type, value))
2034     return FALSE;
2035   return TRUE;
2036 }
2037
2038 /* Ensures the VA driver vendor string was copied */
2039 static gboolean
2040 ensure_vendor_string (GstVaapiDisplay * display)
2041 {
2042   GstVaapiDisplayPrivate *const priv = GST_VAAPI_DISPLAY_GET_PRIVATE (display);
2043   const gchar *vendor_string;
2044
2045   GST_VAAPI_DISPLAY_LOCK (display);
2046   if (!priv->vendor_string) {
2047     vendor_string = vaQueryVendorString (priv->display);
2048     if (vendor_string)
2049       priv->vendor_string = g_strdup (vendor_string);
2050   }
2051   GST_VAAPI_DISPLAY_UNLOCK (display);
2052   return priv->vendor_string != NULL;
2053 }
2054
2055 /**
2056  * gst_vaapi_display_get_vendor_string:
2057  * @display: a #GstVaapiDisplay
2058  *
2059  * Returns the VA driver vendor string attached to the supplied VA @display.
2060  * The @display owns the vendor string, do *not* de-allocate it.
2061  *
2062  * This function is thread safe.
2063  *
2064  * Return value: the current #GstVaapiRotation value
2065  */
2066 const gchar *
2067 gst_vaapi_display_get_vendor_string (GstVaapiDisplay * display)
2068 {
2069   g_return_val_if_fail (display != NULL, NULL);
2070
2071   if (!ensure_vendor_string (display))
2072     return NULL;
2073   return display->priv.vendor_string;
2074 }