Include gstvaapicompat.h in source files only, not headers.
[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 "gstvaapiobject_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_GET_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_GET_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         "format",  format,
448         "width",   width,
449         "height",  height,
450         NULL
451     );
452     if (!image)
453         return NULL;
454
455     if (!image->priv->is_constructed) {
456         g_object_unref(image);
457         return NULL;
458     }
459     return image;
460 }
461
462 /**
463  * gst_vaapi_image_new_with_image:
464  * @display: a #GstVaapiDisplay
465  * @va_image: a VA image
466  *
467  * Creates a new #GstVaapiImage from a foreign VA image. The image
468  * format and dimensions will be extracted from @va_image. This
469  * function is mainly used by gst_vaapi_surface_derive_image() to bind
470  * a VA image to a #GstVaapiImage object.
471  *
472  * Return value: the newly allocated #GstVaapiImage object
473  */
474 GstVaapiImage *
475 gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
476 {
477     GstVaapiImage *image;
478
479     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
480     g_return_val_if_fail(va_image, NULL);
481     g_return_val_if_fail(va_image->image_id != VA_INVALID_ID, NULL);
482     g_return_val_if_fail(va_image->buf != VA_INVALID_ID, NULL);
483
484     GST_DEBUG("VA image 0x%08x, format %" GST_FOURCC_FORMAT ", size %ux%u",
485               va_image->image_id,
486               GST_FOURCC_ARGS(va_image->format.fourcc),
487               va_image->width, va_image->height);
488
489     image = g_object_new(
490         GST_VAAPI_TYPE_IMAGE,
491         "display", display,
492         "image",   va_image,
493         NULL
494     );
495     if (!image)
496         return NULL;
497
498     if (!image->priv->is_constructed) {
499         g_object_unref(image);
500         return NULL;
501     }
502     return image;
503 }
504
505 /**
506  * gst_vaapi_image_get_image:
507  * @image: a #GstVaapiImage
508  * @va_image: a VA image
509  *
510  * Fills @va_image with the VA image used internally.
511  *
512  * Return value: %TRUE on success
513  */
514 gboolean
515 gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
516 {
517     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
518     g_return_val_if_fail(image->priv->is_constructed, FALSE);
519
520     if (va_image)
521         *va_image = image->priv->image;
522
523     return TRUE;
524 }
525
526 /*
527  * _gst_vaapi_image_set_image:
528  * @image: a #GstVaapiImage
529  * @va_image: a VA image
530  *
531  * Initializes #GstVaapiImage with a foreign VA image. This function
532  * will try to "linearize" the VA image. i.e. making sure that the VA
533  * image offsets into the data buffer are in increasing order with the
534  * number of planes available in the image.
535  *
536  * This is an internal function used by gst_vaapi_image_new_with_image().
537  *
538  * Return value: %TRUE on success
539  */
540 gboolean
541 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image)
542 {
543     GstVaapiImagePrivate * const priv = image->priv;
544     GstVaapiImageFormat format;
545     VAImage alt_va_image;
546     const VAImageFormat *alt_va_format;
547
548     if (!va_image)
549         return FALSE;
550
551     format = gst_vaapi_image_format(&va_image->format);
552     if (!format)
553         return FALSE;
554
555     priv->create_image    = FALSE;
556     priv->internal_image  = *va_image;
557     priv->internal_format = format;
558     priv->is_linear       = vaapi_image_is_linear(va_image);
559     priv->image           = *va_image;
560     priv->format          = format;
561     priv->width           = va_image->width;
562     priv->height          = va_image->height;
563
564     /* Try to linearize image */
565     if (!priv->is_linear) {
566         switch (format) {
567         case GST_VAAPI_IMAGE_I420:
568             format = GST_VAAPI_IMAGE_YV12;
569             break;
570         case GST_VAAPI_IMAGE_YV12:
571             format = GST_VAAPI_IMAGE_I420;
572             break;
573         default:
574             format = 0;
575             break;
576         }
577         if (format &&
578             (alt_va_format = gst_vaapi_image_format_get_va_format(format))) {
579             alt_va_image = *va_image;
580             alt_va_image.format = *alt_va_format;
581             SWAP_UINT(alt_va_image.offsets[1], alt_va_image.offsets[2]);
582             SWAP_UINT(alt_va_image.pitches[1], alt_va_image.pitches[2]);
583             if (vaapi_image_is_linear(&alt_va_image)) {
584                 priv->image     = alt_va_image;
585                 priv->format    = format;
586                 priv->is_linear = TRUE;
587                 GST_DEBUG("linearized image to %" GST_FOURCC_FORMAT " format",
588                           GST_FOURCC_ARGS(format));
589             }
590         }
591     }
592     return TRUE;
593 }
594
595 /**
596  * gst_vaapi_image_get_format:
597  * @image: a #GstVaapiImage
598  *
599  * Returns the #GstVaapiImageFormat the @image was created with.
600  *
601  * Return value: the #GstVaapiImageFormat
602  */
603 GstVaapiImageFormat
604 gst_vaapi_image_get_format(GstVaapiImage *image)
605 {
606     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
607     g_return_val_if_fail(image->priv->is_constructed, FALSE);
608
609     return image->priv->format;
610 }
611
612 /**
613  * gst_vaapi_image_get_width:
614  * @image: a #GstVaapiImage
615  *
616  * Returns the @image width.
617  *
618  * Return value: the image width, in pixels
619  */
620 guint
621 gst_vaapi_image_get_width(GstVaapiImage *image)
622 {
623     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
624     g_return_val_if_fail(image->priv->is_constructed, FALSE);
625
626     return image->priv->width;
627 }
628
629 /**
630  * gst_vaapi_image_get_height:
631  * @image: a #GstVaapiImage
632  *
633  * Returns the @image height.
634  *
635  * Return value: the image height, in pixels.
636  */
637 guint
638 gst_vaapi_image_get_height(GstVaapiImage *image)
639 {
640     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
641     g_return_val_if_fail(image->priv->is_constructed, FALSE);
642
643     return image->priv->height;
644 }
645
646 /**
647  * gst_vaapi_image_get_size:
648  * @image: a #GstVaapiImage
649  * @pwidth: return location for the width, or %NULL
650  * @pheight: return location for the height, or %NULL
651  *
652  * Retrieves the dimensions of a #GstVaapiImage.
653  */
654 void
655 gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
656 {
657     g_return_if_fail(GST_VAAPI_IS_IMAGE(image));
658     g_return_if_fail(image->priv->is_constructed);
659
660     if (pwidth)
661         *pwidth = image->priv->width;
662
663     if (pheight)
664         *pheight = image->priv->height;
665 }
666
667 /**
668  * gst_vaapi_image_is_linear:
669  * @image: a #GstVaapiImage
670  *
671  * Checks whether the @image has data planes allocated from a single
672  * buffer and offsets into that buffer are in increasing order with
673  * the number of planes.
674  *
675  * Return value: %TRUE if image data planes are allocated from a single buffer
676  */
677 gboolean
678 gst_vaapi_image_is_linear(GstVaapiImage *image)
679 {
680     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
681     g_return_val_if_fail(image->priv->is_constructed, FALSE);
682
683     return image->priv->is_linear;
684 }
685
686 /**
687  * gst_vaapi_image_is_mapped:
688  * @image: a #GstVaapiImage
689  *
690  * Checks whether the @image is currently mapped or not.
691  *
692  * Return value: %TRUE if the @image is mapped
693  */
694 static inline gboolean
695 _gst_vaapi_image_is_mapped(GstVaapiImage *image)
696 {
697     return image->priv->image_data != NULL;
698 }
699
700 gboolean
701 gst_vaapi_image_is_mapped(GstVaapiImage *image)
702 {
703     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
704     g_return_val_if_fail(image->priv->is_constructed, FALSE);
705
706     return _gst_vaapi_image_is_mapped(image);
707 }
708
709 /**
710  * gst_vaapi_image_map:
711  * @image: a #GstVaapiImage
712  *
713  * Maps the image data buffer. The actual pixels are returned by the
714  * gst_vaapi_image_get_plane() function.
715  *
716  * Return value: %TRUE on success
717  */
718 gboolean
719 gst_vaapi_image_map(GstVaapiImage *image)
720 {
721     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
722     g_return_val_if_fail(image->priv->is_constructed, FALSE);
723
724     return _gst_vaapi_image_map(image);
725 }
726
727 gboolean
728 _gst_vaapi_image_map(GstVaapiImage *image)
729 {
730     GstVaapiDisplay *display;
731     void *image_data;
732     VAStatus status;
733
734     if (_gst_vaapi_image_is_mapped(image))
735         return TRUE;
736
737     display = GST_VAAPI_OBJECT_GET_DISPLAY(image);
738     if (!display)
739         return FALSE;
740
741     GST_VAAPI_DISPLAY_LOCK(display);
742     status = vaMapBuffer(
743         GST_VAAPI_DISPLAY_VADISPLAY(display),
744         image->priv->image.buf,
745         &image_data
746     );
747     GST_VAAPI_DISPLAY_UNLOCK(display);
748     if (!vaapi_check_status(status, "vaMapBuffer()"))
749         return FALSE;
750
751     image->priv->image_data = image_data;
752     return TRUE;
753 }
754
755 /**
756  * gst_vaapi_image_unmap:
757  * @image: a #GstVaapiImage
758  *
759  * Unmaps the image data buffer. Pointers to pixels returned by
760  * gst_vaapi_image_get_plane() are then no longer valid.
761  *
762  * Return value: %TRUE on success
763  */
764 gboolean
765 gst_vaapi_image_unmap(GstVaapiImage *image)
766 {
767     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
768     g_return_val_if_fail(image->priv->is_constructed, FALSE);
769
770     return _gst_vaapi_image_unmap(image);
771 }
772
773 gboolean
774 _gst_vaapi_image_unmap(GstVaapiImage *image)
775 {
776     GstVaapiDisplay *display;
777     VAStatus status;
778
779     if (!_gst_vaapi_image_is_mapped(image))
780         return FALSE;
781
782     display = GST_VAAPI_OBJECT_GET_DISPLAY(image);
783     if (!display)
784         return FALSE;
785
786     GST_VAAPI_DISPLAY_LOCK(display);
787     status = vaUnmapBuffer(
788         GST_VAAPI_DISPLAY_VADISPLAY(display),
789         image->priv->image.buf
790     );
791     GST_VAAPI_DISPLAY_UNLOCK(display);
792     if (!vaapi_check_status(status, "vaUnmapBuffer()"))
793         return FALSE;
794
795     image->priv->image_data = NULL;
796     return TRUE;
797 }
798
799 /**
800  * gst_vaapi_image_get_plane_count:
801  * @image: a #GstVaapiImage
802  *
803  * Retrieves the number of planes available in the @image. The @image
804  * must be mapped for this function to work properly.
805  *
806  * Return value: the number of planes available in the @image
807  */
808 guint
809 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
810 {
811     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
812     g_return_val_if_fail(image->priv->is_constructed, FALSE);
813     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
814
815     return image->priv->image.num_planes;
816 }
817
818 /**
819  * gst_vaapi_image_get_plane:
820  * @image: a #GstVaapiImage
821  * @plane: the requested plane number
822  *
823  * Retrieves the pixels data to the specified @plane. The @image must
824  * be mapped for this function to work properly.
825  *
826  * Return value: the pixels data of the specified @plane
827  */
828 guchar *
829 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
830 {
831     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
832     g_return_val_if_fail(image->priv->is_constructed, FALSE);
833     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
834     g_return_val_if_fail(plane < image->priv->image.num_planes, NULL);
835
836     return image->priv->image_data + image->priv->image.offsets[plane];
837 }
838
839 /**
840  * gst_vaapi_image_get_pitch:
841  * @image: a #GstVaapiImage
842  * @plane: the requested plane number
843  *
844  * Retrieves the line size (stride) of the specified @plane. The
845  * @image must be mapped for this function to work properly.
846  *
847  * Return value: the line size (stride) of the specified plane
848  */
849 guint
850 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
851 {
852     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
853     g_return_val_if_fail(image->priv->is_constructed, FALSE);
854     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
855     g_return_val_if_fail(plane < image->priv->image.num_planes, 0);
856
857     return image->priv->image.pitches[plane];
858 }
859
860 /**
861  * gst_vaapi_image_get_data_size:
862  * @image: a #GstVaapiImage
863  *
864  * Retrieves the underlying image data size. This function could be
865  * used to determine whether the image has a compatible layout with
866  * another image structure.
867  *
868  * Return value: the whole image data size of the @image
869  */
870 guint
871 gst_vaapi_image_get_data_size(GstVaapiImage *image)
872 {
873     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
874     g_return_val_if_fail(image->priv->is_constructed, FALSE);
875
876     return image->priv->image.data_size;
877 }
878
879 /**
880  * gst_vaapi_image_update_from_buffer:
881  * @image: a #GstVaapiImage
882  * @buffer: a #GstBuffer
883  *
884  * Transfers pixels data contained in the #GstBuffer into the
885  * @image. Both image structures shall have the same format.
886  *
887  * Return value: %TRUE on success
888  */
889 gboolean
890 gst_vaapi_image_update_from_buffer(GstVaapiImage *image, GstBuffer *buffer)
891 {
892     GstVaapiImagePrivate *priv;
893     GstStructure *structure;
894     GstCaps *caps;
895     GstVaapiImageFormat format;
896     gint width, height;
897     guint offsets[3], pitches[3], widths[3], heights[3];
898     guint i, j;
899     guchar *data;
900     guint32 data_size;
901
902     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
903     g_return_val_if_fail(image->priv->is_constructed, FALSE);
904     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
905
906     priv      = image->priv;
907     data      = GST_BUFFER_DATA(buffer);
908     data_size = GST_BUFFER_SIZE(buffer);
909     caps      = GST_BUFFER_CAPS(buffer);
910
911     if (!caps)
912         return FALSE;
913
914     format = gst_vaapi_image_format_from_caps(caps);
915     if (format != priv->format)
916         return FALSE;
917
918     structure = gst_caps_get_structure(caps, 0);
919     gst_structure_get_int(structure, "width",  &width);
920     gst_structure_get_int(structure, "height", &height);
921     if (width != priv->width || height != priv->height)
922         return FALSE;
923
924     if (!gst_vaapi_image_map(image))
925         return FALSE;
926
927     if (priv->is_linear && data_size == priv->image.data_size)
928         memcpy(priv->image_data, data, data_size);
929     else {
930         /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
931         const guint width2  = (width  + 1) / 2;
932         const guint height2 = (height + 1) / 2;
933         guint size2;
934         switch (format) {
935         case GST_VAAPI_IMAGE_NV12:
936             offsets[0] = 0;
937             pitches[0] = GST_ROUND_UP_4(width);
938             widths [0] = width;
939             heights[0] = height;
940             offsets[1] = offsets[0] + height * pitches[0];
941             pitches[1] = pitches[0];
942             widths [1] = width2 * 2;
943             heights[1] = height2;
944             size2      = offsets[1] + height2 * pitches[1];
945             break;
946         case GST_VAAPI_IMAGE_YV12:
947         case GST_VAAPI_IMAGE_I420:
948             offsets[0] = 0;
949             pitches[0] = GST_ROUND_UP_4(width);
950             widths [0] = width;
951             heights[0] = height;
952             offsets[1] = offsets[0] + height * pitches[0];
953             pitches[1] = GST_ROUND_UP_4(GST_ROUND_UP_2(width) / 2);
954             widths [1] = width2;
955             heights[1] = height2;
956             offsets[2] = offsets[1] + height2 * pitches[1];
957             pitches[2] = pitches[1];
958             widths [2] = width2;
959             heights[2] = height2;
960             size2      = offsets[2] + height2 * pitches[2];
961             break;
962         case GST_VAAPI_IMAGE_ARGB:
963         case GST_VAAPI_IMAGE_RGBA:
964         case GST_VAAPI_IMAGE_ABGR:
965         case GST_VAAPI_IMAGE_BGRA:
966             offsets[0] = 0;
967             pitches[0] = width * 4;
968             widths [0] = width * 4;
969             heights[0] = height;
970             size2      = offsets[0] + height * pitches[0];
971             break;
972         default:
973             g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
974                     GST_FOURCC_ARGS(format));
975             break;
976         }
977         if (size2 != data_size)
978             g_error("data_size mismatch %d / %u", size2, data_size);
979         for (i = 0; i < priv->image.num_planes; i++) {
980             guchar *src = data + offsets[i];
981             guchar *dst = priv->image_data + priv->image.offsets[i];
982             for (j = 0; j < heights[i]; j++) {
983                 memcpy(dst, src, widths[i]);
984                 src += pitches[i];
985                 dst += priv->image.pitches[i];
986             }
987         }
988     }
989
990     if (!gst_vaapi_image_unmap(image))
991         return FALSE;
992
993     return TRUE;
994 }