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