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