image: add helpers to extract pixels to user buffers.
[profile/ivi/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiimage.c
1 /*
2  *  gstvaapiimage.c - VA image abstraction
3  *
4  *  gstreamer-vaapi (C) 2010-2011 Splitted-Desktop Systems
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public License
8  *  as published by the Free Software Foundation; either version 2.1
9  *  of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301 USA
20  */
21
22 /**
23  * SECTION:gstvaapiimage
24  * @short_description: VA image abstraction
25  */
26
27 #include "config.h"
28 #include <string.h>
29 #include "gstvaapicompat.h"
30 #include "gstvaapiutils.h"
31 #include "gstvaapiimage.h"
32 #include "gstvaapi_priv.h"
33
34 #define DEBUG 1
35 #include "gstvaapidebug.h"
36
37 G_DEFINE_TYPE(GstVaapiImage, gst_vaapi_image, GST_VAAPI_TYPE_OBJECT);
38
39 #define GST_VAAPI_IMAGE_GET_PRIVATE(obj)                \
40     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                 \
41                                  GST_VAAPI_TYPE_IMAGE,  \
42                                  GstVaapiImagePrivate))
43
44 struct _GstVaapiImagePrivate {
45     VAImage             internal_image;
46     VAImage             image;
47     guchar             *image_data;
48     GstVaapiImageFormat internal_format;
49     GstVaapiImageFormat format;
50     guint               width;
51     guint               height;
52     guint               create_image    : 1;
53     guint               is_constructed  : 1;
54     guint               is_linear       : 1;
55 };
56
57 enum {
58     PROP_0,
59
60     PROP_IMAGE,
61     PROP_FORMAT,
62     PROP_WIDTH,
63     PROP_HEIGHT
64 };
65
66 #define SWAP_UINT(a, b) do { \
67         guint v = a;         \
68         a = b;               \
69         b = v;               \
70     } while (0)
71
72 static gboolean
73 _gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image);
74
75 static gboolean
76 _gst_vaapi_image_unmap(GstVaapiImage *image);
77
78 static gboolean
79 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image);
80
81 /*
82  * VAImage wrapper
83  */
84
85 #define VAAPI_TYPE_IMAGE vaapi_image_get_type()
86
87 static gpointer
88 vaapi_image_copy(gpointer va_image)
89 {
90     return g_slice_dup(VAImage, va_image);
91 }
92
93 static void
94 vaapi_image_free(gpointer va_image)
95 {
96     if (G_LIKELY(va_image))
97         g_slice_free(VAImage, va_image);
98 }
99
100 static GType
101 vaapi_image_get_type(void)
102 {
103     static GType type = 0;
104
105     if (G_UNLIKELY(type == 0))
106         type = g_boxed_type_register_static(
107             "VAImage",
108             vaapi_image_copy,
109             vaapi_image_free
110         );
111     return type;
112 }
113
114 static gboolean
115 vaapi_image_is_linear(const VAImage *va_image)
116 {
117     guint i, width, height, width2, height2, data_size;
118
119     for (i = 1; i < va_image->num_planes; i++)
120         if (va_image->offsets[i] < va_image->offsets[i - 1])
121             return FALSE;
122
123     width   = va_image->width;
124     height  = va_image->height;
125     width2  = (width  + 1) / 2;
126     height2 = (height + 1) / 2;
127
128     switch (va_image->format.fourcc) {
129     case VA_FOURCC('N','V','1','2'):
130     case VA_FOURCC('Y','V','1','2'):
131     case VA_FOURCC('I','4','2','0'):
132         data_size = width * height + 2 * width2 * height2;
133         break;
134     case VA_FOURCC('A','Y','U','V'):
135     case VA_FOURCC('A','R','G','B'):
136     case VA_FOURCC('R','G','B','A'):
137     case VA_FOURCC('A','B','G','R'):
138     case VA_FOURCC('B','G','R','A'):
139         data_size = 4 * width * height;
140         break;
141     default:
142         g_error("FIXME: incomplete formats");
143         break;
144     }
145     return va_image->data_size == data_size;
146 }
147
148 static void
149 gst_vaapi_image_destroy(GstVaapiImage *image)
150 {
151     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
152     VAImageID image_id;
153     VAStatus status;
154
155     _gst_vaapi_image_unmap(image);
156
157     image_id = GST_VAAPI_OBJECT_ID(image);
158     GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
159
160     if (image_id != VA_INVALID_ID) {
161         GST_VAAPI_DISPLAY_LOCK(display);
162         status = vaDestroyImage(GST_VAAPI_DISPLAY_VADISPLAY(display), image_id);
163         GST_VAAPI_DISPLAY_UNLOCK(display);
164         if (!vaapi_check_status(status, "vaDestroyImage()"))
165             g_warning("failed to destroy image %" GST_VAAPI_ID_FORMAT,
166                       GST_VAAPI_ID_ARGS(image_id));
167         GST_VAAPI_OBJECT_ID(image) = VA_INVALID_ID;
168     }
169 }
170
171 static gboolean
172 _gst_vaapi_image_create(GstVaapiImage *image, GstVaapiImageFormat format)
173 {
174     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
175     GstVaapiImagePrivate * const priv = image->priv;
176     const VAImageFormat *va_format;
177     VAStatus status;
178
179     if (!gst_vaapi_display_has_image_format(display, format))
180         return FALSE;
181
182     va_format = gst_vaapi_image_format_get_va_format(format);
183     if (!va_format)
184         return FALSE;
185
186     GST_VAAPI_DISPLAY_LOCK(display);
187     status = vaCreateImage(
188         GST_VAAPI_DISPLAY_VADISPLAY(display),
189         (VAImageFormat *)va_format,
190         priv->width,
191         priv->height,
192         &priv->internal_image
193     );
194     GST_VAAPI_DISPLAY_UNLOCK(display);
195     if (status != VA_STATUS_SUCCESS ||
196         priv->internal_image.format.fourcc != va_format->fourcc)
197         return FALSE;
198
199     priv->internal_format = format;
200     return TRUE;
201 }
202
203 static gboolean
204 gst_vaapi_image_create(GstVaapiImage *image)
205 {
206     GstVaapiImagePrivate * const priv = image->priv;
207     GstVaapiImageFormat format = priv->format;
208     const VAImageFormat *va_format;
209     VAImageID image_id;
210
211     if (!priv->create_image)
212         return (priv->image.image_id != VA_INVALID_ID &&
213                 priv->image.buf      != VA_INVALID_ID);
214
215     if (!_gst_vaapi_image_create(image, format)) {
216         switch (format) {
217         case GST_VAAPI_IMAGE_I420:
218             format = GST_VAAPI_IMAGE_YV12;
219             break;
220         case GST_VAAPI_IMAGE_YV12:
221             format = GST_VAAPI_IMAGE_I420;
222             break;
223         default:
224             format = 0;
225             break;
226         }
227         if (!format || !_gst_vaapi_image_create(image, format))
228             return FALSE;
229     }
230     priv->image = priv->internal_image;
231     image_id    = priv->image.image_id;
232
233     if (priv->format != priv->internal_format) {
234         switch (priv->format) {
235         case GST_VAAPI_IMAGE_YV12:
236         case GST_VAAPI_IMAGE_I420:
237             va_format = gst_vaapi_image_format_get_va_format(priv->format);
238             if (!va_format)
239                 return FALSE;
240             priv->image.format = *va_format;
241             SWAP_UINT(priv->image.offsets[1], priv->image.offsets[2]);
242             SWAP_UINT(priv->image.pitches[1], priv->image.pitches[2]);
243             break;
244         default:
245             break;
246         }
247     }
248     priv->is_linear = vaapi_image_is_linear(&priv->image);
249
250     GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
251     GST_VAAPI_OBJECT_ID(image) = image_id;
252     return TRUE;
253 }
254
255 static void
256 gst_vaapi_image_finalize(GObject *object)
257 {
258     gst_vaapi_image_destroy(GST_VAAPI_IMAGE(object));
259
260     G_OBJECT_CLASS(gst_vaapi_image_parent_class)->finalize(object);
261 }
262
263 static void
264 gst_vaapi_image_set_property(
265     GObject      *object,
266     guint         prop_id,
267     const GValue *value,
268     GParamSpec   *pspec
269 )
270 {
271     GstVaapiImage        * const image = GST_VAAPI_IMAGE(object);
272     GstVaapiImagePrivate * const priv  = image->priv;
273
274     switch (prop_id) {
275     case PROP_IMAGE: {
276         const VAImage * const va_image = g_value_get_boxed(value);
277         if (va_image)
278             _gst_vaapi_image_set_image(image, va_image);
279         break;
280     }
281     case PROP_FORMAT:
282         if (priv->create_image)
283             priv->format = g_value_get_uint(value);
284         break;
285     case PROP_WIDTH:
286         if (priv->create_image)
287             priv->width = g_value_get_uint(value);
288         break;
289     case PROP_HEIGHT:
290         if (priv->create_image)
291             priv->height = g_value_get_uint(value);
292         break;
293     default:
294         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
295         break;
296     }
297 }
298
299 static void
300 gst_vaapi_image_get_property(
301     GObject    *object,
302     guint       prop_id,
303     GValue     *value,
304     GParamSpec *pspec
305 )
306 {
307     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
308
309     switch (prop_id) {
310     case PROP_IMAGE:
311         g_value_set_boxed(value, &image->priv->image);
312         break;
313     case PROP_FORMAT:
314         g_value_set_uint(value, gst_vaapi_image_get_format(image));
315         break;
316     case PROP_WIDTH:
317         g_value_set_uint(value, gst_vaapi_image_get_width(image));
318         break;
319     case PROP_HEIGHT:
320         g_value_set_uint(value, gst_vaapi_image_get_height(image));
321         break;
322     default:
323         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
324         break;
325     }
326 }
327
328 static void
329 gst_vaapi_image_constructed(GObject *object)
330 {
331     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
332     GObjectClass *parent_class;
333
334     image->priv->is_constructed = gst_vaapi_image_create(image);
335
336     parent_class = G_OBJECT_CLASS(gst_vaapi_image_parent_class);
337     if (parent_class->constructed)
338         parent_class->constructed(object);
339 }
340
341 static void
342 gst_vaapi_image_class_init(GstVaapiImageClass *klass)
343 {
344     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
345
346     g_type_class_add_private(klass, sizeof(GstVaapiImagePrivate));
347
348     object_class->finalize     = gst_vaapi_image_finalize;
349     object_class->set_property = gst_vaapi_image_set_property;
350     object_class->get_property = gst_vaapi_image_get_property;
351     object_class->constructed  = gst_vaapi_image_constructed;
352
353     g_object_class_install_property
354         (object_class,
355          PROP_IMAGE,
356          g_param_spec_boxed("image",
357                             "Image",
358                             "The underlying VA image",
359                             VAAPI_TYPE_IMAGE,
360                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
361
362     g_object_class_install_property
363         (object_class,
364          PROP_WIDTH,
365          g_param_spec_uint("width",
366                            "width",
367                            "The image width",
368                            0, G_MAXUINT32, 0,
369                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
370
371     g_object_class_install_property
372         (object_class,
373          PROP_HEIGHT,
374          g_param_spec_uint("height",
375                            "heighr",
376                            "The image height",
377                            0, G_MAXUINT32, 0,
378                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
379
380     /**
381      * GstVaapiImage:format:
382      *
383      * The #GstVaapiImageFormat of the image
384      */
385     g_object_class_install_property
386         (object_class,
387          PROP_FORMAT,
388          g_param_spec_uint("format",
389                            "Format",
390                            "The underlying image format",
391                            0, G_MAXUINT32, 0,
392                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
393 }
394
395 static void
396 gst_vaapi_image_init(GstVaapiImage *image)
397 {
398     GstVaapiImagePrivate *priv = GST_VAAPI_IMAGE_GET_PRIVATE(image);
399
400     image->priv                   = priv;
401     priv->image_data              = NULL;
402     priv->width                   = 0;
403     priv->height                  = 0;
404     priv->internal_format         = 0;
405     priv->format                  = 0;
406     priv->create_image            = TRUE;
407     priv->is_constructed          = FALSE;
408     priv->is_linear               = FALSE;
409
410     memset(&priv->internal_image, 0, sizeof(priv->internal_image));
411     priv->internal_image.image_id = VA_INVALID_ID;
412     priv->internal_image.buf      = VA_INVALID_ID;
413
414     memset(&priv->image, 0, sizeof(priv->image));
415     priv->image.image_id          = VA_INVALID_ID;
416     priv->image.buf               = VA_INVALID_ID;
417 }
418
419 /**
420  * gst_vaapi_image_new:
421  * @display: a #GstVaapiDisplay
422  * @format: a #GstVaapiImageFormat
423  * @width: the requested image width
424  * @height: the requested image height
425  *
426  * Creates a new #GstVaapiImage with the specified format and
427  * dimensions.
428  *
429  * Return value: the newly allocated #GstVaapiImage object
430  */
431 GstVaapiImage *
432 gst_vaapi_image_new(
433     GstVaapiDisplay    *display,
434     GstVaapiImageFormat format,
435     guint               width,
436     guint               height
437 )
438 {
439     GstVaapiImage *image;
440
441     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
442     g_return_val_if_fail(width > 0, NULL);
443     g_return_val_if_fail(height > 0, NULL);
444
445     GST_DEBUG("format %" GST_FOURCC_FORMAT ", size %ux%u",
446               GST_FOURCC_ARGS(format), width, height);
447
448     image = g_object_new(
449         GST_VAAPI_TYPE_IMAGE,
450         "display", display,
451         "id",      GST_VAAPI_ID(VA_INVALID_ID),
452         "format",  format,
453         "width",   width,
454         "height",  height,
455         NULL
456     );
457     if (!image)
458         return NULL;
459
460     if (!image->priv->is_constructed) {
461         g_object_unref(image);
462         return NULL;
463     }
464     return image;
465 }
466
467 /**
468  * gst_vaapi_image_new_with_image:
469  * @display: a #GstVaapiDisplay
470  * @va_image: a VA image
471  *
472  * Creates a new #GstVaapiImage from a foreign VA image. The image
473  * format and dimensions will be extracted from @va_image. This
474  * function is mainly used by gst_vaapi_surface_derive_image() to bind
475  * a VA image to a #GstVaapiImage object.
476  *
477  * Return value: the newly allocated #GstVaapiImage object
478  */
479 GstVaapiImage *
480 gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
481 {
482     GstVaapiImage *image;
483
484     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
485     g_return_val_if_fail(va_image, NULL);
486     g_return_val_if_fail(va_image->image_id != VA_INVALID_ID, NULL);
487     g_return_val_if_fail(va_image->buf != VA_INVALID_ID, NULL);
488
489     GST_DEBUG("VA image 0x%08x, format %" GST_FOURCC_FORMAT ", size %ux%u",
490               va_image->image_id,
491               GST_FOURCC_ARGS(va_image->format.fourcc),
492               va_image->width, va_image->height);
493
494     image = g_object_new(
495         GST_VAAPI_TYPE_IMAGE,
496         "display", display,
497         "id",      GST_VAAPI_ID(va_image->image_id),
498         "image",   va_image,
499         NULL
500     );
501     if (!image)
502         return NULL;
503
504     if (!image->priv->is_constructed) {
505         g_object_unref(image);
506         return NULL;
507     }
508     return image;
509 }
510
511 /**
512  * gst_vaapi_image_get_id:
513  * @image: a #GstVaapiImage
514  *
515  * Returns the underlying VAImageID of the @image.
516  *
517  * Return value: the underlying VA image id
518  */
519 GstVaapiID
520 gst_vaapi_image_get_id(GstVaapiImage *image)
521 {
522     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), VA_INVALID_ID);
523     g_return_val_if_fail(image->priv->is_constructed, VA_INVALID_ID);
524
525     return GST_VAAPI_OBJECT_ID(image);
526 }
527
528 /**
529  * gst_vaapi_image_get_image:
530  * @image: a #GstVaapiImage
531  * @va_image: a VA image
532  *
533  * Fills @va_image with the VA image used internally.
534  *
535  * Return value: %TRUE on success
536  */
537 gboolean
538 gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
539 {
540     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
541     g_return_val_if_fail(image->priv->is_constructed, FALSE);
542
543     if (va_image)
544         *va_image = image->priv->image;
545
546     return TRUE;
547 }
548
549 /*
550  * _gst_vaapi_image_set_image:
551  * @image: a #GstVaapiImage
552  * @va_image: a VA image
553  *
554  * Initializes #GstVaapiImage with a foreign VA image. This function
555  * will try to "linearize" the VA image. i.e. making sure that the VA
556  * image offsets into the data buffer are in increasing order with the
557  * number of planes available in the image.
558  *
559  * This is an internal function used by gst_vaapi_image_new_with_image().
560  *
561  * Return value: %TRUE on success
562  */
563 gboolean
564 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image)
565 {
566     GstVaapiImagePrivate * const priv = image->priv;
567     GstVaapiImageFormat format;
568     VAImage alt_va_image;
569     const VAImageFormat *alt_va_format;
570
571     if (!va_image)
572         return FALSE;
573
574     format = gst_vaapi_image_format(&va_image->format);
575     if (!format)
576         return FALSE;
577
578     priv->create_image    = FALSE;
579     priv->internal_image  = *va_image;
580     priv->internal_format = format;
581     priv->is_linear       = vaapi_image_is_linear(va_image);
582     priv->image           = *va_image;
583     priv->format          = format;
584     priv->width           = va_image->width;
585     priv->height          = va_image->height;
586
587     /* Try to linearize image */
588     if (!priv->is_linear) {
589         switch (format) {
590         case GST_VAAPI_IMAGE_I420:
591             format = GST_VAAPI_IMAGE_YV12;
592             break;
593         case GST_VAAPI_IMAGE_YV12:
594             format = GST_VAAPI_IMAGE_I420;
595             break;
596         default:
597             format = 0;
598             break;
599         }
600         if (format &&
601             (alt_va_format = gst_vaapi_image_format_get_va_format(format))) {
602             alt_va_image = *va_image;
603             alt_va_image.format = *alt_va_format;
604             SWAP_UINT(alt_va_image.offsets[1], alt_va_image.offsets[2]);
605             SWAP_UINT(alt_va_image.pitches[1], alt_va_image.pitches[2]);
606             if (vaapi_image_is_linear(&alt_va_image)) {
607                 priv->image     = alt_va_image;
608                 priv->format    = format;
609                 priv->is_linear = TRUE;
610                 GST_DEBUG("linearized image to %" GST_FOURCC_FORMAT " format",
611                           GST_FOURCC_ARGS(format));
612             }
613         }
614     }
615     return TRUE;
616 }
617
618 /**
619  * gst_vaapi_image_get_format:
620  * @image: a #GstVaapiImage
621  *
622  * Returns the #GstVaapiImageFormat the @image was created with.
623  *
624  * Return value: the #GstVaapiImageFormat
625  */
626 GstVaapiImageFormat
627 gst_vaapi_image_get_format(GstVaapiImage *image)
628 {
629     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
630     g_return_val_if_fail(image->priv->is_constructed, 0);
631
632     return image->priv->format;
633 }
634
635 /**
636  * gst_vaapi_image_get_width:
637  * @image: a #GstVaapiImage
638  *
639  * Returns the @image width.
640  *
641  * Return value: the image width, in pixels
642  */
643 guint
644 gst_vaapi_image_get_width(GstVaapiImage *image)
645 {
646     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
647     g_return_val_if_fail(image->priv->is_constructed, 0);
648
649     return image->priv->width;
650 }
651
652 /**
653  * gst_vaapi_image_get_height:
654  * @image: a #GstVaapiImage
655  *
656  * Returns the @image height.
657  *
658  * Return value: the image height, in pixels.
659  */
660 guint
661 gst_vaapi_image_get_height(GstVaapiImage *image)
662 {
663     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
664     g_return_val_if_fail(image->priv->is_constructed, 0);
665
666     return image->priv->height;
667 }
668
669 /**
670  * gst_vaapi_image_get_size:
671  * @image: a #GstVaapiImage
672  * @pwidth: return location for the width, or %NULL
673  * @pheight: return location for the height, or %NULL
674  *
675  * Retrieves the dimensions of a #GstVaapiImage.
676  */
677 void
678 gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
679 {
680     g_return_if_fail(GST_VAAPI_IS_IMAGE(image));
681     g_return_if_fail(image->priv->is_constructed);
682
683     if (pwidth)
684         *pwidth = image->priv->width;
685
686     if (pheight)
687         *pheight = image->priv->height;
688 }
689
690 /**
691  * gst_vaapi_image_is_linear:
692  * @image: a #GstVaapiImage
693  *
694  * Checks whether the @image has data planes allocated from a single
695  * buffer and offsets into that buffer are in increasing order with
696  * the number of planes.
697  *
698  * Return value: %TRUE if image data planes are allocated from a single buffer
699  */
700 gboolean
701 gst_vaapi_image_is_linear(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 image->priv->is_linear;
707 }
708
709 /**
710  * gst_vaapi_image_is_mapped:
711  * @image: a #GstVaapiImage
712  *
713  * Checks whether the @image is currently mapped or not.
714  *
715  * Return value: %TRUE if the @image is mapped
716  */
717 static inline gboolean
718 _gst_vaapi_image_is_mapped(GstVaapiImage *image)
719 {
720     return image->priv->image_data != NULL;
721 }
722
723 gboolean
724 gst_vaapi_image_is_mapped(GstVaapiImage *image)
725 {
726     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
727     g_return_val_if_fail(image->priv->is_constructed, FALSE);
728
729     return _gst_vaapi_image_is_mapped(image);
730 }
731
732 /**
733  * gst_vaapi_image_map:
734  * @image: a #GstVaapiImage
735  *
736  * Maps the image data buffer. The actual pixels are returned by the
737  * gst_vaapi_image_get_plane() function.
738  *
739  * Return value: %TRUE on success
740  */
741 gboolean
742 gst_vaapi_image_map(GstVaapiImage *image)
743 {
744     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
745     g_return_val_if_fail(image->priv->is_constructed, FALSE);
746
747     return _gst_vaapi_image_map(image, NULL);
748 }
749
750 gboolean
751 _gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image)
752 {
753     GstVaapiImagePrivate * const priv = image->priv;
754     GstVaapiDisplay *display;
755     void *image_data;
756     VAStatus status;
757     guint i;
758
759     if (_gst_vaapi_image_is_mapped(image))
760         return TRUE;
761
762     display = GST_VAAPI_OBJECT_DISPLAY(image);
763     if (!display)
764         return FALSE;
765
766     GST_VAAPI_DISPLAY_LOCK(display);
767     status = vaMapBuffer(
768         GST_VAAPI_DISPLAY_VADISPLAY(display),
769         image->priv->image.buf,
770         &image_data
771     );
772     GST_VAAPI_DISPLAY_UNLOCK(display);
773     if (!vaapi_check_status(status, "vaMapBuffer()"))
774         return FALSE;
775
776     image->priv->image_data = image_data;
777
778     if (raw_image) {
779         const VAImage * const va_image = &priv->image;
780         raw_image->format     = priv->format;
781         raw_image->width      = va_image->width;
782         raw_image->height     = va_image->height;
783         raw_image->num_planes = va_image->num_planes;
784         for (i = 0; i < raw_image->num_planes; i++) {
785             raw_image->pixels[i] = image_data + va_image->offsets[i];
786             raw_image->stride[i] = va_image->pitches[i];
787         }
788     }
789     return TRUE;
790 }
791
792 /**
793  * gst_vaapi_image_unmap:
794  * @image: a #GstVaapiImage
795  *
796  * Unmaps the image data buffer. Pointers to pixels returned by
797  * gst_vaapi_image_get_plane() are then no longer valid.
798  *
799  * Return value: %TRUE on success
800  */
801 gboolean
802 gst_vaapi_image_unmap(GstVaapiImage *image)
803 {
804     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
805     g_return_val_if_fail(image->priv->is_constructed, FALSE);
806
807     return _gst_vaapi_image_unmap(image);
808 }
809
810 gboolean
811 _gst_vaapi_image_unmap(GstVaapiImage *image)
812 {
813     GstVaapiDisplay *display;
814     VAStatus status;
815
816     if (!_gst_vaapi_image_is_mapped(image))
817         return FALSE;
818
819     display = GST_VAAPI_OBJECT_DISPLAY(image);
820     if (!display)
821         return FALSE;
822
823     GST_VAAPI_DISPLAY_LOCK(display);
824     status = vaUnmapBuffer(
825         GST_VAAPI_DISPLAY_VADISPLAY(display),
826         image->priv->image.buf
827     );
828     GST_VAAPI_DISPLAY_UNLOCK(display);
829     if (!vaapi_check_status(status, "vaUnmapBuffer()"))
830         return FALSE;
831
832     image->priv->image_data = NULL;
833     return TRUE;
834 }
835
836 /**
837  * gst_vaapi_image_get_plane_count:
838  * @image: a #GstVaapiImage
839  *
840  * Retrieves the number of planes available in the @image. The @image
841  * must be mapped for this function to work properly.
842  *
843  * Return value: the number of planes available in the @image
844  */
845 guint
846 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
847 {
848     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
849     g_return_val_if_fail(image->priv->is_constructed, FALSE);
850     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
851
852     return image->priv->image.num_planes;
853 }
854
855 /**
856  * gst_vaapi_image_get_plane:
857  * @image: a #GstVaapiImage
858  * @plane: the requested plane number
859  *
860  * Retrieves the pixels data to the specified @plane. The @image must
861  * be mapped for this function to work properly.
862  *
863  * Return value: the pixels data of the specified @plane
864  */
865 guchar *
866 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
867 {
868     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
869     g_return_val_if_fail(image->priv->is_constructed, FALSE);
870     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
871     g_return_val_if_fail(plane < image->priv->image.num_planes, NULL);
872
873     return image->priv->image_data + image->priv->image.offsets[plane];
874 }
875
876 /**
877  * gst_vaapi_image_get_pitch:
878  * @image: a #GstVaapiImage
879  * @plane: the requested plane number
880  *
881  * Retrieves the line size (stride) of the specified @plane. The
882  * @image must be mapped for this function to work properly.
883  *
884  * Return value: the line size (stride) of the specified plane
885  */
886 guint
887 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
888 {
889     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
890     g_return_val_if_fail(image->priv->is_constructed, FALSE);
891     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
892     g_return_val_if_fail(plane < image->priv->image.num_planes, 0);
893
894     return image->priv->image.pitches[plane];
895 }
896
897 /**
898  * gst_vaapi_image_get_data_size:
899  * @image: a #GstVaapiImage
900  *
901  * Retrieves the underlying image data size. This function could be
902  * used to determine whether the image has a compatible layout with
903  * another image structure.
904  *
905  * Return value: the whole image data size of the @image
906  */
907 guint
908 gst_vaapi_image_get_data_size(GstVaapiImage *image)
909 {
910     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
911     g_return_val_if_fail(image->priv->is_constructed, FALSE);
912
913     return image->priv->image.data_size;
914 }
915
916 static gboolean
917 init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
918 {
919     GstStructure *structure;
920     GstCaps *caps;
921     GstVaapiImageFormat format;
922     guint width2, height2, size2;
923     gint width, height;
924     guchar *data;
925     guint32 data_size;
926
927     data      = GST_BUFFER_DATA(buffer);
928     data_size = GST_BUFFER_SIZE(buffer);
929     caps      = GST_BUFFER_CAPS(buffer);
930
931     if (!caps)
932         return FALSE;
933
934     format = gst_vaapi_image_format_from_caps(caps);
935
936     structure = gst_caps_get_structure(caps, 0);
937     gst_structure_get_int(structure, "width",  &width);
938     gst_structure_get_int(structure, "height", &height);
939
940     /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
941     raw_image->format = format;
942     raw_image->width  = width;
943     raw_image->height = height;
944     width2  = (width + 1) / 2;
945     height2 = (height + 1) / 2;
946     size2   = 0;
947     switch (format) {
948     case GST_VAAPI_IMAGE_NV12:
949         raw_image->num_planes = 2;
950         raw_image->pixels[0]  = data;
951         raw_image->stride[0]  = GST_ROUND_UP_4(width);
952         size2                += height * raw_image->stride[0];
953         raw_image->pixels[1]  = data + size2;
954         raw_image->stride[1]  = raw_image->stride[0];
955         size2                += height2 * raw_image->stride[1];
956         break;
957     case GST_VAAPI_IMAGE_YV12:
958     case GST_VAAPI_IMAGE_I420:
959         raw_image->num_planes = 3;
960         raw_image->pixels[0]  = data;
961         raw_image->stride[0]  = GST_ROUND_UP_4(width);
962         size2                += height * raw_image->stride[0];
963         raw_image->pixels[1]  = data + size2;
964         raw_image->stride[1]  = GST_ROUND_UP_4(width2);
965         size2                += height2 * raw_image->stride[1];
966         raw_image->pixels[2]  = data + size2;
967         raw_image->stride[2]  = raw_image->stride[1];
968         size2                += height2 * raw_image->stride[2];
969         break;
970     case GST_VAAPI_IMAGE_ARGB:
971     case GST_VAAPI_IMAGE_RGBA:
972     case GST_VAAPI_IMAGE_ABGR:
973     case GST_VAAPI_IMAGE_BGRA:
974         raw_image->num_planes = 1;
975         raw_image->pixels[0]  = data;
976         raw_image->stride[0]  = width * 4;
977         size2                += height * raw_image->stride[0];
978         break;
979     default:
980         g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
981                 GST_FOURCC_ARGS(format));
982         return FALSE;
983     }
984
985     if (size2 != data_size) {
986         g_error("data_size mismatch %d / %u", size2, data_size);
987         if (size2 > data_size)
988             return FALSE;
989     }
990     return TRUE;
991 }
992
993 /* Copy N lines of an image */
994 static inline void
995 memcpy_pic(
996     guchar       *dst,
997     guint         dst_stride,
998     const guchar *src,
999     guint         src_stride,
1000     guint         len,
1001     guint         height
1002 )
1003 {
1004     guint i;
1005
1006     for (i = 0; i < height; i++)  {
1007         memcpy(dst, src, len);
1008         dst += dst_stride;
1009         src += dst_stride;
1010     }
1011 }
1012
1013 /* Copy NV12 images */
1014 static void
1015 copy_image_NV12(
1016     GstVaapiImageRaw        *dst_image,
1017     GstVaapiImageRaw        *src_image,
1018     const GstVaapiRectangle *rect
1019 )
1020 {
1021     guchar *dst, *src;
1022     guint dst_stride, src_stride;
1023
1024     /* Y plane */
1025     dst_stride = dst_image->stride[0];
1026     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1027     src_stride = src_image->stride[0];
1028     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1029     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
1030
1031     /* UV plane */
1032     dst_stride = dst_image->stride[1];
1033     dst = dst_image->pixels[1] + (rect->y / 2) * dst_stride + (rect->x & -2);
1034     src_stride = src_image->stride[1];
1035     src = src_image->pixels[1] + (rect->y / 2) * src_stride + (rect->x & -2);
1036     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height / 2);
1037 }
1038
1039 /* Copy YV12 images */
1040 static void
1041 copy_image_YV12(
1042     GstVaapiImageRaw        *dst_image,
1043     GstVaapiImageRaw        *src_image,
1044     const GstVaapiRectangle *rect
1045 )
1046 {
1047     guchar *dst, *src;
1048     guint dst_stride, src_stride;
1049     guint i, x, y, w, h;
1050
1051     /* Y plane */
1052     dst_stride = dst_image->stride[0];
1053     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1054     src_stride = src_image->stride[0];
1055     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1056     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
1057
1058     /* U/V planes */
1059     x = rect->x / 2;
1060     y = rect->y / 2;
1061     w = rect->width / 2;
1062     h = rect->height / 2;
1063     for (i = 1; i < dst_image->num_planes; i++) {
1064         dst_stride = dst_image->stride[i];
1065         dst = dst_image->pixels[i] + y * dst_stride + x;
1066         src_stride = src_image->stride[i];
1067         src = src_image->pixels[i] + y * src_stride + x;
1068         memcpy_pic(dst, dst_stride, src, src_stride, w, h);
1069     }
1070 }
1071
1072 /* Copy RGBA images */
1073 static void
1074 copy_image_RGBA(
1075     GstVaapiImageRaw        *dst_image,
1076     GstVaapiImageRaw        *src_image,
1077     const GstVaapiRectangle *rect
1078 )
1079 {
1080     guchar *dst, *src;
1081     guint dst_stride, src_stride;
1082
1083     dst_stride = dst_image->stride[0];
1084     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1085     src_stride = src_image->stride[0];
1086     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1087     memcpy_pic(dst, dst_stride, src, src_stride, 4 * rect->width, rect->height);
1088 }
1089
1090 static gboolean
1091 copy_image(
1092     GstVaapiImageRaw        *dst_image,
1093     GstVaapiImageRaw        *src_image,
1094     const GstVaapiRectangle *rect
1095 )
1096 {
1097     GstVaapiRectangle default_rect;
1098
1099     if (dst_image->format != src_image->format ||
1100         dst_image->width  != src_image->width  ||
1101         dst_image->height != src_image->height)
1102         return FALSE;
1103
1104     if (rect) {
1105         if (rect->x >= src_image->width ||
1106             rect->x + src_image->width > src_image->width ||
1107             rect->y >= src_image->height ||
1108             rect->y + src_image->height > src_image->height)
1109             return FALSE;
1110     }
1111     else {
1112         default_rect.x      = 0;
1113         default_rect.y      = 0;
1114         default_rect.width  = src_image->width;
1115         default_rect.height = src_image->height;
1116         rect                = &default_rect;
1117     }
1118
1119     switch (dst_image->format) {
1120     case GST_VAAPI_IMAGE_NV12:
1121         copy_image_NV12(dst_image, src_image, rect);
1122         break;
1123     case GST_VAAPI_IMAGE_YV12:
1124     case GST_VAAPI_IMAGE_I420:
1125         copy_image_YV12(dst_image, src_image, rect);
1126         break;
1127     case GST_VAAPI_IMAGE_ARGB:
1128     case GST_VAAPI_IMAGE_RGBA:
1129     case GST_VAAPI_IMAGE_ABGR:
1130     case GST_VAAPI_IMAGE_BGRA:
1131         copy_image_RGBA(dst_image, src_image, rect);
1132         break;
1133     default:
1134         GST_ERROR("unsupported image format for copy");
1135         return FALSE;
1136     }
1137     return TRUE;
1138 }
1139
1140 /**
1141  * gst_vaapi_image_get_buffer:
1142  * @image: a #GstVaapiImage
1143  * @buffer: a #GstBuffer
1144  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1145  *   whole image
1146  *
1147  * Transfers pixels data contained in the @image into the #GstBuffer.
1148  * Both image structures shall have the same format.
1149  *
1150  * Return value: %TRUE on success
1151  */
1152 gboolean
1153 gst_vaapi_image_get_buffer(
1154     GstVaapiImage     *image,
1155     GstBuffer         *buffer,
1156     GstVaapiRectangle *rect
1157 )
1158 {
1159     GstVaapiImagePrivate *priv;
1160     GstVaapiImageRaw dst_image, src_image;
1161     gboolean success;
1162
1163     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1164     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1165     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
1166
1167     priv = image->priv;
1168
1169     if (!init_image_from_buffer(&dst_image, buffer))
1170         return FALSE;
1171     if (dst_image.format != priv->format)
1172         return FALSE;
1173     if (dst_image.width != priv->width || dst_image.height != priv->height)
1174         return FALSE;
1175
1176     if (!_gst_vaapi_image_map(image, &src_image))
1177         return FALSE;
1178
1179     success = copy_image(&dst_image, &src_image, rect);
1180
1181     if (!_gst_vaapi_image_unmap(image))
1182         return FALSE;
1183
1184     return success;
1185 }
1186
1187 /**
1188  * gst_vaapi_image_get_raw:
1189  * @image: a #GstVaapiImage
1190  * @dst_image: a #GstVaapiImageRaw
1191  * @buffer: a #GstBuffer
1192  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1193  *   whole image
1194  *
1195  * Transfers pixels data contained in the @image into the #GstVaapiImageRaw.
1196  * Both image structures shall have the same format.
1197  *
1198  * Return value: %TRUE on success
1199  */
1200 gboolean
1201 gst_vaapi_image_get_raw(
1202     GstVaapiImage     *image,
1203     GstVaapiImageRaw  *dst_image,
1204     GstVaapiRectangle *rect
1205 )
1206 {
1207     GstVaapiImageRaw src_image;
1208     gboolean success;
1209
1210     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1211     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1212
1213     if (!_gst_vaapi_image_map(image, &src_image))
1214         return FALSE;
1215
1216     success = copy_image(dst_image, &src_image, rect);
1217
1218     if (!_gst_vaapi_image_unmap(image))
1219         return FALSE;
1220
1221     return success;
1222 }
1223
1224 /**
1225  * gst_vaapi_image_update_from_buffer:
1226  * @image: a #GstVaapiImage
1227  * @buffer: a #GstBuffer
1228  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1229  *   whole image
1230  *
1231  * Transfers pixels data contained in the #GstBuffer into the
1232  * @image. Both image structures shall have the same format.
1233  *
1234  * Return value: %TRUE on success
1235  */
1236 gboolean
1237 gst_vaapi_image_update_from_buffer(
1238     GstVaapiImage     *image,
1239     GstBuffer         *buffer,
1240     GstVaapiRectangle *rect
1241 )
1242 {
1243     GstVaapiImagePrivate *priv;
1244     GstVaapiImageRaw dst_image, src_image;
1245     gboolean success;
1246
1247     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1248     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1249     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
1250
1251     priv = image->priv;
1252
1253     if (!init_image_from_buffer(&src_image, buffer))
1254         return FALSE;
1255     if (src_image.format != priv->format)
1256         return FALSE;
1257     if (src_image.width != priv->width || src_image.height != priv->height)
1258         return FALSE;
1259
1260     if (!_gst_vaapi_image_map(image, &dst_image))
1261         return FALSE;
1262
1263     success = copy_image(&dst_image, &src_image, rect);
1264
1265     if (!_gst_vaapi_image_unmap(image))
1266         return FALSE;
1267
1268     return success;
1269 }
1270
1271 /**
1272  * gst_vaapi_image_update_from_raw:
1273  * @image: a #GstVaapiImage
1274  * @src_image: a #GstVaapiImageRaw
1275  * @buffer: a #GstBuffer
1276  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1277  *   whole image
1278  *
1279  * Transfers pixels data contained in the #GstVaapiImageRaw into the
1280  * @image. Both image structures shall have the same format.
1281  *
1282  * Return value: %TRUE on success
1283  */
1284 gboolean
1285 gst_vaapi_image_update_from_raw(
1286     GstVaapiImage     *image,
1287     GstVaapiImageRaw  *src_image,
1288     GstVaapiRectangle *rect
1289 )
1290 {
1291     GstVaapiImageRaw dst_image;
1292     gboolean success;
1293
1294     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1295     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1296
1297     if (!_gst_vaapi_image_map(image, &dst_image))
1298         return FALSE;
1299
1300     success = copy_image(&dst_image, src_image, rect);
1301
1302     if (!_gst_vaapi_image_unmap(image))
1303         return FALSE;
1304
1305     return success;
1306 }