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