Add VA display locking utilities.
[profile/ivi/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapiimage.c
1 /*
2  *  gstvaapiimage.c - VA image abstraction
3  *
4  *  gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
19  */
20
21 #include "config.h"
22 #include <string.h>
23 #include "gstvaapiutils.h"
24 #include "gstvaapiimage.h"
25 #include <va/va_backend.h>
26
27 #define DEBUG 1
28 #include "gstvaapidebug.h"
29
30 G_DEFINE_TYPE(GstVaapiImage, gst_vaapi_image, G_TYPE_OBJECT);
31
32 #define GST_VAAPI_IMAGE_GET_PRIVATE(obj)                \
33     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                 \
34                                  GST_VAAPI_TYPE_IMAGE,  \
35                                  GstVaapiImagePrivate))
36
37 struct _GstVaapiImagePrivate {
38     GstVaapiDisplay    *display;
39     gboolean            is_constructed;
40     VAImage             image;
41     guchar             *image_data;
42     GstVaapiImageFormat internal_format;
43     GstVaapiImageFormat format;
44     guint               width;
45     guint               height;
46 };
47
48 enum {
49     PROP_0,
50
51     PROP_DISPLAY,
52     PROP_IMAGE_ID,
53     PROP_FORMAT,
54     PROP_WIDTH,
55     PROP_HEIGHT
56 };
57
58 #define SWAP_UINT(a, b) do { \
59         unsigned int v = a;  \
60         a = b;               \
61         b = v;               \
62     } while (0)
63
64 static void
65 gst_vaapi_image_destroy(GstVaapiImage *image)
66 {
67     GstVaapiImagePrivate * const priv = image->priv;
68     VAStatus status;
69
70     gst_vaapi_image_unmap(image);
71
72     if (priv->image.image_id != VA_INVALID_ID) {
73         GST_VAAPI_DISPLAY_LOCK(priv->display);
74         status = vaDestroyImage(
75             GST_VAAPI_DISPLAY_VADISPLAY(priv->display),
76             priv->image.image_id
77         );
78         GST_VAAPI_DISPLAY_UNLOCK(priv->display);
79         if (!vaapi_check_status(status, "vaDestroyImage()"))
80             g_warning("failed to destroy image 0x%08x\n", priv->image.image_id);
81         priv->image.image_id = VA_INVALID_ID;
82     }
83
84     if (priv->display) {
85         g_object_unref(priv->display);
86         priv->display = NULL;
87     }
88 }
89
90 static gboolean
91 _gst_vaapi_image_create(GstVaapiImage *image, GstVaapiImageFormat format)
92 {
93     GstVaapiImagePrivate * const priv = image->priv;
94     const VAImageFormat *va_format;
95     VAStatus status;
96
97     if (!gst_vaapi_display_has_image_format(priv->display, format))
98         return FALSE;
99
100     va_format = gst_vaapi_image_format_get_va_format(format);
101     if (!va_format)
102         return FALSE;
103
104     GST_VAAPI_DISPLAY_LOCK(priv->display);
105     status = vaCreateImage(
106         GST_VAAPI_DISPLAY_VADISPLAY(priv->display),
107         (VAImageFormat *)va_format,
108         priv->width,
109         priv->height,
110         &priv->image
111     );
112     GST_VAAPI_DISPLAY_UNLOCK(priv->display);
113     return (status == VA_STATUS_SUCCESS &&
114             priv->image.format.fourcc == va_format->fourcc);
115 }
116
117 static gboolean
118 gst_vaapi_image_create(GstVaapiImage *image)
119 {
120     GstVaapiImagePrivate * const priv = image->priv;
121
122     if (_gst_vaapi_image_create(image, priv->format)) {
123         priv->internal_format = priv->format;
124         return TRUE;
125     }
126
127     switch (priv->format) {
128     case GST_VAAPI_IMAGE_I420:
129         priv->internal_format = GST_VAAPI_IMAGE_YV12;
130         break;
131     case GST_VAAPI_IMAGE_YV12:
132         priv->internal_format = GST_VAAPI_IMAGE_I420;
133         break;
134     default:
135         priv->internal_format = 0;
136         break;
137     }
138     if (!priv->internal_format)
139         return FALSE;
140     if (!_gst_vaapi_image_create(image, priv->internal_format))
141         return FALSE;
142
143     GST_DEBUG("image 0x%08x", priv->image.image_id);
144     return TRUE;
145 }
146
147 static void
148 gst_vaapi_image_finalize(GObject *object)
149 {
150     gst_vaapi_image_destroy(GST_VAAPI_IMAGE(object));
151
152     G_OBJECT_CLASS(gst_vaapi_image_parent_class)->finalize(object);
153 }
154
155 static void
156 gst_vaapi_image_set_property(
157     GObject      *object,
158     guint         prop_id,
159     const GValue *value,
160     GParamSpec   *pspec
161 )
162 {
163     GstVaapiImage        * const image = GST_VAAPI_IMAGE(object);
164     GstVaapiImagePrivate * const priv  = image->priv;
165
166     switch (prop_id) {
167     case PROP_DISPLAY:
168         priv->display = g_object_ref(g_value_get_object(value));
169         break;
170     case PROP_FORMAT:
171         priv->format = g_value_get_uint(value);
172         break;
173     case PROP_WIDTH:
174         priv->width = g_value_get_uint(value);
175         break;
176     case PROP_HEIGHT:
177         priv->height = g_value_get_uint(value);
178         break;
179     default:
180         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
181         break;
182     }
183 }
184
185 static void
186 gst_vaapi_image_get_property(
187     GObject    *object,
188     guint       prop_id,
189     GValue     *value,
190     GParamSpec *pspec
191 )
192 {
193     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
194
195     switch (prop_id) {
196     case PROP_DISPLAY:
197         g_value_set_pointer(value, gst_vaapi_image_get_display(image));
198         break;
199     case PROP_IMAGE_ID:
200         g_value_set_uint(value, gst_vaapi_image_get_id(image));
201         break;
202     case PROP_FORMAT:
203         g_value_set_uint(value, gst_vaapi_image_get_format(image));
204         break;
205     case PROP_WIDTH:
206         g_value_set_uint(value, gst_vaapi_image_get_width(image));
207         break;
208     case PROP_HEIGHT:
209         g_value_set_uint(value, gst_vaapi_image_get_height(image));
210         break;
211     default:
212         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
213         break;
214     }
215 }
216
217 static void
218 gst_vaapi_image_constructed(GObject *object)
219 {
220     GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
221     GObjectClass *parent_class;
222
223     image->priv->is_constructed = gst_vaapi_image_create(image);
224
225     parent_class = G_OBJECT_CLASS(gst_vaapi_image_parent_class);
226     if (parent_class->constructed)
227         parent_class->constructed(object);
228 }
229
230 static void
231 gst_vaapi_image_class_init(GstVaapiImageClass *klass)
232 {
233     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
234
235     g_type_class_add_private(klass, sizeof(GstVaapiImagePrivate));
236
237     object_class->finalize     = gst_vaapi_image_finalize;
238     object_class->set_property = gst_vaapi_image_set_property;
239     object_class->get_property = gst_vaapi_image_get_property;
240     object_class->constructed  = gst_vaapi_image_constructed;
241
242     g_object_class_install_property
243         (object_class,
244          PROP_DISPLAY,
245          g_param_spec_object("display",
246                              "display",
247                              "GStreamer Va display",
248                              GST_VAAPI_TYPE_DISPLAY,
249                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
250
251     g_object_class_install_property
252         (object_class,
253          PROP_IMAGE_ID,
254          g_param_spec_uint("id",
255                            "VA image id",
256                            "VA image id",
257                            0, G_MAXUINT32, VA_INVALID_ID,
258                            G_PARAM_READABLE));
259
260     g_object_class_install_property
261         (object_class,
262          PROP_WIDTH,
263          g_param_spec_uint("width",
264                            "width",
265                            "Image width",
266                            0, G_MAXUINT32, 0,
267                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
268
269     g_object_class_install_property
270         (object_class,
271          PROP_HEIGHT,
272          g_param_spec_uint("height",
273                            "height",
274                            "Image height",
275                            0, G_MAXUINT32, 0,
276                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
277
278     g_object_class_install_property
279         (object_class,
280          PROP_FORMAT,
281          g_param_spec_uint("format",
282                            "format",
283                            "Image format",
284                            0, G_MAXUINT32, 0,
285                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
286 }
287
288 static void
289 gst_vaapi_image_init(GstVaapiImage *image)
290 {
291     GstVaapiImagePrivate *priv = GST_VAAPI_IMAGE_GET_PRIVATE(image);
292
293     image->priv          = priv;
294     priv->display        = NULL;
295     priv->image_data     = NULL;
296     priv->width          = 0;
297     priv->height         = 0;
298     priv->format         = 0;
299
300     memset(&priv->image, 0, sizeof(priv->image));
301     priv->image.image_id = VA_INVALID_ID;
302     priv->image.buf      = VA_INVALID_ID;
303 }
304
305 GstVaapiImage *
306 gst_vaapi_image_new(
307     GstVaapiDisplay    *display,
308     GstVaapiImageFormat format,
309     guint               width,
310     guint               height
311 )
312 {
313     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
314     g_return_val_if_fail(width > 0, NULL);
315     g_return_val_if_fail(height > 0, NULL);
316
317     GST_DEBUG("format %" GST_FOURCC_FORMAT ", size %ux%u",
318               GST_FOURCC_ARGS(format), width, height);
319
320     return g_object_new(GST_VAAPI_TYPE_IMAGE,
321                         "display", display,
322                         "format",  format,
323                         "width",   width,
324                         "height",  height,
325                         NULL);
326 }
327
328 VAImageID
329 gst_vaapi_image_get_id(GstVaapiImage *image)
330 {
331     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), VA_INVALID_ID);
332     g_return_val_if_fail(image->priv->is_constructed, VA_INVALID_ID);
333
334     return image->priv->image.image_id;
335 }
336
337 gboolean
338 gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
339 {
340     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
341     g_return_val_if_fail(image->priv->is_constructed, FALSE);
342
343     if (!va_image)
344         return TRUE;
345
346     *va_image = image->priv->image;
347
348     if (image->priv->format != image->priv->internal_format) {
349         if (!(image->priv->format == GST_VAAPI_IMAGE_I420 &&
350               image->priv->internal_format == GST_VAAPI_IMAGE_YV12) &&
351             !(image->priv->format == GST_VAAPI_IMAGE_YV12 &&
352               image->priv->internal_format == GST_VAAPI_IMAGE_I420))
353             return FALSE;
354         SWAP_UINT(va_image->offsets[1], va_image->offsets[2]);
355         SWAP_UINT(va_image->pitches[1], va_image->pitches[2]);
356     }
357     return TRUE;
358 }
359
360 GstVaapiDisplay *
361 gst_vaapi_image_get_display(GstVaapiImage *image)
362 {
363     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
364     g_return_val_if_fail(image->priv->is_constructed, FALSE);
365
366     return image->priv->display;
367 }
368
369 GstVaapiImageFormat
370 gst_vaapi_image_get_format(GstVaapiImage *image)
371 {
372     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
373     g_return_val_if_fail(image->priv->is_constructed, FALSE);
374
375     return image->priv->format;
376 }
377
378 guint
379 gst_vaapi_image_get_width(GstVaapiImage *image)
380 {
381     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
382     g_return_val_if_fail(image->priv->is_constructed, FALSE);
383
384     return image->priv->width;
385 }
386
387 guint
388 gst_vaapi_image_get_height(GstVaapiImage *image)
389 {
390     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
391     g_return_val_if_fail(image->priv->is_constructed, FALSE);
392
393     return image->priv->height;
394 }
395
396 void
397 gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
398 {
399     g_return_if_fail(GST_VAAPI_IS_IMAGE(image));
400     g_return_if_fail(image->priv->is_constructed);
401
402     if (pwidth)
403         *pwidth = image->priv->width;
404
405     if (pheight)
406         *pheight = image->priv->height;
407 }
408
409 gboolean
410 gst_vaapi_image_is_linear(GstVaapiImage *image)
411 {
412     VAImage va_image;
413     guint i, width, height, width2, height2, data_size;
414
415     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
416     g_return_val_if_fail(image->priv->is_constructed, FALSE);
417
418     if (!gst_vaapi_image_get_image(image, &va_image))
419         return FALSE;
420
421     for (i = 1; i < va_image.num_planes; i++)
422         if (va_image.offsets[i] < va_image.offsets[i - 1])
423             return FALSE;
424
425     width   = image->priv->width;
426     height  = image->priv->height;
427     width2  = (width  + 1) / 2;
428     height2 = (height + 1) / 2;
429
430     switch (image->priv->internal_format) {
431     case GST_VAAPI_IMAGE_NV12:
432     case GST_VAAPI_IMAGE_YV12:
433     case GST_VAAPI_IMAGE_I420:
434         data_size = width * height + 2 * width2 * height2;
435         break;
436     case GST_VAAPI_IMAGE_ARGB:
437     case GST_VAAPI_IMAGE_RGBA:
438     case GST_VAAPI_IMAGE_ABGR:
439     case GST_VAAPI_IMAGE_BGRA:
440         data_size = 4 * width * height;
441         break;
442     default:
443         g_error("FIXME: incomplete formats");
444         break;
445     }
446     return va_image.data_size == data_size;
447 }
448
449 static inline gboolean
450 _gst_vaapi_image_is_mapped(GstVaapiImage *image)
451 {
452     return image->priv->image_data != NULL;
453 }
454
455 gboolean
456 gst_vaapi_image_is_mapped(GstVaapiImage *image)
457 {
458     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
459     g_return_val_if_fail(image->priv->is_constructed, FALSE);
460
461     return _gst_vaapi_image_is_mapped(image);
462 }
463
464 gboolean
465 gst_vaapi_image_map(GstVaapiImage *image)
466 {
467     void *image_data;
468     VAStatus status;
469
470     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
471     g_return_val_if_fail(image->priv->is_constructed, FALSE);
472
473     if (_gst_vaapi_image_is_mapped(image))
474         return TRUE;
475
476     GST_VAAPI_DISPLAY_LOCK(image->priv->display);
477     status = vaMapBuffer(
478         GST_VAAPI_DISPLAY_VADISPLAY(image->priv->display),
479         image->priv->image.buf,
480         &image_data
481     );
482     GST_VAAPI_DISPLAY_UNLOCK(image->priv->display);
483     if (!vaapi_check_status(status, "vaMapBuffer()"))
484         return FALSE;
485
486     image->priv->image_data = image_data;
487     return TRUE;
488 }
489
490 gboolean
491 gst_vaapi_image_unmap(GstVaapiImage *image)
492 {
493     VAStatus status;
494
495     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
496     g_return_val_if_fail(image->priv->is_constructed, FALSE);
497
498     if (!_gst_vaapi_image_is_mapped(image))
499         return FALSE;
500
501     GST_VAAPI_DISPLAY_LOCK(image->priv->display);
502     status = vaUnmapBuffer(
503         GST_VAAPI_DISPLAY_VADISPLAY(image->priv->display),
504         image->priv->image.buf
505     );
506     GST_VAAPI_DISPLAY_UNLOCK(image->priv->display);
507     if (!vaapi_check_status(status, "vaUnmapBuffer()"))
508         return FALSE;
509
510     image->priv->image_data = NULL;
511     return TRUE;
512 }
513
514 guint
515 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
516 {
517     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
518     g_return_val_if_fail(image->priv->is_constructed, FALSE);
519     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
520
521     return image->priv->image.num_planes;
522 }
523
524 guchar *
525 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
526 {
527     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
528     g_return_val_if_fail(image->priv->is_constructed, FALSE);
529     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
530     g_return_val_if_fail(plane < image->priv->image.num_planes, NULL);
531
532     return image->priv->image_data + image->priv->image.offsets[plane];
533 }
534
535 guint
536 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
537 {
538     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
539     g_return_val_if_fail(image->priv->is_constructed, FALSE);
540     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
541     g_return_val_if_fail(plane < image->priv->image.num_planes, 0);
542
543     return image->priv->image.pitches[plane];
544 }
545
546 guint
547 gst_vaapi_image_get_data_size(GstVaapiImage *image)
548 {
549     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
550     g_return_val_if_fail(image->priv->is_constructed, FALSE);
551
552     return image->priv->image.data_size;
553 }
554
555 gboolean
556 gst_vaapi_image_update_from_buffer(GstVaapiImage *image, GstBuffer *buffer)
557 {
558     GstVaapiImagePrivate *priv;
559     GstStructure *structure;
560     GstCaps *caps;
561     GstVaapiImageFormat format;
562     gint width, height;
563     guint offsets[3], pitches[3], widths[3], heights[3];
564     guint i, j;
565     guchar *data;
566     guint32 data_size;
567     gboolean swap_YUV;
568
569     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
570     g_return_val_if_fail(image->priv->is_constructed, FALSE);
571     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
572
573     priv      = image->priv;
574     data      = GST_BUFFER_DATA(buffer);
575     data_size = GST_BUFFER_SIZE(buffer);
576     caps      = GST_BUFFER_CAPS(buffer);
577
578     if (!caps)
579         return FALSE;
580
581     format = gst_vaapi_image_format_from_caps(caps);
582     if (format != priv->format)
583         return FALSE;
584
585     swap_YUV = (priv->format != priv->internal_format &&
586                 ((priv->format == GST_VAAPI_IMAGE_I420 &&
587                   priv->internal_format == GST_VAAPI_IMAGE_YV12) ||
588                  (priv->format == GST_VAAPI_IMAGE_YV12 &&
589                   priv->internal_format == GST_VAAPI_IMAGE_I420)));
590
591     structure = gst_caps_get_structure(caps, 0);
592     gst_structure_get_int(structure, "width",  &width);
593     gst_structure_get_int(structure, "height", &height);
594     if (width != priv->width || height != priv->height)
595         return FALSE;
596
597     if (!gst_vaapi_image_map(image))
598         return FALSE;
599
600     if (format == priv->internal_format && data_size == priv->image.data_size)
601         memcpy(priv->image_data, data, data_size);
602     else {
603         /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
604         const guint width2  = (width  + 1) / 2;
605         const guint height2 = (height + 1) / 2;
606         guint size2;
607         switch (format) {
608         case GST_VAAPI_IMAGE_NV12:
609             offsets[0] = 0;
610             pitches[0] = GST_ROUND_UP_4(width);
611             widths [0] = width;
612             heights[0] = height;
613             offsets[1] = offsets[0] + height * pitches[0];
614             pitches[1] = pitches[0];
615             widths [1] = width2 * 2;
616             heights[1] = height2;
617             size2      = offsets[1] + height2 * pitches[1];
618             break;
619         case GST_VAAPI_IMAGE_YV12:
620         case GST_VAAPI_IMAGE_I420:
621             offsets[0] = 0;
622             pitches[0] = GST_ROUND_UP_4(width);
623             widths [0] = width;
624             heights[0] = height;
625             offsets[1] = offsets[0] + height * pitches[0];
626             pitches[1] = GST_ROUND_UP_4(GST_ROUND_UP_2(width) / 2);
627             widths [1] = width2;
628             heights[1] = height2;
629             offsets[2] = offsets[1] + height2 * pitches[1];
630             pitches[2] = pitches[1];
631             widths [2] = width2;
632             heights[2] = height2;
633             size2      = offsets[2] + height2 * pitches[2];
634             break;
635         case GST_VAAPI_IMAGE_ARGB:
636         case GST_VAAPI_IMAGE_RGBA:
637         case GST_VAAPI_IMAGE_ABGR:
638         case GST_VAAPI_IMAGE_BGRA:
639             offsets[0] = 0;
640             pitches[0] = width * 4;
641             widths [0] = width * 4;
642             heights[0] = height;
643             size2      = offsets[0] + height * pitches[0];
644             break;
645         default:
646             g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
647                     GST_FOURCC_ARGS(format));
648             break;
649         }
650         if (size2 != data_size)
651             g_error("data_size mismatch %d / %u", size2, data_size);
652         if (swap_YUV) {
653             guint offset = offsets[1];
654             guint stride = pitches[1];
655             guint width  = widths [1];
656             guint height = heights[1];
657             offsets[1]   = offsets[2];
658             pitches[1]   = pitches[2];
659             widths [1]   = widths [2];
660             heights[1]   = heights[2];
661             offsets[2]   = offset;
662             pitches[2]   = stride;
663             widths [2]   = width;
664             heights[2]   = height;
665         }
666         for (i = 0; i < priv->image.num_planes; i++) {
667             guchar *src = data + offsets[i];
668             guchar *dst = priv->image_data + priv->image.offsets[i];
669             for (j = 0; j < heights[i]; j++) {
670                 memcpy(dst, src, widths[i]);
671                 src += pitches[i];
672                 dst += priv->image.pitches[i];
673             }
674         }
675     }
676
677     if (!gst_vaapi_image_unmap(image))
678         return FALSE;
679
680     return TRUE;
681 }