legal: add Intel copyright on modified files.
[profile/ivi/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiimage.c
1 /*
2  *  gstvaapiimage.c - VA image abstraction
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011 Intel Corporation
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:gstvaapiimage
25  * @short_description: VA image abstraction
26  */
27
28 #include "config.h"
29 #include <string.h>
30 #include "gstvaapicompat.h"
31 #include "gstvaapiutils.h"
32 #include "gstvaapiimage.h"
33 #include "gstvaapi_priv.h"
34
35 #define DEBUG 1
36 #include "gstvaapidebug.h"
37
38 G_DEFINE_TYPE(GstVaapiImage, gst_vaapi_image, GST_VAAPI_TYPE_OBJECT);
39
40 #define GST_VAAPI_IMAGE_GET_PRIVATE(obj)                \
41     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                 \
42                                  GST_VAAPI_TYPE_IMAGE,  \
43                                  GstVaapiImagePrivate))
44
45 struct _GstVaapiImagePrivate {
46     VAImage             internal_image;
47     VAImage             image;
48     guchar             *image_data;
49     GstVaapiImageFormat internal_format;
50     GstVaapiImageFormat format;
51     guint               width;
52     guint               height;
53     guint               create_image    : 1;
54     guint               is_constructed  : 1;
55     guint               is_linear       : 1;
56 };
57
58 enum {
59     PROP_0,
60
61     PROP_IMAGE,
62     PROP_FORMAT,
63     PROP_WIDTH,
64     PROP_HEIGHT
65 };
66
67 #define SWAP_UINT(a, b) do { \
68         guint v = a;         \
69         a = b;               \
70         b = v;               \
71     } while (0)
72
73 static gboolean
74 _gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image);
75
76 static gboolean
77 _gst_vaapi_image_unmap(GstVaapiImage *image);
78
79 static gboolean
80 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image);
81
82 /*
83  * VAImage wrapper
84  */
85
86 #define VAAPI_TYPE_IMAGE vaapi_image_get_type()
87
88 static gpointer
89 vaapi_image_copy(gpointer va_image)
90 {
91     return g_slice_dup(VAImage, va_image);
92 }
93
94 static void
95 vaapi_image_free(gpointer va_image)
96 {
97     if (G_LIKELY(va_image))
98         g_slice_free(VAImage, va_image);
99 }
100
101 static GType
102 vaapi_image_get_type(void)
103 {
104     static GType type = 0;
105
106     if (G_UNLIKELY(type == 0))
107         type = g_boxed_type_register_static(
108             "VAImage",
109             vaapi_image_copy,
110             vaapi_image_free
111         );
112     return type;
113 }
114
115 static gboolean
116 vaapi_image_is_linear(const VAImage *va_image)
117 {
118     guint i, width, height, width2, height2, data_size;
119
120     for (i = 1; i < va_image->num_planes; i++)
121         if (va_image->offsets[i] < va_image->offsets[i - 1])
122             return FALSE;
123
124     width   = va_image->width;
125     height  = va_image->height;
126     width2  = (width  + 1) / 2;
127     height2 = (height + 1) / 2;
128
129     switch (va_image->format.fourcc) {
130     case VA_FOURCC('N','V','1','2'):
131     case VA_FOURCC('Y','V','1','2'):
132     case VA_FOURCC('I','4','2','0'):
133         data_size = width * height + 2 * width2 * height2;
134         break;
135     case VA_FOURCC('A','Y','U','V'):
136     case VA_FOURCC('A','R','G','B'):
137     case VA_FOURCC('R','G','B','A'):
138     case VA_FOURCC('A','B','G','R'):
139     case VA_FOURCC('B','G','R','A'):
140         data_size = 4 * width * height;
141         break;
142     default:
143         g_error("FIXME: incomplete formats");
144         break;
145     }
146     return va_image->data_size == data_size;
147 }
148
149 static void
150 gst_vaapi_image_destroy(GstVaapiImage *image)
151 {
152     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
153     VAImageID image_id;
154     VAStatus status;
155
156     _gst_vaapi_image_unmap(image);
157
158     image_id = GST_VAAPI_OBJECT_ID(image);
159     GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
160
161     if (image_id != VA_INVALID_ID) {
162         GST_VAAPI_DISPLAY_LOCK(display);
163         status = vaDestroyImage(GST_VAAPI_DISPLAY_VADISPLAY(display), image_id);
164         GST_VAAPI_DISPLAY_UNLOCK(display);
165         if (!vaapi_check_status(status, "vaDestroyImage()"))
166             g_warning("failed to destroy image %" GST_VAAPI_ID_FORMAT,
167                       GST_VAAPI_ID_ARGS(image_id));
168         GST_VAAPI_OBJECT_ID(image) = VA_INVALID_ID;
169     }
170 }
171
172 static gboolean
173 _gst_vaapi_image_create(GstVaapiImage *image, GstVaapiImageFormat format)
174 {
175     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
176     GstVaapiImagePrivate * const priv = image->priv;
177     const VAImageFormat *va_format;
178     VAStatus status;
179
180     if (!gst_vaapi_display_has_image_format(display, format))
181         return FALSE;
182
183     va_format = gst_vaapi_image_format_get_va_format(format);
184     if (!va_format)
185         return FALSE;
186
187     GST_VAAPI_DISPLAY_LOCK(display);
188     status = vaCreateImage(
189         GST_VAAPI_DISPLAY_VADISPLAY(display),
190         (VAImageFormat *)va_format,
191         priv->width,
192         priv->height,
193         &priv->internal_image
194     );
195     GST_VAAPI_DISPLAY_UNLOCK(display);
196     if (status != VA_STATUS_SUCCESS ||
197         priv->internal_image.format.fourcc != va_format->fourcc)
198         return FALSE;
199
200     priv->internal_format = format;
201     return TRUE;
202 }
203
204 static gboolean
205 gst_vaapi_image_create(GstVaapiImage *image)
206 {
207     GstVaapiImagePrivate * const priv = image->priv;
208     GstVaapiImageFormat format = priv->format;
209     const VAImageFormat *va_format;
210     VAImageID image_id;
211
212     if (!priv->create_image)
213         return (priv->image.image_id != VA_INVALID_ID &&
214                 priv->image.buf      != VA_INVALID_ID);
215
216     if (!_gst_vaapi_image_create(image, format)) {
217         switch (format) {
218         case GST_VAAPI_IMAGE_I420:
219             format = GST_VAAPI_IMAGE_YV12;
220             break;
221         case GST_VAAPI_IMAGE_YV12:
222             format = GST_VAAPI_IMAGE_I420;
223             break;
224         default:
225             format = 0;
226             break;
227         }
228         if (!format || !_gst_vaapi_image_create(image, format))
229             return FALSE;
230     }
231     priv->image = priv->internal_image;
232     image_id    = priv->image.image_id;
233
234     if (priv->format != priv->internal_format) {
235         switch (priv->format) {
236         case GST_VAAPI_IMAGE_YV12:
237         case GST_VAAPI_IMAGE_I420:
238             va_format = gst_vaapi_image_format_get_va_format(priv->format);
239             if (!va_format)
240                 return FALSE;
241             priv->image.format = *va_format;
242             SWAP_UINT(priv->image.offsets[1], priv->image.offsets[2]);
243             SWAP_UINT(priv->image.pitches[1], priv->image.pitches[2]);
244             break;
245         default:
246             break;
247         }
248     }
249     priv->is_linear = vaapi_image_is_linear(&priv->image);
250
251     GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
252     GST_VAAPI_OBJECT_ID(image) = image_id;
253     return TRUE;
254 }
255
256 static void
257 gst_vaapi_image_finalize(GObject *object)
258 {
259     gst_vaapi_image_destroy(GST_VAAPI_IMAGE(object));
260
261     G_OBJECT_CLASS(gst_vaapi_image_parent_class)->finalize(object);
262 }
263
264 static void
265 gst_vaapi_image_set_property(
266     GObject      *object,
267     guint         prop_id,
268     const GValue *value,
269     GParamSpec   *pspec
270 )
271 {
272     GstVaapiImage        * const image = GST_VAAPI_IMAGE(object);
273     GstVaapiImagePrivate * const priv  = image->priv;
274
275     switch (prop_id) {
276     case PROP_IMAGE: {
277         const VAImage * const va_image = g_value_get_boxed(value);
278         if (va_image)
279             _gst_vaapi_image_set_image(image, va_image);
280         break;
281     }
282     case PROP_FORMAT:
283         if (priv->create_image)
284             priv->format = g_value_get_uint(value);
285         break;
286     case PROP_WIDTH:
287         if (priv->create_image)
288             priv->width = g_value_get_uint(value);
289         break;
290     case PROP_HEIGHT:
291         if (priv->create_image)
292             priv->height = g_value_get_uint(value);
293         break;
294     default:
295         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
296         break;
297     }
298 }
299
300 static void
301 gst_vaapi_image_get_property(
302     GObject    *object,
303     guint       prop_id,
304     GValue     *value,
305     GParamSpec *pspec
306 )
307 {
308     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
309
310     switch (prop_id) {
311     case PROP_IMAGE:
312         g_value_set_boxed(value, &image->priv->image);
313         break;
314     case PROP_FORMAT:
315         g_value_set_uint(value, gst_vaapi_image_get_format(image));
316         break;
317     case PROP_WIDTH:
318         g_value_set_uint(value, gst_vaapi_image_get_width(image));
319         break;
320     case PROP_HEIGHT:
321         g_value_set_uint(value, gst_vaapi_image_get_height(image));
322         break;
323     default:
324         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
325         break;
326     }
327 }
328
329 static void
330 gst_vaapi_image_constructed(GObject *object)
331 {
332     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
333     GObjectClass *parent_class;
334
335     image->priv->is_constructed = gst_vaapi_image_create(image);
336
337     parent_class = G_OBJECT_CLASS(gst_vaapi_image_parent_class);
338     if (parent_class->constructed)
339         parent_class->constructed(object);
340 }
341
342 static void
343 gst_vaapi_image_class_init(GstVaapiImageClass *klass)
344 {
345     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
346
347     g_type_class_add_private(klass, sizeof(GstVaapiImagePrivate));
348
349     object_class->finalize     = gst_vaapi_image_finalize;
350     object_class->set_property = gst_vaapi_image_set_property;
351     object_class->get_property = gst_vaapi_image_get_property;
352     object_class->constructed  = gst_vaapi_image_constructed;
353
354     g_object_class_install_property
355         (object_class,
356          PROP_IMAGE,
357          g_param_spec_boxed("image",
358                             "Image",
359                             "The underlying VA image",
360                             VAAPI_TYPE_IMAGE,
361                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
362
363     g_object_class_install_property
364         (object_class,
365          PROP_WIDTH,
366          g_param_spec_uint("width",
367                            "width",
368                            "The image width",
369                            0, G_MAXUINT32, 0,
370                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
371
372     g_object_class_install_property
373         (object_class,
374          PROP_HEIGHT,
375          g_param_spec_uint("height",
376                            "heighr",
377                            "The image height",
378                            0, G_MAXUINT32, 0,
379                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
380
381     /**
382      * GstVaapiImage:format:
383      *
384      * The #GstVaapiImageFormat of the image
385      */
386     g_object_class_install_property
387         (object_class,
388          PROP_FORMAT,
389          g_param_spec_uint("format",
390                            "Format",
391                            "The underlying image format",
392                            0, G_MAXUINT32, 0,
393                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
394 }
395
396 static void
397 gst_vaapi_image_init(GstVaapiImage *image)
398 {
399     GstVaapiImagePrivate *priv = GST_VAAPI_IMAGE_GET_PRIVATE(image);
400
401     image->priv                   = priv;
402     priv->image_data              = NULL;
403     priv->width                   = 0;
404     priv->height                  = 0;
405     priv->internal_format         = 0;
406     priv->format                  = 0;
407     priv->create_image            = TRUE;
408     priv->is_constructed          = FALSE;
409     priv->is_linear               = FALSE;
410
411     memset(&priv->internal_image, 0, sizeof(priv->internal_image));
412     priv->internal_image.image_id = VA_INVALID_ID;
413     priv->internal_image.buf      = VA_INVALID_ID;
414
415     memset(&priv->image, 0, sizeof(priv->image));
416     priv->image.image_id          = VA_INVALID_ID;
417     priv->image.buf               = VA_INVALID_ID;
418 }
419
420 /**
421  * gst_vaapi_image_new:
422  * @display: a #GstVaapiDisplay
423  * @format: a #GstVaapiImageFormat
424  * @width: the requested image width
425  * @height: the requested image height
426  *
427  * Creates a new #GstVaapiImage with the specified format and
428  * dimensions.
429  *
430  * Return value: the newly allocated #GstVaapiImage object
431  */
432 GstVaapiImage *
433 gst_vaapi_image_new(
434     GstVaapiDisplay    *display,
435     GstVaapiImageFormat format,
436     guint               width,
437     guint               height
438 )
439 {
440     GstVaapiImage *image;
441
442     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
443     g_return_val_if_fail(width > 0, NULL);
444     g_return_val_if_fail(height > 0, NULL);
445
446     GST_DEBUG("format %" GST_FOURCC_FORMAT ", size %ux%u",
447               GST_FOURCC_ARGS(format), width, height);
448
449     image = g_object_new(
450         GST_VAAPI_TYPE_IMAGE,
451         "display", display,
452         "id",      GST_VAAPI_ID(VA_INVALID_ID),
453         "format",  format,
454         "width",   width,
455         "height",  height,
456         NULL
457     );
458     if (!image)
459         return NULL;
460
461     if (!image->priv->is_constructed) {
462         g_object_unref(image);
463         return NULL;
464     }
465     return image;
466 }
467
468 /**
469  * gst_vaapi_image_new_with_image:
470  * @display: a #GstVaapiDisplay
471  * @va_image: a VA image
472  *
473  * Creates a new #GstVaapiImage from a foreign VA image. The image
474  * format and dimensions will be extracted from @va_image. This
475  * function is mainly used by gst_vaapi_surface_derive_image() to bind
476  * a VA image to a #GstVaapiImage object.
477  *
478  * Return value: the newly allocated #GstVaapiImage object
479  */
480 GstVaapiImage *
481 gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
482 {
483     GstVaapiImage *image;
484
485     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
486     g_return_val_if_fail(va_image, NULL);
487     g_return_val_if_fail(va_image->image_id != VA_INVALID_ID, NULL);
488     g_return_val_if_fail(va_image->buf != VA_INVALID_ID, NULL);
489
490     GST_DEBUG("VA image 0x%08x, format %" GST_FOURCC_FORMAT ", size %ux%u",
491               va_image->image_id,
492               GST_FOURCC_ARGS(va_image->format.fourcc),
493               va_image->width, va_image->height);
494
495     image = g_object_new(
496         GST_VAAPI_TYPE_IMAGE,
497         "display", display,
498         "id",      GST_VAAPI_ID(va_image->image_id),
499         "image",   va_image,
500         NULL
501     );
502     if (!image)
503         return NULL;
504
505     if (!image->priv->is_constructed) {
506         g_object_unref(image);
507         return NULL;
508     }
509     return image;
510 }
511
512 /**
513  * gst_vaapi_image_get_id:
514  * @image: a #GstVaapiImage
515  *
516  * Returns the underlying VAImageID of the @image.
517  *
518  * Return value: the underlying VA image id
519  */
520 GstVaapiID
521 gst_vaapi_image_get_id(GstVaapiImage *image)
522 {
523     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), VA_INVALID_ID);
524     g_return_val_if_fail(image->priv->is_constructed, VA_INVALID_ID);
525
526     return GST_VAAPI_OBJECT_ID(image);
527 }
528
529 /**
530  * gst_vaapi_image_get_image:
531  * @image: a #GstVaapiImage
532  * @va_image: a VA image
533  *
534  * Fills @va_image with the VA image used internally.
535  *
536  * Return value: %TRUE on success
537  */
538 gboolean
539 gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
540 {
541     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
542     g_return_val_if_fail(image->priv->is_constructed, FALSE);
543
544     if (va_image)
545         *va_image = image->priv->image;
546
547     return TRUE;
548 }
549
550 /*
551  * _gst_vaapi_image_set_image:
552  * @image: a #GstVaapiImage
553  * @va_image: a VA image
554  *
555  * Initializes #GstVaapiImage with a foreign VA image. This function
556  * will try to "linearize" the VA image. i.e. making sure that the VA
557  * image offsets into the data buffer are in increasing order with the
558  * number of planes available in the image.
559  *
560  * This is an internal function used by gst_vaapi_image_new_with_image().
561  *
562  * Return value: %TRUE on success
563  */
564 gboolean
565 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image)
566 {
567     GstVaapiImagePrivate * const priv = image->priv;
568     GstVaapiImageFormat format;
569     VAImage alt_va_image;
570     const VAImageFormat *alt_va_format;
571
572     if (!va_image)
573         return FALSE;
574
575     format = gst_vaapi_image_format(&va_image->format);
576     if (!format)
577         return FALSE;
578
579     priv->create_image    = FALSE;
580     priv->internal_image  = *va_image;
581     priv->internal_format = format;
582     priv->is_linear       = vaapi_image_is_linear(va_image);
583     priv->image           = *va_image;
584     priv->format          = format;
585     priv->width           = va_image->width;
586     priv->height          = va_image->height;
587
588     /* Try to linearize image */
589     if (!priv->is_linear) {
590         switch (format) {
591         case GST_VAAPI_IMAGE_I420:
592             format = GST_VAAPI_IMAGE_YV12;
593             break;
594         case GST_VAAPI_IMAGE_YV12:
595             format = GST_VAAPI_IMAGE_I420;
596             break;
597         default:
598             format = 0;
599             break;
600         }
601         if (format &&
602             (alt_va_format = gst_vaapi_image_format_get_va_format(format))) {
603             alt_va_image = *va_image;
604             alt_va_image.format = *alt_va_format;
605             SWAP_UINT(alt_va_image.offsets[1], alt_va_image.offsets[2]);
606             SWAP_UINT(alt_va_image.pitches[1], alt_va_image.pitches[2]);
607             if (vaapi_image_is_linear(&alt_va_image)) {
608                 priv->image     = alt_va_image;
609                 priv->format    = format;
610                 priv->is_linear = TRUE;
611                 GST_DEBUG("linearized image to %" GST_FOURCC_FORMAT " format",
612                           GST_FOURCC_ARGS(format));
613             }
614         }
615     }
616     return TRUE;
617 }
618
619 /**
620  * gst_vaapi_image_get_format:
621  * @image: a #GstVaapiImage
622  *
623  * Returns the #GstVaapiImageFormat the @image was created with.
624  *
625  * Return value: the #GstVaapiImageFormat
626  */
627 GstVaapiImageFormat
628 gst_vaapi_image_get_format(GstVaapiImage *image)
629 {
630     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
631     g_return_val_if_fail(image->priv->is_constructed, 0);
632
633     return image->priv->format;
634 }
635
636 /**
637  * gst_vaapi_image_get_width:
638  * @image: a #GstVaapiImage
639  *
640  * Returns the @image width.
641  *
642  * Return value: the image width, in pixels
643  */
644 guint
645 gst_vaapi_image_get_width(GstVaapiImage *image)
646 {
647     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
648     g_return_val_if_fail(image->priv->is_constructed, 0);
649
650     return image->priv->width;
651 }
652
653 /**
654  * gst_vaapi_image_get_height:
655  * @image: a #GstVaapiImage
656  *
657  * Returns the @image height.
658  *
659  * Return value: the image height, in pixels.
660  */
661 guint
662 gst_vaapi_image_get_height(GstVaapiImage *image)
663 {
664     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
665     g_return_val_if_fail(image->priv->is_constructed, 0);
666
667     return image->priv->height;
668 }
669
670 /**
671  * gst_vaapi_image_get_size:
672  * @image: a #GstVaapiImage
673  * @pwidth: return location for the width, or %NULL
674  * @pheight: return location for the height, or %NULL
675  *
676  * Retrieves the dimensions of a #GstVaapiImage.
677  */
678 void
679 gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
680 {
681     g_return_if_fail(GST_VAAPI_IS_IMAGE(image));
682     g_return_if_fail(image->priv->is_constructed);
683
684     if (pwidth)
685         *pwidth = image->priv->width;
686
687     if (pheight)
688         *pheight = image->priv->height;
689 }
690
691 /**
692  * gst_vaapi_image_is_linear:
693  * @image: a #GstVaapiImage
694  *
695  * Checks whether the @image has data planes allocated from a single
696  * buffer and offsets into that buffer are in increasing order with
697  * the number of planes.
698  *
699  * Return value: %TRUE if image data planes are allocated from a single buffer
700  */
701 gboolean
702 gst_vaapi_image_is_linear(GstVaapiImage *image)
703 {
704     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
705     g_return_val_if_fail(image->priv->is_constructed, FALSE);
706
707     return image->priv->is_linear;
708 }
709
710 /**
711  * gst_vaapi_image_is_mapped:
712  * @image: a #GstVaapiImage
713  *
714  * Checks whether the @image is currently mapped or not.
715  *
716  * Return value: %TRUE if the @image is mapped
717  */
718 static inline gboolean
719 _gst_vaapi_image_is_mapped(GstVaapiImage *image)
720 {
721     return image->priv->image_data != NULL;
722 }
723
724 gboolean
725 gst_vaapi_image_is_mapped(GstVaapiImage *image)
726 {
727     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
728     g_return_val_if_fail(image->priv->is_constructed, FALSE);
729
730     return _gst_vaapi_image_is_mapped(image);
731 }
732
733 /**
734  * gst_vaapi_image_map:
735  * @image: a #GstVaapiImage
736  *
737  * Maps the image data buffer. The actual pixels are returned by the
738  * gst_vaapi_image_get_plane() function.
739  *
740  * Return value: %TRUE on success
741  */
742 gboolean
743 gst_vaapi_image_map(GstVaapiImage *image)
744 {
745     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
746     g_return_val_if_fail(image->priv->is_constructed, FALSE);
747
748     return _gst_vaapi_image_map(image, NULL);
749 }
750
751 gboolean
752 _gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image)
753 {
754     GstVaapiImagePrivate * const priv = image->priv;
755     GstVaapiDisplay *display;
756     void *image_data;
757     VAStatus status;
758     guint i;
759
760     if (_gst_vaapi_image_is_mapped(image))
761         return TRUE;
762
763     display = GST_VAAPI_OBJECT_DISPLAY(image);
764     if (!display)
765         return FALSE;
766
767     GST_VAAPI_DISPLAY_LOCK(display);
768     status = vaMapBuffer(
769         GST_VAAPI_DISPLAY_VADISPLAY(display),
770         image->priv->image.buf,
771         &image_data
772     );
773     GST_VAAPI_DISPLAY_UNLOCK(display);
774     if (!vaapi_check_status(status, "vaMapBuffer()"))
775         return FALSE;
776
777     image->priv->image_data = image_data;
778
779     if (raw_image) {
780         const VAImage * const va_image = &priv->image;
781         raw_image->format     = priv->format;
782         raw_image->width      = va_image->width;
783         raw_image->height     = va_image->height;
784         raw_image->num_planes = va_image->num_planes;
785         for (i = 0; i < raw_image->num_planes; i++) {
786             raw_image->pixels[i] = image_data + va_image->offsets[i];
787             raw_image->stride[i] = va_image->pitches[i];
788         }
789     }
790     return TRUE;
791 }
792
793 /**
794  * gst_vaapi_image_unmap:
795  * @image: a #GstVaapiImage
796  *
797  * Unmaps the image data buffer. Pointers to pixels returned by
798  * gst_vaapi_image_get_plane() are then no longer valid.
799  *
800  * Return value: %TRUE on success
801  */
802 gboolean
803 gst_vaapi_image_unmap(GstVaapiImage *image)
804 {
805     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
806     g_return_val_if_fail(image->priv->is_constructed, FALSE);
807
808     return _gst_vaapi_image_unmap(image);
809 }
810
811 gboolean
812 _gst_vaapi_image_unmap(GstVaapiImage *image)
813 {
814     GstVaapiDisplay *display;
815     VAStatus status;
816
817     if (!_gst_vaapi_image_is_mapped(image))
818         return FALSE;
819
820     display = GST_VAAPI_OBJECT_DISPLAY(image);
821     if (!display)
822         return FALSE;
823
824     GST_VAAPI_DISPLAY_LOCK(display);
825     status = vaUnmapBuffer(
826         GST_VAAPI_DISPLAY_VADISPLAY(display),
827         image->priv->image.buf
828     );
829     GST_VAAPI_DISPLAY_UNLOCK(display);
830     if (!vaapi_check_status(status, "vaUnmapBuffer()"))
831         return FALSE;
832
833     image->priv->image_data = NULL;
834     return TRUE;
835 }
836
837 /**
838  * gst_vaapi_image_get_plane_count:
839  * @image: a #GstVaapiImage
840  *
841  * Retrieves the number of planes available in the @image. The @image
842  * must be mapped for this function to work properly.
843  *
844  * Return value: the number of planes available in the @image
845  */
846 guint
847 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
848 {
849     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
850     g_return_val_if_fail(image->priv->is_constructed, FALSE);
851     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
852
853     return image->priv->image.num_planes;
854 }
855
856 /**
857  * gst_vaapi_image_get_plane:
858  * @image: a #GstVaapiImage
859  * @plane: the requested plane number
860  *
861  * Retrieves the pixels data to the specified @plane. The @image must
862  * be mapped for this function to work properly.
863  *
864  * Return value: the pixels data of the specified @plane
865  */
866 guchar *
867 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
868 {
869     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
870     g_return_val_if_fail(image->priv->is_constructed, FALSE);
871     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
872     g_return_val_if_fail(plane < image->priv->image.num_planes, NULL);
873
874     return image->priv->image_data + image->priv->image.offsets[plane];
875 }
876
877 /**
878  * gst_vaapi_image_get_pitch:
879  * @image: a #GstVaapiImage
880  * @plane: the requested plane number
881  *
882  * Retrieves the line size (stride) of the specified @plane. The
883  * @image must be mapped for this function to work properly.
884  *
885  * Return value: the line size (stride) of the specified plane
886  */
887 guint
888 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
889 {
890     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
891     g_return_val_if_fail(image->priv->is_constructed, FALSE);
892     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
893     g_return_val_if_fail(plane < image->priv->image.num_planes, 0);
894
895     return image->priv->image.pitches[plane];
896 }
897
898 /**
899  * gst_vaapi_image_get_data_size:
900  * @image: a #GstVaapiImage
901  *
902  * Retrieves the underlying image data size. This function could be
903  * used to determine whether the image has a compatible layout with
904  * another image structure.
905  *
906  * Return value: the whole image data size of the @image
907  */
908 guint
909 gst_vaapi_image_get_data_size(GstVaapiImage *image)
910 {
911     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
912     g_return_val_if_fail(image->priv->is_constructed, FALSE);
913
914     return image->priv->image.data_size;
915 }
916
917 static gboolean
918 init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
919 {
920     GstStructure *structure;
921     GstCaps *caps;
922     GstVaapiImageFormat format;
923     guint width2, height2, size2;
924     gint width, height;
925     guchar *data;
926     guint32 data_size;
927
928     data      = GST_BUFFER_DATA(buffer);
929     data_size = GST_BUFFER_SIZE(buffer);
930     caps      = GST_BUFFER_CAPS(buffer);
931
932     if (!caps)
933         return FALSE;
934
935     format = gst_vaapi_image_format_from_caps(caps);
936
937     structure = gst_caps_get_structure(caps, 0);
938     gst_structure_get_int(structure, "width",  &width);
939     gst_structure_get_int(structure, "height", &height);
940
941     /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
942     raw_image->format = format;
943     raw_image->width  = width;
944     raw_image->height = height;
945     width2  = (width + 1) / 2;
946     height2 = (height + 1) / 2;
947     size2   = 0;
948     switch (format) {
949     case GST_VAAPI_IMAGE_NV12:
950         raw_image->num_planes = 2;
951         raw_image->pixels[0]  = data;
952         raw_image->stride[0]  = GST_ROUND_UP_4(width);
953         size2                += height * raw_image->stride[0];
954         raw_image->pixels[1]  = data + size2;
955         raw_image->stride[1]  = raw_image->stride[0];
956         size2                += height2 * raw_image->stride[1];
957         break;
958     case GST_VAAPI_IMAGE_YV12:
959     case GST_VAAPI_IMAGE_I420:
960         raw_image->num_planes = 3;
961         raw_image->pixels[0]  = data;
962         raw_image->stride[0]  = GST_ROUND_UP_4(width);
963         size2                += height * raw_image->stride[0];
964         raw_image->pixels[1]  = data + size2;
965         raw_image->stride[1]  = GST_ROUND_UP_4(width2);
966         size2                += height2 * raw_image->stride[1];
967         raw_image->pixels[2]  = data + size2;
968         raw_image->stride[2]  = raw_image->stride[1];
969         size2                += height2 * raw_image->stride[2];
970         break;
971     case GST_VAAPI_IMAGE_ARGB:
972     case GST_VAAPI_IMAGE_RGBA:
973     case GST_VAAPI_IMAGE_ABGR:
974     case GST_VAAPI_IMAGE_BGRA:
975         raw_image->num_planes = 1;
976         raw_image->pixels[0]  = data;
977         raw_image->stride[0]  = width * 4;
978         size2                += height * raw_image->stride[0];
979         break;
980     default:
981         g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
982                 GST_FOURCC_ARGS(format));
983         return FALSE;
984     }
985
986     if (size2 != data_size) {
987         g_error("data_size mismatch %d / %u", size2, data_size);
988         if (size2 > data_size)
989             return FALSE;
990     }
991     return TRUE;
992 }
993
994 /* Copy N lines of an image */
995 static inline void
996 memcpy_pic(
997     guchar       *dst,
998     guint         dst_stride,
999     const guchar *src,
1000     guint         src_stride,
1001     guint         len,
1002     guint         height
1003 )
1004 {
1005     guint i;
1006
1007     for (i = 0; i < height; i++)  {
1008         memcpy(dst, src, len);
1009         dst += dst_stride;
1010         src += dst_stride;
1011     }
1012 }
1013
1014 /* Copy NV12 images */
1015 static void
1016 copy_image_NV12(
1017     GstVaapiImageRaw        *dst_image,
1018     GstVaapiImageRaw        *src_image,
1019     const GstVaapiRectangle *rect
1020 )
1021 {
1022     guchar *dst, *src;
1023     guint dst_stride, src_stride;
1024
1025     /* Y plane */
1026     dst_stride = dst_image->stride[0];
1027     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1028     src_stride = src_image->stride[0];
1029     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1030     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
1031
1032     /* UV plane */
1033     dst_stride = dst_image->stride[1];
1034     dst = dst_image->pixels[1] + (rect->y / 2) * dst_stride + (rect->x & -2);
1035     src_stride = src_image->stride[1];
1036     src = src_image->pixels[1] + (rect->y / 2) * src_stride + (rect->x & -2);
1037     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height / 2);
1038 }
1039
1040 /* Copy YV12 images */
1041 static void
1042 copy_image_YV12(
1043     GstVaapiImageRaw        *dst_image,
1044     GstVaapiImageRaw        *src_image,
1045     const GstVaapiRectangle *rect
1046 )
1047 {
1048     guchar *dst, *src;
1049     guint dst_stride, src_stride;
1050     guint i, x, y, w, h;
1051
1052     /* Y plane */
1053     dst_stride = dst_image->stride[0];
1054     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1055     src_stride = src_image->stride[0];
1056     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1057     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
1058
1059     /* U/V planes */
1060     x = rect->x / 2;
1061     y = rect->y / 2;
1062     w = rect->width / 2;
1063     h = rect->height / 2;
1064     for (i = 1; i < dst_image->num_planes; i++) {
1065         dst_stride = dst_image->stride[i];
1066         dst = dst_image->pixels[i] + y * dst_stride + x;
1067         src_stride = src_image->stride[i];
1068         src = src_image->pixels[i] + y * src_stride + x;
1069         memcpy_pic(dst, dst_stride, src, src_stride, w, h);
1070     }
1071 }
1072
1073 /* Copy RGBA images */
1074 static void
1075 copy_image_RGBA(
1076     GstVaapiImageRaw        *dst_image,
1077     GstVaapiImageRaw        *src_image,
1078     const GstVaapiRectangle *rect
1079 )
1080 {
1081     guchar *dst, *src;
1082     guint dst_stride, src_stride;
1083
1084     dst_stride = dst_image->stride[0];
1085     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1086     src_stride = src_image->stride[0];
1087     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1088     memcpy_pic(dst, dst_stride, src, src_stride, 4 * rect->width, rect->height);
1089 }
1090
1091 static gboolean
1092 copy_image(
1093     GstVaapiImageRaw        *dst_image,
1094     GstVaapiImageRaw        *src_image,
1095     const GstVaapiRectangle *rect
1096 )
1097 {
1098     GstVaapiRectangle default_rect;
1099
1100     if (dst_image->format != src_image->format ||
1101         dst_image->width  != src_image->width  ||
1102         dst_image->height != src_image->height)
1103         return FALSE;
1104
1105     if (rect) {
1106         if (rect->x >= src_image->width ||
1107             rect->x + src_image->width > src_image->width ||
1108             rect->y >= src_image->height ||
1109             rect->y + src_image->height > src_image->height)
1110             return FALSE;
1111     }
1112     else {
1113         default_rect.x      = 0;
1114         default_rect.y      = 0;
1115         default_rect.width  = src_image->width;
1116         default_rect.height = src_image->height;
1117         rect                = &default_rect;
1118     }
1119
1120     switch (dst_image->format) {
1121     case GST_VAAPI_IMAGE_NV12:
1122         copy_image_NV12(dst_image, src_image, rect);
1123         break;
1124     case GST_VAAPI_IMAGE_YV12:
1125     case GST_VAAPI_IMAGE_I420:
1126         copy_image_YV12(dst_image, src_image, rect);
1127         break;
1128     case GST_VAAPI_IMAGE_ARGB:
1129     case GST_VAAPI_IMAGE_RGBA:
1130     case GST_VAAPI_IMAGE_ABGR:
1131     case GST_VAAPI_IMAGE_BGRA:
1132         copy_image_RGBA(dst_image, src_image, rect);
1133         break;
1134     default:
1135         GST_ERROR("unsupported image format for copy");
1136         return FALSE;
1137     }
1138     return TRUE;
1139 }
1140
1141 /**
1142  * gst_vaapi_image_get_buffer:
1143  * @image: a #GstVaapiImage
1144  * @buffer: a #GstBuffer
1145  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1146  *   whole image
1147  *
1148  * Transfers pixels data contained in the @image into the #GstBuffer.
1149  * Both image structures shall have the same format.
1150  *
1151  * Return value: %TRUE on success
1152  */
1153 gboolean
1154 gst_vaapi_image_get_buffer(
1155     GstVaapiImage     *image,
1156     GstBuffer         *buffer,
1157     GstVaapiRectangle *rect
1158 )
1159 {
1160     GstVaapiImagePrivate *priv;
1161     GstVaapiImageRaw dst_image, src_image;
1162     gboolean success;
1163
1164     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1165     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1166     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
1167
1168     priv = image->priv;
1169
1170     if (!init_image_from_buffer(&dst_image, buffer))
1171         return FALSE;
1172     if (dst_image.format != priv->format)
1173         return FALSE;
1174     if (dst_image.width != priv->width || dst_image.height != priv->height)
1175         return FALSE;
1176
1177     if (!_gst_vaapi_image_map(image, &src_image))
1178         return FALSE;
1179
1180     success = copy_image(&dst_image, &src_image, rect);
1181
1182     if (!_gst_vaapi_image_unmap(image))
1183         return FALSE;
1184
1185     return success;
1186 }
1187
1188 /**
1189  * gst_vaapi_image_get_raw:
1190  * @image: a #GstVaapiImage
1191  * @dst_image: a #GstVaapiImageRaw
1192  * @buffer: a #GstBuffer
1193  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1194  *   whole image
1195  *
1196  * Transfers pixels data contained in the @image into the #GstVaapiImageRaw.
1197  * Both image structures shall have the same format.
1198  *
1199  * Return value: %TRUE on success
1200  */
1201 gboolean
1202 gst_vaapi_image_get_raw(
1203     GstVaapiImage     *image,
1204     GstVaapiImageRaw  *dst_image,
1205     GstVaapiRectangle *rect
1206 )
1207 {
1208     GstVaapiImageRaw src_image;
1209     gboolean success;
1210
1211     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1212     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1213
1214     if (!_gst_vaapi_image_map(image, &src_image))
1215         return FALSE;
1216
1217     success = copy_image(dst_image, &src_image, rect);
1218
1219     if (!_gst_vaapi_image_unmap(image))
1220         return FALSE;
1221
1222     return success;
1223 }
1224
1225 /**
1226  * gst_vaapi_image_update_from_buffer:
1227  * @image: a #GstVaapiImage
1228  * @buffer: a #GstBuffer
1229  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1230  *   whole image
1231  *
1232  * Transfers pixels data contained in the #GstBuffer into the
1233  * @image. Both image structures shall have the same format.
1234  *
1235  * Return value: %TRUE on success
1236  */
1237 gboolean
1238 gst_vaapi_image_update_from_buffer(
1239     GstVaapiImage     *image,
1240     GstBuffer         *buffer,
1241     GstVaapiRectangle *rect
1242 )
1243 {
1244     GstVaapiImagePrivate *priv;
1245     GstVaapiImageRaw dst_image, src_image;
1246     gboolean success;
1247
1248     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1249     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1250     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
1251
1252     priv = image->priv;
1253
1254     if (!init_image_from_buffer(&src_image, buffer))
1255         return FALSE;
1256     if (src_image.format != priv->format)
1257         return FALSE;
1258     if (src_image.width != priv->width || src_image.height != priv->height)
1259         return FALSE;
1260
1261     if (!_gst_vaapi_image_map(image, &dst_image))
1262         return FALSE;
1263
1264     success = copy_image(&dst_image, &src_image, rect);
1265
1266     if (!_gst_vaapi_image_unmap(image))
1267         return FALSE;
1268
1269     return success;
1270 }
1271
1272 /**
1273  * gst_vaapi_image_update_from_raw:
1274  * @image: a #GstVaapiImage
1275  * @src_image: a #GstVaapiImageRaw
1276  * @buffer: a #GstBuffer
1277  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1278  *   whole image
1279  *
1280  * Transfers pixels data contained in the #GstVaapiImageRaw into the
1281  * @image. Both image structures shall have the same format.
1282  *
1283  * Return value: %TRUE on success
1284  */
1285 gboolean
1286 gst_vaapi_image_update_from_raw(
1287     GstVaapiImage     *image,
1288     GstVaapiImageRaw  *src_image,
1289     GstVaapiRectangle *rect
1290 )
1291 {
1292     GstVaapiImageRaw dst_image;
1293     gboolean success;
1294
1295     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1296     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1297
1298     if (!_gst_vaapi_image_map(image, &dst_image))
1299         return FALSE;
1300
1301     success = copy_image(&dst_image, src_image, rect);
1302
1303     if (!_gst_vaapi_image_unmap(image))
1304         return FALSE;
1305
1306     return success;
1307 }