libs: add and expose gst_vaapi_video_format_to_string() helper.
[platform/upstream/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 "gstvaapicompat.h"
31 #include "gstvaapiutils.h"
32 #include "gstvaapiimage.h"
33 #include "gstvaapiobject_priv.h"
34
35 #define DEBUG 1
36 #include "gstvaapidebug.h"
37
38 typedef struct _GstVaapiImageClass              GstVaapiImageClass;
39
40 /**
41  * GstVaapiImage:
42  *
43  * A VA image wrapper
44  */
45 struct _GstVaapiImage {
46     /*< private >*/
47     GstVaapiObject      parent_instance;
48
49     VAImage             internal_image;
50     VAImage             image;
51     guchar             *image_data;
52     GstVideoFormat      internal_format;
53     GstVideoFormat      format;
54     guint               width;
55     guint               height;
56     guint               is_linear       : 1;
57 };
58
59 /**
60  * GstVaapiImageClass:
61  *
62  * A VA image wrapper class
63  */
64 struct _GstVaapiImageClass {
65     /*< private >*/
66     GstVaapiObjectClass parent_class;
67 };
68
69 #define SWAP_UINT(a, b) do { \
70         guint v = a;         \
71         a = b;               \
72         b = v;               \
73     } while (0)
74
75 static gboolean
76 _gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image);
77
78 static gboolean
79 _gst_vaapi_image_unmap(GstVaapiImage *image);
80
81 static gboolean
82 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image);
83
84 /*
85  * VAImage wrapper
86  */
87
88 static gboolean
89 vaapi_image_is_linear(const VAImage *va_image)
90 {
91     guint i, width, height, width2, height2, data_size;
92
93     for (i = 1; i < va_image->num_planes; i++)
94         if (va_image->offsets[i] < va_image->offsets[i - 1])
95             return FALSE;
96
97     width   = va_image->width;
98     height  = va_image->height;
99     width2  = (width  + 1) / 2;
100     height2 = (height + 1) / 2;
101
102     switch (va_image->format.fourcc) {
103     case VA_FOURCC('N','V','1','2'):
104     case VA_FOURCC('Y','V','1','2'):
105     case VA_FOURCC('I','4','2','0'):
106         data_size = width * height + 2 * width2 * height2;
107         break;
108     case VA_FOURCC('Y','U','Y','2'):
109     case VA_FOURCC('U','Y','V','Y'):
110         data_size = 2 * width * height;
111         break;
112     case VA_FOURCC('Y','8','0','0'):
113         data_size = width * height;
114         break;
115     case VA_FOURCC('A','Y','U','V'):
116     case VA_FOURCC('A','R','G','B'):
117     case VA_FOURCC('R','G','B','A'):
118     case VA_FOURCC('A','B','G','R'):
119     case VA_FOURCC('B','G','R','A'):
120     case VA_FOURCC('X','R','G','B'):
121     case VA_FOURCC('R','G','B','X'):
122     case VA_FOURCC('X','B','G','R'):
123     case VA_FOURCC('B','G','R','X'):
124         data_size = 4 * width * height;
125         break;
126     default:
127         g_error("FIXME: incomplete formats %" GST_FOURCC_FORMAT,
128                 GST_FOURCC_ARGS(va_image->format.fourcc));
129         break;
130     }
131     return va_image->data_size == data_size;
132 }
133
134 static void
135 gst_vaapi_image_destroy(GstVaapiImage *image)
136 {
137     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
138     VAImageID image_id;
139     VAStatus status;
140
141     _gst_vaapi_image_unmap(image);
142
143     image_id = GST_VAAPI_OBJECT_ID(image);
144     GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
145
146     if (image_id != VA_INVALID_ID) {
147         GST_VAAPI_DISPLAY_LOCK(display);
148         status = vaDestroyImage(GST_VAAPI_DISPLAY_VADISPLAY(display), image_id);
149         GST_VAAPI_DISPLAY_UNLOCK(display);
150         if (!vaapi_check_status(status, "vaDestroyImage()"))
151             g_warning("failed to destroy image %" GST_VAAPI_ID_FORMAT,
152                       GST_VAAPI_ID_ARGS(image_id));
153         GST_VAAPI_OBJECT_ID(image) = VA_INVALID_ID;
154     }
155 }
156
157 static gboolean
158 _gst_vaapi_image_create(GstVaapiImage *image, GstVideoFormat format)
159 {
160     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(image);
161     const VAImageFormat *va_format;
162     VAStatus status;
163
164     if (!gst_vaapi_display_has_image_format(display, format))
165         return FALSE;
166
167     va_format = gst_vaapi_video_format_to_va_format(format);
168     if (!va_format)
169         return FALSE;
170
171     GST_VAAPI_DISPLAY_LOCK(display);
172     status = vaCreateImage(
173         GST_VAAPI_DISPLAY_VADISPLAY(display),
174         (VAImageFormat *)va_format,
175         image->width,
176         image->height,
177         &image->internal_image
178     );
179     GST_VAAPI_DISPLAY_UNLOCK(display);
180     if (status != VA_STATUS_SUCCESS ||
181         image->internal_image.format.fourcc != va_format->fourcc)
182         return FALSE;
183
184     image->internal_format = format;
185     return TRUE;
186 }
187
188 static gboolean
189 gst_vaapi_image_create(GstVaapiImage *image, GstVideoFormat format,
190     guint width, guint height)
191 {
192     const VAImageFormat *va_format;
193     VAImageID image_id;
194
195     image->format = format;
196     image->width  = width;
197     image->height = height;
198
199     if (!_gst_vaapi_image_create(image, format)) {
200         switch (format) {
201         case GST_VIDEO_FORMAT_I420:
202             format = GST_VIDEO_FORMAT_YV12;
203             break;
204         case GST_VIDEO_FORMAT_YV12:
205             format = GST_VIDEO_FORMAT_I420;
206             break;
207         default:
208             format = 0;
209             break;
210         }
211         if (!format || !_gst_vaapi_image_create(image, format))
212             return FALSE;
213     }
214     image->image = image->internal_image;
215     image_id     = image->image.image_id;
216
217     if (image->format != image->internal_format) {
218         switch (image->format) {
219         case GST_VIDEO_FORMAT_YV12:
220         case GST_VIDEO_FORMAT_I420:
221             va_format = gst_vaapi_video_format_to_va_format(image->format);
222             if (!va_format)
223                 return FALSE;
224             image->image.format = *va_format;
225             SWAP_UINT(image->image.offsets[1], image->image.offsets[2]);
226             SWAP_UINT(image->image.pitches[1], image->image.pitches[2]);
227             break;
228         default:
229             break;
230         }
231     }
232     image->is_linear = vaapi_image_is_linear(&image->image);
233
234     GST_DEBUG("image %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(image_id));
235     GST_VAAPI_OBJECT_ID(image) = image_id;
236     return TRUE;
237 }
238
239 static void
240 gst_vaapi_image_init(GstVaapiImage *image)
241 {
242     image->internal_image.image_id = VA_INVALID_ID;
243     image->internal_image.buf = VA_INVALID_ID;
244     image->image.image_id = VA_INVALID_ID;
245     image->image.buf = VA_INVALID_ID;
246 }
247
248 static void
249 gst_vaapi_image_class_init(GstVaapiImageClass *klass)
250 {
251     GstVaapiObjectClass * const object_class =
252         GST_VAAPI_OBJECT_CLASS(klass);
253
254     object_class->init = (GstVaapiObjectInitFunc)gst_vaapi_image_init;
255 }
256
257 #define gst_vaapi_image_finalize gst_vaapi_image_destroy
258 GST_VAAPI_OBJECT_DEFINE_CLASS_WITH_CODE(
259     GstVaapiImage,
260     gst_vaapi_image,
261     gst_vaapi_image_class_init(&g_class))
262
263 /**
264  * gst_vaapi_image_new:
265  * @display: a #GstVaapiDisplay
266  * @format: a #GstVideoFormat
267  * @width: the requested image width
268  * @height: the requested image height
269  *
270  * Creates a new #GstVaapiImage with the specified format and
271  * dimensions.
272  *
273  * Return value: the newly allocated #GstVaapiImage object
274  */
275 GstVaapiImage *
276 gst_vaapi_image_new(
277     GstVaapiDisplay    *display,
278     GstVideoFormat      format,
279     guint               width,
280     guint               height
281 )
282 {
283     GstVaapiImage *image;
284
285     g_return_val_if_fail(width > 0, NULL);
286     g_return_val_if_fail(height > 0, NULL);
287
288     GST_DEBUG("format %s, size %ux%u", gst_vaapi_video_format_to_string(format),
289               width, height);
290
291     image = gst_vaapi_object_new(gst_vaapi_image_class(), display);
292     if (!image)
293         return NULL;
294
295     if (!gst_vaapi_image_create(image, format, width, height))
296         goto error;
297     return image;
298
299 error:
300     gst_vaapi_object_unref(image);
301     return NULL;
302 }
303
304 /**
305  * gst_vaapi_image_new_with_image:
306  * @display: a #GstVaapiDisplay
307  * @va_image: a VA image
308  *
309  * Creates a new #GstVaapiImage from a foreign VA image. The image
310  * format and dimensions will be extracted from @va_image. This
311  * function is mainly used by gst_vaapi_surface_derive_image() to bind
312  * a VA image to a #GstVaapiImage object.
313  *
314  * Return value: the newly allocated #GstVaapiImage object
315  */
316 GstVaapiImage *
317 gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
318 {
319     GstVaapiImage *image;
320
321     g_return_val_if_fail(va_image, NULL);
322     g_return_val_if_fail(va_image->image_id != VA_INVALID_ID, NULL);
323     g_return_val_if_fail(va_image->buf != VA_INVALID_ID, NULL);
324
325     GST_DEBUG("VA image 0x%08x, format %" GST_FOURCC_FORMAT ", size %ux%u",
326               va_image->image_id,
327               GST_FOURCC_ARGS(va_image->format.fourcc),
328               va_image->width, va_image->height);
329
330     image = gst_vaapi_object_new(gst_vaapi_image_class(), display);
331     if (!image)
332         return NULL;
333
334     if (!_gst_vaapi_image_set_image(image, va_image))
335         goto error;
336     return image;
337
338 error:
339     gst_vaapi_object_unref(image);
340     return NULL;
341 }
342
343 /**
344  * gst_vaapi_image_get_id:
345  * @image: a #GstVaapiImage
346  *
347  * Returns the underlying VAImageID of the @image.
348  *
349  * Return value: the underlying VA image id
350  */
351 GstVaapiID
352 gst_vaapi_image_get_id(GstVaapiImage *image)
353 {
354     g_return_val_if_fail(image != NULL, VA_INVALID_ID);
355
356     return GST_VAAPI_OBJECT_ID(image);
357 }
358
359 /**
360  * gst_vaapi_image_get_image:
361  * @image: a #GstVaapiImage
362  * @va_image: a VA image
363  *
364  * Fills @va_image with the VA image used internally.
365  *
366  * Return value: %TRUE on success
367  */
368 gboolean
369 gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
370 {
371     g_return_val_if_fail(image != NULL, FALSE);
372
373     if (va_image)
374         *va_image = image->image;
375
376     return TRUE;
377 }
378
379 /*
380  * _gst_vaapi_image_set_image:
381  * @image: a #GstVaapiImage
382  * @va_image: a VA image
383  *
384  * Initializes #GstVaapiImage with a foreign VA image. This function
385  * will try to "linearize" the VA image. i.e. making sure that the VA
386  * image offsets into the data buffer are in increasing order with the
387  * number of planes available in the image.
388  *
389  * This is an internal function used by gst_vaapi_image_new_with_image().
390  *
391  * Return value: %TRUE on success
392  */
393 gboolean
394 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image)
395 {
396     GstVideoFormat format;
397     VAImage alt_va_image;
398     const VAImageFormat *alt_va_format;
399
400     format = gst_vaapi_video_format_from_va_format(&va_image->format);
401     if (format == GST_VIDEO_FORMAT_UNKNOWN)
402         return FALSE;
403
404     image->internal_image  = *va_image;
405     image->internal_format = format;
406     image->is_linear       = vaapi_image_is_linear(va_image);
407     image->image           = *va_image;
408     image->format          = format;
409     image->width           = va_image->width;
410     image->height          = va_image->height;
411
412     GST_VAAPI_OBJECT_ID(image) = va_image->image_id;
413
414     /* Try to linearize image */
415     if (!image->is_linear) {
416         switch (format) {
417         case GST_VIDEO_FORMAT_I420:
418             format = GST_VIDEO_FORMAT_YV12;
419             break;
420         case GST_VIDEO_FORMAT_YV12:
421             format = GST_VIDEO_FORMAT_I420;
422             break;
423         default:
424             format = 0;
425             break;
426         }
427         if (format &&
428             (alt_va_format = gst_vaapi_video_format_to_va_format(format))) {
429             alt_va_image = *va_image;
430             alt_va_image.format = *alt_va_format;
431             SWAP_UINT(alt_va_image.offsets[1], alt_va_image.offsets[2]);
432             SWAP_UINT(alt_va_image.pitches[1], alt_va_image.pitches[2]);
433             if (vaapi_image_is_linear(&alt_va_image)) {
434                 image->image     = alt_va_image;
435                 image->format    = format;
436                 image->is_linear = TRUE;
437                 GST_DEBUG("linearized image to %s format",
438                           gst_vaapi_video_format_to_string(format));
439             }
440         }
441     }
442     return TRUE;
443 }
444
445 /**
446  * gst_vaapi_image_get_format:
447  * @image: a #GstVaapiImage
448  *
449  * Returns the #GstVideoFormat the @image was created with.
450  *
451  * Return value: the #GstVideoFormat
452  */
453 GstVideoFormat
454 gst_vaapi_image_get_format(GstVaapiImage *image)
455 {
456     g_return_val_if_fail(image != NULL, 0);
457
458     return image->format;
459 }
460
461 /**
462  * gst_vaapi_image_get_width:
463  * @image: a #GstVaapiImage
464  *
465  * Returns the @image width.
466  *
467  * Return value: the image width, in pixels
468  */
469 guint
470 gst_vaapi_image_get_width(GstVaapiImage *image)
471 {
472     g_return_val_if_fail(image != NULL, 0);
473
474     return image->width;
475 }
476
477 /**
478  * gst_vaapi_image_get_height:
479  * @image: a #GstVaapiImage
480  *
481  * Returns the @image height.
482  *
483  * Return value: the image height, in pixels.
484  */
485 guint
486 gst_vaapi_image_get_height(GstVaapiImage *image)
487 {
488     g_return_val_if_fail(image != NULL, 0);
489
490     return image->height;
491 }
492
493 /**
494  * gst_vaapi_image_get_size:
495  * @image: a #GstVaapiImage
496  * @pwidth: return location for the width, or %NULL
497  * @pheight: return location for the height, or %NULL
498  *
499  * Retrieves the dimensions of a #GstVaapiImage.
500  */
501 void
502 gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
503 {
504     g_return_if_fail(image != NULL);
505
506     if (pwidth)
507         *pwidth = image->width;
508
509     if (pheight)
510         *pheight = image->height;
511 }
512
513 /**
514  * gst_vaapi_image_is_linear:
515  * @image: a #GstVaapiImage
516  *
517  * Checks whether the @image has data planes allocated from a single
518  * buffer and offsets into that buffer are in increasing order with
519  * the number of planes.
520  *
521  * Return value: %TRUE if image data planes are allocated from a single buffer
522  */
523 gboolean
524 gst_vaapi_image_is_linear(GstVaapiImage *image)
525 {
526     g_return_val_if_fail(image != NULL, FALSE);
527
528     return image->is_linear;
529 }
530
531 /**
532  * gst_vaapi_image_is_mapped:
533  * @image: a #GstVaapiImage
534  *
535  * Checks whether the @image is currently mapped or not.
536  *
537  * Return value: %TRUE if the @image is mapped
538  */
539 static inline gboolean
540 _gst_vaapi_image_is_mapped(GstVaapiImage *image)
541 {
542     return image->image_data != NULL;
543 }
544
545 gboolean
546 gst_vaapi_image_is_mapped(GstVaapiImage *image)
547 {
548     g_return_val_if_fail(image != NULL, FALSE);
549
550     return _gst_vaapi_image_is_mapped(image);
551 }
552
553 /**
554  * gst_vaapi_image_map:
555  * @image: a #GstVaapiImage
556  *
557  * Maps the image data buffer. The actual pixels are returned by the
558  * gst_vaapi_image_get_plane() function.
559  *
560  * Return value: %TRUE on success
561  */
562 gboolean
563 gst_vaapi_image_map(GstVaapiImage *image)
564 {
565     g_return_val_if_fail(image != NULL, FALSE);
566
567     return _gst_vaapi_image_map(image, NULL);
568 }
569
570 gboolean
571 _gst_vaapi_image_map(GstVaapiImage *image, GstVaapiImageRaw *raw_image)
572 {
573     GstVaapiDisplay *display;
574     VAStatus status;
575     guint i;
576
577     if (_gst_vaapi_image_is_mapped(image))
578         goto map_success;
579
580     display = GST_VAAPI_OBJECT_DISPLAY(image);
581     if (!display)
582         return FALSE;
583
584     GST_VAAPI_DISPLAY_LOCK(display);
585     status = vaMapBuffer(
586         GST_VAAPI_DISPLAY_VADISPLAY(display),
587         image->image.buf,
588         (void **)&image->image_data
589     );
590     GST_VAAPI_DISPLAY_UNLOCK(display);
591     if (!vaapi_check_status(status, "vaMapBuffer()"))
592         return FALSE;
593
594 map_success:
595     if (raw_image) {
596         const VAImage * const va_image = &image->image;
597         raw_image->format     = image->format;
598         raw_image->width      = va_image->width;
599         raw_image->height     = va_image->height;
600         raw_image->num_planes = va_image->num_planes;
601         for (i = 0; i < raw_image->num_planes; i++) {
602             raw_image->pixels[i] = (guchar *)image->image_data +
603                 va_image->offsets[i];
604             raw_image->stride[i] = va_image->pitches[i];
605         }
606     }
607     return TRUE;
608 }
609
610 /**
611  * gst_vaapi_image_unmap:
612  * @image: a #GstVaapiImage
613  *
614  * Unmaps the image data buffer. Pointers to pixels returned by
615  * gst_vaapi_image_get_plane() are then no longer valid.
616  *
617  * Return value: %TRUE on success
618  */
619 gboolean
620 gst_vaapi_image_unmap(GstVaapiImage *image)
621 {
622     g_return_val_if_fail(image != NULL, FALSE);
623
624     return _gst_vaapi_image_unmap(image);
625 }
626
627 gboolean
628 _gst_vaapi_image_unmap(GstVaapiImage *image)
629 {
630     GstVaapiDisplay *display;
631     VAStatus status;
632
633     if (!_gst_vaapi_image_is_mapped(image))
634         return TRUE;
635
636     display = GST_VAAPI_OBJECT_DISPLAY(image);
637     if (!display)
638         return FALSE;
639
640     GST_VAAPI_DISPLAY_LOCK(display);
641     status = vaUnmapBuffer(
642         GST_VAAPI_DISPLAY_VADISPLAY(display),
643         image->image.buf
644     );
645     GST_VAAPI_DISPLAY_UNLOCK(display);
646     if (!vaapi_check_status(status, "vaUnmapBuffer()"))
647         return FALSE;
648
649     image->image_data = NULL;
650     return TRUE;
651 }
652
653 /**
654  * gst_vaapi_image_get_plane_count:
655  * @image: a #GstVaapiImage
656  *
657  * Retrieves the number of planes available in the @image. The @image
658  * must be mapped for this function to work properly.
659  *
660  * Return value: the number of planes available in the @image
661  */
662 guint
663 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
664 {
665     g_return_val_if_fail(image != NULL, 0);
666     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
667
668     return image->image.num_planes;
669 }
670
671 /**
672  * gst_vaapi_image_get_plane:
673  * @image: a #GstVaapiImage
674  * @plane: the requested plane number
675  *
676  * Retrieves the pixels data to the specified @plane. The @image must
677  * be mapped for this function to work properly.
678  *
679  * Return value: the pixels data of the specified @plane
680  */
681 guchar *
682 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
683 {
684     g_return_val_if_fail(image != NULL, NULL);
685     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
686     g_return_val_if_fail(plane < image->image.num_planes, NULL);
687
688     return image->image_data + image->image.offsets[plane];
689 }
690
691 /**
692  * gst_vaapi_image_get_pitch:
693  * @image: a #GstVaapiImage
694  * @plane: the requested plane number
695  *
696  * Retrieves the line size (stride) of the specified @plane. The
697  * @image must be mapped for this function to work properly.
698  *
699  * Return value: the line size (stride) of the specified plane
700  */
701 guint
702 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
703 {
704     g_return_val_if_fail(image != NULL, 0);
705     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
706     g_return_val_if_fail(plane < image->image.num_planes, 0);
707
708     return image->image.pitches[plane];
709 }
710
711 /**
712  * gst_vaapi_image_get_data_size:
713  * @image: a #GstVaapiImage
714  *
715  * Retrieves the underlying image data size. This function could be
716  * used to determine whether the image has a compatible layout with
717  * another image structure.
718  *
719  * Return value: the whole image data size of the @image
720  */
721 guint
722 gst_vaapi_image_get_data_size(GstVaapiImage *image)
723 {
724     g_return_val_if_fail(image != NULL, 0);
725
726     return image->image.data_size;
727 }
728
729 #if GST_CHECK_VERSION(1,0,0)
730 #include <gst/video/gstvideometa.h>
731
732 static gboolean
733 init_image_from_video_meta(GstVaapiImageRaw *raw_image, GstVideoMeta *vmeta)
734 {
735     GST_FIXME("map from GstVideoMeta + add fini_image_from_buffer()");
736     return FALSE;
737 }
738
739 static gboolean
740 init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
741 {
742     GstVideoMeta * const vmeta = gst_buffer_get_video_meta(buffer);
743
744     return vmeta ? init_image_from_video_meta(raw_image, vmeta) : FALSE;
745 }
746 #else
747 static gboolean
748 init_image_from_buffer(GstVaapiImageRaw *raw_image, GstBuffer *buffer)
749 {
750     GstCaps *caps;
751     guchar *data;
752     guint32 data_size;
753     GstVideoInfo vi;
754     guint i, frame_size;
755
756     data      = GST_BUFFER_DATA(buffer);
757     data_size = GST_BUFFER_SIZE(buffer);
758     caps      = GST_BUFFER_CAPS(buffer);
759
760     if (!caps)
761         return FALSE;
762
763     if (!gst_video_info_from_caps(&vi, caps))
764         goto error_unsupported_caps;
765
766     /* Check for compatible data size */
767     frame_size = GST_VIDEO_INFO_SIZE(&vi);
768     if (frame_size != data_size)
769         goto error_incompatible_size;
770
771     raw_image->format = GST_VIDEO_INFO_FORMAT(&vi);
772     raw_image->width  = GST_VIDEO_INFO_WIDTH(&vi);
773     raw_image->height = GST_VIDEO_INFO_HEIGHT(&vi);
774
775     raw_image->num_planes = GST_VIDEO_INFO_N_PLANES(&vi);
776     for (i = 0; i < raw_image->num_planes; i++) {
777         raw_image->pixels[i] = data + GST_VIDEO_INFO_PLANE_OFFSET(&vi, i);
778         raw_image->stride[i] = GST_VIDEO_INFO_PLANE_STRIDE(&vi, i);
779     }
780     return TRUE;
781
782     /* ERRORS */
783 error_unsupported_caps:
784     GST_ERROR("unsupported caps %" GST_PTR_FORMAT, caps);
785     return FALSE;
786 error_incompatible_size:
787     GST_ERROR("incompatible frame size (%u) with buffer size (%u)",
788               frame_size, data_size);
789     return FALSE;
790 }
791 #endif
792
793 /* Copy N lines of an image */
794 static inline void
795 memcpy_pic(
796     guchar       *dst,
797     guint         dst_stride,
798     const guchar *src,
799     guint         src_stride,
800     guint         len,
801     guint         height
802 )
803 {
804     guint i;
805
806     for (i = 0; i < height; i++)  {
807         memcpy(dst, src, len);
808         dst += dst_stride;
809         src += src_stride;
810     }
811 }
812
813 /* Copy NV12 images */
814 static void
815 copy_image_NV12(
816     GstVaapiImageRaw        *dst_image,
817     GstVaapiImageRaw        *src_image,
818     const GstVaapiRectangle *rect
819 )
820 {
821     guchar *dst, *src;
822     guint dst_stride, src_stride;
823
824     /* Y plane */
825     dst_stride = dst_image->stride[0];
826     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
827     src_stride = src_image->stride[0];
828     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
829     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
830
831     /* UV plane */
832     dst_stride = dst_image->stride[1];
833     dst = dst_image->pixels[1] + (rect->y / 2) * dst_stride + (rect->x & -2);
834     src_stride = src_image->stride[1];
835     src = src_image->pixels[1] + (rect->y / 2) * src_stride + (rect->x & -2);
836     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height / 2);
837 }
838
839 /* Copy YV12 images */
840 static void
841 copy_image_YV12(
842     GstVaapiImageRaw        *dst_image,
843     GstVaapiImageRaw        *src_image,
844     const GstVaapiRectangle *rect
845 )
846 {
847     guchar *dst, *src;
848     guint dst_stride, src_stride;
849     guint i, x, y, w, h;
850
851     /* Y plane */
852     dst_stride = dst_image->stride[0];
853     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
854     src_stride = src_image->stride[0];
855     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
856     memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height);
857
858     /* U/V planes */
859     x = rect->x / 2;
860     y = rect->y / 2;
861     w = rect->width / 2;
862     h = rect->height / 2;
863     for (i = 1; i < dst_image->num_planes; i++) {
864         dst_stride = dst_image->stride[i];
865         dst = dst_image->pixels[i] + y * dst_stride + x;
866         src_stride = src_image->stride[i];
867         src = src_image->pixels[i] + y * src_stride + x;
868         memcpy_pic(dst, dst_stride, src, src_stride, w, h);
869     }
870 }
871
872 /* Copy YUY2 images */
873 static void
874 copy_image_YUY2(
875     GstVaapiImageRaw        *dst_image,
876     GstVaapiImageRaw        *src_image,
877     const GstVaapiRectangle *rect
878 )
879 {
880     guchar *dst, *src;
881     guint dst_stride, src_stride;
882
883     /* YUV 4:2:2, full vertical resolution */
884     dst_stride = dst_image->stride[0];
885     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x * 2;
886     src_stride = src_image->stride[0];
887     src = src_image->pixels[0] + rect->y * src_stride + rect->x * 2;
888     memcpy_pic(dst, dst_stride, src, src_stride, rect->width * 2, rect->height);
889 }
890
891 /* Copy RGBA images */
892 static void
893 copy_image_RGBA(
894     GstVaapiImageRaw        *dst_image,
895     GstVaapiImageRaw        *src_image,
896     const GstVaapiRectangle *rect
897 )
898 {
899     guchar *dst, *src;
900     guint dst_stride, src_stride;
901
902     dst_stride = dst_image->stride[0];
903     dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x;
904     src_stride = src_image->stride[0];
905     src = src_image->pixels[0] + rect->y * src_stride + rect->x;
906     memcpy_pic(dst, dst_stride, src, src_stride, 4 * rect->width, rect->height);
907 }
908
909 static gboolean
910 copy_image(
911     GstVaapiImageRaw        *dst_image,
912     GstVaapiImageRaw        *src_image,
913     const GstVaapiRectangle *rect
914 )
915 {
916     GstVaapiRectangle default_rect;
917
918     if (dst_image->format != src_image->format ||
919         dst_image->width  != src_image->width  ||
920         dst_image->height != src_image->height)
921         return FALSE;
922
923     if (rect) {
924         if (rect->x >= src_image->width ||
925             rect->x + rect->width > src_image->width ||
926             rect->y >= src_image->height ||
927             rect->y + rect->height > src_image->height)
928             return FALSE;
929     }
930     else {
931         default_rect.x      = 0;
932         default_rect.y      = 0;
933         default_rect.width  = src_image->width;
934         default_rect.height = src_image->height;
935         rect                = &default_rect;
936     }
937
938     switch (dst_image->format) {
939     case GST_VIDEO_FORMAT_NV12:
940         copy_image_NV12(dst_image, src_image, rect);
941         break;
942     case GST_VIDEO_FORMAT_YV12:
943     case GST_VIDEO_FORMAT_I420:
944         copy_image_YV12(dst_image, src_image, rect);
945         break;
946     case GST_VIDEO_FORMAT_YUY2:
947     case GST_VIDEO_FORMAT_UYVY:
948         copy_image_YUY2(dst_image, src_image, rect);
949         break;
950     case GST_VIDEO_FORMAT_ARGB:
951     case GST_VIDEO_FORMAT_RGBA:
952     case GST_VIDEO_FORMAT_ABGR:
953     case GST_VIDEO_FORMAT_BGRA:
954         copy_image_RGBA(dst_image, src_image, rect);
955         break;
956     default:
957         GST_ERROR("unsupported image format for copy");
958         return FALSE;
959     }
960     return TRUE;
961 }
962
963 /**
964  * gst_vaapi_image_get_buffer:
965  * @image: a #GstVaapiImage
966  * @buffer: a #GstBuffer
967  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
968  *   whole image
969  *
970  * Transfers pixels data contained in the @image into the #GstBuffer.
971  * Both image structures shall have the same format.
972  *
973  * Return value: %TRUE on success
974  */
975 gboolean
976 gst_vaapi_image_get_buffer(
977     GstVaapiImage     *image,
978     GstBuffer         *buffer,
979     GstVaapiRectangle *rect
980 )
981 {
982     GstVaapiImageRaw dst_image, src_image;
983     gboolean success;
984
985     g_return_val_if_fail(image != NULL, FALSE);
986     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
987
988     if (!init_image_from_buffer(&dst_image, buffer))
989         return FALSE;
990     if (dst_image.format != image->format)
991         return FALSE;
992     if (dst_image.width != image->width || dst_image.height != image->height)
993         return FALSE;
994
995     if (!_gst_vaapi_image_map(image, &src_image))
996         return FALSE;
997
998     success = copy_image(&dst_image, &src_image, rect);
999
1000     if (!_gst_vaapi_image_unmap(image))
1001         return FALSE;
1002
1003     return success;
1004 }
1005
1006 /**
1007  * gst_vaapi_image_get_raw:
1008  * @image: a #GstVaapiImage
1009  * @dst_image: a #GstVaapiImageRaw
1010  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1011  *   whole image
1012  *
1013  * Transfers pixels data contained in the @image into the #GstVaapiImageRaw.
1014  * Both image structures shall have the same format.
1015  *
1016  * Return value: %TRUE on success
1017  */
1018 gboolean
1019 gst_vaapi_image_get_raw(
1020     GstVaapiImage     *image,
1021     GstVaapiImageRaw  *dst_image,
1022     GstVaapiRectangle *rect
1023 )
1024 {
1025     GstVaapiImageRaw src_image;
1026     gboolean success;
1027
1028     g_return_val_if_fail(image != NULL, FALSE);
1029
1030     if (!_gst_vaapi_image_map(image, &src_image))
1031         return FALSE;
1032
1033     success = copy_image(dst_image, &src_image, rect);
1034
1035     if (!_gst_vaapi_image_unmap(image))
1036         return FALSE;
1037
1038     return success;
1039 }
1040
1041 /**
1042  * gst_vaapi_image_update_from_buffer:
1043  * @image: a #GstVaapiImage
1044  * @buffer: a #GstBuffer
1045  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1046  *   whole image
1047  *
1048  * Transfers pixels data contained in the #GstBuffer into the
1049  * @image. Both image structures shall have the same format.
1050  *
1051  * Return value: %TRUE on success
1052  */
1053 gboolean
1054 gst_vaapi_image_update_from_buffer(
1055     GstVaapiImage     *image,
1056     GstBuffer         *buffer,
1057     GstVaapiRectangle *rect
1058 )
1059 {
1060     GstVaapiImageRaw dst_image, src_image;
1061     gboolean success;
1062
1063     g_return_val_if_fail(image != NULL, FALSE);
1064     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
1065
1066     if (!init_image_from_buffer(&src_image, buffer))
1067         return FALSE;
1068     if (src_image.format != image->format)
1069         return FALSE;
1070     if (src_image.width != image->width || src_image.height != image->height)
1071         return FALSE;
1072
1073     if (!_gst_vaapi_image_map(image, &dst_image))
1074         return FALSE;
1075
1076     success = copy_image(&dst_image, &src_image, rect);
1077
1078     if (!_gst_vaapi_image_unmap(image))
1079         return FALSE;
1080
1081     return success;
1082 }
1083
1084 /**
1085  * gst_vaapi_image_update_from_raw:
1086  * @image: a #GstVaapiImage
1087  * @src_image: a #GstVaapiImageRaw
1088  * @buffer: a #GstBuffer
1089  * @rect: a #GstVaapiRectangle expressing a region, or %NULL for the
1090  *   whole image
1091  *
1092  * Transfers pixels data contained in the #GstVaapiImageRaw into the
1093  * @image. Both image structures shall have the same format.
1094  *
1095  * Return value: %TRUE on success
1096  */
1097 gboolean
1098 gst_vaapi_image_update_from_raw(
1099     GstVaapiImage     *image,
1100     GstVaapiImageRaw  *src_image,
1101     GstVaapiRectangle *rect
1102 )
1103 {
1104     GstVaapiImageRaw dst_image;
1105     gboolean success;
1106
1107     g_return_val_if_fail(image != NULL, FALSE);
1108
1109     if (!_gst_vaapi_image_map(image, &dst_image))
1110         return FALSE;
1111
1112     success = copy_image(&dst_image, src_image, rect);
1113
1114     if (!_gst_vaapi_image_unmap(image))
1115         return FALSE;
1116
1117     return success;
1118 }