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