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