gstvaapidisplay.c 52.9 KB
Newer Older
gb's avatar
gb committed
1 2 3
/*
 *  gstvaapidisplay.c - VA display abstraction
 *
4
 *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5
 *    Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
6
 *  Copyright (C) 2011-2013 Intel Corporation
7
 *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
gb's avatar
gb committed
8
 *
gb's avatar
gb committed
9 10 11 12
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2.1
 *  of the License, or (at your option) any later version.
gb's avatar
gb committed
13
 *
gb's avatar
gb committed
14
 *  This library is distributed in the hope that it will be useful,
gb's avatar
gb committed
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
gb's avatar
gb committed
16 17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
gb's avatar
gb committed
18
 *
gb's avatar
gb committed
19 20 21 22
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301 USA
gb's avatar
gb committed
23 24
 */

gb's avatar
gb committed
25
/**
gb's avatar
gb committed
26 27
 * SECTION:gstvaapidisplay
 * @short_description: VA display abstraction
gb's avatar
gb committed
28 29
 */

30
#include "sysdeps.h"
31
#include <string.h>
gb's avatar
gb committed
32
#include "gstvaapiutils.h"
33
#include "gstvaapivalue.h"
gb's avatar
gb committed
34
#include "gstvaapidisplay.h"
35
#include "gstvaapidisplay_priv.h"
36
#include "gstvaapiworkarounds.h"
37
#include "gstvaapiversion.h"
gb's avatar
gb committed
38 39

#define DEBUG 1
gb's avatar
gb committed
40
#include "gstvaapidebug.h"
gb's avatar
gb committed
41

gb's avatar
gb committed
42 43
GST_DEBUG_CATEGORY(gst_debug_vaapi);

44 45 46 47
/* Ensure those symbols are actually defined in the resulting libraries */
#undef gst_vaapi_display_ref
#undef gst_vaapi_display_unref
#undef gst_vaapi_display_replace
gb's avatar
gb committed
48

49 50 51 52 53 54
typedef struct _GstVaapiConfig GstVaapiConfig;
struct _GstVaapiConfig {
    GstVaapiProfile     profile;
    GstVaapiEntrypoint  entrypoint;
};

55 56 57 58
typedef struct _GstVaapiProperty GstVaapiProperty;
struct _GstVaapiProperty {
    const gchar        *name;
    VADisplayAttribute  attribute;
59
    gint                old_value;
60 61
};

62 63
typedef struct _GstVaapiFormatInfo GstVaapiFormatInfo;
struct _GstVaapiFormatInfo {
64
    GstVideoFormat      format;
65 66 67
    guint               flags;
};

68 69
#define DEFAULT_RENDER_MODE     GST_VAAPI_RENDER_MODE_TEXTURE
#define DEFAULT_ROTATION        GST_VAAPI_ROTATION_0
70

gb's avatar
gb committed
71 72 73
enum {
    PROP_0,

74 75
    PROP_RENDER_MODE,
    PROP_ROTATION,
76 77 78 79
    PROP_HUE,
    PROP_SATURATION,
    PROP_BRIGHTNESS,
    PROP_CONTRAST,
80 81

    N_PROPERTIES
gb's avatar
gb committed
82 83
};

84 85
static GstVaapiDisplayCache *g_display_cache = NULL;

86 87
static GParamSpec *g_properties[N_PROPERTIES] = { NULL, };

88 89 90
static void
gst_vaapi_display_properties_init(void);

91 92 93 94 95 96
static gboolean
get_attribute(GstVaapiDisplay *display, VADisplayAttribType type, gint *value);

static gboolean
set_attribute(GstVaapiDisplay *display, VADisplayAttribType type, gint value);

97 98 99 100 101 102
static gboolean
get_color_balance(GstVaapiDisplay *display, guint prop_id, gfloat *v);

static gboolean
set_color_balance(GstVaapiDisplay *display, guint prop_id, gfloat v);

103
static void
104
libgstvaapi_init_once(void)
105 106 107 108 109 110
{
    static gsize g_once = FALSE;

    if (!g_once_init_enter(&g_once))
        return;

111 112
    GST_DEBUG_CATEGORY_INIT(gst_debug_vaapi, "vaapi", 0, "VA-API helper");

113 114 115
    /* Dump gstreamer-vaapi version for debugging purposes */
    GST_INFO("gstreamer-vaapi version %s", GST_VAAPI_VERSION_ID);

116 117
    gst_vaapi_display_properties_init();

118 119 120
    g_once_init_leave(&g_once, TRUE);
}

121 122 123 124 125 126 127 128 129 130 131 132 133 134
static inline GstVaapiDisplayCache *
get_display_cache(void)
{
    if (!g_display_cache)
        g_display_cache = gst_vaapi_display_cache_new();
    return g_display_cache;
}

GstVaapiDisplayCache *
gst_vaapi_display_get_cache(void)
{
    return get_display_cache();
}

135 136 137 138 139 140 141 142 143 144 145
static void
free_display_cache(void)
{
    if (!g_display_cache)
        return;
    if (gst_vaapi_display_cache_get_size(g_display_cache) > 0)
        return;
    gst_vaapi_display_cache_free(g_display_cache);
    g_display_cache = NULL;
}

146 147 148 149 150 151 152 153 154
/* GstVaapiDisplayType enumerations */
GType
gst_vaapi_display_type_get_type(void)
{
    static GType g_type = 0;

    static const GEnumValue display_types[] = {
        { GST_VAAPI_DISPLAY_TYPE_ANY,
          "Auto detection", "any" },
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
155
#if USE_X11
156 157
        { GST_VAAPI_DISPLAY_TYPE_X11,
          "VA/X11 display", "x11" },
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
158
#endif
159 160 161
#if USE_GLX
        { GST_VAAPI_DISPLAY_TYPE_GLX,
          "VA/GLX display", "glx" },
162 163 164 165
#endif
#if USE_WAYLAND
        { GST_VAAPI_DISPLAY_TYPE_WAYLAND,
          "VA/Wayland display", "wayland" },
166 167 168 169
#endif
#if USE_DRM
        { GST_VAAPI_DISPLAY_TYPE_DRM,
          "VA/DRM display", "drm" },
170 171 172 173 174 175 176 177 178
#endif
        { 0, NULL, NULL },
    };

    if (!g_type)
        g_type = g_enum_register_static("GstVaapiDisplayType", display_types);
    return g_type;
}

179
/* Append GstVideoFormat to formats array */
gb's avatar
gb committed
180
static inline void
181
append_format(GArray *formats, GstVideoFormat format, guint flags)
gb's avatar
gb committed
182
{
183 184 185 186 187
    GstVaapiFormatInfo fi;

    fi.format = format;
    fi.flags = flags;
    g_array_append_val(formats, fi);
gb's avatar
gb committed
188 189 190
}

/* Append VAImageFormats to formats array */
191
static void
192 193
append_formats(GArray *formats, const VAImageFormat *va_formats,
    guint *flags, guint n)
194
{
195
    GstVideoFormat format;
196 197
    const GstVaapiFormatInfo *YV12_fip = NULL;
    const GstVaapiFormatInfo *I420_fip = NULL;
gb's avatar
gb committed
198 199 200 201
    guint i;

    for (i = 0; i < n; i++) {
        const VAImageFormat * const va_format = &va_formats[i];
202
        const GstVaapiFormatInfo **fipp;
203

Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
204
        format = gst_vaapi_video_format_from_va_format(va_format);
205
        if (format == GST_VIDEO_FORMAT_UNKNOWN) {
gb's avatar
gb committed
206 207 208
            GST_DEBUG("unsupported format %" GST_FOURCC_FORMAT,
                      GST_FOURCC_ARGS(va_format->fourcc));
            continue;
209
        }
210
        append_format(formats, format, flags ? flags[i] : 0);
gb's avatar
gb committed
211 212

        switch (format) {
213
        case GST_VIDEO_FORMAT_YV12:
214
            fipp = &YV12_fip;
gb's avatar
gb committed
215
            break;
216
        case GST_VIDEO_FORMAT_I420:
217
            fipp = &I420_fip;
gb's avatar
gb committed
218 219
            break;
        default:
220
            fipp = NULL;
gb's avatar
gb committed
221
            break;
222
        }
223 224 225
        if (fipp)
            *fipp = &g_array_index(formats, GstVaapiFormatInfo,
                formats->len - 1);
226
    }
227 228 229

    /* Append I420 (resp. YV12) format if YV12 (resp. I420) is not
       supported by the underlying driver */
230
    if (YV12_fip && !I420_fip)
231
        append_format(formats, GST_VIDEO_FORMAT_I420, YV12_fip->flags);
232
    else if (I420_fip && !YV12_fip)
233
        append_format(formats, GST_VIDEO_FORMAT_YV12, I420_fip->flags);
234 235 236
}

/* Sort image formats. Prefer YUV formats first */
gb's avatar
gb committed
237 238
static gint
compare_yuv_formats(gconstpointer a, gconstpointer b)
239
{
240 241
    const GstVideoFormat fmt1 = ((GstVaapiFormatInfo *)a)->format;
    const GstVideoFormat fmt2 = ((GstVaapiFormatInfo *)b)->format;
242

Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
243 244
    const gboolean is_fmt1_yuv = gst_vaapi_video_format_is_yuv(fmt1);
    const gboolean is_fmt2_yuv = gst_vaapi_video_format_is_yuv(fmt2);
245 246 247 248

    if (is_fmt1_yuv != is_fmt2_yuv)
        return is_fmt1_yuv ? -1 : 1;

Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
249 250
    return ((gint)gst_vaapi_video_format_get_score(fmt1) -
            (gint)gst_vaapi_video_format_get_score(fmt2));
251 252 253
}

/* Sort subpicture formats. Prefer RGB formats first */
gb's avatar
gb committed
254 255
static gint
compare_rgb_formats(gconstpointer a, gconstpointer b)
256
{
257 258
    const GstVideoFormat fmt1 = ((GstVaapiFormatInfo *)a)->format;
    const GstVideoFormat fmt2 = ((GstVaapiFormatInfo *)b)->format;
259

Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
260 261
    const gboolean is_fmt1_rgb = gst_vaapi_video_format_is_rgb(fmt1);
    const gboolean is_fmt2_rgb = gst_vaapi_video_format_is_rgb(fmt2);
262 263 264 265

    if (is_fmt1_rgb != is_fmt2_rgb)
        return is_fmt1_rgb ? -1 : 1;

Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
266 267
    return ((gint)gst_vaapi_video_format_get_score(fmt1) -
            (gint)gst_vaapi_video_format_get_score(fmt2));
gb's avatar
gb committed
268 269
}

270
/* Check if configs array contains profile at entrypoint */
gb's avatar
gb committed
271
static inline gboolean
272 273 274 275 276
find_config(
    GArray             *configs,
    GstVaapiProfile     profile,
    GstVaapiEntrypoint  entrypoint
)
gb's avatar
gb committed
277
{
278
    GstVaapiConfig *config;
gb's avatar
gb committed
279 280
    guint i;

281 282 283
    if (!configs)
        return FALSE;

284 285 286
    for (i = 0; i < configs->len; i++) {
        config = &g_array_index(configs, GstVaapiConfig, i);
        if (config->profile == profile && config->entrypoint == entrypoint)
gb's avatar
gb committed
287
            return TRUE;
288
    }
gb's avatar
gb committed
289 290 291
    return FALSE;
}

292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
/* HACK: append H.263 Baseline profile if MPEG-4:2 Simple profile is supported */
static void
append_h263_config(GArray *configs)
{
    GstVaapiConfig *config, tmp_config;
    GstVaapiConfig *mpeg4_simple_config = NULL;
    GstVaapiConfig *h263_baseline_config = NULL;
    guint i;

    if (!WORKAROUND_H263_BASELINE_DECODE_PROFILE)
        return;

    if (!configs)
        return;

    for (i = 0; i < configs->len; i++) {
        config = &g_array_index(configs, GstVaapiConfig, i);
        if (config->profile == GST_VAAPI_PROFILE_MPEG4_SIMPLE)
            mpeg4_simple_config = config;
        else if (config->profile == GST_VAAPI_PROFILE_H263_BASELINE)
            h263_baseline_config = config;
    }

    if (mpeg4_simple_config && !h263_baseline_config) {
        tmp_config = *mpeg4_simple_config;
        tmp_config.profile = GST_VAAPI_PROFILE_H263_BASELINE;
        g_array_append_val(configs, tmp_config);
    }
}

322
/* Convert configs array to profiles as GstCaps */
gb's avatar
gb committed
323
static GstCaps *
324
get_profile_caps(GArray *configs)
gb's avatar
gb committed
325
{
326
    GstVaapiConfig *config;
gb's avatar
gb committed
327 328 329
    GstCaps *out_caps, *caps;
    guint i;

330 331 332
    if (!configs)
        return NULL;

gb's avatar
gb committed
333 334 335 336
    out_caps = gst_caps_new_empty();
    if (!out_caps)
        return NULL;

337 338 339
    for (i = 0; i < configs->len; i++) {
        config = &g_array_index(configs, GstVaapiConfig, i);
        caps   = gst_vaapi_profile_get_caps(config->profile);
gb's avatar
gb committed
340
        if (caps)
341
            out_caps = gst_caps_merge(out_caps, caps);
gb's avatar
gb committed
342 343 344 345
    }
    return out_caps;
}

346 347
/* Find format info */
static const GstVaapiFormatInfo *
348
find_format_info(GArray *formats, GstVideoFormat format)
349 350 351 352 353 354 355 356 357 358 359 360
{
    const GstVaapiFormatInfo *fip;
    guint i;

    for (i = 0; i < formats->len; i++) {
        fip = &g_array_index(formats, GstVaapiFormatInfo, i);
        if (fip->format == format)
            return fip;
    }
    return NULL;
}

gb's avatar
gb committed
361 362
/* Check if formats array contains format */
static inline gboolean
363
find_format(GArray *formats, GstVideoFormat format)
gb's avatar
gb committed
364
{
365
    return find_format_info(formats, format) != NULL;
gb's avatar
gb committed
366 367 368 369
}

/* Convert formats array to GstCaps */
static GstCaps *
gb's avatar
gb committed
370
get_format_caps(GArray *formats)
gb's avatar
gb committed
371
{
372
    const GstVaapiFormatInfo *fip;
gb's avatar
gb committed
373 374 375 376 377 378 379 380
    GstCaps *out_caps, *caps;
    guint i;

    out_caps = gst_caps_new_empty();
    if (!out_caps)
        return NULL;

    for (i = 0; i < formats->len; i++) {
381
        fip = &g_array_index(formats, GstVaapiFormatInfo, i);
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
382
        caps = gst_vaapi_video_format_to_caps(fip->format);
gb's avatar
gb committed
383 384 385 386
        if (caps)
            gst_caps_append(out_caps, caps);
    }
    return out_caps;
387 388
}

389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
/* Find display attribute */
static const GstVaapiProperty *
find_property(GArray *properties, const gchar *name)
{
    GstVaapiProperty *prop;
    guint i;

    if (!name)
        return NULL;

    for (i = 0; i < properties->len; i++) {
        prop = &g_array_index(properties, GstVaapiProperty, i);
        if (strcmp(prop->name, name) == 0)
            return prop;
    }
    return NULL;
}

#if 0
static const GstVaapiProperty *
find_property_by_type(GArray *properties, VADisplayAttribType type)
{
    GstVaapiProperty *prop;
    guint i;

    for (i = 0; i < properties->len; i++) {
        prop = &g_array_index(properties, GstVaapiProperty, i);
        if (prop->attribute.type == type)
            return prop;
    }
    return NULL;
}
#endif

423 424 425
static inline const GstVaapiProperty *
find_property_by_pspec(GstVaapiDisplay *display, GParamSpec *pspec)
{
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
    GstVaapiDisplayPrivate * const priv =
        GST_VAAPI_DISPLAY_GET_PRIVATE(display);

    return find_property(priv->properties, pspec->name);
}

static guint
find_property_id(const gchar *name)
{
    typedef struct {
        const gchar *name;
        guint id;
    } property_map;

    static const property_map g_property_map[] = {
        { GST_VAAPI_DISPLAY_PROP_RENDER_MODE,   PROP_RENDER_MODE },
        { GST_VAAPI_DISPLAY_PROP_ROTATION,      PROP_ROTATION    },
        { GST_VAAPI_DISPLAY_PROP_HUE,           PROP_HUE         },
        { GST_VAAPI_DISPLAY_PROP_SATURATION,    PROP_SATURATION  },
        { GST_VAAPI_DISPLAY_PROP_BRIGHTNESS,    PROP_BRIGHTNESS  },
        { GST_VAAPI_DISPLAY_PROP_CONTRAST,      PROP_CONTRAST    },
        { NULL, }
    };

    const property_map *m;
    for (m = g_property_map; m->name != NULL; m++) {
        if (strcmp(m->name, name) == 0)
            return m->id;
    }
    return 0;
456 457
}

458 459 460
static void
gst_vaapi_display_calculate_pixel_aspect_ratio(GstVaapiDisplay *display)
{
461 462
    GstVaapiDisplayPrivate * const priv =
        GST_VAAPI_DISPLAY_GET_PRIVATE(display);
463
    gdouble ratio, delta;
464
    gint i, j, index, windex;
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486

    static const gint par[][2] = {
        {1, 1},         /* regular screen            */
        {16, 15},       /* PAL TV                    */
        {11, 10},       /* 525 line Rec.601 video    */
        {54, 59},       /* 625 line Rec.601 video    */
        {64, 45},       /* 1280x1024 on 16:9 display */
        {5, 3},         /* 1280x1024 on  4:3 display */
        {4, 3}          /*  800x600  on 16:9 display */
    };

    /* First, calculate the "real" ratio based on the X values;
     * which is the "physical" w/h divided by the w/h in pixels of the
     * display */
    if (!priv->width || !priv->height || !priv->width_mm || !priv->height_mm)
        ratio = 1.0;
    else
        ratio = (gdouble)(priv->width_mm * priv->height) /
            (priv->height_mm * priv->width);
    GST_DEBUG("calculated pixel aspect ratio: %f", ratio);

    /* Now, find the one from par[][2] with the lowest delta to the real one */
487 488 489 490
#define DELTA(idx, w) (ABS(ratio - ((gdouble)par[idx][w] / par[idx][!(w)])))
    delta  = DELTA(0, 0);
    index  = 0;
    windex = 0;
491 492

    for (i = 1; i < G_N_ELEMENTS(par); i++) {
493 494 495 496 497 498 499
        for (j = 0; j < 2; j++) {
            const gdouble this_delta = DELTA(i, j);
            if (this_delta < delta) {
                index  = i;
                windex = j;
                delta  = this_delta;
            }
500 501 502 503
        }
    }
#undef DELTA

504 505
    priv->par_n = par[index][windex];
    priv->par_d = par[index][windex ^ 1];
506 507
}

gb's avatar
gb committed
508 509 510
static void
gst_vaapi_display_destroy(GstVaapiDisplay *display)
{
511 512
    GstVaapiDisplayPrivate * const priv =
        GST_VAAPI_DISPLAY_GET_PRIVATE(display);
gb's avatar
gb committed
513

gb's avatar
gb committed
514 515 516 517 518 519 520 521
    if (priv->decoders) {
        g_array_free(priv->decoders, TRUE);
        priv->decoders = NULL;
    }

    if (priv->encoders) {
        g_array_free(priv->encoders, TRUE);
        priv->encoders = NULL;
gb's avatar
gb committed
522 523 524
    }

    if (priv->image_formats) {
gb's avatar
gb committed
525
        g_array_free(priv->image_formats, TRUE);
gb's avatar
gb committed
526 527 528 529
        priv->image_formats = NULL;
    }

    if (priv->subpicture_formats) {
gb's avatar
gb committed
530
        g_array_free(priv->subpicture_formats, TRUE);
gb's avatar
gb committed
531 532 533
        priv->subpicture_formats = NULL;
    }

534 535 536 537 538
    if (priv->properties) {
        g_array_free(priv->properties, TRUE);
        priv->properties = NULL;
    }

gb's avatar
gb committed
539
    if (priv->display) {
540 541
        if (!priv->parent)
            vaTerminate(priv->display);
gb's avatar
gb committed
542 543
        priv->display = NULL;
    }
544

545
    if (!priv->use_foreign_display) {
546 547 548 549
        GstVaapiDisplayClass *klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
        if (klass->close_display)
            klass->close_display(display);
    }
550

551
    gst_vaapi_display_replace_internal(&priv->parent, NULL);
552

553 554 555 556
    if (g_display_cache) {
        gst_vaapi_display_cache_remove(get_display_cache(), display);
        free_display_cache();
    }
gb's avatar
gb committed
557 558 559
}

static gboolean
560 561
gst_vaapi_display_create(GstVaapiDisplay *display,
    GstVaapiDisplayInitType init_type, gpointer init_value)
gb's avatar
gb committed
562
{
563 564 565 566
    GstVaapiDisplayPrivate * const priv =
        GST_VAAPI_DISPLAY_GET_PRIVATE(display);
    const GstVaapiDisplayClass * const klass =
        GST_VAAPI_DISPLAY_GET_CLASS(display);
567
    GstVaapiDisplayCache *cache;
gb's avatar
gb committed
568
    gboolean            has_errors      = TRUE;
569
    VADisplayAttribute *display_attrs   = NULL;
gb's avatar
gb committed
570
    VAProfile          *profiles        = NULL;
gb's avatar
gb committed
571
    VAEntrypoint       *entrypoints     = NULL;
gb's avatar
gb committed
572 573
    VAImageFormat      *formats         = NULL;
    unsigned int       *flags           = NULL;
gb's avatar
gb committed
574
    gint                i, j, n, num_entrypoints, major_version, minor_version;
gb's avatar
gb committed
575
    VAStatus            status;
576 577 578 579 580
    GstVaapiDisplayInfo info;
    const GstVaapiDisplayInfo *cached_info = NULL;

    memset(&info, 0, sizeof(info));
    info.display = display;
581
    info.display_type = priv->display_type;
gb's avatar
gb committed
582

583 584 585 586 587 588 589 590 591 592 593 594
    switch (init_type) {
    case GST_VAAPI_DISPLAY_INIT_FROM_VA_DISPLAY:
        info.va_display = init_value;
        priv->display = init_value;
        priv->use_foreign_display = TRUE;
        break;
    case GST_VAAPI_DISPLAY_INIT_FROM_DISPLAY_NAME:
        if (klass->open_display && !klass->open_display(display, init_value))
            return FALSE;
        goto create_display;
    case GST_VAAPI_DISPLAY_INIT_FROM_NATIVE_DISPLAY:
        if (klass->bind_display && !klass->bind_display(display, init_value))
595
            return FALSE;
596 597
        // fall-through
    create_display:
598 599 600
        if (!klass->get_display || !klass->get_display(display, &info))
            return FALSE;
        priv->display = info.va_display;
601
        priv->display_type = info.display_type;
gb's avatar
gb committed
602 603
        if (klass->get_size)
            klass->get_size(display, &priv->width, &priv->height);
604 605 606
        if (klass->get_size_mm)
            klass->get_size_mm(display, &priv->width_mm, &priv->height_mm);
        gst_vaapi_display_calculate_pixel_aspect_ratio(display);
607
        break;
608 609 610 611
    }
    if (!priv->display)
        return FALSE;

612 613 614
    cache = get_display_cache();
    if (!cache)
        return FALSE;
615 616
    cached_info = gst_vaapi_display_cache_lookup_by_va_display(cache,
        info.va_display);
617
    if (cached_info) {
618 619
        gst_vaapi_display_replace_internal(&priv->parent,
            cached_info->display);
620
        priv->display_type = cached_info->display_type;
621 622 623 624 625 626 627 628
    }

    if (!priv->parent) {
        status = vaInitialize(priv->display, &major_version, &minor_version);
        if (!vaapi_check_status(status, "vaInitialize()"))
            goto end;
        GST_DEBUG("VA-API version %d.%d", major_version, minor_version);
    }
gb's avatar
gb committed
629 630

    /* VA profiles */
gb's avatar
gb committed
631 632 633
    profiles = g_new(VAProfile, vaMaxNumProfiles(priv->display));
    if (!profiles)
        goto end;
gb's avatar
gb committed
634 635 636
    entrypoints = g_new(VAEntrypoint, vaMaxNumEntrypoints(priv->display));
    if (!entrypoints)
        goto end;
gb's avatar
gb committed
637
    status = vaQueryConfigProfiles(priv->display, profiles, &n);
gb's avatar
gb committed
638
    if (!vaapi_check_status(status, "vaQueryConfigProfiles()"))
gb's avatar
gb committed
639
        goto end;
gb's avatar
gb committed
640

gb's avatar
gb committed
641
    GST_DEBUG("%d profiles", n);
642 643 644 645 646 647
    for (i = 0; i < n; i++) {
#if VA_CHECK_VERSION(0,34,0)
        /* Introduced in VA/VPP API */
        if (profiles[i] == VAProfileNone)
            continue;
#endif
gb's avatar
gb committed
648
        GST_DEBUG("  %s", string_of_VAProfile(profiles[i]));
649
    }
gb's avatar
gb committed
650

651
    priv->decoders = g_array_new(FALSE, FALSE, sizeof(GstVaapiConfig));
gb's avatar
gb committed
652 653
    if (!priv->decoders)
        goto end;
654
    priv->encoders = g_array_new(FALSE, FALSE, sizeof(GstVaapiConfig));
gb's avatar
gb committed
655
    if (!priv->encoders)
gb's avatar
gb committed
656
        goto end;
gb's avatar
gb committed
657 658

    for (i = 0; i < n; i++) {
659
        GstVaapiConfig config;
gb's avatar
gb committed
660

661 662
        config.profile = gst_vaapi_profile(profiles[i]);
        if (!config.profile)
gb's avatar
gb committed
663 664 665 666 667 668 669 670
            continue;

        status = vaQueryConfigEntrypoints(
            priv->display,
            profiles[i],
            entrypoints, &num_entrypoints
        );
        if (!vaapi_check_status(status, "vaQueryConfigEntrypoints()"))
671
            continue;
gb's avatar
gb committed
672 673

        for (j = 0; j < num_entrypoints; j++) {
674 675 676 677 678 679
            config.entrypoint = gst_vaapi_entrypoint(entrypoints[j]);
            switch (config.entrypoint) {
            case GST_VAAPI_ENTRYPOINT_VLD:
            case GST_VAAPI_ENTRYPOINT_IDCT:
            case GST_VAAPI_ENTRYPOINT_MOCO:
                g_array_append_val(priv->decoders, config);
gb's avatar
gb committed
680
                break;
681 682
            case GST_VAAPI_ENTRYPOINT_SLICE_ENCODE:
                g_array_append_val(priv->encoders, config);
gb's avatar
gb committed
683 684 685 686
                break;
            }
        }
    }
687
    append_h263_config(priv->decoders);
gb's avatar
gb committed
688

689 690 691 692 693 694 695 696 697 698 699 700
    /* Video processing API */
#if USE_VA_VPP
    status = vaQueryConfigEntrypoints(priv->display, VAProfileNone,
        entrypoints, &num_entrypoints);
    if (vaapi_check_status(status, "vaQueryEntrypoints() [VAProfileNone]")) {
        for (j = 0; j < num_entrypoints; j++) {
            if (entrypoints[j] == VAEntrypointVideoProc)
                priv->has_vpp = TRUE;
        }
    }
#endif

701 702 703 704 705 706 707 708 709 710 711
    /* VA display attributes */
    display_attrs =
        g_new(VADisplayAttribute, vaMaxNumDisplayAttributes(priv->display));
    if (!display_attrs)
        goto end;

    n = 0; /* XXX: workaround old GMA500 bug */
    status = vaQueryDisplayAttributes(priv->display, display_attrs, &n);
    if (!vaapi_check_status(status, "vaQueryDisplayAttributes()"))
        goto end;

712 713 714 715
    priv->properties = g_array_new(FALSE, FALSE, sizeof(GstVaapiProperty));
    if (!priv->properties)
        goto end;

716
    GST_DEBUG("%d display attributes", n);
717 718 719
    for (i = 0; i < n; i++) {
        VADisplayAttribute * const attr = &display_attrs[i];
        GstVaapiProperty prop;
720
        gint value;
721 722 723 724

        GST_DEBUG("  %s", string_of_VADisplayAttributeType(attr->type));

        switch (attr->type) {
725 726 727 728 729 730 731 732 733 734 735
#if !VA_CHECK_VERSION(0,34,0)
        case VADisplayAttribDirectSurface:
            prop.name = GST_VAAPI_DISPLAY_PROP_RENDER_MODE;
            break;
#endif
        case VADisplayAttribRenderMode:
            prop.name = GST_VAAPI_DISPLAY_PROP_RENDER_MODE;
            break;
        case VADisplayAttribRotation:
            prop.name = GST_VAAPI_DISPLAY_PROP_ROTATION;
            break;
736 737 738 739 740 741 742 743 744 745 746 747
        case VADisplayAttribHue:
            prop.name = GST_VAAPI_DISPLAY_PROP_HUE;
            break;
        case VADisplayAttribSaturation:
            prop.name = GST_VAAPI_DISPLAY_PROP_SATURATION;
            break;
        case VADisplayAttribBrightness:
            prop.name = GST_VAAPI_DISPLAY_PROP_BRIGHTNESS;
            break;
        case VADisplayAttribContrast:
            prop.name = GST_VAAPI_DISPLAY_PROP_CONTRAST;
            break;
748
        default:
749
            prop.name = NULL;
750 751
            break;
        }
752
        if (!prop.name)
753
            continue;
754 755 756

        /* Assume the attribute is really supported if we can get the
         * actual and current value */
757 758 759 760
        if (!get_attribute(display, attr->type, &value))
            continue;

        /* Some drivers (e.g. EMGD) have completely random initial
761 762 763
         * values */
        if (value < attr->min_value || value > attr->max_value)
            continue;
764

765
        prop.attribute = *attr;
766 767
        prop.old_value = value;
        g_array_append_val(priv->properties, prop);
768
    }
769

gb's avatar
gb committed
770
    /* VA image formats */
gb's avatar
gb committed
771 772 773 774
    formats = g_new(VAImageFormat, vaMaxNumImageFormats(priv->display));
    if (!formats)
        goto end;
    status = vaQueryImageFormats(priv->display, formats, &n);
gb's avatar
gb committed
775
    if (!vaapi_check_status(status, "vaQueryImageFormats()"))
gb's avatar
gb committed
776
        goto end;
gb's avatar
gb committed
777

gb's avatar
gb committed
778 779
    GST_DEBUG("%d image formats", n);
    for (i = 0; i < n; i++)
780
        GST_DEBUG("  %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS(formats[i].fourcc));
gb's avatar
gb committed
781

gb's avatar
gb committed
782
    priv->image_formats =
783
        g_array_new(FALSE, FALSE, sizeof(GstVaapiFormatInfo));
gb's avatar
gb committed
784 785
    if (!priv->image_formats)
        goto end;
786
    append_formats(priv->image_formats, formats, NULL, n);
gb's avatar
gb committed
787
    g_array_sort(priv->image_formats, compare_yuv_formats);
788

gb's avatar
gb committed
789
    /* VA subpicture formats */
gb's avatar
gb committed
790 791 792 793 794
    n = vaMaxNumSubpictureFormats(priv->display);
    formats = g_renew(VAImageFormat, formats, n);
    flags   = g_new(guint, n);
    if (!formats || !flags)
        goto end;
795
    status = vaQuerySubpictureFormats(priv->display, formats, flags, (guint *)&n);
gb's avatar
gb committed
796
    if (!vaapi_check_status(status, "vaQuerySubpictureFormats()"))
gb's avatar
gb committed
797
        goto end;
798

gb's avatar
gb committed
799
    GST_DEBUG("%d subpicture formats", n);
800
    for (i = 0; i < n; i++) {
801
        GST_DEBUG("  %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS(formats[i].fourcc));
802 803
        flags[i] = to_GstVaapiSubpictureFlags(flags[i]);
    }
gb's avatar
gb committed
804

gb's avatar
gb committed
805
    priv->subpicture_formats =
806
        g_array_new(FALSE, FALSE, sizeof(GstVaapiFormatInfo));
gb's avatar
gb committed
807 808
    if (!priv->subpicture_formats)
        goto end;
809
    append_formats(priv->subpicture_formats, formats, flags, n);
gb's avatar
gb committed
810 811
    g_array_sort(priv->subpicture_formats, compare_rgb_formats);

812 813 814 815 816
    if (!cached_info) {
        if (!gst_vaapi_display_cache_add(cache, &info))
            goto end;
    }

gb's avatar
gb committed
817 818
    has_errors = FALSE;
end:
819
    g_free(display_attrs);
gb's avatar
gb committed
820
    g_free(profiles);
gb's avatar
gb committed
821
    g_free(entrypoints);
gb's avatar
gb committed
822 823 824
    g_free(formats);
    g_free(flags);
    return !has_errors;
gb's avatar
gb committed
825 826
}

gb's avatar
gb committed
827 828 829
static void
gst_vaapi_display_lock_default(GstVaapiDisplay *display)
{
830
    GstVaapiDisplayPrivate *priv = GST_VAAPI_DISPLAY_GET_PRIVATE(display);
831 832

    if (priv->parent)
833
        priv = GST_VAAPI_DISPLAY_GET_PRIVATE(priv->parent);
834
    g_rec_mutex_lock(&priv->mutex);
gb's avatar
gb committed
835 836 837 838 839
}

static void
gst_vaapi_display_unlock_default(GstVaapiDisplay *display)
{
840
    GstVaapiDisplayPrivate *priv = GST_VAAPI_DISPLAY_GET_PRIVATE(display);
841 842

    if (priv->parent)
843
        priv = GST_VAAPI_DISPLAY_GET_PRIVATE(priv->parent);
844
    g_rec_mutex_unlock(&priv->mutex);
gb's avatar
gb committed
845 846
}

gb's avatar
gb committed
847
static void
848
gst_vaapi_display_init(GstVaapiDisplay *display)
gb's avatar
gb committed
849
{
850 851 852 853
    GstVaapiDisplayPrivate * const priv =
        GST_VAAPI_DISPLAY_GET_PRIVATE(display);
    const GstVaapiDisplayClass * const dpy_class =
        GST_VAAPI_DISPLAY_GET_CLASS(display);
gb's avatar
gb committed
854

855 856 857
    priv->display_type  = GST_VAAPI_DISPLAY_TYPE_ANY;
    priv->par_n         = 1;
    priv->par_d         = 1;
gb's avatar
gb committed
858

859
    g_rec_mutex_init(&priv->mutex);
gb's avatar
gb committed
860

861 862
    if (dpy_class->init)
        dpy_class->init(display);
gb's avatar
gb committed
863 864 865
}

static void
866
gst_vaapi_display_finalize(GstVaapiDisplay *display)
gb's avatar
gb committed
867
{
868 869
    GstVaapiDisplayPrivate * const priv =
        GST_VAAPI_DISPLAY_GET_PRIVATE(display);
gb's avatar
gb committed
870

871 872
    gst_vaapi_display_destroy(display);
    g_rec_mutex_clear(&priv->mutex);
873 874
}

875
void
gb's avatar
gb committed
876 877
gst_vaapi_display_class_init(GstVaapiDisplayClass *klass)
{
878 879
    GstVaapiMiniObjectClass * const object_class =
        GST_VAAPI_MINI_OBJECT_CLASS(klass);
gb's avatar
gb committed
880
    GstVaapiDisplayClass * const dpy_class = GST_VAAPI_DISPLAY_CLASS(klass);
gb's avatar
gb committed
881

882
    libgstvaapi_init_once();
883

884 885
    object_class->size          = sizeof(GstVaapiDisplay);
    object_class->finalize      = (GDestroyNotify)gst_vaapi_display_finalize;
886 887
    dpy_class->lock             = gst_vaapi_display_lock_default;
    dpy_class->unlock           = gst_vaapi_display_unlock_default;
888
}
gb's avatar
gb committed
889

890 891 892
static void
gst_vaapi_display_properties_init(void)
{
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
    /**
     * GstVaapiDisplay:render-mode:
     *
     * The VA display rendering mode, expressed as a #GstVaapiRenderMode.
     */
    g_properties[PROP_RENDER_MODE] =
        g_param_spec_enum(GST_VAAPI_DISPLAY_PROP_RENDER_MODE,
                          "render mode",
                          "The display rendering mode",
                          GST_VAAPI_TYPE_RENDER_MODE,
                          DEFAULT_RENDER_MODE,
                          G_PARAM_READWRITE);

    /**
     * GstVaapiDisplay:rotation:
     *
     * The VA display rotation mode, expressed as a #GstVaapiRotation.
     */
    g_properties[PROP_ROTATION] =
        g_param_spec_enum(GST_VAAPI_DISPLAY_PROP_ROTATION,
                          "rotation",
                          "The display rotation mode",
                          GST_VAAPI_TYPE_ROTATION,
                          DEFAULT_ROTATION,
                          G_PARAM_READWRITE);

919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
    /**
     * GstVaapiDisplay:hue:
     *
     * The VA display hue, expressed as a float value. Range is -180.0
     * to 180.0. Default value is 0.0 and represents no modification.
     */
    g_properties[PROP_HUE] =
        g_param_spec_float(GST_VAAPI_DISPLAY_PROP_HUE,
                           "hue",
                           "The display hue value",
                           -180.0, 180.0, 0.0,
                           G_PARAM_READWRITE);

    /**
     * GstVaapiDisplay:saturation:
     *
     * The VA display saturation, expressed as a float value. Range is
     * 0.0 to 2.0. Default value is 1.0 and represents no modification.
     */
    g_properties[PROP_SATURATION] =
        g_param_spec_float(GST_VAAPI_DISPLAY_PROP_SATURATION,
                           "saturation",
                           "The display saturation value",
                           0.0, 2.0, 1.0,
                           G_PARAM_READWRITE);

    /**
     * GstVaapiDisplay:brightness:
     *
     * The VA display brightness, expressed as a float value. Range is
     * -1.0 to 1.0. Default value is 0.0 and represents no modification.
     */
    g_properties[PROP_BRIGHTNESS] =
        g_param_spec_float(GST_VAAPI_DISPLAY_PROP_BRIGHTNESS,
                           "brightness",
                           "The display brightness value",
                           -1.0, 1.0, 0.0,
                           G_PARAM_READWRITE);

    /**
     * GstVaapiDisplay:contrast:
     *
     * The VA display contrast, expressed as a float value. Range is
     * 0.0 to 2.0. Default value is 1.0 and represents no modification.
     */
    g_properties[PROP_CONTRAST] =
        g_param_spec_float(GST_VAAPI_DISPLAY_PROP_CONTRAST,
                           "contrast",
                           "The display contrast value",
                           0.0, 2.0, 1.0,
                           G_PARAM_READWRITE);
970
}
971

972 973 974 975 976 977 978 979 980 981 982
static inline const GstVaapiDisplayClass *
gst_vaapi_display_class(void)
{
    static GstVaapiDisplayClass g_class;
    static gsize g_class_init = FALSE;

    if (g_once_init_enter(&g_class_init)) {
        gst_vaapi_display_class_init(&g_class);
        g_once_init_leave(&g_class_init, TRUE);
    }
    return &g_class;
gb's avatar
gb committed
983 984
}

985 986 987
GstVaapiDisplay *
gst_vaapi_display_new(const GstVaapiDisplayClass *klass,
    GstVaapiDisplayInitType init_type, gpointer init_value)
gb's avatar
gb committed
988
{
989
    GstVaapiDisplay *display;
gb's avatar
gb committed
990

991 992 993 994
    display = (GstVaapiDisplay *)
        gst_vaapi_mini_object_new0(GST_VAAPI_MINI_OBJECT_CLASS(klass));
    if (!display)
        return NULL;
gb's avatar
gb committed
995

996 997 998 999 1000 1001 1002 1003
    gst_vaapi_display_init(display);
    if (!gst_vaapi_display_create(display, init_type, init_value))
        goto error;
    return display;

error:
    gst_vaapi_display_unref_internal(display);
    return NULL;
gb's avatar
gb committed
1004 1005
}

gb's avatar
gb committed
1006 1007 1008 1009 1010 1011 1012 1013 1014
/**
 * gst_vaapi_display_new_with_display:
 * @va_display: a #VADisplay
 *
 * Creates a new #GstVaapiDisplay, using @va_display as the VA
 * display.
 *
 * Return value: the newly created #GstVaapiDisplay object
 */
gb's avatar
gb committed
1015 1016
GstVaapiDisplay *
gst_vaapi_display_new_with_display(VADisplay va_display)
gb's avatar
gb committed
1017
{
1018 1019 1020 1021 1022 1023 1024 1025
    GstVaapiDisplayCache * const cache = get_display_cache();
    const GstVaapiDisplayInfo *info;

    g_return_val_if_fail(va_display != NULL, NULL);
    g_return_val_if_fail(cache != NULL, NULL);

    info = gst_vaapi_display_cache_lookup_by_va_display(cache, va_display);
    if (info)
1026
        return gst_vaapi_display_ref_internal(info->display);
1027

1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
    return gst_vaapi_display_new(gst_vaapi_display_class(),
        GST_VAAPI_DISPLAY_INIT_FROM_VA_DISPLAY, va_display);
}

/**
 * gst_vaapi_display_ref:
 * @display: a #GstVaapiDisplay
 *
 * Atomically increas