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