Tizen 2.0 Release
[framework/multimedia/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     void *image_data;
758     VAStatus status;
759     guint i;
760
761     if (_gst_vaapi_image_is_mapped(image))
762         return TRUE;
763
764     display = GST_VAAPI_OBJECT_DISPLAY(image);
765     if (!display)
766         return FALSE;
767
768     GST_VAAPI_DISPLAY_LOCK(display);
769     status = vaMapBuffer(
770         GST_VAAPI_DISPLAY_VADISPLAY(display),
771         image->priv->image.buf,
772         &image_data
773     );
774     GST_VAAPI_DISPLAY_UNLOCK(display);
775     if (!vaapi_check_status(status, "vaMapBuffer()"))
776         return FALSE;
777
778     image->priv->image_data = image_data;
779
780     if (raw_image) {
781         const VAImage * const va_image = &priv->image;
782         raw_image->format     = priv->format;
783         raw_image->width      = va_image->width;
784         raw_image->height     = va_image->height;
785         raw_image->num_planes = va_image->num_planes;
786         for (i = 0; i < raw_image->num_planes; i++) {
787             raw_image->pixels[i] = image_data + va_image->offsets[i];
788             raw_image->stride[i] = va_image->pitches[i];
789         }
790     }
791     return TRUE;
792 }
793
794 /**
795  * gst_vaapi_image_unmap:
796  * @image: a #GstVaapiImage
797  *
798  * Unmaps the image data buffer. Pointers to pixels returned by
799  * gst_vaapi_image_get_plane() are then no longer valid.
800  *
801  * Return value: %TRUE on success
802  */
803 gboolean
804 gst_vaapi_image_unmap(GstVaapiImage *image)
805 {
806     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
807     g_return_val_if_fail(image->priv->is_constructed, FALSE);
808
809     return _gst_vaapi_image_unmap(image);
810 }
811
812 gboolean
813 _gst_vaapi_image_unmap(GstVaapiImage *image)
814 {
815     GstVaapiDisplay *display;
816     VAStatus status;
817
818     if (!_gst_vaapi_image_is_mapped(image))
819         return FALSE;
820
821     display = GST_VAAPI_OBJECT_DISPLAY(image);
822     if (!display)
823         return FALSE;
824
825     GST_VAAPI_DISPLAY_LOCK(display);
826     status = vaUnmapBuffer(
827         GST_VAAPI_DISPLAY_VADISPLAY(display),
828         image->priv->image.buf
829     );
830     GST_VAAPI_DISPLAY_UNLOCK(display);
831     if (!vaapi_check_status(status, "vaUnmapBuffer()"))
832         return FALSE;
833
834     image->priv->image_data = NULL;
835     return TRUE;
836 }
837
838 /**
839  * gst_vaapi_image_ensure_mapped_buffer:
840  * @image: a #GstVaapiImage
841  *
842  * initialize mapped image buffer in case of optimizition on
843  * nothing did between map and unmap operations.
844  *
845  * Return value: %TRUE on success
846  */
847
848 gboolean
849 gst_vaapi_image_ensure_mapped_buffer(GstVaapiImage *image)
850 {
851     GstVaapiImagePrivate * const priv = image->priv;
852     guchar *user_ptr;
853     guchar tmp;
854     guint index;
855
856     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
857     g_return_val_if_fail(priv->is_constructed, FALSE);
858
859     if (!_gst_vaapi_image_is_mapped(image) || !priv->image_data)
860         return FALSE;
861
862 #undef GST_VAAPI_IMAGE_PAGE_SIZE
863 #define GST_VAAPI_IMAGE_PAGE_SIZE 4096
864
865     /* read each page make sure virtual buffer mapped*/
866     user_ptr = priv->image_data;
867     for (index = 0;
868          index < priv->image.data_size;
869          index += GST_VAAPI_IMAGE_PAGE_SIZE) {
870         tmp = *(user_ptr + index);
871         /*prevent from compiling optimization*/
872         if (tmp == 0)
873             *(user_ptr + index) = 0;
874     }
875
876     return TRUE;
877 }
878
879 /**
880  * gst_vaapi_image_get_plane_count:
881  * @image: a #GstVaapiImage
882  *
883  * Retrieves the number of planes available in the @image. The @image
884  * must be mapped for this function to work properly.
885  *
886  * Return value: the number of planes available in the @image
887  */
888 guint
889 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
890 {
891     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
892     g_return_val_if_fail(image->priv->is_constructed, FALSE);
893     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
894
895     return image->priv->image.num_planes;
896 }
897
898 /**
899  * gst_vaapi_image_get_plane:
900  * @image: a #GstVaapiImage
901  * @plane: the requested plane number
902  *
903  * Retrieves the pixels data to the specified @plane. The @image must
904  * be mapped for this function to work properly.
905  *
906  * Return value: the pixels data of the specified @plane
907  */
908 guchar *
909 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
910 {
911     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
912     g_return_val_if_fail(image->priv->is_constructed, FALSE);
913     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
914     g_return_val_if_fail(plane < image->priv->image.num_planes, NULL);
915
916     return image->priv->image_data + image->priv->image.offsets[plane];
917 }
918
919 /**
920  * gst_vaapi_image_get_pitch:
921  * @image: a #GstVaapiImage
922  * @plane: the requested plane number
923  *
924  * Retrieves the line size (stride) of the specified @plane. The
925  * @image must be mapped for this function to work properly.
926  *
927  * Return value: the line size (stride) of the specified plane
928  */
929 guint
930 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
931 {
932     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
933     g_return_val_if_fail(image->priv->is_constructed, FALSE);
934     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
935     g_return_val_if_fail(plane < image->priv->image.num_planes, 0);
936
937     return image->priv->image.pitches[plane];
938 }
939
940 /**
941  * gst_vaapi_image_get_data_size:
942  * @image: a #GstVaapiImage
943  *
944  * Retrieves the underlying image data size. This function could be
945  * used to determine whether the image has a compatible layout with
946  * another image structure.
947  *
948  * Return value: the whole image data size of the @image
949  */
950 guint
951 gst_vaapi_image_get_data_size(GstVaapiImage *image)
952 {
953     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
954     g_return_val_if_fail(image->priv->is_constructed, FALSE);
955
956     return image->priv->image.data_size;
957 }
958
959 static gboolean
960 init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
961 {
962     GstStructure *structure;
963     GstCaps *caps;
964     GstVaapiImageFormat format;
965     guint width2, height2, size2;
966     gint width, height;
967     guchar *data;
968     guint32 data_size;
969
970     data      = GST_BUFFER_DATA(buffer);
971     data_size = GST_BUFFER_SIZE(buffer);
972     caps      = GST_BUFFER_CAPS(buffer);
973
974     if (!caps)
975         return FALSE;
976
977     format = gst_vaapi_image_format_from_caps(caps);
978
979     structure = gst_caps_get_structure(caps, 0);
980     gst_structure_get_int(structure, "width",  &width);
981     gst_structure_get_int(structure, "height", &height);
982
983     /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
984     raw_image->format = format;
985     raw_image->width  = width;
986     raw_image->height = height;
987     width2  = (width + 1) / 2;
988     height2 = (height + 1) / 2;
989     size2   = 0;
990     switch (format) {
991     case GST_VAAPI_IMAGE_NV12:
992         raw_image->num_planes = 2;
993         raw_image->pixels[0]  = data;
994         raw_image->stride[0]  = GST_ROUND_UP_4(width);
995         size2                += height * raw_image->stride[0];
996         raw_image->pixels[1]  = data + size2;
997         raw_image->stride[1]  = raw_image->stride[0];
998         size2                += height2 * raw_image->stride[1];
999         break;
1000     case GST_VAAPI_IMAGE_YV12:
1001     case GST_VAAPI_IMAGE_I420:
1002         raw_image->num_planes = 3;
1003         raw_image->pixels[0]  = data;
1004         raw_image->stride[0]  = GST_ROUND_UP_4(width);
1005         size2                += height * raw_image->stride[0];
1006         raw_image->pixels[1]  = data + size2;
1007         raw_image->stride[1]  = GST_ROUND_UP_4(width2);
1008         size2                += height2 * raw_image->stride[1];
1009         raw_image->pixels[2]  = data + size2;
1010         raw_image->stride[2]  = raw_image->stride[1];
1011         size2                += height2 * raw_image->stride[2];
1012         break;
1013     case GST_VAAPI_IMAGE_ARGB:
1014     case GST_VAAPI_IMAGE_RGBA:
1015     case GST_VAAPI_IMAGE_ABGR:
1016     case GST_VAAPI_IMAGE_BGRA:
1017         raw_image->num_planes = 1;
1018         raw_image->pixels[0]  = data;
1019         raw_image->stride[0]  = width * 4;
1020         size2                += height * raw_image->stride[0];
1021         break;
1022     default:
1023         g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
1024                 GST_FOURCC_ARGS(format));
1025         return FALSE;
1026     }
1027
1028     if (size2 != data_size) {
1029         g_error("data_size mismatch %d / %u", size2, data_size);
1030         if (size2 > data_size)
1031             return FALSE;
1032     }
1033     return TRUE;
1034 }
1035
1036 /* Copy N lines of an image */
1037 static inline void
1038 memcpy_pic(
1039     guchar       *dst,
1040     guint         dst_stride,
1041     const guchar *src,
1042     guint         src_stride,
1043     guint         len,
1044     guint         height
1045 )
1046 {
1047     guint i;
1048
1049     for (i = 0; i < height; i++)  {
1050         memcpy(dst, src, len);
1051         dst += dst_stride;
1052         src += src_stride;
1053     }
1054 }
1055
1056 /* Copy NV12 images */
1057 static void
1058 copy_image_NV12(
1059     GstVaapiImageRaw        *dst_image,
1060     GstVaapiImageRaw        *src_image,
1061     const GstVaapiRectangle *rect
1062 )
1063 {
1064     guchar *dst, *src;
1065     guint dst_stride, src_stride;
1066
1067     /* Y plane */
1068     dst_stride = dst_image->stride[0];
1069     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1070     src_stride = src_image->stride[0];
1071     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1072     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
1073
1074     /* UV plane */
1075     dst_stride = dst_image->stride[1];
1076     dst = dst_image->pixels[1] + (rect->y / 2) * dst_stride + (rect->x & -2);
1077     src_stride = src_image->stride[1];
1078     src = src_image->pixels[1] + (rect->y / 2) * src_stride + (rect->x & -2);
1079     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height / 2);
1080 }
1081
1082 /* Copy YV12 images */
1083 static void
1084 copy_image_YV12(
1085     GstVaapiImageRaw        *dst_image,
1086     GstVaapiImageRaw        *src_image,
1087     const GstVaapiRectangle *rect
1088 )
1089 {
1090     guchar *dst, *src;
1091     guint dst_stride, src_stride;
1092     guint i, x, y, w, h;
1093
1094     /* Y plane */
1095     dst_stride = dst_image->stride[0];
1096     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1097     src_stride = src_image->stride[0];
1098     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1099     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
1100
1101     /* U/V planes */
1102     x = rect->x / 2;
1103     y = rect->y / 2;
1104     w = rect->width / 2;
1105     h = rect->height / 2;
1106     for (i = 1; i < dst_image->num_planes; i++) {
1107         dst_stride = dst_image->stride[i];
1108         dst = dst_image->pixels[i] + y * dst_stride + x;
1109         src_stride = src_image->stride[i];
1110         src = src_image->pixels[i] + y * src_stride + x;
1111         memcpy_pic(dst, dst_stride, src, src_stride, w, h);
1112     }
1113 }
1114
1115 /* Copy RGBA images */
1116 static void
1117 copy_image_RGBA(
1118     GstVaapiImageRaw        *dst_image,
1119     GstVaapiImageRaw        *src_image,
1120     const GstVaapiRectangle *rect
1121 )
1122 {
1123     guchar *dst, *src;
1124     guint dst_stride, src_stride;
1125
1126     dst_stride = dst_image->stride[0];
1127     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
1128     src_stride = src_image->stride[0];
1129     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
1130     memcpy_pic(dst, dst_stride, src, src_stride, 4 * rect->width, rect->height);
1131 }
1132
1133 static gboolean
1134 copy_image(
1135     GstVaapiImageRaw        *dst_image,
1136     GstVaapiImageRaw        *src_image,
1137     const GstVaapiRectangle *rect
1138 )
1139 {
1140     GstVaapiRectangle default_rect;
1141
1142     if (dst_image->format != src_image->format ||
1143         dst_image->width  != src_image->width  ||
1144         dst_image->height < src_image->height)
1145         return FALSE;
1146
1147     if (rect) {
1148         if (rect->x >= src_image->width ||
1149             rect->x + src_image->width > src_image->width ||
1150             rect->y >= src_image->height ||
1151             rect->y + src_image->height > src_image->height)
1152             return FALSE;
1153     }
1154     else {
1155         default_rect.x      = 0;
1156         default_rect.y      = 0;
1157         default_rect.width  = src_image->width;
1158         default_rect.height = src_image->height;
1159         rect                = &default_rect;
1160     }
1161
1162     switch (dst_image->format) {
1163     case GST_VAAPI_IMAGE_NV12:
1164         copy_image_NV12(dst_image, src_image, rect);
1165         break;
1166     case GST_VAAPI_IMAGE_YV12:
1167     case GST_VAAPI_IMAGE_I420:
1168         copy_image_YV12(dst_image, src_image, rect);
1169         break;
1170     case GST_VAAPI_IMAGE_ARGB:
1171     case GST_VAAPI_IMAGE_RGBA:
1172     case GST_VAAPI_IMAGE_ABGR:
1173     case GST_VAAPI_IMAGE_BGRA:
1174         copy_image_RGBA(dst_image, src_image, rect);
1175         break;
1176     default:
1177         GST_ERROR("unsupported image format for copy");
1178         return FALSE;
1179     }
1180     return TRUE;
1181 }
1182
1183 /**
1184  * gst_vaapi_image_get_buffer:
1185  * @image: a #GstVaapiImage
1186  * @buffer: a #GstBuffer
1187  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1188  *   whole image
1189  *
1190  * Transfers pixels data contained in the @image into the #GstBuffer.
1191  * Both image structures shall have the same format.
1192  *
1193  * Return value: %TRUE on success
1194  */
1195 gboolean
1196 gst_vaapi_image_get_buffer(
1197     GstVaapiImage     *image,
1198     GstBuffer         *buffer,
1199     GstVaapiRectangle *rect
1200 )
1201 {
1202     GstVaapiImagePrivate *priv;
1203     GstVaapiImageRaw dst_image, src_image;
1204     gboolean success;
1205
1206     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1207     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1208     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
1209
1210     priv = image->priv;
1211
1212     if (!init_image_from_buffer(&dst_image, buffer))
1213         return FALSE;
1214     if (dst_image.format != priv->format)
1215         return FALSE;
1216     if (dst_image.width != priv->width || dst_image.height != priv->height)
1217         return FALSE;
1218
1219     if (!_gst_vaapi_image_map(image, &src_image))
1220         return FALSE;
1221
1222     success = copy_image(&dst_image, &src_image, rect);
1223
1224     if (!_gst_vaapi_image_unmap(image))
1225         return FALSE;
1226
1227     return success;
1228 }
1229
1230 /**
1231  * gst_vaapi_image_get_raw:
1232  * @image: a #GstVaapiImage
1233  * @dst_image: a #GstVaapiImageRaw
1234  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1235  *   whole image
1236  *
1237  * Transfers pixels data contained in the @image into the #GstVaapiImageRaw.
1238  * Both image structures shall have the same format.
1239  *
1240  * Return value: %TRUE on success
1241  */
1242 gboolean
1243 gst_vaapi_image_get_raw(
1244     GstVaapiImage     *image,
1245     GstVaapiImageRaw  *dst_image,
1246     GstVaapiRectangle *rect
1247 )
1248 {
1249     GstVaapiImageRaw src_image;
1250     gboolean success;
1251
1252     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1253     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1254
1255     if (!_gst_vaapi_image_map(image, &src_image))
1256         return FALSE;
1257
1258     success = copy_image(dst_image, &src_image, rect);
1259
1260     if (!_gst_vaapi_image_unmap(image))
1261         return FALSE;
1262
1263     return success;
1264 }
1265
1266 /**
1267  * gst_vaapi_image_update_from_buffer:
1268  * @image: a #GstVaapiImage
1269  * @buffer: a #GstBuffer
1270  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1271  *   whole image
1272  *
1273  * Transfers pixels data contained in the #GstBuffer into the
1274  * @image. Both image structures shall have the same format.
1275  *
1276  * Return value: %TRUE on success
1277  */
1278 gboolean
1279 gst_vaapi_image_update_from_buffer(
1280     GstVaapiImage     *image,
1281     GstBuffer         *buffer,
1282     GstVaapiRectangle *rect
1283 )
1284 {
1285     GstVaapiImagePrivate *priv;
1286     GstVaapiImageRaw dst_image, src_image;
1287     gboolean success;
1288
1289     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1290     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1291     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
1292
1293     priv = image->priv;
1294
1295     if (!init_image_from_buffer(&src_image, buffer))
1296         return FALSE;
1297     if (src_image.format != priv->format)
1298         return FALSE;
1299     if (src_image.width != priv->width || src_image.height > priv->height)
1300         return FALSE;
1301
1302     if (!_gst_vaapi_image_map(image, &dst_image))
1303         return FALSE;
1304
1305     success = copy_image(&dst_image, &src_image, rect);
1306
1307     if (!_gst_vaapi_image_unmap(image))
1308         return FALSE;
1309
1310     return success;
1311 }
1312
1313 /**
1314  * gst_vaapi_image_update_from_raw:
1315  * @image: a #GstVaapiImage
1316  * @src_image: a #GstVaapiImageRaw
1317  * @buffer: a #GstBuffer
1318  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1319  *   whole image
1320  *
1321  * Transfers pixels data contained in the #GstVaapiImageRaw into the
1322  * @image. Both image structures shall have the same format.
1323  *
1324  * Return value: %TRUE on success
1325  */
1326 gboolean
1327 gst_vaapi_image_update_from_raw(
1328     GstVaapiImage     *image,
1329     GstVaapiImageRaw  *src_image,
1330     GstVaapiRectangle *rect
1331 )
1332 {
1333     GstVaapiImageRaw dst_image;
1334     gboolean success;
1335
1336     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
1337     g_return_val_if_fail(image->priv->is_constructed, FALSE);
1338
1339     if (!_gst_vaapi_image_map(image, &dst_image))
1340         return FALSE;
1341
1342     success = copy_image(&dst_image, src_image, rect);
1343
1344     if (!_gst_vaapi_image_unmap(image))
1345         return FALSE;
1346
1347     return success;
1348 }
1349
1350 static void
1351 _yuv411_to_nv12(
1352     const guint8 *y_src,
1353     guint32 y_src_stride,
1354     const guint8 *u_src,
1355     guint32 u_src_stride,
1356     const guint8 *v_src,
1357     guint32 v_src_stride,
1358     guint8 *y_dest,
1359     guint32 y_dest_stride,
1360     guint8 *uv_dest,
1361     guint32 uv_dest_stride,
1362     guint32 width,
1363     guint32 height
1364 )
1365 {
1366     guint32 row, column;
1367     guint32 uv_height;
1368
1369     memcpy_pic(y_dest, y_dest_stride, y_src, y_src_stride, width, height);
1370     uv_height = GST_ROUND_UP_2(height)/2;
1371     for (row = 0; row < uv_height; row++) {
1372         for (column = 0; column < width/2; column++) {
1373             uv_dest[column*2] = u_src[column];
1374             uv_dest[column*2+1] = v_src[column];
1375         }
1376         u_src += u_src_stride;
1377         v_src += v_src_stride;
1378         uv_dest += uv_dest_stride;
1379     }
1380 }
1381
1382 static void
1383 _nv12_to_yuv411(
1384     const guint8 *y_src,
1385     guint32 y_src_stride,
1386     const guint8 *uv_src,
1387     guint32 uv_src_stride,
1388     guint8 *y_dest,
1389     guint32 y_dest_stride,
1390     guint8 *u_dest,
1391     guint32 u_dest_stride,
1392     guint8 *v_dest,
1393     guint32 v_dest_stride,
1394     guint32 width,
1395     guint32 height
1396 )
1397 {
1398     guint32 row, column;
1399     guint32 uv_height;
1400
1401     memcpy_pic(y_dest, y_dest_stride, y_src, y_src_stride, width, height);
1402     uv_height = GST_ROUND_UP_2(height)/2;
1403     for (row = 0; row < uv_height; row++) {
1404         for (column = 0; column < width/2; column++) {
1405             u_dest[column] = uv_src[column*2];
1406             v_dest[column] = uv_src[column*2+1];
1407         }
1408         uv_src += uv_src_stride;
1409         u_dest += u_dest_stride;
1410         v_dest += v_dest_stride;
1411     }
1412 }
1413
1414 static void
1415 _yuy2_to_yuv411(
1416     const guint8 *yuv_src,
1417     guint32 yuv_src_stride,
1418     guint8 *y_dest,
1419     guint32 y_dest_stride,
1420     guint8 *u_dest,
1421     guint32 u_dest_stride,
1422     guint8 *v_dest,
1423     guint32 v_dest_stride,
1424     gboolean uv_interlaced,
1425     guint32 width,
1426     guint32 height
1427 )
1428 {
1429     guint32 row, column;
1430     const guint8 *yuv_next_src;
1431     guint8 *y_next_dest;
1432     guint32 interval;
1433
1434     interval = uv_interlaced ? 2 : 1;
1435     for (row = 0; row < height/2; row++) {
1436         yuv_next_src = yuv_src + yuv_src_stride;
1437         y_next_dest = y_dest + y_dest_stride;
1438         for (column = 0; column < width/2; column++) {
1439             y_dest[column*2] = yuv_src[column*4];
1440             y_dest[column*2+1] = yuv_src[column*4+2];
1441             y_next_dest[column*2] = yuv_next_src[column*4];
1442             y_next_dest[column*2+1] = yuv_next_src[column*4+2];
1443             u_dest[column*interval] = ((yuv_src[column*4+1] + yuv_next_src[column*4+1])>>1);
1444             v_dest[column*interval] = ((yuv_src[column*4+3] + yuv_next_src[column*4+3])>>1);
1445         }
1446         yuv_src = yuv_next_src + yuv_src_stride;
1447         y_dest = y_next_dest + y_dest_stride;
1448         u_dest += u_dest_stride;
1449         v_dest += v_dest_stride;
1450     }
1451
1452     /*  odd line */
1453     if (height%2) {
1454         for (column = 0; column < width/2; column++) {
1455           y_dest[column*2] = yuv_src[column*4];
1456           y_dest[column*2+1] = yuv_src[column*4+2];
1457           u_dest[column*interval] = yuv_src[column*4+1];
1458           v_dest[column*interval] = yuv_src[column*4+3];
1459         }
1460     }
1461
1462 }
1463
1464 static gboolean
1465 _image_convert_to_nv12(
1466     const guint8 *src,
1467     guint32 width,
1468     guint32 height,
1469     guint32 src_format,
1470     guint8 *y_dest,
1471     guint32 y_dest_stride,
1472     guint8 *uv_dest,
1473     guint32 uv_dest_stride
1474 )
1475 {
1476     const guint8 *y_src, *u_src, *v_src, *uv_src;
1477     guint32 ystride, ustride, vstride, uv_stride;
1478
1479     switch (src_format) {
1480         case GST_MAKE_FOURCC('N','V','1','2'): {
1481             y_src = src;
1482             ystride = GST_ROUND_UP_4(width);
1483             uv_src = src + ystride*GST_ROUND_UP_2(height);
1484             uv_stride = ystride;
1485             memcpy_pic(y_dest, y_dest_stride, y_src, ystride, width, height);
1486             memcpy_pic(uv_dest, uv_dest_stride, uv_src, uv_stride,
1487                        width, GST_ROUND_UP_2(height)/2);
1488             break;
1489         }
1490
1491         case GST_MAKE_FOURCC('I','4','2','0'): {
1492             y_src = src;
1493             ystride = GST_ROUND_UP_4(width);
1494             u_src = src + ystride*GST_ROUND_UP_2(height);
1495             ustride = GST_ROUND_UP_8(ystride)/2;
1496             v_src = u_src + ustride*GST_ROUND_UP_2(height)/2;
1497             vstride = GST_ROUND_UP_8(ystride)/2;
1498
1499             _yuv411_to_nv12(y_src, ystride, u_src, ustride, v_src, vstride,
1500                             y_dest, y_dest_stride,
1501                             uv_dest, uv_dest_stride,
1502                             width, height);
1503             break;
1504         }
1505
1506         case GST_MAKE_FOURCC('Y','V','1','2'):{
1507             y_src = src;
1508             ystride = GST_ROUND_UP_4(width);
1509             v_src = src + ystride*GST_ROUND_UP_2(height);
1510             vstride = GST_ROUND_UP_8(ystride)/2;
1511             u_src = v_src + vstride*GST_ROUND_UP_2(height)/2;
1512             ustride = GST_ROUND_UP_8(ystride)/2;
1513
1514             _yuv411_to_nv12(y_src, ystride, u_src, ustride, v_src, vstride,
1515                             y_dest, y_dest_stride, uv_dest, uv_dest_stride,
1516                             width, height);
1517             break;
1518         }
1519
1520         case GST_MAKE_FOURCC('Y', 'U', 'Y', '2'): {
1521             y_src = src;
1522             ystride = GST_ROUND_UP_2(width)*2;
1523             _yuy2_to_yuv411(y_src, ystride,
1524                             y_dest, y_dest_stride,
1525                             uv_dest, uv_dest_stride,
1526                             uv_dest+1, uv_dest_stride,
1527                             TRUE,
1528                             width, height);
1529             break;
1530         }
1531
1532         default:
1533           return FALSE;
1534     }
1535     return TRUE;
1536 }
1537
1538 static gboolean
1539 _image_convert_to_yuv411(
1540     const guint8 *src,
1541     guint32 width,
1542     guint32 height,
1543     guint32 src_format,
1544     guint8 *y_dest,
1545     guint32 y_dest_stride,
1546     guint8 *u_dest,
1547     guint32 u_dest_stride,
1548     guint8 *v_dest,
1549     guint32 v_dest_stride
1550 )
1551 {
1552     const guint8 *y_src, *u_src, *v_src, *uv_src;
1553     guint32 ystride, ustride, vstride, uv_stride;
1554
1555     switch (src_format) {
1556         case GST_MAKE_FOURCC('N','V','1','2'): {
1557             y_src = src;
1558             ystride = GST_ROUND_UP_4(width);
1559             uv_src = src + ystride*GST_ROUND_UP_2(height);
1560             uv_stride = ystride;
1561             _nv12_to_yuv411(y_src, ystride, uv_src, uv_stride,
1562                             y_dest, y_dest_stride,
1563                             u_dest, u_dest_stride,
1564                             v_dest, v_dest_stride,
1565                             width, height);
1566             break;
1567         }
1568
1569         case GST_MAKE_FOURCC('I','4','2','0'):
1570         case GST_MAKE_FOURCC('Y','V','1','2'):{
1571             y_src = src;
1572             ystride = GST_ROUND_UP_4(width);
1573             if (GST_MAKE_FOURCC('I','4','2','0') == src_format) {
1574                 u_src = src + ystride*GST_ROUND_UP_2(height);
1575                 ustride = GST_ROUND_UP_8(ystride)/2;
1576                 v_src = u_src + ustride*GST_ROUND_UP_2(height)/2;
1577                 vstride = GST_ROUND_UP_8(ystride)/2;
1578             } else {
1579                 v_src = src + ystride*GST_ROUND_UP_2(height);
1580                 vstride = GST_ROUND_UP_8(ystride)/2;
1581                 u_src = v_src + vstride*GST_ROUND_UP_2(height)/2;
1582                 ustride = GST_ROUND_UP_8(ystride)/2;
1583             }
1584
1585             memcpy_pic(y_dest, y_dest_stride, y_src, ystride, width, height);
1586             memcpy_pic(u_dest, u_dest_stride, u_src, ustride, width/2, GST_ROUND_UP_2(height)/2);
1587             memcpy_pic(v_dest, v_dest_stride, v_src, vstride, width/2, GST_ROUND_UP_2(height)/2);
1588             break;
1589         }
1590
1591         case GST_MAKE_FOURCC('Y', 'U', 'Y', '2'): {
1592             y_src = src;
1593             ystride = GST_ROUND_UP_2(width)*2;
1594             _yuy2_to_yuv411(y_src, ystride,
1595                             y_dest, y_dest_stride,
1596                             u_dest, u_dest_stride,
1597                             v_dest, v_dest_stride,
1598                             FALSE,
1599                             width, height);
1600             break;
1601         }
1602
1603         default:
1604           return FALSE;
1605     }
1606     return TRUE;
1607 }
1608
1609
1610 gboolean
1611 gst_vaapi_convert_buffer_to_image(
1612     GstVaapiImage *image,
1613     GstBuffer *inbuf
1614     )
1615 {
1616     GstVaapiImagePrivate *priv;
1617     guint width, height;
1618     GstVaapiImageFormat image_format;
1619     gboolean success = TRUE;
1620     GstCaps *buffer_caps;
1621     GstStructure *structure;
1622     guint32 in_format = 0;
1623
1624     priv = image->priv;
1625     gst_vaapi_image_get_size(image, &width, &height);
1626     image_format = gst_vaapi_image_get_format(image);
1627
1628     /* get buffer format */
1629     buffer_caps = GST_BUFFER_CAPS(inbuf);
1630     if (!buffer_caps)
1631         return FALSE;
1632     structure = gst_caps_get_structure(buffer_caps, 0);
1633     if (!structure)
1634         return FALSE;
1635     if (!gst_structure_get_fourcc(structure, "format", &in_format))
1636         return FALSE;
1637
1638     /* currently only support YUV convert */
1639     if ( (in_format != GST_MAKE_FOURCC('N','V','1','2')
1640           && in_format != GST_MAKE_FOURCC('Y','V','1','2')
1641           && in_format != GST_MAKE_FOURCC('I','4','2','0')
1642           && in_format != GST_MAKE_FOURCC('Y', 'U', 'Y', '2'))
1643         || (image_format != GST_VAAPI_IMAGE_NV12
1644           && image_format != GST_VAAPI_IMAGE_YV12
1645           && image_format != GST_VAAPI_IMAGE_I420)
1646         )
1647     {
1648         return FALSE;
1649     }
1650
1651
1652     gst_vaapi_image_map(image);
1653     switch (image_format) {
1654       case GST_VAAPI_IMAGE_NV12: {
1655           g_assert(priv->image.num_planes == 2);
1656           success = _image_convert_to_nv12(GST_BUFFER_DATA(inbuf),
1657                                            width, height, in_format,
1658                                            priv->image_data+priv->image.offsets[0],
1659                                            priv->image.pitches[0],
1660                                            priv->image_data+priv->image.offsets[1],
1661                                            priv->image.pitches[1]);
1662           break;
1663       }
1664
1665       case GST_VAAPI_IMAGE_I420: {
1666           g_assert(priv->image.num_planes == 3);
1667           success = _image_convert_to_yuv411(GST_BUFFER_DATA(inbuf),
1668                                            width, height, in_format,
1669                                            priv->image_data+priv->image.offsets[0],
1670                                            priv->image.pitches[0],
1671                                            priv->image_data+priv->image.offsets[1],
1672                                            priv->image.pitches[1],
1673                                            priv->image_data+priv->image.offsets[2],
1674                                            priv->image.pitches[2]);
1675           break;
1676       }
1677       case GST_VAAPI_IMAGE_YV12:{
1678           g_assert(priv->image.num_planes == 3);
1679           success = _image_convert_to_yuv411(GST_BUFFER_DATA(inbuf),
1680                                            width, height, in_format,
1681                                            priv->image_data+priv->image.offsets[0],
1682                                            priv->image.pitches[0],
1683                                            priv->image_data+priv->image.offsets[2],
1684                                            priv->image.pitches[2],
1685                                            priv->image_data+priv->image.offsets[1],
1686                                            priv->image.pitches[1]);
1687           break;
1688       }
1689
1690       default:
1691         success = FALSE;
1692         break;
1693     }
1694     gst_vaapi_image_unmap(image);
1695
1696     return success;
1697 }
1698