Improve debugging messages.
[platform/upstream/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiimage.c
1 /*
2  *  gstvaapiimage.c - VA image abstraction
3  *
4  *  gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
19  */
20
21 /**
22  * SECTION:gst-vaapi-image
23  * @short_description:
24  */
25
26 #include "config.h"
27 #include <string.h>
28 #include "gstvaapiutils.h"
29 #include "gstvaapiimage.h"
30 #include <va/va_backend.h>
31
32 #define DEBUG 1
33 #include "gstvaapidebug.h"
34
35 G_DEFINE_TYPE(GstVaapiImage, gst_vaapi_image, G_TYPE_OBJECT);
36
37 #define GST_VAAPI_IMAGE_GET_PRIVATE(obj)                \
38     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                 \
39                                  GST_VAAPI_TYPE_IMAGE,  \
40                                  GstVaapiImagePrivate))
41
42 struct _GstVaapiImagePrivate {
43     GstVaapiDisplay    *display;
44     VAImage             internal_image;
45     VAImage             image;
46     guchar             *image_data;
47     GstVaapiImageFormat internal_format;
48     GstVaapiImageFormat format;
49     guint               width;
50     guint               height;
51     guint               create_image    : 1;
52     guint               is_constructed  : 1;
53     guint               is_linear       : 1;
54 };
55
56 enum {
57     PROP_0,
58
59     PROP_DISPLAY,
60     PROP_IMAGE,
61     PROP_IMAGE_ID,
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);
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','R','G','B'):
136     case VA_FOURCC('R','G','B','A'):
137     case VA_FOURCC('A','B','G','R'):
138     case VA_FOURCC('B','G','R','A'):
139         data_size = 4 * width * height;
140         break;
141     default:
142         g_error("FIXME: incomplete formats");
143         break;
144     }
145     return va_image->data_size == data_size;
146 }
147
148 static void
149 gst_vaapi_image_destroy(GstVaapiImage *image)
150 {
151     GstVaapiImagePrivate * const priv = image->priv;
152     VAStatus status;
153
154     _gst_vaapi_image_unmap(image);
155
156     GST_DEBUG("image 0x%08x", priv->internal_image.image_id);
157
158     if (priv->internal_image.image_id != VA_INVALID_ID) {
159         GST_VAAPI_DISPLAY_LOCK(priv->display);
160         status = vaDestroyImage(
161             GST_VAAPI_DISPLAY_VADISPLAY(priv->display),
162             priv->internal_image.image_id
163         );
164         GST_VAAPI_DISPLAY_UNLOCK(priv->display);
165         if (!vaapi_check_status(status, "vaDestroyImage()"))
166             g_warning("failed to destroy image 0x%08x\n",
167                       priv->internal_image.image_id);
168         priv->internal_image.image_id = VA_INVALID_ID;
169     }
170
171     if (priv->display) {
172         g_object_unref(priv->display);
173         priv->display = NULL;
174     }
175 }
176
177 static gboolean
178 _gst_vaapi_image_create(GstVaapiImage *image, GstVaapiImageFormat format)
179 {
180     GstVaapiImagePrivate * const priv = image->priv;
181     const VAImageFormat *va_format;
182     VAStatus status;
183
184     if (!gst_vaapi_display_has_image_format(priv->display, format))
185         return FALSE;
186
187     va_format = gst_vaapi_image_format_get_va_format(format);
188     if (!va_format)
189         return FALSE;
190
191     GST_VAAPI_DISPLAY_LOCK(priv->display);
192     status = vaCreateImage(
193         GST_VAAPI_DISPLAY_VADISPLAY(priv->display),
194         (VAImageFormat *)va_format,
195         priv->width,
196         priv->height,
197         &priv->internal_image
198     );
199     GST_VAAPI_DISPLAY_UNLOCK(priv->display);
200     if (status != VA_STATUS_SUCCESS ||
201         priv->internal_image.format.fourcc != va_format->fourcc)
202         return FALSE;
203
204     priv->internal_format = format;
205     return TRUE;
206 }
207
208 static gboolean
209 gst_vaapi_image_create(GstVaapiImage *image)
210 {
211     GstVaapiImagePrivate * const priv = image->priv;
212     GstVaapiImageFormat format = priv->format;
213     const VAImageFormat *va_format;
214
215     if (!priv->create_image)
216         return (priv->image.image_id != VA_INVALID_ID &&
217                 priv->image.buf      != VA_INVALID_ID);
218
219     if (!_gst_vaapi_image_create(image, format)) {
220         switch (format) {
221         case GST_VAAPI_IMAGE_I420:
222             format = GST_VAAPI_IMAGE_YV12;
223             break;
224         case GST_VAAPI_IMAGE_YV12:
225             format = GST_VAAPI_IMAGE_I420;
226             break;
227         default:
228             format = 0;
229             break;
230         }
231         if (!format || !_gst_vaapi_image_create(image, format))
232             return FALSE;
233     }
234     priv->image = priv->internal_image;
235
236     if (priv->format != priv->internal_format) {
237         switch (priv->format) {
238         case GST_VAAPI_IMAGE_YV12:
239         case GST_VAAPI_IMAGE_I420:
240             va_format = gst_vaapi_image_format_get_va_format(priv->format);
241             if (!va_format)
242                 return FALSE;
243             priv->image.format = *va_format;
244             SWAP_UINT(priv->image.offsets[1], priv->image.offsets[2]);
245             SWAP_UINT(priv->image.pitches[1], priv->image.pitches[2]);
246             break;
247         default:
248             break;
249         }
250     }
251
252     GST_DEBUG("image 0x%08x", priv->image.image_id);
253     priv->is_linear = vaapi_image_is_linear(&priv->image);
254     return TRUE;
255 }
256
257 static void
258 gst_vaapi_image_finalize(GObject *object)
259 {
260     gst_vaapi_image_destroy(GST_VAAPI_IMAGE(object));
261
262     G_OBJECT_CLASS(gst_vaapi_image_parent_class)->finalize(object);
263 }
264
265 static void
266 gst_vaapi_image_set_property(
267     GObject      *object,
268     guint         prop_id,
269     const GValue *value,
270     GParamSpec   *pspec
271 )
272 {
273     GstVaapiImage        * const image = GST_VAAPI_IMAGE(object);
274     GstVaapiImagePrivate * const priv  = image->priv;
275
276     switch (prop_id) {
277     case PROP_DISPLAY:
278         priv->display = g_object_ref(g_value_get_object(value));
279         break;
280     case PROP_IMAGE: {
281         const VAImage * const va_image = g_value_get_boxed(value);
282         if (va_image)
283             _gst_vaapi_image_set_image(image, va_image);
284         break;
285     }
286     case PROP_FORMAT:
287         priv->format = g_value_get_uint(value);
288         break;
289     case PROP_WIDTH:
290         priv->width = g_value_get_uint(value);
291         break;
292     case PROP_HEIGHT:
293         priv->height = g_value_get_uint(value);
294         break;
295     default:
296         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
297         break;
298     }
299 }
300
301 static void
302 gst_vaapi_image_get_property(
303     GObject    *object,
304     guint       prop_id,
305     GValue     *value,
306     GParamSpec *pspec
307 )
308 {
309     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
310
311     switch (prop_id) {
312     case PROP_DISPLAY:
313         g_value_set_pointer(value, gst_vaapi_image_get_display(image));
314         break;
315     case PROP_IMAGE:
316         g_value_set_boxed(value, &image->priv->image);
317         break;
318     case PROP_IMAGE_ID:
319         g_value_set_uint(value, gst_vaapi_image_get_id(image));
320         break;
321     case PROP_FORMAT:
322         g_value_set_uint(value, gst_vaapi_image_get_format(image));
323         break;
324     case PROP_WIDTH:
325         g_value_set_uint(value, gst_vaapi_image_get_width(image));
326         break;
327     case PROP_HEIGHT:
328         g_value_set_uint(value, gst_vaapi_image_get_height(image));
329         break;
330     default:
331         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
332         break;
333     }
334 }
335
336 static void
337 gst_vaapi_image_constructed(GObject *object)
338 {
339     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
340     GObjectClass *parent_class;
341
342     image->priv->is_constructed = gst_vaapi_image_create(image);
343
344     parent_class = G_OBJECT_CLASS(gst_vaapi_image_parent_class);
345     if (parent_class->constructed)
346         parent_class->constructed(object);
347 }
348
349 static void
350 gst_vaapi_image_class_init(GstVaapiImageClass *klass)
351 {
352     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
353
354     g_type_class_add_private(klass, sizeof(GstVaapiImagePrivate));
355
356     object_class->finalize     = gst_vaapi_image_finalize;
357     object_class->set_property = gst_vaapi_image_set_property;
358     object_class->get_property = gst_vaapi_image_get_property;
359     object_class->constructed  = gst_vaapi_image_constructed;
360
361     /**
362      * GstVaapiImage:display:
363      *
364      * The #GstVaapiDisplay this image is bound to.
365      */
366     g_object_class_install_property
367         (object_class,
368          PROP_DISPLAY,
369          g_param_spec_object("display",
370                              "Display",
371                              "The GstVaapiDisplay this image is bound to",
372                              GST_VAAPI_TYPE_DISPLAY,
373                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
374
375     g_object_class_install_property
376         (object_class,
377          PROP_IMAGE,
378          g_param_spec_boxed("image",
379                             "Image",
380                             "The underlying VA image",
381                             VAAPI_TYPE_IMAGE,
382                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
383
384     /**
385      * GstVaapiImage:id:
386      *
387      * The underlying #VAImageID of the image.
388      */
389     g_object_class_install_property
390         (object_class,
391          PROP_IMAGE_ID,
392          g_param_spec_uint("id",
393                            "VA image id",
394                            "The underlying VA image id",
395                            0, G_MAXUINT32, VA_INVALID_ID,
396                            G_PARAM_READABLE));
397
398     g_object_class_install_property
399         (object_class,
400          PROP_WIDTH,
401          g_param_spec_uint("width",
402                            "width",
403                            "The image width",
404                            0, G_MAXUINT32, 0,
405                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
406
407     g_object_class_install_property
408         (object_class,
409          PROP_HEIGHT,
410          g_param_spec_uint("height",
411                            "heighr",
412                            "The image height",
413                            0, G_MAXUINT32, 0,
414                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
415
416     /**
417      * GstVaapiImage:format:
418      *
419      * The #GstVaapiImageFormat of the image
420      */
421     g_object_class_install_property
422         (object_class,
423          PROP_FORMAT,
424          g_param_spec_uint("format",
425                            "Format",
426                            "The underlying image format",
427                            0, G_MAXUINT32, 0,
428                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
429 }
430
431 static void
432 gst_vaapi_image_init(GstVaapiImage *image)
433 {
434     GstVaapiImagePrivate *priv = GST_VAAPI_IMAGE_GET_PRIVATE(image);
435
436     image->priv                   = priv;
437     priv->display                 = NULL;
438     priv->image_data              = NULL;
439     priv->width                   = 0;
440     priv->height                  = 0;
441     priv->internal_format         = 0;
442     priv->format                  = 0;
443     priv->create_image            = TRUE;
444     priv->is_constructed          = FALSE;
445     priv->is_linear               = FALSE;
446
447     memset(&priv->internal_image, 0, sizeof(priv->internal_image));
448     priv->internal_image.image_id = VA_INVALID_ID;
449     priv->internal_image.buf      = VA_INVALID_ID;
450
451     memset(&priv->image, 0, sizeof(priv->image));
452     priv->image.image_id          = VA_INVALID_ID;
453     priv->image.buf               = VA_INVALID_ID;
454 }
455
456 /**
457  * gst_vaapi_image_new:
458  * @display: a #GstVaapiDisplay
459  * @format: a #GstVaapiImageFormat
460  * @width: the requested image width
461  * @height: the requested image height
462  *
463  * Creates a new #GstVaapiImage with the specified format and
464  * dimensions.
465  *
466  * Return value: the newly allocated #GstVaapiImage object
467  */
468 GstVaapiImage *
469 gst_vaapi_image_new(
470     GstVaapiDisplay    *display,
471     GstVaapiImageFormat format,
472     guint               width,
473     guint               height
474 )
475 {
476     GstVaapiImage *image;
477
478     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
479     g_return_val_if_fail(width > 0, NULL);
480     g_return_val_if_fail(height > 0, NULL);
481
482     GST_DEBUG("format %" GST_FOURCC_FORMAT ", size %ux%u",
483               GST_FOURCC_ARGS(format), width, height);
484
485     image = g_object_new(
486         GST_VAAPI_TYPE_IMAGE,
487         "display", display,
488         "format",  format,
489         "width",   width,
490         "height",  height,
491         NULL
492     );
493     if (!image)
494         return NULL;
495
496     if (!image->priv->is_constructed) {
497         g_object_unref(image);
498         return NULL;
499     }
500     return image;
501 }
502
503 /**
504  * gst_vaapi_image_new_with_image:
505  * @display: a #GstVaapiDisplay
506  * @va_image: a VA image
507  *
508  * Creates a new #GstVaapiImage from a foreign VA image. The image
509  * format and dimensions will be extracted from @va_image. This
510  * function is mainly used by gst_vaapi_surface_derive_image() to bind
511  * a VA image to a #GstVaapiImage object.
512  *
513  * Return value: the newly allocated #GstVaapiImage object
514  */
515 GstVaapiImage *
516 gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
517 {
518     GstVaapiImage *image;
519
520     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
521     g_return_val_if_fail(va_image, NULL);
522     g_return_val_if_fail(va_image->image_id != VA_INVALID_ID, NULL);
523     g_return_val_if_fail(va_image->buf != VA_INVALID_ID, NULL);
524
525     GST_DEBUG("VA image 0x%08x, format %" GST_FOURCC_FORMAT ", size %ux%u",
526               va_image->image_id,
527               GST_FOURCC_ARGS(va_image->format.fourcc),
528               va_image->width, va_image->height);
529
530     image = g_object_new(
531         GST_VAAPI_TYPE_IMAGE,
532         "display", display,
533         "image",   va_image,
534         NULL
535     );
536     if (!image)
537         return NULL;
538
539     if (!image->priv->is_constructed) {
540         g_object_unref(image);
541         return NULL;
542     }
543     return image;
544 }
545
546 /**
547  * gst_vaapi_image_get_id:
548  * @image: a #GstVaapiImage
549  *
550  * Returns the underlying VAImageID of the @image.
551  *
552  * Return value: the underlying VA image id
553  */
554 VAImageID
555 gst_vaapi_image_get_id(GstVaapiImage *image)
556 {
557     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), VA_INVALID_ID);
558     g_return_val_if_fail(image->priv->is_constructed, VA_INVALID_ID);
559
560     return image->priv->image.image_id;
561 }
562
563 /**
564  * gst_vaapi_image_get_image:
565  * @image: a #GstVaapiImage
566  * @va_image: (output): a VA image
567  *
568  * Fills @va_image with the VA image used internally.
569  *
570  * Return value: %TRUE on success
571  */
572 gboolean
573 gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
574 {
575     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
576     g_return_val_if_fail(image->priv->is_constructed, FALSE);
577
578     if (va_image)
579         *va_image = image->priv->image;
580
581     return TRUE;
582 }
583
584 /*
585  * _gst_vaapi_image_set_image:
586  * @image: a #GstVaapiImage
587  * @va_image: a VA image
588  *
589  * Initializes #GstVaapiImage with a foreign VA image. This function
590  * will try to "linearize" the VA image. i.e. making sure that the VA
591  * image offsets into the data buffer are in increasing order with the
592  * number of planes available in the image.
593  *
594  * This is an internal function used by gst_vaapi_image_new_with_image().
595  *
596  * Return value: %TRUE on success
597  */
598 gboolean
599 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image)
600 {
601     GstVaapiImagePrivate * const priv = image->priv;
602     GstVaapiImageFormat format;
603     VAImage alt_va_image;
604     const VAImageFormat *alt_va_format;
605
606     if (!va_image)
607         return FALSE;
608
609     format = gst_vaapi_image_format(&va_image->format);
610     if (!format)
611         return FALSE;
612
613     priv->create_image    = FALSE;
614     priv->internal_image  = *va_image;
615     priv->internal_format = format;
616     priv->is_linear       = vaapi_image_is_linear(va_image);
617     priv->image           = *va_image;
618     priv->format          = format;
619     priv->width           = va_image->width;
620     priv->height          = va_image->height;
621
622     /* Try to linearize image */
623     if (!priv->is_linear) {
624         switch (format) {
625         case GST_VAAPI_IMAGE_I420:
626             format = GST_VAAPI_IMAGE_YV12;
627             break;
628         case GST_VAAPI_IMAGE_YV12:
629             format = GST_VAAPI_IMAGE_I420;
630             break;
631         default:
632             format = 0;
633             break;
634         }
635         if (format &&
636             (alt_va_format = gst_vaapi_image_format_get_va_format(format))) {
637             alt_va_image = *va_image;
638             alt_va_image.format = *alt_va_format;
639             SWAP_UINT(alt_va_image.offsets[1], alt_va_image.offsets[2]);
640             SWAP_UINT(alt_va_image.pitches[1], alt_va_image.pitches[2]);
641             if (vaapi_image_is_linear(&alt_va_image)) {
642                 priv->image     = alt_va_image;
643                 priv->format    = format;
644                 priv->is_linear = TRUE;
645                 GST_DEBUG("linearized image to %" GST_FOURCC_FORMAT " format",
646                           GST_FOURCC_ARGS(format));
647             }
648         }
649     }
650     return TRUE;
651 }
652
653 /**
654  * gst_vaapi_image_get_display:
655  * @image: a #GstVaapiImage
656  *
657  * Returns the #GstVaapiDisplay this @image is bound to.
658  *
659  * Return value: the parent #GstVaapiDisplay object
660  */
661 GstVaapiDisplay *
662 gst_vaapi_image_get_display(GstVaapiImage *image)
663 {
664     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
665     g_return_val_if_fail(image->priv->is_constructed, FALSE);
666
667     return image->priv->display;
668 }
669
670 /**
671  * gst_vaapi_image_get_format:
672  * @image: a #GstVaapiImage
673  *
674  * Returns the #GstVaapiImageFormat the @image was created with.
675  *
676  * Return value: the #GstVaapiImageFormat
677  */
678 GstVaapiImageFormat
679 gst_vaapi_image_get_format(GstVaapiImage *image)
680 {
681     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
682     g_return_val_if_fail(image->priv->is_constructed, FALSE);
683
684     return image->priv->format;
685 }
686
687 /**
688  * gst_vaapi_image_get_width:
689  * @image: a #GstVaapiImage
690  *
691  * Returns the @image width.
692  *
693  * Return value: the image width, in pixels
694  */
695 guint
696 gst_vaapi_image_get_width(GstVaapiImage *image)
697 {
698     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
699     g_return_val_if_fail(image->priv->is_constructed, FALSE);
700
701     return image->priv->width;
702 }
703
704 /**
705  * gst_vaapi_image_get_height:
706  * @image: a #GstVaapiImage
707  *
708  * Returns the @image height.
709  *
710  * Return value: the image height, in pixels.
711  */
712 guint
713 gst_vaapi_image_get_height(GstVaapiImage *image)
714 {
715     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
716     g_return_val_if_fail(image->priv->is_constructed, FALSE);
717
718     return image->priv->height;
719 }
720
721 /**
722  * gst_vaapi_image_get_size:
723  * @image: a #GstVaapiImage
724  * @pwidth: (out) (allow-none): return location for the width, or %NULL
725  * @pheight: (out) (allow-none): return location for the height, or %NULL
726  *
727  * Retrieves the dimensions of a #GstVaapiImage.
728  */
729 void
730 gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
731 {
732     g_return_if_fail(GST_VAAPI_IS_IMAGE(image));
733     g_return_if_fail(image->priv->is_constructed);
734
735     if (pwidth)
736         *pwidth = image->priv->width;
737
738     if (pheight)
739         *pheight = image->priv->height;
740 }
741
742 /**
743  * gst_vaapi_image_is_linear:
744  * @image: a #GstVaapiImage
745  *
746  * Checks whether the @image has data planes allocated from a single
747  * buffer and offsets into that buffer are in increasing order with
748  * the number of planes.
749  *
750  * Return value: %TRUE if image data planes are allocated from a single buffer
751  */
752 gboolean
753 gst_vaapi_image_is_linear(GstVaapiImage *image)
754 {
755     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
756     g_return_val_if_fail(image->priv->is_constructed, FALSE);
757
758     return image->priv->is_linear;
759 }
760
761 /**
762  * gst_vaapi_image_is_mapped:
763  * @image: a #GstVaapiImage
764  *
765  * Checks whether the @image is currently mapped or not.
766  *
767  * Return value: %TRUE if the @image is mapped
768  */
769 static inline gboolean
770 _gst_vaapi_image_is_mapped(GstVaapiImage *image)
771 {
772     return image->priv->image_data != NULL;
773 }
774
775 gboolean
776 gst_vaapi_image_is_mapped(GstVaapiImage *image)
777 {
778     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
779     g_return_val_if_fail(image->priv->is_constructed, FALSE);
780
781     return _gst_vaapi_image_is_mapped(image);
782 }
783
784 /**
785  * gst_vaapi_image_map:
786  * @image: a #GstVaapiImage
787  *
788  * Maps the image data buffer. The actual pixels are returned by the
789  * gst_vaapi_image_get_plane() function.
790  *
791  * Return value: %TRUE on success
792  */
793 gboolean
794 gst_vaapi_image_map(GstVaapiImage *image)
795 {
796     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
797     g_return_val_if_fail(image->priv->is_constructed, FALSE);
798
799     return _gst_vaapi_image_map(image);
800 }
801
802 gboolean
803 _gst_vaapi_image_map(GstVaapiImage *image)
804 {
805     void *image_data;
806     VAStatus status;
807
808     if (_gst_vaapi_image_is_mapped(image))
809         return TRUE;
810
811     GST_VAAPI_DISPLAY_LOCK(image->priv->display);
812     status = vaMapBuffer(
813         GST_VAAPI_DISPLAY_VADISPLAY(image->priv->display),
814         image->priv->image.buf,
815         &image_data
816     );
817     GST_VAAPI_DISPLAY_UNLOCK(image->priv->display);
818     if (!vaapi_check_status(status, "vaMapBuffer()"))
819         return FALSE;
820
821     image->priv->image_data = image_data;
822     return TRUE;
823 }
824
825 /**
826  * gst_vaapi_image_unmap:
827  * @image: a #GstVaapiImage
828  *
829  * Unmaps the image data buffer. Pointers to pixels returned by
830  * gst_vaapi_image_get_plane() are then no longer valid.
831  *
832  * Return value: %TRUE on success
833  */
834 gboolean
835 gst_vaapi_image_unmap(GstVaapiImage *image)
836 {
837     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
838     g_return_val_if_fail(image->priv->is_constructed, FALSE);
839
840     return _gst_vaapi_image_unmap(image);
841 }
842
843 gboolean
844 _gst_vaapi_image_unmap(GstVaapiImage *image)
845 {
846     VAStatus status;
847
848     if (!_gst_vaapi_image_is_mapped(image))
849         return FALSE;
850
851     GST_VAAPI_DISPLAY_LOCK(image->priv->display);
852     status = vaUnmapBuffer(
853         GST_VAAPI_DISPLAY_VADISPLAY(image->priv->display),
854         image->priv->image.buf
855     );
856     GST_VAAPI_DISPLAY_UNLOCK(image->priv->display);
857     if (!vaapi_check_status(status, "vaUnmapBuffer()"))
858         return FALSE;
859
860     image->priv->image_data = NULL;
861     return TRUE;
862 }
863
864 /**
865  * gst_vaapi_image_get_plane_count:
866  * @image: a #GstVaapiImage
867  *
868  * Retrieves the number of planes available in the @image. The @image
869  * must be mapped for this function to work properly.
870  *
871  * Return value: the number of planes available in the @image
872  */
873 guint
874 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
875 {
876     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
877     g_return_val_if_fail(image->priv->is_constructed, FALSE);
878     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
879
880     return image->priv->image.num_planes;
881 }
882
883 /**
884  * gst_vaapi_image_get_plane:
885  * @image: a #GstVaapiImage
886  * @plane: the requested plane number
887  *
888  * Retrieves the pixels data to the specified @plane. The @image must
889  * be mapped for this function to work properly.
890  *
891  * Return value: the pixels data of the specified @plane
892  */
893 guchar *
894 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
895 {
896     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
897     g_return_val_if_fail(image->priv->is_constructed, FALSE);
898     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
899     g_return_val_if_fail(plane < image->priv->image.num_planes, NULL);
900
901     return image->priv->image_data + image->priv->image.offsets[plane];
902 }
903
904 /**
905  * gst_vaapi_image_get_pitch:
906  * @image: a #GstVaapiImage
907  * @plane: the requested plane number
908  *
909  * Retrieves the line size (stride) of the specified @plane. The
910  * @image must be mapped for this function to work properly.
911  *
912  * Return value: the line size (stride) of the specified plane
913  */
914 guint
915 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
916 {
917     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
918     g_return_val_if_fail(image->priv->is_constructed, FALSE);
919     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
920     g_return_val_if_fail(plane < image->priv->image.num_planes, 0);
921
922     return image->priv->image.pitches[plane];
923 }
924
925 /**
926  * gst_vaapi_image_get_data_size:
927  * @image: a #GstVaapiImage
928  *
929  * Retrieves the underlying image data size. This function could be
930  * used to determine whether the image has a compatible layout with
931  * another image structure.
932  *
933  * Return value: the whole image data size of the @image
934  */
935 guint
936 gst_vaapi_image_get_data_size(GstVaapiImage *image)
937 {
938     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
939     g_return_val_if_fail(image->priv->is_constructed, FALSE);
940
941     return image->priv->image.data_size;
942 }
943
944 /**
945  * gst_vaapi_image_update_from_buffer:
946  * @image: a #GstVaapiImage
947  * @buffer: a #GstBuffer
948  *
949  * Transfers pixels data contained in the #GstBuffer into the
950  * @image. Both image structures shall have the same format.
951  *
952  * Return value: %TRUE on success
953  */
954 gboolean
955 gst_vaapi_image_update_from_buffer(GstVaapiImage *image, GstBuffer *buffer)
956 {
957     GstVaapiImagePrivate *priv;
958     GstStructure *structure;
959     GstCaps *caps;
960     GstVaapiImageFormat format;
961     gint width, height;
962     guint offsets[3], pitches[3], widths[3], heights[3];
963     guint i, j;
964     guchar *data;
965     guint32 data_size;
966
967     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
968     g_return_val_if_fail(image->priv->is_constructed, FALSE);
969     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
970
971     priv      = image->priv;
972     data      = GST_BUFFER_DATA(buffer);
973     data_size = GST_BUFFER_SIZE(buffer);
974     caps      = GST_BUFFER_CAPS(buffer);
975
976     if (!caps)
977         return FALSE;
978
979     format = gst_vaapi_image_format_from_caps(caps);
980     if (format != priv->format)
981         return FALSE;
982
983     structure = gst_caps_get_structure(caps, 0);
984     gst_structure_get_int(structure, "width",  &width);
985     gst_structure_get_int(structure, "height", &height);
986     if (width != priv->width || height != priv->height)
987         return FALSE;
988
989     if (!gst_vaapi_image_map(image))
990         return FALSE;
991
992     if (priv->is_linear && data_size == priv->image.data_size)
993         memcpy(priv->image_data, data, data_size);
994     else {
995         /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
996         const guint width2  = (width  + 1) / 2;
997         const guint height2 = (height + 1) / 2;
998         guint size2;
999         switch (format) {
1000         case GST_VAAPI_IMAGE_NV12:
1001             offsets[0] = 0;
1002             pitches[0] = GST_ROUND_UP_4(width);
1003             widths [0] = width;
1004             heights[0] = height;
1005             offsets[1] = offsets[0] + height * pitches[0];
1006             pitches[1] = pitches[0];
1007             widths [1] = width2 * 2;
1008             heights[1] = height2;
1009             size2      = offsets[1] + height2 * pitches[1];
1010             break;
1011         case GST_VAAPI_IMAGE_YV12:
1012         case GST_VAAPI_IMAGE_I420:
1013             offsets[0] = 0;
1014             pitches[0] = GST_ROUND_UP_4(width);
1015             widths [0] = width;
1016             heights[0] = height;
1017             offsets[1] = offsets[0] + height * pitches[0];
1018             pitches[1] = GST_ROUND_UP_4(GST_ROUND_UP_2(width) / 2);
1019             widths [1] = width2;
1020             heights[1] = height2;
1021             offsets[2] = offsets[1] + height2 * pitches[1];
1022             pitches[2] = pitches[1];
1023             widths [2] = width2;
1024             heights[2] = height2;
1025             size2      = offsets[2] + height2 * pitches[2];
1026             break;
1027         case GST_VAAPI_IMAGE_ARGB:
1028         case GST_VAAPI_IMAGE_RGBA:
1029         case GST_VAAPI_IMAGE_ABGR:
1030         case GST_VAAPI_IMAGE_BGRA:
1031             offsets[0] = 0;
1032             pitches[0] = width * 4;
1033             widths [0] = width * 4;
1034             heights[0] = height;
1035             size2      = offsets[0] + height * pitches[0];
1036             break;
1037         default:
1038             g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
1039                     GST_FOURCC_ARGS(format));
1040             break;
1041         }
1042         if (size2 != data_size)
1043             g_error("data_size mismatch %d / %u", size2, data_size);
1044         for (i = 0; i < priv->image.num_planes; i++) {
1045             guchar *src = data + offsets[i];
1046             guchar *dst = priv->image_data + priv->image.offsets[i];
1047             for (j = 0; j < heights[i]; j++) {
1048                 memcpy(dst, src, widths[i]);
1049                 src += pitches[i];
1050                 dst += priv->image.pitches[i];
1051             }
1052         }
1053     }
1054
1055     if (!gst_vaapi_image_unmap(image))
1056         return FALSE;
1057
1058     return TRUE;
1059 }