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