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