gstvaapicontext.c 27.3 KB
Newer Older
gb's avatar
gb committed
1 2 3
/*
 *  gstvaapicontext.c - VA context 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 25 26 27 28 29
 */

/**
 * SECTION:gstvaapicontext
 * @short_description: VA context abstraction
 */

30
#include "sysdeps.h"
gb's avatar
gb committed
31 32 33
#include <assert.h>
#include "gstvaapicompat.h"
#include "gstvaapicontext.h"
34
#include "gstvaapiobject_priv.h"
35 36
#include "gstvaapisurface.h"
#include "gstvaapisurface_priv.h"
gb's avatar
gb committed
37
#include "gstvaapisurfacepool.h"
38
#include "gstvaapisurfaceproxy.h"
39
#include "gstvaapivideopool_priv.h"
40 41
#include "gstvaapiimage.h"
#include "gstvaapisubpicture.h"
gb's avatar
gb committed
42 43 44 45 46
#include "gstvaapiutils.h"

#define DEBUG 1
#include "gstvaapidebug.h"

47
typedef struct _GstVaapiContextClass            GstVaapiContextClass;
gb's avatar
gb committed
48

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
/**
 * GstVaapiContext:
 *
 * A VA context wrapper.
 */
struct _GstVaapiContext {
    /*< private >*/
    GstVaapiObject      parent_instance;

    GstVaapiContextInfo info;
    VAConfigID          config_id;
    GPtrArray          *surfaces;
    GstVaapiVideoPool  *surfaces_pool;
    GPtrArray          *overlays[2];
    guint               overlay_id;
};

/**
 * GstVaapiContextClass:
 *
 * A VA context wrapper class.
 */
struct _GstVaapiContextClass {
    /*< private >*/
    GstVaapiObjectClass parent_class;
};
gb's avatar
gb committed
75

76 77 78 79
typedef struct _GstVaapiOverlayRectangle GstVaapiOverlayRectangle;
struct _GstVaapiOverlayRectangle {
    GstVaapiContext    *context;
    GstVaapiSubpicture *subpicture;
80
    GstVaapiRectangle   render_rect;
81
    guint               seq_num;
82
    guint               layer_id;
83
    GstBuffer          *rect_buffer;
84
    GstVideoOverlayRectangle *rect;
85
    guint               is_associated   : 1;
86 87
};

88 89 90 91 92 93 94
static guint
get_max_ref_frames(GstVaapiProfile profile)
{
    guint ref_frames;

    switch (gst_vaapi_profile_get_codec(profile)) {
    case GST_VAAPI_CODEC_H264:  ref_frames = 16; break;
95
    case GST_VAAPI_CODEC_JPEG:  ref_frames =  0; break;
96 97 98 99 100
    default:                    ref_frames =  2; break;
    }
    return ref_frames;
}

101 102 103 104 105 106 107 108
static inline void
gst_video_overlay_rectangle_replace(GstVideoOverlayRectangle **old_rect_ptr,
    GstVideoOverlayRectangle *new_rect)
{
    gst_mini_object_replace((GstMiniObject **)old_rect_ptr,
        GST_MINI_OBJECT_CAST(new_rect));
}

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
#define overlay_rectangle_ref(overlay) \
    gst_vaapi_mini_object_ref(GST_VAAPI_MINI_OBJECT(overlay))

#define overlay_rectangle_unref(overlay) \
    gst_vaapi_mini_object_unref(GST_VAAPI_MINI_OBJECT(overlay))

#define overlay_rectangle_replace(old_overlay_ptr, new_overlay) \
    gst_vaapi_mini_object_replace((GstVaapiMiniObject **)(old_overlay_ptr), \
        (GstVaapiMiniObject *)(new_overlay))

static void
overlay_rectangle_finalize(GstVaapiOverlayRectangle *overlay);

static gboolean
overlay_rectangle_associate(GstVaapiOverlayRectangle *overlay);

static gboolean
overlay_rectangle_deassociate(GstVaapiOverlayRectangle *overlay);

static inline const GstVaapiMiniObjectClass *
overlay_rectangle_class(void)
{
    static const GstVaapiMiniObjectClass GstVaapiOverlayRectangleClass = {
        sizeof(GstVaapiOverlayRectangle),
        (GDestroyNotify)overlay_rectangle_finalize
    };
    return &GstVaapiOverlayRectangleClass;
}

138
static GstVaapiOverlayRectangle *
139 140
overlay_rectangle_new(GstVideoOverlayRectangle *rect, GstVaapiContext *context,
    guint layer_id)
141 142
{
    GstVaapiOverlayRectangle *overlay;
143
    GstVaapiRectangle *render_rect;
144
    guint width, height, flags;
145
    gint x, y;
146

147 148
    overlay = (GstVaapiOverlayRectangle *)
        gst_vaapi_mini_object_new0(overlay_rectangle_class());
149 150 151
    if (!overlay)
        return NULL;

152 153
    overlay->context    = context;
    overlay->seq_num    = gst_video_overlay_rectangle_get_seqnum(rect);
154
    overlay->layer_id   = layer_id;
155
    overlay->rect       = gst_video_overlay_rectangle_ref(rect);
156

157
    flags = gst_video_overlay_rectangle_get_flags(rect);
158
    gst_buffer_replace(&overlay->rect_buffer,
159
        gst_video_overlay_rectangle_get_pixels_unscaled_raw(rect, flags));
160 161 162
    if (!overlay->rect_buffer)
        goto error;

163 164 165 166 167 168 169 170 171 172 173 174
    overlay->subpicture = gst_vaapi_subpicture_new_from_overlay_rectangle(
        GST_VAAPI_OBJECT_DISPLAY(context), rect);
    if (!overlay->subpicture)
        goto error;

    gst_video_overlay_rectangle_get_render_rectangle(rect,
        &x, &y, &width, &height);
    render_rect = &overlay->render_rect;
    render_rect->x = x;
    render_rect->y = y;
    render_rect->width = width;
    render_rect->height = height;
175
    return overlay;
176 177 178 179

error:
    overlay_rectangle_unref(overlay);
    return NULL;
180 181 182
}

static void
183
overlay_rectangle_finalize(GstVaapiOverlayRectangle *overlay)
184
{
185
    gst_buffer_replace(&overlay->rect_buffer, NULL);
186 187
    gst_video_overlay_rectangle_unref(overlay->rect);

188
    if (overlay->subpicture) {
189
        overlay_rectangle_deassociate(overlay);
190
        gst_vaapi_object_unref(overlay->subpicture);
191 192
        overlay->subpicture = NULL;
    }
193 194 195 196 197 198
}

static gboolean
overlay_rectangle_associate(GstVaapiOverlayRectangle *overlay)
{
    GstVaapiSubpicture * const subpicture = overlay->subpicture;
199
    GPtrArray * const surfaces = overlay->context->surfaces;
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
    guint i, n_associated;

    if (overlay->is_associated)
        return TRUE;

    n_associated = 0;
    for (i = 0; i < surfaces->len; i++) {
        GstVaapiSurface * const surface = g_ptr_array_index(surfaces, i);
        if (gst_vaapi_surface_associate_subpicture(surface, subpicture,
                NULL, &overlay->render_rect))
            n_associated++;
    }

    overlay->is_associated = TRUE;
    return n_associated == surfaces->len;
}

static gboolean
overlay_rectangle_deassociate(GstVaapiOverlayRectangle *overlay)
{
    GstVaapiSubpicture * const subpicture = overlay->subpicture;
221
    GPtrArray * const surfaces = overlay->context->surfaces;
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
    guint i, n_associated;

    if (!overlay->is_associated)
        return TRUE;

    n_associated = surfaces->len;
    for (i = 0; i < surfaces->len; i++) {
        GstVaapiSurface * const surface = g_ptr_array_index(surfaces, i);
        if (gst_vaapi_surface_deassociate_subpicture(surface, subpicture))
            n_associated--;
    }

    overlay->is_associated = FALSE;
    return n_associated == 0;
}

238 239 240 241
static gboolean
overlay_rectangle_changed_pixels(GstVaapiOverlayRectangle *overlay,
    GstVideoOverlayRectangle *rect)
{
242
    guint flags;
243 244
    GstBuffer *buffer;

245 246
    if (overlay->seq_num == gst_video_overlay_rectangle_get_seqnum(rect))
        return FALSE;
247 248 249 250

    flags = to_GstVideoOverlayFormatFlags(
        gst_vaapi_subpicture_get_flags(overlay->subpicture));

251 252 253
    buffer = gst_video_overlay_rectangle_get_pixels_unscaled_raw(rect, flags);
    if (!buffer)
        return FALSE;
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
#if GST_CHECK_VERSION(1,0,0)
    {
        const guint n_blocks = gst_buffer_n_memory(buffer);
        gsize ofs;
        guint i;

        if (buffer == overlay->rect_buffer)
            return TRUE;

        if (n_blocks != gst_buffer_n_memory(overlay->rect_buffer))
            return FALSE;

        for (i = 0; i < n_blocks; i++) {
            GstMemory * const mem1 = gst_buffer_peek_memory(buffer, i);
            GstMemory * const mem2 =
                gst_buffer_peek_memory(overlay->rect_buffer, i);
            if (!gst_memory_is_span(mem1, mem2, &ofs))
                return FALSE;
        }
    }
#else
275 276
    if (GST_BUFFER_DATA(overlay->rect_buffer) != GST_BUFFER_DATA(buffer))
        return FALSE;
277
#endif
278
    return TRUE;
279 280
}

281
static gboolean
282
overlay_rectangle_changed_render_rect(GstVaapiOverlayRectangle *overlay,
283 284 285 286 287 288 289 290 291 292 293 294 295
    GstVideoOverlayRectangle *rect)
{
    GstVaapiRectangle * const render_rect = &overlay->render_rect;
    guint width, height;
    gint x, y;

    gst_video_overlay_rectangle_get_render_rectangle(rect,
        &x, &y, &width, &height);

    if (x == render_rect->x &&
        y == render_rect->y &&
        width == render_rect->width &&
        height == render_rect->height)
296
        return FALSE;
297 298 299 300 301

    render_rect->x = x;
    render_rect->y = y;
    render_rect->width = width;
    render_rect->height = height;
302
    return TRUE;
303 304
}

305 306 307 308
static inline gboolean
overlay_rectangle_update_global_alpha(GstVaapiOverlayRectangle *overlay,
    GstVideoOverlayRectangle *rect)
{
309
#ifdef HAVE_GST_VIDEO_OVERLAY_HWCAPS
310 311 312
    const guint flags = gst_video_overlay_rectangle_get_flags(rect);
    if (!(flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_GLOBAL_ALPHA))
        return TRUE;
313
#endif
314 315 316 317
    return gst_vaapi_subpicture_set_global_alpha(overlay->subpicture,
        gst_video_overlay_rectangle_get_global_alpha(rect));
}

318 319
static gboolean
overlay_rectangle_update(GstVaapiOverlayRectangle *overlay,
320
    GstVideoOverlayRectangle *rect, gboolean *reassociate_ptr)
321 322 323
{
    if (overlay_rectangle_changed_pixels(overlay, rect))
        return FALSE;
324 325
    if (overlay_rectangle_changed_render_rect(overlay, rect))
        *reassociate_ptr = TRUE;
326 327
    if (!overlay_rectangle_update_global_alpha(overlay, rect))
        return FALSE;
328 329 330 331
    gst_video_overlay_rectangle_replace(&overlay->rect, rect);
    return TRUE;
}

332 333 334 335 336
static inline GPtrArray *
overlay_new(void)
{
    return g_ptr_array_new_with_free_func(
        (GDestroyNotify)gst_vaapi_mini_object_unref);
337 338 339
}

static void
340
overlay_destroy(GPtrArray **overlay_ptr)
341
{
342
    GPtrArray * const overlay = *overlay_ptr;
343

344 345 346 347 348 349 350 351 352 353 354
    if (!overlay)
        return;
    g_ptr_array_unref(overlay);
    *overlay_ptr = NULL;
}

static void
overlay_clear(GPtrArray *overlay)
{
    if (overlay && overlay->len > 0)
        g_ptr_array_remove_range(overlay, 0, overlay->len);
355 356
}

357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
static GstVaapiOverlayRectangle *
overlay_lookup(GPtrArray *overlays, GstVideoOverlayRectangle *rect)
{
    guint i;

    for (i = 0; i < overlays->len; i++) {
        GstVaapiOverlayRectangle * const overlay =
            g_ptr_array_index(overlays, i);

        if (overlay->rect == rect)
            return overlay;
    }
    return NULL;
}

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
static gboolean
overlay_reassociate(GPtrArray *overlays)
{
    guint i;

    for (i = 0; i < overlays->len; i++)
        overlay_rectangle_deassociate(g_ptr_array_index(overlays, i));

    for (i = 0; i < overlays->len; i++) {
        if (!overlay_rectangle_associate(g_ptr_array_index(overlays, i)))
            return FALSE;
    }
    return TRUE;
}

387
static void
388
gst_vaapi_context_clear_overlay(GstVaapiContext *context)
389
{
390 391 392
    overlay_clear(context->overlays[0]);
    overlay_clear(context->overlays[1]);
    context->overlay_id = 0;
393 394 395 396 397 398
}

static inline void
gst_vaapi_context_destroy_overlay(GstVaapiContext *context)
{
    gst_vaapi_context_clear_overlay(context);
399 400
}

gb's avatar
gb committed
401 402 403
static void
unref_surface_cb(gpointer data, gpointer user_data)
{
404 405 406
    GstVaapiSurface * const surface = GST_VAAPI_SURFACE(data);

    gst_vaapi_surface_set_parent_context(surface, NULL);
407
    gst_vaapi_object_unref(surface);
gb's avatar
gb committed
408 409 410 411 412
}

static void
gst_vaapi_context_destroy_surfaces(GstVaapiContext *context)
{
413 414
    gst_vaapi_context_destroy_overlay(context);

415 416 417 418
    if (context->surfaces) {
        g_ptr_array_foreach(context->surfaces, unref_surface_cb, NULL);
        g_ptr_array_free(context->surfaces, TRUE);
        context->surfaces = NULL;
gb's avatar
gb committed
419
    }
420
    gst_vaapi_video_pool_replace(&context->surfaces_pool, NULL);
gb's avatar
gb committed
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
}

static void
gst_vaapi_context_destroy(GstVaapiContext *context)
{
    GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(context);
    VAContextID context_id;
    VAStatus status;

    context_id = GST_VAAPI_OBJECT_ID(context);
    GST_DEBUG("context %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(context_id));

    if (context_id != VA_INVALID_ID) {
        GST_VAAPI_DISPLAY_LOCK(display);
        status = vaDestroyContext(
            GST_VAAPI_DISPLAY_VADISPLAY(display),
            context_id
        );
        GST_VAAPI_DISPLAY_UNLOCK(display);
        if (!vaapi_check_status(status, "vaDestroyContext()"))
            g_warning("failed to destroy context %" GST_VAAPI_ID_FORMAT,
                      GST_VAAPI_ID_ARGS(context_id));
        GST_VAAPI_OBJECT_ID(context) = VA_INVALID_ID;
    }

446
    if (context->config_id != VA_INVALID_ID) {
gb's avatar
gb committed
447 448 449
        GST_VAAPI_DISPLAY_LOCK(display);
        status = vaDestroyConfig(
            GST_VAAPI_DISPLAY_VADISPLAY(display),
450
            context->config_id
gb's avatar
gb committed
451 452 453 454
        );
        GST_VAAPI_DISPLAY_UNLOCK(display);
        if (!vaapi_check_status(status, "vaDestroyConfig()"))
            g_warning("failed to destroy config %" GST_VAAPI_ID_FORMAT,
455 456
                      GST_VAAPI_ID_ARGS(context->config_id));
        context->config_id = VA_INVALID_ID;
gb's avatar
gb committed
457 458 459
    }
}

460 461 462
static gboolean
gst_vaapi_context_create_overlay(GstVaapiContext *context)
{
463
    if (!context->overlays[0] || !context->overlays[1])
464 465
        return FALSE;

466
    gst_vaapi_context_clear_overlay(context);
467 468 469
    return TRUE;
}

gb's avatar
gb committed
470 471 472
static gboolean
gst_vaapi_context_create_surfaces(GstVaapiContext *context)
{
473
    const GstVaapiContextInfo * const cip = &context->info;
474
    GstVideoInfo vi;
gb's avatar
gb committed
475
    GstVaapiSurface *surface;
476
    guint i, num_surfaces;
gb's avatar
gb committed
477 478 479 480

    /* Number of scratch surfaces beyond those used as reference */
    const guint SCRATCH_SURFACES_COUNT = 4;

481 482 483
    if (!gst_vaapi_context_create_overlay(context))
        return FALSE;

484 485 486
    if (!context->surfaces) {
        context->surfaces = g_ptr_array_new();
        if (!context->surfaces)
gb's avatar
gb committed
487 488 489
            return FALSE;
    }

490
    if (!context->surfaces_pool) {
491 492
        gst_video_info_set_format(&vi, GST_VIDEO_FORMAT_ENCODED,
            cip->width, cip->height);
493
        context->surfaces_pool = gst_vaapi_surface_pool_new(
494
            GST_VAAPI_OBJECT_DISPLAY(context), &vi);
495
        if (!context->surfaces_pool)
gb's avatar
gb committed
496 497 498
            return FALSE;
    }

499 500
    num_surfaces = cip->ref_frames + SCRATCH_SURFACES_COUNT;
    gst_vaapi_video_pool_set_capacity(context->surfaces_pool, num_surfaces);
gb's avatar
gb committed
501

502
    for (i = context->surfaces->len; i < num_surfaces; i++) {
gb's avatar
gb committed
503 504 505
        surface = gst_vaapi_surface_new(
            GST_VAAPI_OBJECT_DISPLAY(context),
            GST_VAAPI_CHROMA_TYPE_YUV420,
506
            cip->width, cip->height
gb's avatar
gb committed
507 508 509
        );
        if (!surface)
            return FALSE;
510
        gst_vaapi_surface_set_parent_context(surface, context);
511 512
        g_ptr_array_add(context->surfaces, surface);
        if (!gst_vaapi_video_pool_add_object(context->surfaces_pool, surface))
gb's avatar
gb committed
513 514 515 516 517 518 519 520
            return FALSE;
    }
    return TRUE;
}

static gboolean
gst_vaapi_context_create(GstVaapiContext *context)
{
521
    const GstVaapiContextInfo * const cip = &context->info;
gb's avatar
gb committed
522 523 524 525 526 527 528 529 530 531 532
    GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(context);
    VAProfile va_profile;
    VAEntrypoint va_entrypoint;
    VAConfigAttrib attrib;
    VAContextID context_id;
    VASurfaceID surface_id;
    VAStatus status;
    GArray *surfaces = NULL;
    gboolean success = FALSE;
    guint i;

533
    if (!context->surfaces && !gst_vaapi_context_create_surfaces(context))
gb's avatar
gb committed
534 535 536 537 538 539
        goto end;

    surfaces = g_array_sized_new(
        FALSE,
        FALSE,
        sizeof(VASurfaceID),
540
        context->surfaces->len
gb's avatar
gb committed
541 542 543 544
    );
    if (!surfaces)
        goto end;

545 546 547
    for (i = 0; i < context->surfaces->len; i++) {
        GstVaapiSurface * const surface =
            g_ptr_array_index(context->surfaces, i);
gb's avatar
gb committed
548 549 550 551 552
        if (!surface)
            goto end;
        surface_id = GST_VAAPI_OBJECT_ID(surface);
        g_array_append_val(surfaces, surface_id);
    }
553
    assert(surfaces->len == context->surfaces->len);
gb's avatar
gb committed
554

555
    if (!cip->profile || !cip->entrypoint)
gb's avatar
gb committed
556
        goto end;
557 558
    va_profile    = gst_vaapi_profile_get_va_profile(cip->profile);
    va_entrypoint = gst_vaapi_entrypoint_get_va_entrypoint(cip->entrypoint);
gb's avatar
gb committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579

    GST_VAAPI_DISPLAY_LOCK(display);
    attrib.type = VAConfigAttribRTFormat;
    status = vaGetConfigAttributes(
        GST_VAAPI_DISPLAY_VADISPLAY(display),
        va_profile,
        va_entrypoint,
        &attrib, 1
    );
    GST_VAAPI_DISPLAY_UNLOCK(display);
    if (!vaapi_check_status(status, "vaGetConfigAttributes()"))
        goto end;
    if (!(attrib.value & VA_RT_FORMAT_YUV420))
        goto end;

    GST_VAAPI_DISPLAY_LOCK(display);
    status = vaCreateConfig(
        GST_VAAPI_DISPLAY_VADISPLAY(display),
        va_profile,
        va_entrypoint,
        &attrib, 1,
580
        &context->config_id
gb's avatar
gb committed
581 582 583 584 585 586 587 588
    );
    GST_VAAPI_DISPLAY_UNLOCK(display);
    if (!vaapi_check_status(status, "vaCreateConfig()"))
        goto end;

    GST_VAAPI_DISPLAY_LOCK(display);
    status = vaCreateContext(
        GST_VAAPI_DISPLAY_VADISPLAY(display),
589 590
        context->config_id,
        cip->width, cip->height,
gb's avatar
gb committed
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
        VA_PROGRESSIVE,
        (VASurfaceID *)surfaces->data, surfaces->len,
        &context_id
    );
    GST_VAAPI_DISPLAY_UNLOCK(display);
    if (!vaapi_check_status(status, "vaCreateContext()"))
        goto end;

    GST_DEBUG("context %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(context_id));
    GST_VAAPI_OBJECT_ID(context) = context_id;
    success = TRUE;
end:
    if (surfaces)
        g_array_free(surfaces, TRUE);
    return success;
}

608 609
static inline void
gst_vaapi_context_init(GstVaapiContext *context, const GstVaapiContextInfo *cip)
gb's avatar
gb committed
610
{
611 612 613 614
    context->info        = *cip;
    context->config_id   = VA_INVALID_ID;
    context->overlays[0] = overlay_new();
    context->overlays[1] = overlay_new();
gb's avatar
gb committed
615 616 617
}

static void
618
gst_vaapi_context_finalize(GstVaapiContext *context)
gb's avatar
gb committed
619
{
620 621 622 623
    overlay_destroy(&context->overlays[0]);
    overlay_destroy(&context->overlays[1]);
    gst_vaapi_context_destroy(context);
    gst_vaapi_context_destroy_surfaces(context);
gb's avatar
gb committed
624 625
}

626
GST_VAAPI_OBJECT_DEFINE_CLASS(GstVaapiContext, gst_vaapi_context)
gb's avatar
gb committed
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645

/**
 * gst_vaapi_context_new:
 * @display: a #GstVaapiDisplay
 * @profile: a #GstVaapiProfile
 * @entrypoint: a #GstVaapiEntrypoint
 * @width: coded width from the bitstream
 * @height: coded height from the bitstream
 *
 * Creates a new #GstVaapiContext with the specified codec @profile
 * and @entrypoint.
 *
 * Return value: the newly allocated #GstVaapiContext object
 */
GstVaapiContext *
gst_vaapi_context_new(
    GstVaapiDisplay    *display,
    GstVaapiProfile     profile,
    GstVaapiEntrypoint  entrypoint,
646 647
    guint               width,
    guint               height
gb's avatar
gb committed
648
)
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
{
    GstVaapiContextInfo info;

    info.profile    = profile;
    info.entrypoint = entrypoint;
    info.width      = width;
    info.height     = height;
    info.ref_frames = get_max_ref_frames(profile);
    return gst_vaapi_context_new_full(display, &info);
}

/**
 * gst_vaapi_context_new_full:
 * @display: a #GstVaapiDisplay
 * @cip: a pointer to the #GstVaapiContextInfo
 *
 * Creates a new #GstVaapiContext with the configuration specified by
 * @cip, thus including profile, entry-point, encoded size and maximum
 * number of reference frames reported by the bitstream.
 *
 * Return value: the newly allocated #GstVaapiContext object
 */
GstVaapiContext *
672 673
gst_vaapi_context_new_full(GstVaapiDisplay *display,
    const GstVaapiContextInfo *cip)
gb's avatar
gb committed
674 675 676
{
    GstVaapiContext *context;

677 678 679 680
    g_return_val_if_fail(cip->profile, NULL);
    g_return_val_if_fail(cip->entrypoint, NULL);
    g_return_val_if_fail(cip->width > 0, NULL);
    g_return_val_if_fail(cip->height > 0, NULL);
gb's avatar
gb committed
681

682 683
    context = gst_vaapi_object_new(gst_vaapi_context_class(), display);
    if (!context)
gb's avatar
gb committed
684
        return NULL;
685 686 687 688

    gst_vaapi_context_init(context, cip);
    if (!gst_vaapi_context_create(context))
        goto error;
gb's avatar
gb committed
689
    return context;
690 691 692 693

error:
    gst_vaapi_object_unref(context);
    return NULL;
gb's avatar
gb committed
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
}

/**
 * gst_vaapi_context_reset:
 * @context: a #GstVaapiContext
 * @profile: a #GstVaapiProfile
 * @entrypoint: a #GstVaapiEntrypoint
 * @width: coded width from the bitstream
 * @height: coded height from the bitstream
 *
 * Resets @context to the specified codec @profile and @entrypoint.
 * The surfaces will be reallocated if the coded size changed.
 *
 * Return value: %TRUE on success
 */
gboolean
gst_vaapi_context_reset(
    GstVaapiContext    *context,
    GstVaapiProfile     profile,
    GstVaapiEntrypoint  entrypoint,
    unsigned int        width,
    unsigned int        height
)
717 718 719 720 721 722 723
{
    GstVaapiContextInfo info;

    info.profile    = profile;
    info.entrypoint = entrypoint;
    info.width      = width;
    info.height     = height;
724
    info.ref_frames = context->info.ref_frames;
725 726 727 728 729 730 731

    return gst_vaapi_context_reset_full(context, &info);
}

/**
 * gst_vaapi_context_reset_full:
 * @context: a #GstVaapiContext
732
 * @new_cip: a pointer to the new #GstVaapiContextInfo details
733
 *
734
 * Resets @context to the configuration specified by @new_cip, thus
735 736 737 738 739 740
 * including profile, entry-point, encoded size and maximum number of
 * reference frames reported by the bitstream.
 *
 * Return value: %TRUE on success
 */
gboolean
741 742
gst_vaapi_context_reset_full(GstVaapiContext *context,
    const GstVaapiContextInfo *new_cip)
gb's avatar
gb committed
743
{
744
    GstVaapiContextInfo * const cip = &context->info;
gb's avatar
gb committed
745 746
    gboolean size_changed, codec_changed;

747 748
    size_changed = cip->width != new_cip->width ||
        cip->height != new_cip->height;
gb's avatar
gb committed
749 750
    if (size_changed) {
        gst_vaapi_context_destroy_surfaces(context);
751 752
        cip->width  = new_cip->width;
        cip->height = new_cip->height;
gb's avatar
gb committed
753 754
    }

755 756
    codec_changed = cip->profile != new_cip->profile ||
        cip->entrypoint != new_cip->entrypoint;
gb's avatar
gb committed
757 758
    if (codec_changed) {
        gst_vaapi_context_destroy(context);
759 760
        cip->profile    = new_cip->profile;
        cip->entrypoint = new_cip->entrypoint;
gb's avatar
gb committed
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
    }

    if (size_changed && !gst_vaapi_context_create_surfaces(context))
        return FALSE;

    if (codec_changed && !gst_vaapi_context_create(context))
        return FALSE;

    return TRUE;
}

/**
 * gst_vaapi_context_get_id:
 * @context: a #GstVaapiContext
 *
 * Returns the underlying VAContextID of the @context.
 *
 * Return value: the underlying VA context id
 */
GstVaapiID
gst_vaapi_context_get_id(GstVaapiContext *context)
{
783
    g_return_val_if_fail(context != NULL, VA_INVALID_ID);
gb's avatar
gb committed
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798

    return GST_VAAPI_OBJECT_ID(context);
}

/**
 * gst_vaapi_context_get_profile:
 * @context: a #GstVaapiContext
 *
 * Returns the VA profile used by the @context.
 *
 * Return value: the VA profile used by the @context
 */
GstVaapiProfile
gst_vaapi_context_get_profile(GstVaapiContext *context)
{
799
    g_return_val_if_fail(context != NULL, 0);
gb's avatar
gb committed
800

801
    return context->info.profile;
gb's avatar
gb committed
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
}

/**
 * gst_vaapi_context_set_profile:
 * @context: a #GstVaapiContext
 * @profile: the new #GstVaapiProfile to use
 *
 * Sets the new @profile to use with the @context. If @profile matches
 * the previous profile, this call has no effect. Otherwise, the
 * underlying VA context is recreated, while keeping the previously
 * allocated surfaces.
 *
 * Return value: %TRUE on success
 */
gboolean
gst_vaapi_context_set_profile(GstVaapiContext *context, GstVaapiProfile profile)
{
819
    g_return_val_if_fail(context != NULL, FALSE);
gb's avatar
gb committed
820 821 822 823
    g_return_val_if_fail(profile, FALSE);

    return gst_vaapi_context_reset(context,
                                   profile,
824 825 826
                                   context->info.entrypoint,
                                   context->info.width,
                                   context->info.height);
gb's avatar
gb committed
827 828 829 830 831 832 833 834 835 836 837 838 839
}

/**
 * gst_vaapi_context_get_entrypoint:
 * @context: a #GstVaapiContext
 *
 * Returns the VA entrypoint used by the @context
 *
 * Return value: the VA entrypoint used by the @context
 */
GstVaapiEntrypoint
gst_vaapi_context_get_entrypoint(GstVaapiContext *context)
{
840
    g_return_val_if_fail(context != NULL, 0);
gb's avatar
gb committed
841

842
    return context->info.entrypoint;
gb's avatar
gb committed
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
}

/**
 * gst_vaapi_context_get_size:
 * @context: a #GstVaapiContext
 * @pwidth: return location for the width, or %NULL
 * @pheight: return location for the height, or %NULL
 *
 * Retrieves the size of the surfaces attached to @context.
 */
void
gst_vaapi_context_get_size(
    GstVaapiContext *context,
    guint           *pwidth,
    guint           *pheight
)
{
860
    g_return_if_fail(context != NULL);
gb's avatar
gb committed
861 862

    if (pwidth)
863
        *pwidth = context->info.width;
gb's avatar
gb committed
864 865

    if (pheight)
866
        *pheight = context->info.height;
gb's avatar
gb committed
867 868
}

869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
/**
 * gst_vaapi_context_get_surface_proxy:
 * @context: a #GstVaapiContext
 *
 * Acquires a free surface, wrapped into a #GstVaapiSurfaceProxy. The
 * returned surface will be automatically released when the proxy is
 * destroyed. So, it is enough to call gst_vaapi_surface_proxy_unref()
 * after usage.
 *
 * This function returns %NULL if there is no free surface available
 * in the pool. The surfaces are pre-allocated during context creation
 * though.
 *
 * Return value: a free surface, or %NULL if none is available
 */
GstVaapiSurfaceProxy *
gst_vaapi_context_get_surface_proxy(GstVaapiContext *context)
{
887
    g_return_val_if_fail(context != NULL, NULL);
888 889

    return gst_vaapi_surface_proxy_new_from_pool(
890
        GST_VAAPI_SURFACE_POOL(context->surfaces_pool));
891 892
}

gb's avatar
gb committed
893 894 895 896 897 898 899 900 901 902 903
/**
 * gst_vaapi_context_get_surface_count:
 * @context: a #GstVaapiContext
 *
 * Retrieves the number of free surfaces left in the pool.
 *
 * Return value: the number of free surfaces available in the pool
 */
guint
gst_vaapi_context_get_surface_count(GstVaapiContext *context)
{
904
    g_return_val_if_fail(context != NULL, 0);
gb's avatar
gb committed
905

906
    return gst_vaapi_video_pool_get_size(context->surfaces_pool);
gb's avatar
gb committed
907 908
}

909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
/**
 * gst_vaapi_context_apply_composition:
 * @context: a #GstVaapiContext
 * @composition: a #GstVideoOverlayComposition
 *
 * Applies video composition planes to all surfaces bound to @context.
 * This helper function resets any additional subpictures the user may
 * have associated himself. A %NULL @composition will also clear all
 * the existing subpictures.
 *
 * Return value: %TRUE if all composition planes could be applied,
 *   %FALSE otherwise
 */
gboolean
gst_vaapi_context_apply_composition(
    GstVaapiContext            *context,
    GstVideoOverlayComposition *composition
)
{
928
    GPtrArray *curr_overlay, *next_overlay;
929
    guint i, n_rectangles;
930
    gboolean reassociate = FALSE;
931

932
    g_return_val_if_fail(context != NULL, FALSE);
933

934
    if (!context->surfaces)
935 936
        return FALSE;

937
    if (!composition) {
938
        gst_vaapi_context_clear_overlay(context);
939
        return TRUE;
940 941
    }

942 943
    curr_overlay = context->overlays[context->overlay_id];
    next_overlay = context->overlays[context->overlay_id ^ 1];
944
    overlay_clear(next_overlay);
945 946 947

    n_rectangles = gst_video_overlay_composition_n_rectangles(composition);
    for (i = 0; i < n_rectangles; i++) {
948 949 950
        GstVideoOverlayRectangle * const rect =
            gst_video_overlay_composition_get_rectangle(composition, i);
        GstVaapiOverlayRectangle *overlay;
951

952
        overlay = overlay_lookup(curr_overlay, rect);
953
        if (overlay && overlay_rectangle_update(overlay, rect, &reassociate)) {
954
            overlay_rectangle_ref(overlay);
955 956 957
            if (overlay->layer_id != i)
                reassociate = TRUE;
        }
958
        else {
959
            overlay = overlay_rectangle_new(rect, context, i);
960 961 962 963
            if (!overlay) {
                GST_WARNING("could not create VA overlay rectangle");
                goto error;
            }
964
            reassociate = TRUE;
965
        }
966
        g_ptr_array_add(next_overlay, overlay);
967
    }
968 969

    overlay_clear(curr_overlay);
970
    context->overlay_id ^= 1;
971 972 973

    if (reassociate && !overlay_reassociate(next_overlay))
        return FALSE;
974
    return TRUE;
975 976

error:
977
    gst_vaapi_context_clear_overlay(context);
978
    return FALSE;
979
}