gstvaapiimage.c 29.3 KB
Newer Older
1 2 3
/*
 *  gstvaapiimage.c - VA image abstraction
 *
4
 *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5
 *  Copyright (C) 2011-2012 Intel Corporation
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.
11
 *
gb's avatar
gb committed
12
 *  This library is distributed in the hope that it will be useful,
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.
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
21 22
 */

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

28
#include "sysdeps.h"
29
#include <string.h>
30
#include "gstvaapicompat.h"
gb's avatar
gb committed
31
#include "gstvaapiutils.h"
32
#include "gstvaapiimage.h"
33
#include "gstvaapiobject_priv.h"
34 35

#define DEBUG 1
gb's avatar
gb committed
36
#include "gstvaapidebug.h"
37

38
typedef struct _GstVaapiImageClass              GstVaapiImageClass;
39

40 41 42 43 44 45 46 47
/**
 * GstVaapiImage:
 *
 * A VA image wrapper
 */
struct _GstVaapiImage {
    /*< private >*/
    GstVaapiObject      parent_instance;
48

49
    VAImage             internal_image;
50 51
    VAImage             image;
    guchar             *image_data;
52
    GstVaapiImageFormat internal_format;
53
    GstVaapiImageFormat format;
54 55
    guint               width;
    guint               height;
56
    guint               is_linear       : 1;
57 58
};

59 60 61 62 63 64 65 66
/**
 * GstVaapiImageClass:
 *
 * A VA image wrapper class
 */
struct _GstVaapiImageClass {
    /*< private >*/
    GstVaapiObjectClass parent_class;
67 68
};

69
#define SWAP_UINT(a, b) do { \
gb's avatar
gb committed
70
        guint v = a;         \
71 72 73 74
        a = b;               \
        b = v;               \
    } while (0)

75
static gboolean
76
_gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image);
77 78 79 80

static gboolean
_gst_vaapi_image_unmap(GstVaapiImage *image);

gb's avatar
gb committed
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
static gboolean
_gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image);

/*
 * VAImage wrapper
 */

static gboolean
vaapi_image_is_linear(const VAImage *va_image)
{
    guint i, width, height, width2, height2, data_size;

    for (i = 1; i < va_image->num_planes; i++)
        if (va_image->offsets[i] < va_image->offsets[i - 1])
            return FALSE;

    width   = va_image->width;
    height  = va_image->height;
    width2  = (width  + 1) / 2;
    height2 = (height + 1) / 2;

    switch (va_image->format.fourcc) {
    case VA_FOURCC('N','V','1','2'):
    case VA_FOURCC('Y','V','1','2'):
    case VA_FOURCC('I','4','2','0'):
        data_size = width * height + 2 * width2 * height2;
        break;
gb's avatar
gb committed
108
    case VA_FOURCC('A','Y','U','V'):
gb's avatar
gb committed
109 110 111 112 113 114 115 116 117 118 119 120 121
    case VA_FOURCC('A','R','G','B'):
    case VA_FOURCC('R','G','B','A'):
    case VA_FOURCC('A','B','G','R'):
    case VA_FOURCC('B','G','R','A'):
        data_size = 4 * width * height;
        break;
    default:
        g_error("FIXME: incomplete formats");
        break;
    }
    return va_image->data_size == data_size;
}

122 123 124
static void
gst_vaapi_image_destroy(GstVaapiImage *image)
{
gb's avatar
gb committed
125
    GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
126
    VAImageID image_id;
127 128
    VAStatus status;

129
    _gst_vaapi_image_unmap(image);
130

131 132
    image_id = GST_VAAPI_OBJECT_ID(image);
    GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
gb's avatar
gb committed
133

134
    if (image_id != VA_INVALID_ID) {
135
        GST_VAAPI_DISPLAY_LOCK(display);
136
        status = vaDestroyImage(GST_VAAPI_DISPLAY_VADISPLAY(display), image_id);
137
        GST_VAAPI_DISPLAY_UNLOCK(display);
138
        if (!vaapi_check_status(status, "vaDestroyImage()"))
gb's avatar
gb committed
139
            g_warning("failed to destroy image %" GST_VAAPI_ID_FORMAT,
140 141
                      GST_VAAPI_ID_ARGS(image_id));
        GST_VAAPI_OBJECT_ID(image) = VA_INVALID_ID;
142 143 144 145
    }
}

static gboolean
146
_gst_vaapi_image_create(GstVaapiImage *image, GstVaapiImageFormat format)
147
{
gb's avatar
gb committed
148
    GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
149
    const VAImageFormat *va_format;
150 151
    VAStatus status;

152
    if (!gst_vaapi_display_has_image_format(display, format))
153 154
        return FALSE;

155 156 157
    va_format = gst_vaapi_image_format_get_va_format(format);
    if (!va_format)
        return FALSE;
158

159
    GST_VAAPI_DISPLAY_LOCK(display);
160
    status = vaCreateImage(
161
        GST_VAAPI_DISPLAY_VADISPLAY(display),
162
        (VAImageFormat *)va_format,
163 164 165
        image->width,
        image->height,
        &image->internal_image
166
    );
167
    GST_VAAPI_DISPLAY_UNLOCK(display);
168
    if (status != VA_STATUS_SUCCESS ||
169
        image->internal_image.format.fourcc != va_format->fourcc)
170 171
        return FALSE;

172
    image->internal_format = format;
173
    return TRUE;
174 175
}

176
static gboolean
177 178
gst_vaapi_image_create(GstVaapiImage *image, GstVaapiImageFormat format,
    guint width, guint height)
179 180
{
    const VAImageFormat *va_format;
181
    VAImageID image_id;
182

183 184 185
    image->format = format;
    image->width  = width;
    image->height = height;
186

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
    if (!_gst_vaapi_image_create(image, format)) {
        switch (format) {
        case GST_VAAPI_IMAGE_I420:
            format = GST_VAAPI_IMAGE_YV12;
            break;
        case GST_VAAPI_IMAGE_YV12:
            format = GST_VAAPI_IMAGE_I420;
            break;
        default:
            format = 0;
            break;
        }
        if (!format || !_gst_vaapi_image_create(image, format))
            return FALSE;
    }
202 203
    image->image = image->internal_image;
    image_id     = image->image.image_id;
204

205 206
    if (image->format != image->internal_format) {
        switch (image->format) {
207 208
        case GST_VAAPI_IMAGE_YV12:
        case GST_VAAPI_IMAGE_I420:
209
            va_format = gst_vaapi_image_format_get_va_format(image->format);
210 211
            if (!va_format)
                return FALSE;
212 213 214
            image->image.format = *va_format;
            SWAP_UINT(image->image.offsets[1], image->image.offsets[2]);
            SWAP_UINT(image->image.pitches[1], image->image.pitches[2]);
215 216 217 218 219
            break;
        default:
            break;
        }
    }
220
    image->is_linear = vaapi_image_is_linear(&image->image);
221 222 223

    GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
    GST_VAAPI_OBJECT_ID(image) = image_id;
224 225 226 227
    return TRUE;
}

static void
228
gst_vaapi_image_init(GstVaapiImage *image)
229
{
230 231 232 233
    image->internal_image.image_id = VA_INVALID_ID;
    image->internal_image.buf = VA_INVALID_ID;
    image->image.image_id = VA_INVALID_ID;
    image->image.buf = VA_INVALID_ID;
234 235 236 237 238
}

static void
gst_vaapi_image_class_init(GstVaapiImageClass *klass)
{
239 240
    GstVaapiObjectClass * const object_class =
        GST_VAAPI_OBJECT_CLASS(klass);
241

242
    object_class->init = (GstVaapiObjectInitFunc)gst_vaapi_image_init;
243 244
}

245 246 247 248 249 250
#define gst_vaapi_image_finalize gst_vaapi_image_destroy
GST_VAAPI_OBJECT_DEFINE_CLASS_WITH_CODE(
    GstVaapiImage,
    gst_vaapi_image,
    gst_vaapi_image_class_init(&g_class))

gb's avatar
gb committed
251 252 253 254 255 256 257 258 259 260 261 262
/**
 * gst_vaapi_image_new:
 * @display: a #GstVaapiDisplay
 * @format: a #GstVaapiImageFormat
 * @width: the requested image width
 * @height: the requested image height
 *
 * Creates a new #GstVaapiImage with the specified format and
 * dimensions.
 *
 * Return value: the newly allocated #GstVaapiImage object
 */
263 264 265
GstVaapiImage *
gst_vaapi_image_new(
    GstVaapiDisplay    *display,
266
    GstVaapiImageFormat format,
267
    guint               width,
268
    guint               height
269 270
)
{
271 272
    GstVaapiImage *image;

gb's avatar
gb committed
273 274 275
    g_return_val_if_fail(width > 0, NULL);
    g_return_val_if_fail(height > 0, NULL);

gb's avatar
gb committed
276 277
    GST_DEBUG("format %" GST_FOURCC_FORMAT ", size %ux%u",
              GST_FOURCC_ARGS(format), width, height);
278

279
    image = gst_vaapi_object_new(gst_vaapi_image_class(), display);
280 281 282
    if (!image)
        return NULL;

283 284
    if (!gst_vaapi_image_create(image, format, width, height))
        goto error;
285
    return image;
286 287 288 289

error:
    gst_vaapi_object_unref(image);
    return NULL;
290 291
}

gb's avatar
gb committed
292 293 294 295 296 297 298 299 300 301 302 303
/**
 * gst_vaapi_image_new_with_image:
 * @display: a #GstVaapiDisplay
 * @va_image: a VA image
 *
 * Creates a new #GstVaapiImage from a foreign VA image. The image
 * format and dimensions will be extracted from @va_image. This
 * function is mainly used by gst_vaapi_surface_derive_image() to bind
 * a VA image to a #GstVaapiImage object.
 *
 * Return value: the newly allocated #GstVaapiImage object
 */
gb's avatar
gb committed
304 305 306 307 308 309 310 311 312 313 314 315 316 317
GstVaapiImage *
gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
{
    GstVaapiImage *image;

    g_return_val_if_fail(va_image, NULL);
    g_return_val_if_fail(va_image->image_id != VA_INVALID_ID, NULL);
    g_return_val_if_fail(va_image->buf != VA_INVALID_ID, NULL);

    GST_DEBUG("VA image 0x%08x, format %" GST_FOURCC_FORMAT ", size %ux%u",
              va_image->image_id,
              GST_FOURCC_ARGS(va_image->format.fourcc),
              va_image->width, va_image->height);

318
    image = gst_vaapi_object_new(gst_vaapi_image_class(), display);
gb's avatar
gb committed
319 320 321
    if (!image)
        return NULL;

322 323
    if (!_gst_vaapi_image_set_image(image, va_image))
        goto error;
gb's avatar
gb committed
324
    return image;
325 326 327 328

error:
    gst_vaapi_object_unref(image);
    return NULL;
gb's avatar
gb committed
329 330
}

331 332 333 334 335 336 337 338
/**
 * gst_vaapi_image_get_id:
 * @image: a #GstVaapiImage
 *
 * Returns the underlying VAImageID of the @image.
 *
 * Return value: the underlying VA image id
 */
339
GstVaapiID
340 341
gst_vaapi_image_get_id(GstVaapiImage *image)
{
342
    g_return_val_if_fail(image != NULL, VA_INVALID_ID);
343 344 345 346

    return GST_VAAPI_OBJECT_ID(image);
}

gb's avatar
gb committed
347 348 349
/**
 * gst_vaapi_image_get_image:
 * @image: a #GstVaapiImage
350
 * @va_image: a VA image
gb's avatar
gb committed
351 352 353 354 355
 *
 * Fills @va_image with the VA image used internally.
 *
 * Return value: %TRUE on success
 */
356 357 358
gboolean
gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
{
359
    g_return_val_if_fail(image != NULL, FALSE);
360

361
    if (va_image)
362
        *va_image = image->image;
363 364 365 366

    return TRUE;
}

gb's avatar
gb committed
367 368 369 370 371 372 373 374 375 376 377 378 379 380
/*
 * _gst_vaapi_image_set_image:
 * @image: a #GstVaapiImage
 * @va_image: a VA image
 *
 * Initializes #GstVaapiImage with a foreign VA image. This function
 * will try to "linearize" the VA image. i.e. making sure that the VA
 * image offsets into the data buffer are in increasing order with the
 * number of planes available in the image.
 *
 * This is an internal function used by gst_vaapi_image_new_with_image().
 *
 * Return value: %TRUE on success
 */
gb's avatar
gb committed
381 382 383 384 385 386 387 388 389 390 391
gboolean
_gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image)
{
    GstVaapiImageFormat format;
    VAImage alt_va_image;
    const VAImageFormat *alt_va_format;

    format = gst_vaapi_image_format(&va_image->format);
    if (!format)
        return FALSE;

392 393 394 395 396 397 398 399 400
    image->internal_image  = *va_image;
    image->internal_format = format;
    image->is_linear       = vaapi_image_is_linear(va_image);
    image->image           = *va_image;
    image->format          = format;
    image->width           = va_image->width;
    image->height          = va_image->height;

    GST_VAAPI_OBJECT_ID(image) = va_image->image_id;
gb's avatar
gb committed
401 402

    /* Try to linearize image */
403
    if (!image->is_linear) {
gb's avatar
gb committed
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
        switch (format) {
        case GST_VAAPI_IMAGE_I420:
            format = GST_VAAPI_IMAGE_YV12;
            break;
        case GST_VAAPI_IMAGE_YV12:
            format = GST_VAAPI_IMAGE_I420;
            break;
        default:
            format = 0;
            break;
        }
        if (format &&
            (alt_va_format = gst_vaapi_image_format_get_va_format(format))) {
            alt_va_image = *va_image;
            alt_va_image.format = *alt_va_format;
            SWAP_UINT(alt_va_image.offsets[1], alt_va_image.offsets[2]);
            SWAP_UINT(alt_va_image.pitches[1], alt_va_image.pitches[2]);
            if (vaapi_image_is_linear(&alt_va_image)) {
422 423 424
                image->image     = alt_va_image;
                image->format    = format;
                image->is_linear = TRUE;
gb's avatar
gb committed
425 426 427 428 429 430 431 432
                GST_DEBUG("linearized image to %" GST_FOURCC_FORMAT " format",
                          GST_FOURCC_ARGS(format));
            }
        }
    }
    return TRUE;
}

gb's avatar
gb committed
433 434 435 436 437 438 439 440
/**
 * gst_vaapi_image_get_format:
 * @image: a #GstVaapiImage
 *
 * Returns the #GstVaapiImageFormat the @image was created with.
 *
 * Return value: the #GstVaapiImageFormat
 */
gb's avatar
gb committed
441 442 443
GstVaapiImageFormat
gst_vaapi_image_get_format(GstVaapiImage *image)
{
444
    g_return_val_if_fail(image != NULL, 0);
gb's avatar
gb committed
445

446
    return image->format;
gb's avatar
gb committed
447 448
}

gb's avatar
gb committed
449 450 451 452 453 454 455 456
/**
 * gst_vaapi_image_get_width:
 * @image: a #GstVaapiImage
 *
 * Returns the @image width.
 *
 * Return value: the image width, in pixels
 */
457 458 459
guint
gst_vaapi_image_get_width(GstVaapiImage *image)
{
460
    g_return_val_if_fail(image != NULL, 0);
461

462
    return image->width;
463 464
}

gb's avatar
gb committed
465 466 467 468 469 470 471 472
/**
 * gst_vaapi_image_get_height:
 * @image: a #GstVaapiImage
 *
 * Returns the @image height.
 *
 * Return value: the image height, in pixels.
 */
473 474 475
guint
gst_vaapi_image_get_height(GstVaapiImage *image)
{
476
    g_return_val_if_fail(image != NULL, 0);
477

478
    return image->height;
479 480
}

gb's avatar
gb committed
481 482 483
/**
 * gst_vaapi_image_get_size:
 * @image: a #GstVaapiImage
484 485
 * @pwidth: return location for the width, or %NULL
 * @pheight: return location for the height, or %NULL
gb's avatar
gb committed
486 487 488
 *
 * Retrieves the dimensions of a #GstVaapiImage.
 */
gb's avatar
gb committed
489 490 491
void
gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
{
492
    g_return_if_fail(image != NULL);
gb's avatar
gb committed
493 494

    if (pwidth)
495
        *pwidth = image->width;
gb's avatar
gb committed
496 497

    if (pheight)
498
        *pheight = image->height;
gb's avatar
gb committed
499 500
}

gb's avatar
gb committed
501 502 503 504 505 506 507 508 509 510
/**
 * gst_vaapi_image_is_linear:
 * @image: a #GstVaapiImage
 *
 * Checks whether the @image has data planes allocated from a single
 * buffer and offsets into that buffer are in increasing order with
 * the number of planes.
 *
 * Return value: %TRUE if image data planes are allocated from a single buffer
 */
511 512 513
gboolean
gst_vaapi_image_is_linear(GstVaapiImage *image)
{
514
    g_return_val_if_fail(image != NULL, FALSE);
515

516
    return image->is_linear;
517 518
}

gb's avatar
gb committed
519 520 521 522 523 524 525 526
/**
 * gst_vaapi_image_is_mapped:
 * @image: a #GstVaapiImage
 *
 * Checks whether the @image is currently mapped or not.
 *
 * Return value: %TRUE if the @image is mapped
 */
527 528 529
static inline gboolean
_gst_vaapi_image_is_mapped(GstVaapiImage *image)
{
530
    return image->image_data != NULL;
531 532 533 534 535
}

gboolean
gst_vaapi_image_is_mapped(GstVaapiImage *image)
{
536
    g_return_val_if_fail(image != NULL, FALSE);
537 538 539 540

    return _gst_vaapi_image_is_mapped(image);
}

gb's avatar
gb committed
541 542 543 544 545 546 547 548 549
/**
 * gst_vaapi_image_map:
 * @image: a #GstVaapiImage
 *
 * Maps the image data buffer. The actual pixels are returned by the
 * gst_vaapi_image_get_plane() function.
 *
 * Return value: %TRUE on success
 */
550 551 552
gboolean
gst_vaapi_image_map(GstVaapiImage *image)
{
553
    g_return_val_if_fail(image != NULL, FALSE);
554

555
    return _gst_vaapi_image_map(image, NULL);
556 557 558
}

gboolean
559
_gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image)
560
{
561
    GstVaapiDisplay *display;
562
    VAStatus status;
563
    guint i;
564

565
    if (_gst_vaapi_image_is_mapped(image))
566
        goto map_success;
567

gb's avatar
gb committed
568
    display = GST_VAAPI_OBJECT_DISPLAY(image);
569 570 571 572
    if (!display)
        return FALSE;

    GST_VAAPI_DISPLAY_LOCK(display);
573
    status = vaMapBuffer(
574
        GST_VAAPI_DISPLAY_VADISPLAY(display),
575 576
        image->image.buf,
        (void **)&image->image_data
577
    );
578
    GST_VAAPI_DISPLAY_UNLOCK(display);
579 580 581
    if (!vaapi_check_status(status, "vaMapBuffer()"))
        return FALSE;

582
map_success:
583
    if (raw_image) {
584 585
        const VAImage * const va_image = &image->image;
        raw_image->format     = image->format;
586 587 588 589
        raw_image->width      = va_image->width;
        raw_image->height     = va_image->height;
        raw_image->num_planes = va_image->num_planes;
        for (i = 0; i < raw_image->num_planes; i++) {
590
            raw_image->pixels[i] = (guchar *)image->image_data +
591
                va_image->offsets[i];
592 593 594
            raw_image->stride[i] = va_image->pitches[i];
        }
    }
595 596 597
    return TRUE;
}

gb's avatar
gb committed
598 599 600 601 602 603 604 605 606
/**
 * gst_vaapi_image_unmap:
 * @image: a #GstVaapiImage
 *
 * Unmaps the image data buffer. Pointers to pixels returned by
 * gst_vaapi_image_get_plane() are then no longer valid.
 *
 * Return value: %TRUE on success
 */
607 608 609
gboolean
gst_vaapi_image_unmap(GstVaapiImage *image)
{
610
    g_return_val_if_fail(image != NULL, FALSE);
611

612 613 614 615 616 617
    return _gst_vaapi_image_unmap(image);
}

gboolean
_gst_vaapi_image_unmap(GstVaapiImage *image)
{
618
    GstVaapiDisplay *display;
619 620
    VAStatus status;

621
    if (!_gst_vaapi_image_is_mapped(image))
622
        return TRUE;
623

gb's avatar
gb committed
624
    display = GST_VAAPI_OBJECT_DISPLAY(image);
625 626 627 628
    if (!display)
        return FALSE;

    GST_VAAPI_DISPLAY_LOCK(display);
629
    status = vaUnmapBuffer(
630
        GST_VAAPI_DISPLAY_VADISPLAY(display),
631
        image->image.buf
632
    );
633
    GST_VAAPI_DISPLAY_UNLOCK(display);
634 635 636
    if (!vaapi_check_status(status, "vaUnmapBuffer()"))
        return FALSE;

637
    image->image_data = NULL;
638 639 640
    return TRUE;
}

gb's avatar
gb committed
641 642 643 644 645 646 647 648 649
/**
 * gst_vaapi_image_get_plane_count:
 * @image: a #GstVaapiImage
 *
 * Retrieves the number of planes available in the @image. The @image
 * must be mapped for this function to work properly.
 *
 * Return value: the number of planes available in the @image
 */
650 651 652
guint
gst_vaapi_image_get_plane_count(GstVaapiImage *image)
{
653
    g_return_val_if_fail(image != NULL, 0);
654 655
    g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);

656
    return image->image.num_planes;
657 658
}

gb's avatar
gb committed
659 660 661 662 663 664 665 666 667 668
/**
 * gst_vaapi_image_get_plane:
 * @image: a #GstVaapiImage
 * @plane: the requested plane number
 *
 * Retrieves the pixels data to the specified @plane. The @image must
 * be mapped for this function to work properly.
 *
 * Return value: the pixels data of the specified @plane
 */
669 670 671
guchar *
gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
{
672
    g_return_val_if_fail(image != NULL, NULL);
673
    g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
674
    g_return_val_if_fail(plane < image->image.num_planes, NULL);
675

676
    return image->image_data + image->image.offsets[plane];
677 678
}

gb's avatar
gb committed
679 680 681 682 683 684 685 686 687 688
/**
 * gst_vaapi_image_get_pitch:
 * @image: a #GstVaapiImage
 * @plane: the requested plane number
 *
 * Retrieves the line size (stride) of the specified @plane. The
 * @image must be mapped for this function to work properly.
 *
 * Return value: the line size (stride) of the specified plane
 */
689 690 691
guint
gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
{
692
    g_return_val_if_fail(image != NULL, 0);
693
    g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
694
    g_return_val_if_fail(plane < image->image.num_planes, 0);
695

696
    return image->image.pitches[plane];
697
}
698

gb's avatar
gb committed
699 700 701 702 703 704 705 706 707 708
/**
 * gst_vaapi_image_get_data_size:
 * @image: a #GstVaapiImage
 *
 * Retrieves the underlying image data size. This function could be
 * used to determine whether the image has a compatible layout with
 * another image structure.
 *
 * Return value: the whole image data size of the @image
 */
709 710 711
guint
gst_vaapi_image_get_data_size(GstVaapiImage *image)
{
712
    g_return_val_if_fail(image != NULL, 0);
713

714
    return image->image.data_size;
715 716
}

717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
#if GST_CHECK_VERSION(1,0,0)
#include <gst/video/gstvideometa.h>

static gboolean
init_image_from_video_meta(GstVaapiImageRaw *raw_image, GstVideoMeta *vmeta)
{
    GST_FIXME("map from GstVideoMeta + add fini_image_from_buffer()");
    return FALSE;
}

static gboolean
init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
{
    GstVideoMeta * const vmeta = gst_buffer_get_video_meta(buffer);

    return vmeta ? init_image_from_video_meta(raw_image, vmeta) : FALSE;
}
#else
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
static gboolean
init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
{
    GstStructure *structure;
    GstCaps *caps;
    GstVaapiImageFormat format;
    guint width2, height2, size2;
    gint width, height;
    guchar *data;
    guint32 data_size;

    data      = GST_BUFFER_DATA(buffer);
    data_size = GST_BUFFER_SIZE(buffer);
    caps      = GST_BUFFER_CAPS(buffer);

    if (!caps)
        return FALSE;

    format = gst_vaapi_image_format_from_caps(caps);

    structure = gst_caps_get_structure(caps, 0);
    gst_structure_get_int(structure, "width",  &width);
    gst_structure_get_int(structure, "height", &height);

    /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
    raw_image->format = format;
    raw_image->width  = width;
    raw_image->height = height;
    width2  = (width + 1) / 2;
    height2 = (height + 1) / 2;
    size2   = 0;
    switch (format) {
    case GST_VAAPI_IMAGE_NV12:
        raw_image->num_planes = 2;
        raw_image->pixels[0]  = data;
        raw_image->stride[0]  = GST_ROUND_UP_4(width);
        size2                += height * raw_image->stride[0];
        raw_image->pixels[1]  = data + size2;
        raw_image->stride[1]  = raw_image->stride[0];
        size2                += height2 * raw_image->stride[1];
        break;
    case GST_VAAPI_IMAGE_YV12:
    case GST_VAAPI_IMAGE_I420:
        raw_image->num_planes = 3;
        raw_image->pixels[0]  = data;
        raw_image->stride[0]  = GST_ROUND_UP_4(width);
        size2                += height * raw_image->stride[0];
        raw_image->pixels[1]  = data + size2;
        raw_image->stride[1]  = GST_ROUND_UP_4(width2);
        size2                += height2 * raw_image->stride[1];
        raw_image->pixels[2]  = data + size2;
        raw_image->stride[2]  = raw_image->stride[1];
        size2                += height2 * raw_image->stride[2];
        break;
    case GST_VAAPI_IMAGE_ARGB:
    case GST_VAAPI_IMAGE_RGBA:
    case GST_VAAPI_IMAGE_ABGR:
    case GST_VAAPI_IMAGE_BGRA:
        raw_image->num_planes = 1;
        raw_image->pixels[0]  = data;
        raw_image->stride[0]  = width * 4;
        size2                += height * raw_image->stride[0];
        break;
    default:
        g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
                GST_FOURCC_ARGS(format));
        return FALSE;
    }

    if (size2 != data_size) {
        g_error("data_size mismatch %d / %u", size2, data_size);
        if (size2 > data_size)
            return FALSE;
    }
    return TRUE;
}
811
#endif
812

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
/* Copy N lines of an image */
static inline void
memcpy_pic(
    guchar       *dst,
    guint         dst_stride,
    const guchar *src,
    guint         src_stride,
    guint         len,
    guint         height
)
{
    guint i;

    for (i = 0; i < height; i++)  {
        memcpy(dst, src, len);
        dst += dst_stride;
829
        src += src_stride;
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 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 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 956 957 958 959
    }
}

/* Copy NV12 images */
static void
copy_image_NV12(
    GstVaapiImageRaw        *dst_image,
    GstVaapiImageRaw        *src_image,
    const GstVaapiRectangle *rect
)
{
    guchar *dst, *src;
    guint dst_stride, src_stride;

    /* Y plane */
    dst_stride = dst_image->stride[0];
    dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
    src_stride = src_image->stride[0];
    src = src_image->pixels[0] + rect->y * src_stride + rect->x;
    memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);

    /* UV plane */
    dst_stride = dst_image->stride[1];
    dst = dst_image->pixels[1] + (rect->y / 2) * dst_stride + (rect->x & -2);
    src_stride = src_image->stride[1];
    src = src_image->pixels[1] + (rect->y / 2) * src_stride + (rect->x & -2);
    memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height / 2);
}

/* Copy YV12 images */
static void
copy_image_YV12(
    GstVaapiImageRaw        *dst_image,
    GstVaapiImageRaw        *src_image,
    const GstVaapiRectangle *rect
)
{
    guchar *dst, *src;
    guint dst_stride, src_stride;
    guint i, x, y, w, h;

    /* Y plane */
    dst_stride = dst_image->stride[0];
    dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
    src_stride = src_image->stride[0];
    src = src_image->pixels[0] + rect->y * src_stride + rect->x;
    memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);

    /* U/V planes */
    x = rect->x / 2;
    y = rect->y / 2;
    w = rect->width / 2;
    h = rect->height / 2;
    for (i = 1; i < dst_image->num_planes; i++) {
        dst_stride = dst_image->stride[i];
        dst = dst_image->pixels[i] + y * dst_stride + x;
        src_stride = src_image->stride[i];
        src = src_image->pixels[i] + y * src_stride + x;
        memcpy_pic(dst, dst_stride, src, src_stride, w, h);
    }
}

/* Copy RGBA images */
static void
copy_image_RGBA(
    GstVaapiImageRaw        *dst_image,
    GstVaapiImageRaw        *src_image,
    const GstVaapiRectangle *rect
)
{
    guchar *dst, *src;
    guint dst_stride, src_stride;

    dst_stride = dst_image->stride[0];
    dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
    src_stride = src_image->stride[0];
    src = src_image->pixels[0] + rect->y * src_stride + rect->x;
    memcpy_pic(dst, dst_stride, src, src_stride, 4 * rect->width, rect->height);
}

static gboolean
copy_image(
    GstVaapiImageRaw        *dst_image,
    GstVaapiImageRaw        *src_image,
    const GstVaapiRectangle *rect
)
{
    GstVaapiRectangle default_rect;

    if (dst_image->format != src_image->format ||
        dst_image->width  != src_image->width  ||
        dst_image->height != src_image->height)
        return FALSE;

    if (rect) {
        if (rect->x >= src_image->width ||
            rect->x + src_image->width > src_image->width ||
            rect->y >= src_image->height ||
            rect->y + src_image->height > src_image->height)
            return FALSE;
    }
    else {
        default_rect.x      = 0;
        default_rect.y      = 0;
        default_rect.width  = src_image->width;
        default_rect.height = src_image->height;
        rect                = &default_rect;
    }

    switch (dst_image->format) {
    case GST_VAAPI_IMAGE_NV12:
        copy_image_NV12(dst_image, src_image, rect);
        break;
    case GST_VAAPI_IMAGE_YV12:
    case GST_VAAPI_IMAGE_I420:
        copy_image_YV12(dst_image, src_image, rect);
        break;
    case GST_VAAPI_IMAGE_ARGB:
    case GST_VAAPI_IMAGE_RGBA:
    case GST_VAAPI_IMAGE_ABGR:
    case GST_VAAPI_IMAGE_BGRA:
        copy_image_RGBA(dst_image, src_image, rect);
        break;
    default:
        GST_ERROR("unsupported image format for copy");
        return FALSE;
    }
    return TRUE;
}

960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
/**
 * gst_vaapi_image_get_buffer:
 * @image: a #GstVaapiImage
 * @buffer: a #GstBuffer
 * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
 *   whole image
 *
 * Transfers pixels data contained in the @image into the #GstBuffer.
 * Both image structures shall have the same format.
 *
 * Return value: %TRUE on success
 */
gboolean
gst_vaapi_image_get_buffer(
    GstVaapiImage     *image,
    GstBuffer         *buffer,
    GstVaapiRectangle *rect
)
{
    GstVaapiImageRaw dst_image, src_image;
    gboolean success;

982
    g_return_val_if_fail(image != NULL, FALSE);
983 984 985 986
    g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);

    if (!init_image_from_buffer(&dst_image, buffer))
        return FALSE;
987
    if (dst_image.format != image->format)
988
        return FALSE;
989
    if (dst_image.width != image->width || dst_image.height != image->height)
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
        return FALSE;

    if (!_gst_vaapi_image_map(image, &src_image))
        return FALSE;

    success = copy_image(&dst_image, &src_image, rect);

    if (!_gst_vaapi_image_unmap(image))
        return FALSE;

    return success;
}

/**
 * gst_vaapi_image_get_raw:
 * @image: a #GstVaapiImage
 * @dst_image: a #GstVaapiImageRaw
 * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
 *   whole image
 *
 * Transfers pixels data contained in the @image into the #GstVaapiImageRaw.
 * Both image structures shall have the same format.
 *
 * Return value: %TRUE on success
 */
gboolean
gst_vaapi_image_get_raw(
    GstVaapiImage     *image,
    GstVaapiImageRaw  *dst_image,
    GstVaapiRectangle *rect
)
{
    GstVaapiImageRaw src_image;
    gboolean success;

1025
    g_return_val_if_fail(image != NULL, FALSE);
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037

    if (!_gst_vaapi_image_map(image, &src_image))
        return FALSE;

    success = copy_image(dst_image, &src_image, rect);

    if (!_gst_vaapi_image_unmap(image))
        return FALSE;

    return success;
}

gb's avatar
gb committed
1038 1039 1040 1041
/**
 * gst_vaapi_image_update_from_buffer:
 * @image: a #GstVaapiImage
 * @buffer: a #GstBuffer
1042 1043
 * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
 *   whole image
gb's avatar
gb committed
1044 1045 1046 1047 1048 1049
 *
 * Transfers pixels data contained in the #GstBuffer into the
 * @image. Both image structures shall have the same format.
 *
 * Return value: %TRUE on success
 */
1050
gboolean
1051 1052 1053 1054 1055
gst_vaapi_image_update_from_buffer(
    GstVaapiImage     *image,
    GstBuffer         *buffer,
    GstVaapiRectangle *rect
)
1056
{
1057 1058
    GstVaapiImageRaw dst_image, src_image;
    gboolean success;
1059

1060
    g_return_val_if_fail(image != NULL, FALSE);
1061 1062
    g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);

1063
    if (!init_image_from_buffer(&src_image, buffer))
1064
        return FALSE;
1065
    if (src_image.format != image->format)
1066
        return FALSE;
1067
    if (src_image.width != image->width || src_image.height != image->height)
1068 1069
        return FALSE;

1070
    if (!_gst_vaapi_image_map(image, &dst_image))
1071 1072
        return FALSE;

1073 1074 1075
    success = copy_image(&dst_image, &src_image, rect);

    if (!_gst_vaapi_image_unmap(image))
1076 1077
        return FALSE;

1078
    return success;
1079
}
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103

/**
 * gst_vaapi_image_update_from_raw:
 * @image: a #GstVaapiImage
 * @src_image: a #GstVaapiImageRaw
 * @buffer: a #GstBuffer
 * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
 *   whole image
 *
 * Transfers pixels data contained in the #GstVaapiImageRaw into the
 * @image. Both image structures shall have the same format.
 *
 * Return value: %TRUE on success
 */
gboolean
gst_vaapi_image_update_from_raw(
    GstVaapiImage     *image,
    GstVaapiImageRaw  *src_image,
    GstVaapiRectangle *rect
)
{
    GstVaapiImageRaw dst_image;
    gboolean success;

1104
    g_return_val_if_fail(image != NULL, FALSE);
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115

    if (!_gst_vaapi_image_map(image, &dst_image))
        return FALSE;

    success = copy_image(&dst_image, src_image, rect);

    if (!_gst_vaapi_image_unmap(image))
        return FALSE;

    return success;
}