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