image: fix update from NV12 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 /* Copy N lines of an image */
917 static inline void
918 memcpy_pic(
919     guchar       *dst,
920     guint         dst_stride,
921     const guchar *src,
922     guint         src_stride,
923     guint         len,
924     guint         height
925 )
926 {
927     guint i;
928
929     for (i = 0; i < height; i++)  {
930         memcpy(dst, src, len);
931         dst += dst_stride;
932         src += dst_stride;
933     }
934 }
935
936 /* Copy NV12 images */
937 static void
938 copy_image_NV12(
939     GstVaapiImageRaw        *dst_image,
940     GstVaapiImageRaw        *src_image,
941     const GstVaapiRectangle *rect
942 )
943 {
944     guchar *dst, *src;
945     guint dst_stride, src_stride;
946
947     /* Y plane */
948     dst_stride = dst_image->stride[0];
949     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
950     src_stride = src_image->stride[0];
951     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
952     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
953
954     /* UV plane */
955     dst_stride = dst_image->stride[1];
956     dst = dst_image->pixels[1] + (rect->y / 2) * dst_stride + (rect->x & -2);
957     src_stride = src_image->stride[1];
958     src = src_image->pixels[1] + (rect->y / 2) * src_stride + (rect->x & -2);
959     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height / 2);
960 }
961
962 /* Copy YV12 images */
963 static void
964 copy_image_YV12(
965     GstVaapiImageRaw        *dst_image,
966     GstVaapiImageRaw        *src_image,
967     const GstVaapiRectangle *rect
968 )
969 {
970     guchar *dst, *src;
971     guint dst_stride, src_stride;
972     guint i, x, y, w, h;
973
974     /* Y plane */
975     dst_stride = dst_image->stride[0];
976     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
977     src_stride = src_image->stride[0];
978     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
979     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
980
981     /* U/V planes */
982     x = rect->x / 2;
983     y = rect->y / 2;
984     w = rect->width / 2;
985     h = rect->height / 2;
986     for (i = 1; i < dst_image->num_planes; i++) {
987         dst_stride = dst_image->stride[i];
988         dst = dst_image->pixels[i] + y * dst_stride + x;
989         src_stride = src_image->stride[i];
990         src = src_image->pixels[i] + y * src_stride + x;
991         memcpy_pic(dst, dst_stride, src, src_stride, w, h);
992     }
993 }
994
995 /* Copy RGBA images */
996 static void
997 copy_image_RGBA(
998     GstVaapiImageRaw        *dst_image,
999     GstVaapiImageRaw        *src_image,
1000     const GstVaapiRectangle *rect
1001 )
1002 {
1003     guchar *dst, *src;
1004     guint dst_stride, src_stride;
1005
1006     dst_stride = dst_image->stride[0];
1007     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1008     src_stride = src_image->stride[0];
1009     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1010     memcpy_pic(dst, dst_stride, src, src_stride, 4 * rect->width, rect->height);
1011 }
1012
1013 static gboolean
1014 copy_image(
1015     GstVaapiImageRaw        *dst_image,
1016     GstVaapiImageRaw        *src_image,
1017     const GstVaapiRectangle *rect
1018 )
1019 {
1020     GstVaapiRectangle default_rect;
1021
1022     if (dst_image->format != src_image->format ||
1023         dst_image->width  != src_image->width  ||
1024         dst_image->height != src_image->height)
1025         return FALSE;
1026
1027     if (rect) {
1028         if (rect->x >= src_image->width ||
1029             rect->x + src_image->width > src_image->width ||
1030             rect->y >= src_image->height ||
1031             rect->y + src_image->height > src_image->height)
1032             return FALSE;
1033     }
1034     else {
1035         default_rect.x      = 0;
1036         default_rect.y      = 0;
1037         default_rect.width  = src_image->width;
1038         default_rect.height = src_image->height;
1039         rect                = &default_rect;
1040     }
1041
1042     switch (dst_image->format) {
1043     case GST_VAAPI_IMAGE_NV12:
1044         copy_image_NV12(dst_image, src_image, rect);
1045         break;
1046     case GST_VAAPI_IMAGE_YV12:
1047     case GST_VAAPI_IMAGE_I420:
1048         copy_image_YV12(dst_image, src_image, rect);
1049         break;
1050     case GST_VAAPI_IMAGE_ARGB:
1051     case GST_VAAPI_IMAGE_RGBA:
1052     case GST_VAAPI_IMAGE_ABGR:
1053     case GST_VAAPI_IMAGE_BGRA:
1054         copy_image_RGBA(dst_image, src_image, rect);
1055         break;
1056     default:
1057         GST_ERROR("unsupported image format for copy");
1058         return FALSE;
1059     }
1060     return TRUE;
1061 }
1062
1063 /**
1064  * gst_vaapi_image_update_from_buffer:
1065  * @image: a #GstVaapiImage
1066  * @buffer: a #GstBuffer
1067  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1068  *   whole image
1069  *
1070  * Transfers pixels data contained in the #GstBuffer into the
1071  * @image. Both image structures shall have the same format.
1072  *
1073  * Return value: %TRUE on success
1074  */
1075 gboolean
1076 gst_vaapi_image_update_from_buffer(
1077     GstVaapiImage     *image,
1078     GstBuffer         *buffer,
1079     GstVaapiRectangle *rect
1080 )
1081 {
1082     GstVaapiImagePrivate *priv;
1083     GstStructure *structure;
1084     GstCaps *caps;
1085     GstVaapiImageFormat format;
1086     GstVaapiImageRaw dst_image, src_image;
1087     guint width2, height2, size2;
1088     gint width, height;
1089     guchar *data;
1090     guint32 data_size;
1091     gboolean success;
1092
1093     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1094     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1095     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
1096
1097     priv      = image->priv;
1098     data      = GST_BUFFER_DATA(buffer);
1099     data_size = GST_BUFFER_SIZE(buffer);
1100     caps      = GST_BUFFER_CAPS(buffer);
1101
1102     if (!caps)
1103         return FALSE;
1104
1105     format = gst_vaapi_image_format_from_caps(caps);
1106     if (format != priv->format)
1107         return FALSE;
1108
1109     structure = gst_caps_get_structure(caps, 0);
1110     gst_structure_get_int(structure, "width",  &width);
1111     gst_structure_get_int(structure, "height", &height);
1112     if (width != priv->width || height != priv->height)
1113         return FALSE;
1114
1115     if (!_gst_vaapi_image_map(image, &dst_image))
1116         return FALSE;
1117
1118     /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
1119     src_image.format = priv->format;
1120     src_image.width  = width;
1121     src_image.height = height;
1122     width2  = (width + 1) / 2;
1123     height2 = (height + 1) / 2;
1124     size2   = 0;
1125     switch (format) {
1126     case GST_VAAPI_IMAGE_NV12:
1127         src_image.num_planes = 2;
1128         src_image.pixels[0]  = data;
1129         src_image.stride[0]  = GST_ROUND_UP_4(width);
1130         size2               += height * src_image.stride[0];
1131         src_image.pixels[1]  = data + size2;
1132         src_image.stride[1]  = src_image.stride[0];
1133         size2               += height2 * src_image.stride[1];
1134         break;
1135     case GST_VAAPI_IMAGE_YV12:
1136     case GST_VAAPI_IMAGE_I420:
1137         src_image.num_planes = 3;
1138         src_image.pixels[0]  = data;
1139         src_image.stride[0]  = GST_ROUND_UP_4(width);
1140         size2               += height * src_image.stride[0];
1141         src_image.pixels[1]  = data + size2;
1142         src_image.stride[1]  = GST_ROUND_UP_4(width2);
1143         size2               += height2 * src_image.stride[1];
1144         src_image.pixels[2]  = data + size2;
1145         src_image.stride[2]  = src_image.stride[1];
1146         size2               += height2 * src_image.stride[2];
1147         break;
1148     case GST_VAAPI_IMAGE_ARGB:
1149     case GST_VAAPI_IMAGE_RGBA:
1150     case GST_VAAPI_IMAGE_ABGR:
1151     case GST_VAAPI_IMAGE_BGRA:
1152         src_image.num_planes = 1;
1153         src_image.pixels[0]  = data;
1154         src_image.stride[0]  = width * 4;
1155         size2               += height * src_image.stride[0];
1156         break;
1157     default:
1158         g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
1159                 GST_FOURCC_ARGS(format));
1160         break;
1161     }
1162
1163     if (size2 != data_size) {
1164         g_error("data_size mismatch %d / %u", size2, data_size);
1165         if (size2 > data_size) {
1166             _gst_vaapi_image_unmap(image);
1167             return FALSE;
1168         }
1169     }
1170
1171     success = copy_image(&dst_image, &src_image, rect);
1172
1173     if (!_gst_vaapi_image_unmap(image))
1174         return FALSE;
1175
1176     return success;
1177 }
1178
1179 /**
1180  * gst_vaapi_image_update_from_raw:
1181  * @image: a #GstVaapiImage
1182  * @src_image: a #GstVaapiImageRaw
1183  * @buffer: a #GstBuffer
1184  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1185  *   whole image
1186  *
1187  * Transfers pixels data contained in the #GstVaapiImageRaw into the
1188  * @image. Both image structures shall have the same format.
1189  *
1190  * Return value: %TRUE on success
1191  */
1192 gboolean
1193 gst_vaapi_image_update_from_raw(
1194     GstVaapiImage     *image,
1195     GstVaapiImageRaw  *src_image,
1196     GstVaapiRectangle *rect
1197 )
1198 {
1199     GstVaapiImageRaw dst_image;
1200     gboolean success;
1201
1202     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1203     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1204
1205     if (!_gst_vaapi_image_map(image, &dst_image))
1206         return FALSE;
1207
1208     success = copy_image(&dst_image, src_image, rect);
1209
1210     if (!_gst_vaapi_image_unmap(image))
1211         return FALSE;
1212
1213     return success;
1214 }