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