vaapiconvert: change direct-rendering=0 to vaPutImage and support YUY2 colorspace...
[profile/ivi/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiimage.c
1 /*
2  *  gstvaapiimage.c - VA image abstraction
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011-2012 Intel Corporation
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:gstvaapiimage
25  * @short_description: VA image abstraction
26  */
27
28 #include "sysdeps.h"
29 #include <string.h>
30 #include "gst/gstutils.h"
31 #include "gstvaapicompat.h"
32 #include "gstvaapiutils.h"
33 #include "gstvaapiimage.h"
34 #include "gstvaapi_priv.h"
35
36 #define DEBUG 1
37 #include "gstvaapidebug.h"
38
39 G_DEFINE_TYPE(GstVaapiImage, gst_vaapi_image, GST_VAAPI_TYPE_OBJECT)
40
41 #define GST_VAAPI_IMAGE_GET_PRIVATE(obj)                \
42     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                 \
43                                  GST_VAAPI_TYPE_IMAGE,  \
44                                  GstVaapiImagePrivate))
45
46 struct _GstVaapiImagePrivate {
47     VAImage             internal_image;
48     VAImage             image;
49     guchar             *image_data;
50     GstVaapiImageFormat internal_format;
51     GstVaapiImageFormat format;
52     guint               width;
53     guint               height;
54     guint               create_image    : 1;
55     guint               is_constructed  : 1;
56     guint               is_linear       : 1;
57 };
58
59 enum {
60     PROP_0,
61
62     PROP_IMAGE,
63     PROP_FORMAT,
64     PROP_WIDTH,
65     PROP_HEIGHT
66 };
67
68 #define SWAP_UINT(a, b) do { \
69         guint v = a;         \
70         a = b;               \
71         b = v;               \
72     } while (0)
73
74 static gboolean
75 _gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image);
76
77 static gboolean
78 _gst_vaapi_image_unmap(GstVaapiImage *image);
79
80 static gboolean
81 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image);
82
83 /*
84  * VAImage wrapper
85  */
86
87 #define VAAPI_TYPE_IMAGE vaapi_image_get_type()
88
89 static gpointer
90 vaapi_image_copy(gpointer va_image)
91 {
92     return g_slice_dup(VAImage, va_image);
93 }
94
95 static void
96 vaapi_image_free(gpointer va_image)
97 {
98     if (G_LIKELY(va_image))
99         g_slice_free(VAImage, va_image);
100 }
101
102 static GType
103 vaapi_image_get_type(void)
104 {
105     static GType type = 0;
106
107     if (G_UNLIKELY(type == 0))
108         type = g_boxed_type_register_static(
109             "VAImage",
110             vaapi_image_copy,
111             vaapi_image_free
112         );
113     return type;
114 }
115
116 static gboolean
117 vaapi_image_is_linear(const VAImage *va_image)
118 {
119     guint i, width, height, width2, height2, data_size;
120
121     for (i = 1; i < va_image->num_planes; i++)
122         if (va_image->offsets[i] < va_image->offsets[i - 1])
123             return FALSE;
124
125     width   = va_image->width;
126     height  = va_image->height;
127     width2  = (width  + 1) / 2;
128     height2 = (height + 1) / 2;
129
130     switch (va_image->format.fourcc) {
131     case VA_FOURCC('N','V','1','2'):
132     case VA_FOURCC('Y','V','1','2'):
133     case VA_FOURCC('I','4','2','0'):
134         data_size = width * height + 2 * width2 * height2;
135         break;
136     case VA_FOURCC('A','Y','U','V'):
137     case VA_FOURCC('A','R','G','B'):
138     case VA_FOURCC('R','G','B','A'):
139     case VA_FOURCC('A','B','G','R'):
140     case VA_FOURCC('B','G','R','A'):
141         data_size = 4 * width * height;
142         break;
143     default:
144         g_error("FIXME: incomplete formats");
145         break;
146     }
147     return va_image->data_size == data_size;
148 }
149
150 static void
151 gst_vaapi_image_destroy(GstVaapiImage *image)
152 {
153     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
154     VAImageID image_id;
155     VAStatus status;
156
157     _gst_vaapi_image_unmap(image);
158
159     image_id = GST_VAAPI_OBJECT_ID(image);
160     GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
161
162     if (image_id != VA_INVALID_ID) {
163         GST_VAAPI_DISPLAY_LOCK(display);
164         status = vaDestroyImage(GST_VAAPI_DISPLAY_VADISPLAY(display), image_id);
165         GST_VAAPI_DISPLAY_UNLOCK(display);
166         if (!vaapi_check_status(status, "vaDestroyImage()"))
167             g_warning("failed to destroy image %" GST_VAAPI_ID_FORMAT,
168                       GST_VAAPI_ID_ARGS(image_id));
169         GST_VAAPI_OBJECT_ID(image) = VA_INVALID_ID;
170     }
171 }
172
173 static gboolean
174 _gst_vaapi_image_create(GstVaapiImage *image, GstVaapiImageFormat format)
175 {
176     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
177     GstVaapiImagePrivate * const priv = image->priv;
178     const VAImageFormat *va_format;
179     VAStatus status;
180
181     if (!gst_vaapi_display_has_image_format(display, format))
182         return FALSE;
183
184     va_format = gst_vaapi_image_format_get_va_format(format);
185     if (!va_format)
186         return FALSE;
187
188     GST_VAAPI_DISPLAY_LOCK(display);
189     status = vaCreateImage(
190         GST_VAAPI_DISPLAY_VADISPLAY(display),
191         (VAImageFormat *)va_format,
192         priv->width,
193         priv->height,
194         &priv->internal_image
195     );
196     GST_VAAPI_DISPLAY_UNLOCK(display);
197     if (status != VA_STATUS_SUCCESS ||
198         priv->internal_image.format.fourcc != va_format->fourcc)
199         return FALSE;
200
201     priv->internal_format = format;
202     return TRUE;
203 }
204
205 static gboolean
206 gst_vaapi_image_create(GstVaapiImage *image)
207 {
208     GstVaapiImagePrivate * const priv = image->priv;
209     GstVaapiImageFormat format = priv->format;
210     const VAImageFormat *va_format;
211     VAImageID image_id;
212
213     if (!priv->create_image)
214         return (priv->image.image_id != VA_INVALID_ID &&
215                 priv->image.buf      != VA_INVALID_ID);
216
217     if (!_gst_vaapi_image_create(image, format)) {
218         switch (format) {
219         case GST_VAAPI_IMAGE_I420:
220             format = GST_VAAPI_IMAGE_YV12;
221             break;
222         case GST_VAAPI_IMAGE_YV12:
223             format = GST_VAAPI_IMAGE_I420;
224             break;
225         default:
226             format = 0;
227             break;
228         }
229         if (!format || !_gst_vaapi_image_create(image, format))
230             return FALSE;
231     }
232     priv->image = priv->internal_image;
233     image_id    = priv->image.image_id;
234
235     if (priv->format != priv->internal_format) {
236         switch (priv->format) {
237         case GST_VAAPI_IMAGE_YV12:
238         case GST_VAAPI_IMAGE_I420:
239             va_format = gst_vaapi_image_format_get_va_format(priv->format);
240             if (!va_format)
241                 return FALSE;
242             priv->image.format = *va_format;
243             SWAP_UINT(priv->image.offsets[1], priv->image.offsets[2]);
244             SWAP_UINT(priv->image.pitches[1], priv->image.pitches[2]);
245             break;
246         default:
247             break;
248         }
249     }
250     priv->is_linear = vaapi_image_is_linear(&priv->image);
251
252     GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
253     GST_VAAPI_OBJECT_ID(image) = image_id;
254     return TRUE;
255 }
256
257 static void
258 gst_vaapi_image_finalize(GObject *object)
259 {
260     gst_vaapi_image_destroy(GST_VAAPI_IMAGE(object));
261
262     G_OBJECT_CLASS(gst_vaapi_image_parent_class)->finalize(object);
263 }
264
265 static void
266 gst_vaapi_image_set_property(
267     GObject      *object,
268     guint         prop_id,
269     const GValue *value,
270     GParamSpec   *pspec
271 )
272 {
273     GstVaapiImage        * const image = GST_VAAPI_IMAGE(object);
274     GstVaapiImagePrivate * const priv  = image->priv;
275
276     switch (prop_id) {
277     case PROP_IMAGE: {
278         const VAImage * const va_image = g_value_get_boxed(value);
279         if (va_image)
280             _gst_vaapi_image_set_image(image, va_image);
281         break;
282     }
283     case PROP_FORMAT:
284         if (priv->create_image)
285             priv->format = g_value_get_uint(value);
286         break;
287     case PROP_WIDTH:
288         if (priv->create_image)
289             priv->width = g_value_get_uint(value);
290         break;
291     case PROP_HEIGHT:
292         if (priv->create_image)
293             priv->height = g_value_get_uint(value);
294         break;
295     default:
296         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
297         break;
298     }
299 }
300
301 static void
302 gst_vaapi_image_get_property(
303     GObject    *object,
304     guint       prop_id,
305     GValue     *value,
306     GParamSpec *pspec
307 )
308 {
309     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
310
311     switch (prop_id) {
312     case PROP_IMAGE:
313         g_value_set_boxed(value, &image->priv->image);
314         break;
315     case PROP_FORMAT:
316         g_value_set_uint(value, gst_vaapi_image_get_format(image));
317         break;
318     case PROP_WIDTH:
319         g_value_set_uint(value, gst_vaapi_image_get_width(image));
320         break;
321     case PROP_HEIGHT:
322         g_value_set_uint(value, gst_vaapi_image_get_height(image));
323         break;
324     default:
325         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
326         break;
327     }
328 }
329
330 static void
331 gst_vaapi_image_constructed(GObject *object)
332 {
333     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
334     GObjectClass *parent_class;
335
336     image->priv->is_constructed = gst_vaapi_image_create(image);
337
338     parent_class = G_OBJECT_CLASS(gst_vaapi_image_parent_class);
339     if (parent_class->constructed)
340         parent_class->constructed(object);
341 }
342
343 static void
344 gst_vaapi_image_class_init(GstVaapiImageClass *klass)
345 {
346     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
347
348     g_type_class_add_private(klass, sizeof(GstVaapiImagePrivate));
349
350     object_class->finalize     = gst_vaapi_image_finalize;
351     object_class->set_property = gst_vaapi_image_set_property;
352     object_class->get_property = gst_vaapi_image_get_property;
353     object_class->constructed  = gst_vaapi_image_constructed;
354
355     g_object_class_install_property
356         (object_class,
357          PROP_IMAGE,
358          g_param_spec_boxed("image",
359                             "Image",
360                             "The underlying VA image",
361                             VAAPI_TYPE_IMAGE,
362                             G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
363
364     g_object_class_install_property
365         (object_class,
366          PROP_WIDTH,
367          g_param_spec_uint("width",
368                            "width",
369                            "The image width",
370                            0, G_MAXUINT32, 0,
371                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
372
373     g_object_class_install_property
374         (object_class,
375          PROP_HEIGHT,
376          g_param_spec_uint("height",
377                            "heighr",
378                            "The image height",
379                            0, G_MAXUINT32, 0,
380                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
381
382     /**
383      * GstVaapiImage:format:
384      *
385      * The #GstVaapiImageFormat of the image
386      */
387     g_object_class_install_property
388         (object_class,
389          PROP_FORMAT,
390          g_param_spec_uint("format",
391                            "Format",
392                            "The underlying image format",
393                            0, G_MAXUINT32, 0,
394                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
395 }
396
397 static void
398 gst_vaapi_image_init(GstVaapiImage *image)
399 {
400     GstVaapiImagePrivate *priv = GST_VAAPI_IMAGE_GET_PRIVATE(image);
401
402     image->priv                   = priv;
403     priv->image_data              = NULL;
404     priv->width                   = 0;
405     priv->height                  = 0;
406     priv->internal_format         = 0;
407     priv->format                  = 0;
408     priv->create_image            = TRUE;
409     priv->is_constructed          = FALSE;
410     priv->is_linear               = FALSE;
411
412     memset(&priv->internal_image, 0, sizeof(priv->internal_image));
413     priv->internal_image.image_id = VA_INVALID_ID;
414     priv->internal_image.buf      = VA_INVALID_ID;
415
416     memset(&priv->image, 0, sizeof(priv->image));
417     priv->image.image_id          = VA_INVALID_ID;
418     priv->image.buf               = VA_INVALID_ID;
419 }
420
421 /**
422  * gst_vaapi_image_new:
423  * @display: a #GstVaapiDisplay
424  * @format: a #GstVaapiImageFormat
425  * @width: the requested image width
426  * @height: the requested image height
427  *
428  * Creates a new #GstVaapiImage with the specified format and
429  * dimensions.
430  *
431  * Return value: the newly allocated #GstVaapiImage object
432  */
433 GstVaapiImage *
434 gst_vaapi_image_new(
435     GstVaapiDisplay    *display,
436     GstVaapiImageFormat format,
437     guint               width,
438     guint               height
439 )
440 {
441     GstVaapiImage *image;
442
443     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
444     g_return_val_if_fail(width > 0, NULL);
445     g_return_val_if_fail(height > 0, NULL);
446
447     GST_DEBUG("format %" GST_FOURCC_FORMAT ", size %ux%u",
448               GST_FOURCC_ARGS(format), width, height);
449
450     image = g_object_new(
451         GST_VAAPI_TYPE_IMAGE,
452         "display", display,
453         "id",      GST_VAAPI_ID(VA_INVALID_ID),
454         "format",  format,
455         "width",   width,
456         "height",  height,
457         NULL
458     );
459     if (!image)
460         return NULL;
461
462     if (!image->priv->is_constructed) {
463         g_object_unref(image);
464         return NULL;
465     }
466     return image;
467 }
468
469 /**
470  * gst_vaapi_image_new_with_image:
471  * @display: a #GstVaapiDisplay
472  * @va_image: a VA image
473  *
474  * Creates a new #GstVaapiImage from a foreign VA image. The image
475  * format and dimensions will be extracted from @va_image. This
476  * function is mainly used by gst_vaapi_surface_derive_image() to bind
477  * a VA image to a #GstVaapiImage object.
478  *
479  * Return value: the newly allocated #GstVaapiImage object
480  */
481 GstVaapiImage *
482 gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
483 {
484     GstVaapiImage *image;
485
486     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
487     g_return_val_if_fail(va_image, NULL);
488     g_return_val_if_fail(va_image->image_id != VA_INVALID_ID, NULL);
489     g_return_val_if_fail(va_image->buf != VA_INVALID_ID, NULL);
490
491     GST_DEBUG("VA image 0x%08x, format %" GST_FOURCC_FORMAT ", size %ux%u",
492               va_image->image_id,
493               GST_FOURCC_ARGS(va_image->format.fourcc),
494               va_image->width, va_image->height);
495
496     image = g_object_new(
497         GST_VAAPI_TYPE_IMAGE,
498         "display", display,
499         "id",      GST_VAAPI_ID(va_image->image_id),
500         "image",   va_image,
501         NULL
502     );
503     if (!image)
504         return NULL;
505
506     if (!image->priv->is_constructed) {
507         g_object_unref(image);
508         return NULL;
509     }
510     return image;
511 }
512
513 /**
514  * gst_vaapi_image_get_id:
515  * @image: a #GstVaapiImage
516  *
517  * Returns the underlying VAImageID of the @image.
518  *
519  * Return value: the underlying VA image id
520  */
521 GstVaapiID
522 gst_vaapi_image_get_id(GstVaapiImage *image)
523 {
524     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), VA_INVALID_ID);
525     g_return_val_if_fail(image->priv->is_constructed, VA_INVALID_ID);
526
527     return GST_VAAPI_OBJECT_ID(image);
528 }
529
530 /**
531  * gst_vaapi_image_get_image:
532  * @image: a #GstVaapiImage
533  * @va_image: a VA image
534  *
535  * Fills @va_image with the VA image used internally.
536  *
537  * Return value: %TRUE on success
538  */
539 gboolean
540 gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
541 {
542     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
543     g_return_val_if_fail(image->priv->is_constructed, FALSE);
544
545     if (va_image)
546         *va_image = image->priv->image;
547
548     return TRUE;
549 }
550
551 /*
552  * _gst_vaapi_image_set_image:
553  * @image: a #GstVaapiImage
554  * @va_image: a VA image
555  *
556  * Initializes #GstVaapiImage with a foreign VA image. This function
557  * will try to "linearize" the VA image. i.e. making sure that the VA
558  * image offsets into the data buffer are in increasing order with the
559  * number of planes available in the image.
560  *
561  * This is an internal function used by gst_vaapi_image_new_with_image().
562  *
563  * Return value: %TRUE on success
564  */
565 gboolean
566 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image)
567 {
568     GstVaapiImagePrivate * const priv = image->priv;
569     GstVaapiImageFormat format;
570     VAImage alt_va_image;
571     const VAImageFormat *alt_va_format;
572
573     if (!va_image)
574         return FALSE;
575
576     format = gst_vaapi_image_format(&va_image->format);
577     if (!format)
578         return FALSE;
579
580     priv->create_image    = FALSE;
581     priv->internal_image  = *va_image;
582     priv->internal_format = format;
583     priv->is_linear       = vaapi_image_is_linear(va_image);
584     priv->image           = *va_image;
585     priv->format          = format;
586     priv->width           = va_image->width;
587     priv->height          = va_image->height;
588
589     /* Try to linearize image */
590     if (!priv->is_linear) {
591         switch (format) {
592         case GST_VAAPI_IMAGE_I420:
593             format = GST_VAAPI_IMAGE_YV12;
594             break;
595         case GST_VAAPI_IMAGE_YV12:
596             format = GST_VAAPI_IMAGE_I420;
597             break;
598         default:
599             format = 0;
600             break;
601         }
602         if (format &&
603             (alt_va_format = gst_vaapi_image_format_get_va_format(format))) {
604             alt_va_image = *va_image;
605             alt_va_image.format = *alt_va_format;
606             SWAP_UINT(alt_va_image.offsets[1], alt_va_image.offsets[2]);
607             SWAP_UINT(alt_va_image.pitches[1], alt_va_image.pitches[2]);
608             if (vaapi_image_is_linear(&alt_va_image)) {
609                 priv->image     = alt_va_image;
610                 priv->format    = format;
611                 priv->is_linear = TRUE;
612                 GST_DEBUG("linearized image to %" GST_FOURCC_FORMAT " format",
613                           GST_FOURCC_ARGS(format));
614             }
615         }
616     }
617     return TRUE;
618 }
619
620 /**
621  * gst_vaapi_image_get_format:
622  * @image: a #GstVaapiImage
623  *
624  * Returns the #GstVaapiImageFormat the @image was created with.
625  *
626  * Return value: the #GstVaapiImageFormat
627  */
628 GstVaapiImageFormat
629 gst_vaapi_image_get_format(GstVaapiImage *image)
630 {
631     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
632     g_return_val_if_fail(image->priv->is_constructed, 0);
633
634     return image->priv->format;
635 }
636
637 /**
638  * gst_vaapi_image_get_width:
639  * @image: a #GstVaapiImage
640  *
641  * Returns the @image width.
642  *
643  * Return value: the image width, in pixels
644  */
645 guint
646 gst_vaapi_image_get_width(GstVaapiImage *image)
647 {
648     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
649     g_return_val_if_fail(image->priv->is_constructed, 0);
650
651     return image->priv->width;
652 }
653
654 /**
655  * gst_vaapi_image_get_height:
656  * @image: a #GstVaapiImage
657  *
658  * Returns the @image height.
659  *
660  * Return value: the image height, in pixels.
661  */
662 guint
663 gst_vaapi_image_get_height(GstVaapiImage *image)
664 {
665     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
666     g_return_val_if_fail(image->priv->is_constructed, 0);
667
668     return image->priv->height;
669 }
670
671 /**
672  * gst_vaapi_image_get_size:
673  * @image: a #GstVaapiImage
674  * @pwidth: return location for the width, or %NULL
675  * @pheight: return location for the height, or %NULL
676  *
677  * Retrieves the dimensions of a #GstVaapiImage.
678  */
679 void
680 gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
681 {
682     g_return_if_fail(GST_VAAPI_IS_IMAGE(image));
683     g_return_if_fail(image->priv->is_constructed);
684
685     if (pwidth)
686         *pwidth = image->priv->width;
687
688     if (pheight)
689         *pheight = image->priv->height;
690 }
691
692 /**
693  * gst_vaapi_image_is_linear:
694  * @image: a #GstVaapiImage
695  *
696  * Checks whether the @image has data planes allocated from a single
697  * buffer and offsets into that buffer are in increasing order with
698  * the number of planes.
699  *
700  * Return value: %TRUE if image data planes are allocated from a single buffer
701  */
702 gboolean
703 gst_vaapi_image_is_linear(GstVaapiImage *image)
704 {
705     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
706     g_return_val_if_fail(image->priv->is_constructed, FALSE);
707
708     return image->priv->is_linear;
709 }
710
711 /**
712  * gst_vaapi_image_is_mapped:
713  * @image: a #GstVaapiImage
714  *
715  * Checks whether the @image is currently mapped or not.
716  *
717  * Return value: %TRUE if the @image is mapped
718  */
719 static inline gboolean
720 _gst_vaapi_image_is_mapped(GstVaapiImage *image)
721 {
722     return image->priv->image_data != NULL;
723 }
724
725 gboolean
726 gst_vaapi_image_is_mapped(GstVaapiImage *image)
727 {
728     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
729     g_return_val_if_fail(image->priv->is_constructed, FALSE);
730
731     return _gst_vaapi_image_is_mapped(image);
732 }
733
734 /**
735  * gst_vaapi_image_map:
736  * @image: a #GstVaapiImage
737  *
738  * Maps the image data buffer. The actual pixels are returned by the
739  * gst_vaapi_image_get_plane() function.
740  *
741  * Return value: %TRUE on success
742  */
743 gboolean
744 gst_vaapi_image_map(GstVaapiImage *image)
745 {
746     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
747     g_return_val_if_fail(image->priv->is_constructed, FALSE);
748
749     return _gst_vaapi_image_map(image, NULL);
750 }
751
752 gboolean
753 _gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image)
754 {
755     GstVaapiImagePrivate * const priv = image->priv;
756     GstVaapiDisplay *display;
757     VAStatus status;
758     guint i;
759
760     if (_gst_vaapi_image_is_mapped(image))
761         goto map_success;
762
763     display = GST_VAAPI_OBJECT_DISPLAY(image);
764     if (!display)
765         return FALSE;
766
767     GST_VAAPI_DISPLAY_LOCK(display);
768     status = vaMapBuffer(
769         GST_VAAPI_DISPLAY_VADISPLAY(display),
770         priv->image.buf,
771         (void **)&priv->image_data
772     );
773     GST_VAAPI_DISPLAY_UNLOCK(display);
774     if (!vaapi_check_status(status, "vaMapBuffer()"))
775         return FALSE;
776
777 map_success:
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] = (guchar *)priv->image_data +
786                 va_image->offsets[i];
787             raw_image->stride[i] = va_image->pitches[i];
788         }
789     }
790     return TRUE;
791 }
792
793 /**
794  * gst_vaapi_image_unmap:
795  * @image: a #GstVaapiImage
796  *
797  * Unmaps the image data buffer. Pointers to pixels returned by
798  * gst_vaapi_image_get_plane() are then no longer valid.
799  *
800  * Return value: %TRUE on success
801  */
802 gboolean
803 gst_vaapi_image_unmap(GstVaapiImage *image)
804 {
805     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
806     g_return_val_if_fail(image->priv->is_constructed, FALSE);
807
808     return _gst_vaapi_image_unmap(image);
809 }
810
811 gboolean
812 _gst_vaapi_image_unmap(GstVaapiImage *image)
813 {
814     GstVaapiDisplay *display;
815     VAStatus status;
816
817     if (!_gst_vaapi_image_is_mapped(image))
818         return TRUE;
819
820     display = GST_VAAPI_OBJECT_DISPLAY(image);
821     if (!display)
822         return FALSE;
823
824     GST_VAAPI_DISPLAY_LOCK(display);
825     status = vaUnmapBuffer(
826         GST_VAAPI_DISPLAY_VADISPLAY(display),
827         image->priv->image.buf
828     );
829     GST_VAAPI_DISPLAY_UNLOCK(display);
830     if (!vaapi_check_status(status, "vaUnmapBuffer()"))
831         return FALSE;
832
833     image->priv->image_data = NULL;
834     return TRUE;
835 }
836
837 /**
838  * gst_vaapi_image_get_plane_count:
839  * @image: a #GstVaapiImage
840  *
841  * Retrieves the number of planes available in the @image. The @image
842  * must be mapped for this function to work properly.
843  *
844  * Return value: the number of planes available in the @image
845  */
846 guint
847 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
848 {
849     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
850     g_return_val_if_fail(image->priv->is_constructed, FALSE);
851     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
852
853     return image->priv->image.num_planes;
854 }
855
856 /**
857  * gst_vaapi_image_get_plane:
858  * @image: a #GstVaapiImage
859  * @plane: the requested plane number
860  *
861  * Retrieves the pixels data to the specified @plane. The @image must
862  * be mapped for this function to work properly.
863  *
864  * Return value: the pixels data of the specified @plane
865  */
866 guchar *
867 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
868 {
869     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
870     g_return_val_if_fail(image->priv->is_constructed, FALSE);
871     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
872     g_return_val_if_fail(plane < image->priv->image.num_planes, NULL);
873
874     return image->priv->image_data + image->priv->image.offsets[plane];
875 }
876
877 /**
878  * gst_vaapi_image_get_pitch:
879  * @image: a #GstVaapiImage
880  * @plane: the requested plane number
881  *
882  * Retrieves the line size (stride) of the specified @plane. The
883  * @image must be mapped for this function to work properly.
884  *
885  * Return value: the line size (stride) of the specified plane
886  */
887 guint
888 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
889 {
890     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
891     g_return_val_if_fail(image->priv->is_constructed, FALSE);
892     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
893     g_return_val_if_fail(plane < image->priv->image.num_planes, 0);
894
895     return image->priv->image.pitches[plane];
896 }
897
898 /**
899  * gst_vaapi_image_get_data_size:
900  * @image: a #GstVaapiImage
901  *
902  * Retrieves the underlying image data size. This function could be
903  * used to determine whether the image has a compatible layout with
904  * another image structure.
905  *
906  * Return value: the whole image data size of the @image
907  */
908 guint
909 gst_vaapi_image_get_data_size(GstVaapiImage *image)
910 {
911     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
912     g_return_val_if_fail(image->priv->is_constructed, FALSE);
913
914     return image->priv->image.data_size;
915 }
916
917 static gboolean
918 init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
919 {
920     GstStructure *structure;
921     GstCaps *caps;
922     GstVaapiImageFormat format;
923     guint width2, height2, size2;
924     gint width, height;
925     guchar *data;
926     guint32 data_size;
927
928     data      = GST_BUFFER_DATA(buffer);
929     data_size = GST_BUFFER_SIZE(buffer);
930     caps      = GST_BUFFER_CAPS(buffer);
931
932     if (!caps)
933         return FALSE;
934
935     format = gst_vaapi_image_format_from_caps(caps);
936
937     structure = gst_caps_get_structure(caps, 0);
938     gst_structure_get_int(structure, "width",  &width);
939     gst_structure_get_int(structure, "height", &height);
940
941     /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
942     raw_image->format = format;
943     raw_image->width  = width;
944     raw_image->height = height;
945     width2  = (width + 1) / 2;
946     height2 = (height + 1) / 2;
947     size2   = 0;
948     switch (format) {
949     case GST_VAAPI_IMAGE_NV12:
950         raw_image->num_planes = 2;
951         raw_image->pixels[0]  = data;
952         raw_image->stride[0]  = GST_ROUND_UP_4(width);
953         size2                += height * raw_image->stride[0];
954         raw_image->pixels[1]  = data + size2;
955         raw_image->stride[1]  = raw_image->stride[0];
956         size2                += height2 * raw_image->stride[1];
957         break;
958     case GST_VAAPI_IMAGE_YV12:
959     case GST_VAAPI_IMAGE_I420:
960         raw_image->num_planes = 3;
961         raw_image->pixels[0]  = data;
962         raw_image->stride[0]  = GST_ROUND_UP_4(width);
963         size2                += height * raw_image->stride[0];
964         raw_image->pixels[1]  = data + size2;
965         raw_image->stride[1]  = GST_ROUND_UP_4(width2);
966         size2                += height2 * raw_image->stride[1];
967         raw_image->pixels[2]  = data + size2;
968         raw_image->stride[2]  = raw_image->stride[1];
969         size2                += height2 * raw_image->stride[2];
970         break;
971     case GST_VAAPI_IMAGE_ARGB:
972     case GST_VAAPI_IMAGE_RGBA:
973     case GST_VAAPI_IMAGE_ABGR:
974     case GST_VAAPI_IMAGE_BGRA:
975         raw_image->num_planes = 1;
976         raw_image->pixels[0]  = data;
977         raw_image->stride[0]  = width * 4;
978         size2                += height * raw_image->stride[0];
979         break;
980     default:
981         g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
982                 GST_FOURCC_ARGS(format));
983         return FALSE;
984     }
985
986     if (size2 != data_size) {
987         g_error("data_size mismatch %d / %u", size2, data_size);
988         if (size2 > data_size)
989             return FALSE;
990     }
991     return TRUE;
992 }
993
994 /* Copy N lines of an image */
995 static inline void
996 memcpy_pic(
997     guchar       *dst,
998     guint         dst_stride,
999     const guchar *src,
1000     guint         src_stride,
1001     guint         len,
1002     guint         height
1003 )
1004 {
1005     guint i;
1006
1007     for (i = 0; i < height; i++)  {
1008         memcpy(dst, src, len);
1009         dst += dst_stride;
1010         src += src_stride;
1011     }
1012 }
1013
1014 /* Copy NV12 images */
1015 static void
1016 copy_image_NV12(
1017     GstVaapiImageRaw        *dst_image,
1018     GstVaapiImageRaw        *src_image,
1019     const GstVaapiRectangle *rect
1020 )
1021 {
1022     guchar *dst, *src;
1023     guint dst_stride, src_stride;
1024
1025     /* Y plane */
1026     dst_stride = dst_image->stride[0];
1027     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1028     src_stride = src_image->stride[0];
1029     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1030     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
1031
1032     /* UV plane */
1033     dst_stride = dst_image->stride[1];
1034     dst = dst_image->pixels[1] + (rect->y / 2) * dst_stride + (rect->x & -2);
1035     src_stride = src_image->stride[1];
1036     src = src_image->pixels[1] + (rect->y / 2) * src_stride + (rect->x & -2);
1037     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height / 2);
1038 }
1039
1040 /* Copy YV12 images */
1041 static void
1042 copy_image_YV12(
1043     GstVaapiImageRaw        *dst_image,
1044     GstVaapiImageRaw        *src_image,
1045     const GstVaapiRectangle *rect
1046 )
1047 {
1048     guchar *dst, *src;
1049     guint dst_stride, src_stride;
1050     guint i, x, y, w, h;
1051
1052     /* Y plane */
1053     dst_stride = dst_image->stride[0];
1054     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1055     src_stride = src_image->stride[0];
1056     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1057     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
1058
1059     /* U/V planes */
1060     x = rect->x / 2;
1061     y = rect->y / 2;
1062     w = rect->width / 2;
1063     h = rect->height / 2;
1064     for (i = 1; i < dst_image->num_planes; i++) {
1065         dst_stride = dst_image->stride[i];
1066         dst = dst_image->pixels[i] + y * dst_stride + x;
1067         src_stride = src_image->stride[i];
1068         src = src_image->pixels[i] + y * src_stride + x;
1069         memcpy_pic(dst, dst_stride, src, src_stride, w, h);
1070     }
1071 }
1072
1073 /* Copy RGBA images */
1074 static void
1075 copy_image_RGBA(
1076     GstVaapiImageRaw        *dst_image,
1077     GstVaapiImageRaw        *src_image,
1078     const GstVaapiRectangle *rect
1079 )
1080 {
1081     guchar *dst, *src;
1082     guint dst_stride, src_stride;
1083
1084     dst_stride = dst_image->stride[0];
1085     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1086     src_stride = src_image->stride[0];
1087     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1088     memcpy_pic(dst, dst_stride, src, src_stride, 4 * rect->width, rect->height);
1089 }
1090
1091 static gboolean
1092 copy_image(
1093     GstVaapiImageRaw        *dst_image,
1094     GstVaapiImageRaw        *src_image,
1095     const GstVaapiRectangle *rect
1096 )
1097 {
1098     GstVaapiRectangle default_rect;
1099
1100     if (dst_image->format != src_image->format ||
1101         dst_image->width  != src_image->width  ||
1102         dst_image->height != src_image->height)
1103         return FALSE;
1104
1105     if (rect) {
1106         if (rect->x >= src_image->width ||
1107             rect->x + src_image->width > src_image->width ||
1108             rect->y >= src_image->height ||
1109             rect->y + src_image->height > src_image->height)
1110             return FALSE;
1111     }
1112     else {
1113         default_rect.x      = 0;
1114         default_rect.y      = 0;
1115         default_rect.width  = src_image->width;
1116         default_rect.height = src_image->height;
1117         rect                = &default_rect;
1118     }
1119
1120     switch (dst_image->format) {
1121     case GST_VAAPI_IMAGE_NV12:
1122         copy_image_NV12(dst_image, src_image, rect);
1123         break;
1124     case GST_VAAPI_IMAGE_YV12:
1125     case GST_VAAPI_IMAGE_I420:
1126         copy_image_YV12(dst_image, src_image, rect);
1127         break;
1128     case GST_VAAPI_IMAGE_ARGB:
1129     case GST_VAAPI_IMAGE_RGBA:
1130     case GST_VAAPI_IMAGE_ABGR:
1131     case GST_VAAPI_IMAGE_BGRA:
1132         copy_image_RGBA(dst_image, src_image, rect);
1133         break;
1134     default:
1135         GST_ERROR("unsupported image format for copy");
1136         return FALSE;
1137     }
1138     return TRUE;
1139 }
1140
1141 /**
1142  * gst_vaapi_image_get_buffer:
1143  * @image: a #GstVaapiImage
1144  * @buffer: a #GstBuffer
1145  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1146  *   whole image
1147  *
1148  * Transfers pixels data contained in the @image into the #GstBuffer.
1149  * Both image structures shall have the same format.
1150  *
1151  * Return value: %TRUE on success
1152  */
1153 gboolean
1154 gst_vaapi_image_get_buffer(
1155     GstVaapiImage     *image,
1156     GstBuffer         *buffer,
1157     GstVaapiRectangle *rect
1158 )
1159 {
1160     GstVaapiImagePrivate *priv;
1161     GstVaapiImageRaw dst_image, src_image;
1162     gboolean success;
1163
1164     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1165     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1166     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
1167
1168     priv = image->priv;
1169
1170     if (!init_image_from_buffer(&dst_image, buffer))
1171         return FALSE;
1172     if (dst_image.format != priv->format)
1173         return FALSE;
1174     if (dst_image.width != priv->width || dst_image.height != priv->height)
1175         return FALSE;
1176
1177     if (!_gst_vaapi_image_map(image, &src_image))
1178         return FALSE;
1179
1180     success = copy_image(&dst_image, &src_image, rect);
1181
1182     if (!_gst_vaapi_image_unmap(image))
1183         return FALSE;
1184
1185     return success;
1186 }
1187
1188 /**
1189  * gst_vaapi_image_get_raw:
1190  * @image: a #GstVaapiImage
1191  * @dst_image: a #GstVaapiImageRaw
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 }
1307
1308 static void
1309 _yuv411_to_nv12(
1310     const guint8 *y_src,
1311     guint32 y_src_stride,
1312     const guint8 *u_src,
1313     guint32 u_src_stride,
1314     const guint8 *v_src,
1315     guint32 v_src_stride,
1316     guint8 *y_dest,
1317     guint32 y_dest_stride,
1318     guint8 *uv_dest,
1319     guint32 uv_dest_stride,
1320     guint32 width,
1321     guint32 height
1322 )
1323 {
1324     guint32 row, column;
1325     guint32 uv_height;
1326
1327     memcpy_pic(y_dest, y_dest_stride, y_src, y_src_stride, width, height);
1328     uv_height = GST_ROUND_UP_2(height)/2;
1329     for (row = 0; row < uv_height; row++) {
1330         for (column = 0; column < width/2; column++) {
1331             uv_dest[column*2] = u_src[column];
1332             uv_dest[column*2+1] = v_src[column];
1333         }
1334         u_src += u_src_stride;
1335         v_src += v_src_stride;
1336         uv_dest += uv_dest_stride;
1337     }
1338 }
1339
1340 static void
1341 _nv12_to_yuv411(
1342     const guint8 *y_src,
1343     guint32 y_src_stride,
1344     const guint8 *uv_src,
1345     guint32 uv_src_stride,
1346     guint8 *y_dest,
1347     guint32 y_dest_stride,
1348     guint8 *u_dest,
1349     guint32 u_dest_stride,
1350     guint8 *v_dest,
1351     guint32 v_dest_stride,
1352     guint32 width,
1353     guint32 height
1354 )
1355 {
1356     guint32 row, column;
1357     guint32 uv_height;
1358
1359     memcpy_pic(y_dest, y_dest_stride, y_src, y_src_stride, width, height);
1360     uv_height = GST_ROUND_UP_2(height)/2;
1361     for (row = 0; row < uv_height; row++) {
1362         for (column = 0; column < width/2; column++) {
1363             u_dest[column] = uv_src[column*2];
1364             v_dest[column] = uv_src[column*2+1];
1365         }
1366         uv_src += uv_src_stride;
1367         u_dest += u_dest_stride;
1368         v_dest += v_dest_stride;
1369     }
1370 }
1371
1372 static void
1373 _yuy2_to_yuv411(
1374     const guint8 *yuv_src,
1375     guint32 yuv_src_stride,
1376     guint8 *y_dest,
1377     guint32 y_dest_stride,
1378     guint8 *u_dest,
1379     guint32 u_dest_stride,
1380     guint8 *v_dest,
1381     guint32 v_dest_stride,
1382     gboolean uv_interlaced,
1383     guint32 width,
1384     guint32 height
1385 )
1386 {
1387     guint32 row, column;
1388     const guint8 *yuv_next_src;
1389     guint8 *y_next_dest;
1390     guint32 interval;
1391
1392     interval = uv_interlaced ? 2 : 1;
1393     for (row = 0; row < height/2; row++) {
1394         yuv_next_src = yuv_src + yuv_src_stride;
1395         y_next_dest = y_dest + y_dest_stride;
1396         for (column = 0; column < width/2; column++) {
1397             y_dest[column*2] = yuv_src[column*4];
1398             y_dest[column*2+1] = yuv_src[column*4+2];
1399             y_next_dest[column*2] = yuv_next_src[column*4];
1400             y_next_dest[column*2+1] = yuv_next_src[column*4+2];
1401             u_dest[column*interval] = ((yuv_src[column*4+1] + yuv_next_src[column*4+1])>>1);
1402             v_dest[column*interval] = ((yuv_src[column*4+3] + yuv_next_src[column*4+3])>>1);
1403         }
1404         yuv_src = yuv_next_src + yuv_src_stride;
1405         y_dest = y_next_dest + y_dest_stride;
1406         u_dest += u_dest_stride;
1407         v_dest += v_dest_stride;
1408     }
1409
1410     /*  odd line */
1411     if (height%2) {
1412         for (column = 0; column < width/2; column++) {
1413           y_dest[column*2] = yuv_src[column*4];
1414           y_dest[column*2+1] = yuv_src[column*4+2];
1415           u_dest[column*interval] = yuv_src[column*4+1];
1416           v_dest[column*interval] = yuv_src[column*4+3];
1417         }
1418     }
1419
1420 }
1421
1422 static gboolean
1423 _image_convert_to_nv12(
1424     const guint8 *src,
1425     guint32 width,
1426     guint32 height,
1427     guint32 src_format,
1428     guint8 *y_dest,
1429     guint32 y_dest_stride,
1430     guint8 *uv_dest,
1431     guint32 uv_dest_stride
1432 )
1433 {
1434     const guint8 *y_src, *u_src, *v_src, *uv_src;
1435     guint32 ystride, ustride, vstride, uv_stride;
1436
1437     switch (src_format) {
1438         case GST_MAKE_FOURCC('N','V','1','2'): {
1439             y_src = src;
1440             ystride = GST_ROUND_UP_4(width);
1441             uv_src = src + ystride*GST_ROUND_UP_2(height);
1442             uv_stride = ystride;
1443             memcpy_pic(y_dest, y_dest_stride, y_src, ystride, width, height);
1444             memcpy_pic(uv_dest, uv_dest_stride, uv_src, uv_stride,
1445                        width, GST_ROUND_UP_2(height)/2);
1446             break;
1447         }
1448
1449         case GST_MAKE_FOURCC('I','4','2','0'): {
1450             y_src = src;
1451             ystride = GST_ROUND_UP_4(width);
1452             u_src = src + ystride*GST_ROUND_UP_2(height);
1453             ustride = GST_ROUND_UP_8(ystride)/2;
1454             v_src = u_src + ustride*GST_ROUND_UP_2(height)/2;
1455             vstride = GST_ROUND_UP_8(ystride)/2;
1456
1457             _yuv411_to_nv12(y_src, ystride, u_src, ustride, v_src, vstride,
1458                             y_dest, y_dest_stride,
1459                             uv_dest, uv_dest_stride,
1460                             width, height);
1461             break;
1462         }
1463
1464         case GST_MAKE_FOURCC('Y','V','1','2'):{
1465             y_src = src;
1466             ystride = GST_ROUND_UP_4(width);
1467             v_src = src + ystride*GST_ROUND_UP_2(height);
1468             vstride = GST_ROUND_UP_8(ystride)/2;
1469             u_src = v_src + vstride*GST_ROUND_UP_2(height)/2;
1470             ustride = GST_ROUND_UP_8(ystride)/2;
1471
1472             _yuv411_to_nv12(y_src, ystride, u_src, ustride, v_src, vstride,
1473                             y_dest, y_dest_stride, uv_dest, uv_dest_stride,
1474                             width, height);
1475             break;
1476         }
1477
1478         case GST_MAKE_FOURCC('Y', 'U', 'Y', '2'): {
1479             y_src = src;
1480             ystride = GST_ROUND_UP_2(width)*2;
1481             _yuy2_to_yuv411(y_src, ystride,
1482                             y_dest, y_dest_stride,
1483                             uv_dest, uv_dest_stride,
1484                             uv_dest+1, uv_dest_stride,
1485                             TRUE,
1486                             width, height);
1487             break;
1488         }
1489
1490         default:
1491           return FALSE;
1492     }
1493     return TRUE;
1494 }
1495
1496 static gboolean
1497 _image_convert_to_yuv411(
1498     const guint8 *src,
1499     guint32 width,
1500     guint32 height,
1501     guint32 src_format,
1502     guint8 *y_dest,
1503     guint32 y_dest_stride,
1504     guint8 *u_dest,
1505     guint32 u_dest_stride,
1506     guint8 *v_dest,
1507     guint32 v_dest_stride
1508 )
1509 {
1510     const guint8 *y_src, *u_src, *v_src, *uv_src;
1511     guint32 ystride, ustride, vstride, uv_stride;
1512
1513     switch (src_format) {
1514         case GST_MAKE_FOURCC('N','V','1','2'): {
1515             y_src = src;
1516             ystride = GST_ROUND_UP_4(width);
1517             uv_src = src + ystride*GST_ROUND_UP_2(height);
1518             uv_stride = ystride;
1519             _nv12_to_yuv411(y_src, ystride, uv_src, uv_stride,
1520                             y_dest, y_dest_stride,
1521                             u_dest, u_dest_stride,
1522                             v_dest, v_dest_stride,
1523                             width, height);
1524             break;
1525         }
1526
1527         case GST_MAKE_FOURCC('I','4','2','0'):
1528         case GST_MAKE_FOURCC('Y','V','1','2'):{
1529             y_src = src;
1530             ystride = GST_ROUND_UP_4(width);
1531             if (GST_MAKE_FOURCC('I','4','2','0') == src_format) {
1532                 u_src = src + ystride*GST_ROUND_UP_2(height);
1533                 ustride = GST_ROUND_UP_8(ystride)/2;
1534                 v_src = u_src + ustride*GST_ROUND_UP_2(height)/2;
1535                 vstride = GST_ROUND_UP_8(ystride)/2;
1536             } else {
1537                 v_src = src + ystride*GST_ROUND_UP_2(height);
1538                 vstride = GST_ROUND_UP_8(ystride)/2;
1539                 u_src = v_src + vstride*GST_ROUND_UP_2(height)/2;
1540                 ustride = GST_ROUND_UP_8(ystride)/2;
1541             }
1542
1543             memcpy_pic(y_dest, y_dest_stride, y_src, ystride, width, height);
1544             memcpy_pic(u_dest, u_dest_stride, u_src, ustride, width/2, GST_ROUND_UP_2(height)/2);
1545             memcpy_pic(v_dest, v_dest_stride, v_src, vstride, width/2, GST_ROUND_UP_2(height)/2);
1546             break;
1547         }
1548
1549         case GST_MAKE_FOURCC('Y', 'U', 'Y', '2'): {
1550             y_src = src;
1551             ystride = GST_ROUND_UP_2(width)*2;
1552             _yuy2_to_yuv411(y_src, ystride,
1553                             y_dest, y_dest_stride,
1554                             u_dest, u_dest_stride,
1555                             v_dest, v_dest_stride,
1556                             FALSE,
1557                             width, height);
1558             break;
1559         }
1560
1561         default:
1562           return FALSE;
1563     }
1564     return TRUE;
1565 }
1566
1567
1568 gboolean
1569 gst_vaapi_convert_buffer_to_image(
1570     GstVaapiImage *image,
1571     GstBuffer *inbuf
1572     )
1573 {
1574     GstVaapiImagePrivate *priv;
1575     guint width, height;
1576     GstVaapiImageFormat image_format;
1577     gboolean success = TRUE;
1578     GstCaps *buffer_caps;
1579     GstStructure *structure;
1580     guint32 in_format = 0;
1581
1582     priv = image->priv;
1583     gst_vaapi_image_get_size(image, &width, &height);
1584     image_format = gst_vaapi_image_get_format(image);
1585
1586     /* get buffer format */
1587     buffer_caps = GST_BUFFER_CAPS(inbuf);
1588     if (!buffer_caps)
1589         return FALSE;
1590     structure = gst_caps_get_structure(buffer_caps, 0);
1591     if (!structure)
1592         return FALSE;
1593     if (!gst_structure_get_fourcc(structure, "format", &in_format))
1594         return FALSE;
1595
1596     /* currently only support YUV convert */
1597     if ( (in_format != GST_MAKE_FOURCC('N','V','1','2')
1598           && in_format != GST_MAKE_FOURCC('Y','V','1','2')
1599           && in_format != GST_MAKE_FOURCC('I','4','2','0')
1600           && in_format != GST_MAKE_FOURCC('Y', 'U', 'Y', '2'))
1601         || (image_format != GST_VAAPI_IMAGE_NV12
1602           && image_format != GST_VAAPI_IMAGE_YV12
1603           && image_format != GST_VAAPI_IMAGE_I420)
1604         )
1605     {
1606         return FALSE;
1607     }
1608
1609
1610     gst_vaapi_image_map(image);
1611     switch (image_format) {
1612       case GST_VAAPI_IMAGE_NV12: {
1613           g_assert(priv->image.num_planes == 2);
1614           success = _image_convert_to_nv12(GST_BUFFER_DATA(inbuf),
1615                                            width, height, in_format,
1616                                            priv->image_data+priv->image.offsets[0],
1617                                            priv->image.pitches[0],
1618                                            priv->image_data+priv->image.offsets[1],
1619                                            priv->image.pitches[1]);
1620           break;
1621       }
1622
1623       case GST_VAAPI_IMAGE_I420: {
1624           g_assert(priv->image.num_planes == 3);
1625           success = _image_convert_to_yuv411(GST_BUFFER_DATA(inbuf),
1626                                            width, height, in_format,
1627                                            priv->image_data+priv->image.offsets[0],
1628                                            priv->image.pitches[0],
1629                                            priv->image_data+priv->image.offsets[1],
1630                                            priv->image.pitches[1],
1631                                            priv->image_data+priv->image.offsets[2],
1632                                            priv->image.pitches[2]);
1633           break;
1634       }
1635       case GST_VAAPI_IMAGE_YV12:{
1636           g_assert(priv->image.num_planes == 3);
1637           success = _image_convert_to_yuv411(GST_BUFFER_DATA(inbuf),
1638                                            width, height, in_format,
1639                                            priv->image_data+priv->image.offsets[0],
1640                                            priv->image.pitches[0],
1641                                            priv->image_data+priv->image.offsets[2],
1642                                            priv->image.pitches[2],
1643                                            priv->image_data+priv->image.offsets[1],
1644                                            priv->image.pitches[1]);
1645           break;
1646       }
1647
1648       default:
1649         success = FALSE;
1650         break;
1651     }
1652     gst_vaapi_image_unmap(image);
1653
1654     return success;
1655 }
1656