gstvaapidisplay.c 52.4 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
 *  Copyright (C) 2011-2013 Intel Corporation
gb's avatar
gb committed
6
 *
gb's avatar
gb committed
7 8 9 10
 *  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
11
 *
gb's avatar
gb committed
12
 *  This library is distributed in the hope that it will be useful,
gb's avatar
gb committed
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
gb's avatar
gb committed
14 15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
gb's avatar
gb committed
16
 *
gb's avatar
gb committed
17 18 19 20
 *  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
21 22
 */

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

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

#define DEBUG 1
gb's avatar
gb committed
38
#include "gstvaapidebug.h"
gb's avatar
gb committed
39

gb's avatar
gb committed
40 41
GST_DEBUG_CATEGORY(gst_debug_vaapi);

42 43 44 45
/* 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
46

47 48 49 50 51 52
typedef struct _GstVaapiConfig GstVaapiConfig;
struct _GstVaapiConfig {
    GstVaapiProfile     profile;
    GstVaapiEntrypoint  entrypoint;
};

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

60 61 62 63 64 65
typedef struct _GstVaapiFormatInfo GstVaapiFormatInfo;
struct _GstVaapiFormatInfo {
    GstVaapiImageFormat format;
    guint               flags;
};

66 67
#define DEFAULT_RENDER_MODE     GST_VAAPI_RENDER_MODE_TEXTURE
#define DEFAULT_ROTATION        GST_VAAPI_ROTATION_0
68

gb's avatar
gb committed
69 70 71
enum {
    PROP_0,

72 73
    PROP_RENDER_MODE,
    PROP_ROTATION,
74 75 76 77
    PROP_HUE,
    PROP_SATURATION,
    PROP_BRIGHTNESS,
    PROP_CONTRAST,
78 79

    N_PROPERTIES
gb's avatar
gb committed
80 81
};

82 83
static GstVaapiDisplayCache *g_display_cache = NULL;

84 85
static GParamSpec *g_properties[N_PROPERTIES] = { NULL, };

86 87 88
static void
gst_vaapi_display_properties_init(void);

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

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

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

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

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

    if (!g_once_init_enter(&g_once))
        return;

109 110
    GST_DEBUG_CATEGORY_INIT(gst_debug_vaapi, "vaapi", 0, "VA-API helper");

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

114 115
    gst_vaapi_display_properties_init();

116 117 118
    g_once_init_leave(&g_once, TRUE);
}

119 120 121 122 123 124 125 126 127 128 129 130 131 132
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();
}

133 134 135 136 137 138 139 140 141 142 143
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;
}

144 145 146 147 148 149 150 151 152
/* 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
153
#if USE_X11
154 155
        { GST_VAAPI_DISPLAY_TYPE_X11,
          "VA/X11 display", "x11" },
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
156
#endif
157 158 159
#if USE_GLX
        { GST_VAAPI_DISPLAY_TYPE_GLX,
          "VA/GLX display", "glx" },
160 161 162 163
#endif
#if USE_WAYLAND
        { GST_VAAPI_DISPLAY_TYPE_WAYLAND,
          "VA/Wayland display", "wayland" },
164 165 166 167
#endif
#if USE_DRM
        { GST_VAAPI_DISPLAY_TYPE_DRM,
          "VA/DRM display", "drm" },
168 169 170 171 172 173 174 175 176
#endif
        { 0, NULL, NULL },
    };

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

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

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

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

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

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

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

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

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

    const gboolean is_fmt1_yuv = gst_vaapi_image_format_is_yuv(fmt1);
    const gboolean is_fmt2_yuv = gst_vaapi_image_format_is_yuv(fmt2);

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

gb's avatar
gb committed
247 248
    return ((gint)gst_vaapi_image_format_get_score(fmt1) -
            (gint)gst_vaapi_image_format_get_score(fmt2));
249 250 251
}

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

    const gboolean is_fmt1_rgb = gst_vaapi_image_format_is_rgb(fmt1);
    const gboolean is_fmt2_rgb = gst_vaapi_image_format_is_rgb(fmt2);

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

gb's avatar
gb committed
264 265 266 267
    return ((gint)gst_vaapi_image_format_get_score(fmt1) -
            (gint)gst_vaapi_image_format_get_score(fmt2));
}

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

279 280 281
    if (!configs)
        return FALSE;

282 283 284
    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
285
            return TRUE;
286
    }
gb's avatar
gb committed
287 288 289
    return FALSE;
}

290 291 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
/* 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);
    }
}

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

328 329 330
    if (!configs)
        return NULL;

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

335 336 337
    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
338
        if (caps)
339
            out_caps = gst_caps_merge(out_caps, caps);
gb's avatar
gb committed
340 341 342 343
    }
    return out_caps;
}

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
/* Find format info */
static const GstVaapiFormatInfo *
find_format_info(GArray *formats, GstVaapiImageFormat format)
{
    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
359 360 361 362
/* Check if formats array contains format */
static inline gboolean
find_format(GArray *formats, GstVaapiImageFormat format)
{
363
    return find_format_info(formats, format) != NULL;
gb's avatar
gb committed
364 365 366 367
}

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

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

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

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
/* 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

421 422 423
static inline const GstVaapiProperty *
find_property_by_pspec(GstVaapiDisplay *display, GParamSpec *pspec)
{
424 425 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
    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;
454 455
}

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

    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 */
485 486 487 488
#define DELTA(idx, w) (ABS(ratio - ((gdouble)par[idx][w] / par[idx][!(w)])))
    delta  = DELTA(0, 0);
    index  = 0;
    windex = 0;
489 490

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

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

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

gb's avatar
gb committed
512 513 514 515 516 517 518 519
    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
520 521 522
    }

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

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

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

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

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

549
    gst_vaapi_display_replace_internal(&priv->parent, NULL);
550

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

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

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

581 582 583 584 585 586 587 588 589 590 591 592
    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))
593
            return FALSE;
594 595
        // fall-through
    create_display:
596 597 598
        if (!klass->get_display || !klass->get_display(display, &info))
            return FALSE;
        priv->display = info.va_display;
599
        priv->display_type = info.display_type;
gb's avatar
gb committed
600 601
        if (klass->get_size)
            klass->get_size(display, &priv->width, &priv->height);
602 603 604
        if (klass->get_size_mm)
            klass->get_size_mm(display, &priv->width_mm, &priv->height_mm);
        gst_vaapi_display_calculate_pixel_aspect_ratio(display);
605
        break;
606 607 608 609
    }
    if (!priv->display)
        return FALSE;

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

    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
627 628

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

gb's avatar
gb committed
639
    GST_DEBUG("%d profiles", n);
640 641 642 643 644 645
    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
646
        GST_DEBUG("  %s", string_of_VAProfile(profiles[i]));
647
    }
gb's avatar
gb committed
648

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

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

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

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

        for (j = 0; j < num_entrypoints; j++) {
672 673 674 675 676 677
            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
678
                break;
679 680
            case GST_VAAPI_ENTRYPOINT_SLICE_ENCODE:
                g_array_append_val(priv->encoders, config);
gb's avatar
gb committed
681 682 683 684
                break;
            }
        }
    }
685
    append_h263_config(priv->decoders);
gb's avatar
gb committed
686

687 688 689 690 691 692 693 694 695 696 697
    /* 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;

698 699 700 701
    priv->properties = g_array_new(FALSE, FALSE, sizeof(GstVaapiProperty));
    if (!priv->properties)
        goto end;

702
    GST_DEBUG("%d display attributes", n);
703 704 705
    for (i = 0; i < n; i++) {
        VADisplayAttribute * const attr = &display_attrs[i];
        GstVaapiProperty prop;
706
        gint value;
707 708 709 710

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

        switch (attr->type) {
711 712 713 714 715 716 717 718 719 720 721
#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;
722 723 724 725 726 727 728 729 730 731 732 733
        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;
734
        default:
735
            prop.name = NULL;
736 737
            break;
        }
738
        if (!prop.name)
739
            continue;
740 741 742

        /* Assume the attribute is really supported if we can get the
         * actual and current value */
743 744 745 746
        if (!get_attribute(display, attr->type, &value))
            continue;

        /* Some drivers (e.g. EMGD) have completely random initial
747 748 749
         * values */
        if (value < attr->min_value || value > attr->max_value)
            continue;
750

751
        prop.attribute = *attr;
752 753
        prop.old_value = value;
        g_array_append_val(priv->properties, prop);
754
    }
755

gb's avatar
gb committed
756
    /* VA image formats */
gb's avatar
gb committed
757 758 759 760
    formats = g_new(VAImageFormat, vaMaxNumImageFormats(priv->display));
    if (!formats)
        goto end;
    status = vaQueryImageFormats(priv->display, formats, &n);
gb's avatar
gb committed
761
    if (!vaapi_check_status(status, "vaQueryImageFormats()"))
gb's avatar
gb committed
762
        goto end;
gb's avatar
gb committed
763

gb's avatar
gb committed
764 765
    GST_DEBUG("%d image formats", n);
    for (i = 0; i < n; i++)
766
        GST_DEBUG("  %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS(formats[i].fourcc));
gb's avatar
gb committed
767

gb's avatar
gb committed
768
    priv->image_formats =
769
        g_array_new(FALSE, FALSE, sizeof(GstVaapiFormatInfo));
gb's avatar
gb committed
770 771
    if (!priv->image_formats)
        goto end;
772
    append_formats(priv->image_formats, formats, NULL, n);
gb's avatar
gb committed
773
    g_array_sort(priv->image_formats, compare_yuv_formats);
774

gb's avatar
gb committed
775
    /* VA subpicture formats */
gb's avatar
gb committed
776 777 778 779 780
    n = vaMaxNumSubpictureFormats(priv->display);
    formats = g_renew(VAImageFormat, formats, n);
    flags   = g_new(guint, n);
    if (!formats || !flags)
        goto end;
781
    status = vaQuerySubpictureFormats(priv->display, formats, flags, (guint *)&n);
gb's avatar
gb committed
782
    if (!vaapi_check_status(status, "vaQuerySubpictureFormats()"))
gb's avatar
gb committed
783
        goto end;
784

gb's avatar
gb committed
785
    GST_DEBUG("%d subpicture formats", n);
786
    for (i = 0; i < n; i++) {
787
        GST_DEBUG("  %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS(formats[i].fourcc));
788 789
        flags[i] = to_GstVaapiSubpictureFlags(flags[i]);
    }
gb's avatar
gb committed
790

gb's avatar
gb committed
791
    priv->subpicture_formats =
792
        g_array_new(FALSE, FALSE, sizeof(GstVaapiFormatInfo));
gb's avatar
gb committed
793 794
    if (!priv->subpicture_formats)
        goto end;
795
    append_formats(priv->subpicture_formats, formats, flags, n);
gb's avatar
gb committed
796 797
    g_array_sort(priv->subpicture_formats, compare_rgb_formats);

798 799 800 801 802
    if (!cached_info) {
        if (!gst_vaapi_display_cache_add(cache, &info))
            goto end;
    }

gb's avatar
gb committed
803 804
    has_errors = FALSE;
end:
805
    g_free(display_attrs);
gb's avatar
gb committed
806
    g_free(profiles);
gb's avatar
gb committed
807
    g_free(entrypoints);
gb's avatar
gb committed
808 809 810
    g_free(formats);
    g_free(flags);
    return !has_errors;
gb's avatar
gb committed
811 812
}

gb's avatar
gb committed
813 814 815
static void
gst_vaapi_display_lock_default(GstVaapiDisplay *display)
{
816
    GstVaapiDisplayPrivate *priv = GST_VAAPI_DISPLAY_GET_PRIVATE(display);
817 818

    if (priv->parent)
819
        priv = GST_VAAPI_DISPLAY_GET_PRIVATE(priv->parent);
820
    g_rec_mutex_lock(&priv->mutex);
gb's avatar
gb committed
821 822 823 824 825
}

static void
gst_vaapi_display_unlock_default(GstVaapiDisplay *display)
{
826
    GstVaapiDisplayPrivate *priv = GST_VAAPI_DISPLAY_GET_PRIVATE(display);
827 828

    if (priv->parent)
829
        priv = GST_VAAPI_DISPLAY_GET_PRIVATE(priv->parent);
830
    g_rec_mutex_unlock(&priv->mutex);
gb's avatar
gb committed
831 832
}

gb's avatar
gb committed
833
static void
834
gst_vaapi_display_init(GstVaapiDisplay *display)
gb's avatar
gb committed
835
{
836 837 838 839
    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
840

841 842 843
    priv->display_type  = GST_VAAPI_DISPLAY_TYPE_ANY;
    priv->par_n         = 1;
    priv->par_d         = 1;
gb's avatar
gb committed
844

845
    g_rec_mutex_init(&priv->mutex);
gb's avatar
gb committed
846

847 848
    if (dpy_class->init)
        dpy_class->init(display);
gb's avatar
gb committed
849 850 851
}

static void
852
gst_vaapi_display_finalize(GstVaapiDisplay *display)
gb's avatar
gb committed
853
{
854 855
    GstVaapiDisplayPrivate * const priv =
        GST_VAAPI_DISPLAY_GET_PRIVATE(display);
gb's avatar
gb committed
856

857 858
    gst_vaapi_display_destroy(display);
    g_rec_mutex_clear(&priv->mutex);
859 860
}

861
void
gb's avatar
gb committed
862 863
gst_vaapi_display_class_init(GstVaapiDisplayClass *klass)
{
864 865
    GstVaapiMiniObjectClass * const object_class =
        GST_VAAPI_MINI_OBJECT_CLASS(klass);
gb's avatar
gb committed
866
    GstVaapiDisplayClass * const dpy_class = GST_VAAPI_DISPLAY_CLASS(klass);
gb's avatar
gb committed
867

868
    libgstvaapi_init_once();
869

870 871
    object_class->size          = sizeof(GstVaapiDisplay);
    object_class->finalize      = (GDestroyNotify)gst_vaapi_display_finalize;
872 873
    dpy_class->lock             = gst_vaapi_display_lock_default;
    dpy_class->unlock           = gst_vaapi_display_unlock_default;
874
}
gb's avatar
gb committed
875

876 877 878
static void
gst_vaapi_display_properties_init(void)
{
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
    /**
     * 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);

905 906 907 908 909 910 911 912 913 914 915 916 917 918 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
    /**
     * 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);
956
}
957

958 959 960 961 962 963 964 965 966 967 968
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
969 970
}

971 972 973
GstVaapiDisplay *
gst_vaapi_display_new(const GstVaapiDisplayClass *klass,
    GstVaapiDisplayInitType init_type, gpointer init_value)
gb's avatar
gb committed
974
{
975
    GstVaapiDisplay *display;
gb's avatar
gb committed
976

977 978 979 980
    display = (GstVaapiDisplay *)
        gst_vaapi_mini_object_new0(GST_VAAPI_MINI_OBJECT_CLASS(klass));
    if (!display)
        return NULL;
gb's avatar
gb committed
981

982 983 984 985 986 987 988 989
    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
990 991
}

gb's avatar
gb committed
992 993 994 995 996 997 998 999 1000
/**
 * 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
1001 1002
GstVaapiDisplay *
gst_vaapi_display_new_with_display(VADisplay va_display)
gb's avatar
gb committed
1003
{
1004 1005 1006 1007 1008 1009 1010 1011
    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)
1012
        return gst_vaapi_display_ref_internal(info->display);
1013

1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 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
    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 increases the reference count of the given @display by one.
 *
 * Returns: The same @display argument
 */
GstVaapiDisplay *
gst_vaapi_display_ref(GstVaapiDisplay *display)
{
    return gst_vaapi_display_ref_internal(display);
}

/**
 * gst_vaapi_display_unref:
 * @display: a #GstVaapiDisplay
 *
 * Atomically decreases the reference count of the @display by one. If
 * the reference count reaches zero, the display will be free'd.
 */
void
gst_vaapi_display_unref(GstVaapiDisplay *display)
{
    gst_vaapi_display_unref_internal(display);
}

/**
 * gst_vaapi_display_replace:
 * @old_display_ptr: a pointer to a #GstVaapiDisplay
 * @new_display: a #GstVaapiDisplay
 *
 * Atomically replaces the display display held in @old_display_ptr
 * with @new_display. This means that @old_display_ptr shall reference
 * a valid display. However, @new_display can be NULL.
 */
void
gst_vaapi_display_replace(GstVaapiDisplay **old_display_ptr,
    GstVaapiDisplay *new_display)
{
    gst_vaapi_display_replace_internal(old_display_ptr, new_display);
gb's avatar
gb committed
1059 1060
}

gb's avatar
gb committed
1061 1062 1063 1064 1065 1066 1067 1068
/**
 * gst_vaapi_display_lock:
 * @display: a #GstVaapiDisplay
 *
 * Locks @display. If @display is already locked by another thread,
 * the current thread will block until @display is unlocked by the
 * other thread.
 */
gb's avatar
gb committed
1069 1070 1071 1072 1073
void
gst_vaapi_display_lock(GstVaapiDisplay *display)
{
    GstVaapiDisplayClass *klass;

1074
    g_return_if_fail(display != NULL);
gb's avatar
gb committed
1075 1076

    klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
1077 1078
    if (klass->lock)
        klass->lock(display);
gb's avatar
gb committed
1079 1080
}

gb's avatar
gb committed
1081 1082 1083 1084 1085 1086 1087 1088
/**
 * gst_vaapi_display_unlock:
 * @display: a #GstVaapiDisplay
 *
 * Unlocks @display. If another thread is blocked in a
 * gst_vaapi_display_lock() call for @display, it will be woken and
 * can lock @display itself.
 */
gb's avatar
gb committed
1089 1090 1091 1092 1093
void
gst_vaapi_display_unlock(GstVaapiDisplay *display)
{
    GstVaapiDisplayClass *klass;

1094
    g_return_if_fail(display != NULL);
gb's avatar
gb committed
1095 1096

    klass = GST_VAAPI_DISPLAY_GET_CLASS(display);
1097 1098
    if (klass->unlock)
        klass->unlock(display);
gb's avatar
gb committed
1099 1100
}

1101 1102