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