Optimize gst_vaapi_image_is_linear() and simplify gst_vaapi_image_update_from_buffer().
[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         unsigned int 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     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
377     g_return_val_if_fail(width > 0, NULL);
378     g_return_val_if_fail(height > 0, NULL);
379
380     GST_DEBUG("format %" GST_FOURCC_FORMAT ", size %ux%u",
381               GST_FOURCC_ARGS(format), width, height);
382
383     return g_object_new(GST_VAAPI_TYPE_IMAGE,
384                         "display", display,
385                         "format",  format,
386                         "width",   width,
387                         "height",  height,
388                         NULL);
389 }
390
391 VAImageID
392 gst_vaapi_image_get_id(GstVaapiImage *image)
393 {
394     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), VA_INVALID_ID);
395     g_return_val_if_fail(image->priv->is_constructed, VA_INVALID_ID);
396
397     return image->priv->image.image_id;
398 }
399
400 gboolean
401 gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
402 {
403     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
404     g_return_val_if_fail(image->priv->is_constructed, FALSE);
405
406     if (va_image)
407         *va_image = image->priv->image;
408
409     return TRUE;
410 }
411
412 GstVaapiDisplay *
413 gst_vaapi_image_get_display(GstVaapiImage *image)
414 {
415     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
416     g_return_val_if_fail(image->priv->is_constructed, FALSE);
417
418     return image->priv->display;
419 }
420
421 GstVaapiImageFormat
422 gst_vaapi_image_get_format(GstVaapiImage *image)
423 {
424     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
425     g_return_val_if_fail(image->priv->is_constructed, FALSE);
426
427     return image->priv->format;
428 }
429
430 guint
431 gst_vaapi_image_get_width(GstVaapiImage *image)
432 {
433     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
434     g_return_val_if_fail(image->priv->is_constructed, FALSE);
435
436     return image->priv->width;
437 }
438
439 guint
440 gst_vaapi_image_get_height(GstVaapiImage *image)
441 {
442     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
443     g_return_val_if_fail(image->priv->is_constructed, FALSE);
444
445     return image->priv->height;
446 }
447
448 void
449 gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
450 {
451     g_return_if_fail(GST_VAAPI_IS_IMAGE(image));
452     g_return_if_fail(image->priv->is_constructed);
453
454     if (pwidth)
455         *pwidth = image->priv->width;
456
457     if (pheight)
458         *pheight = image->priv->height;
459 }
460
461 gboolean
462 gst_vaapi_image_is_linear(GstVaapiImage *image)
463 {
464     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
465     g_return_val_if_fail(image->priv->is_constructed, FALSE);
466
467     return image->priv->is_linear;
468 }
469
470 static inline gboolean
471 _gst_vaapi_image_is_mapped(GstVaapiImage *image)
472 {
473     return image->priv->image_data != NULL;
474 }
475
476 gboolean
477 gst_vaapi_image_is_mapped(GstVaapiImage *image)
478 {
479     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
480     g_return_val_if_fail(image->priv->is_constructed, FALSE);
481
482     return _gst_vaapi_image_is_mapped(image);
483 }
484
485 gboolean
486 gst_vaapi_image_map(GstVaapiImage *image)
487 {
488     void *image_data;
489     VAStatus status;
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     if (_gst_vaapi_image_is_mapped(image))
495         return TRUE;
496
497     GST_VAAPI_DISPLAY_LOCK(image->priv->display);
498     status = vaMapBuffer(
499         GST_VAAPI_DISPLAY_VADISPLAY(image->priv->display),
500         image->priv->image.buf,
501         &image_data
502     );
503     GST_VAAPI_DISPLAY_UNLOCK(image->priv->display);
504     if (!vaapi_check_status(status, "vaMapBuffer()"))
505         return FALSE;
506
507     image->priv->image_data = image_data;
508     return TRUE;
509 }
510
511 gboolean
512 gst_vaapi_image_unmap(GstVaapiImage *image)
513 {
514     VAStatus status;
515
516     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
517     g_return_val_if_fail(image->priv->is_constructed, FALSE);
518
519     if (!_gst_vaapi_image_is_mapped(image))
520         return FALSE;
521
522     GST_VAAPI_DISPLAY_LOCK(image->priv->display);
523     status = vaUnmapBuffer(
524         GST_VAAPI_DISPLAY_VADISPLAY(image->priv->display),
525         image->priv->image.buf
526     );
527     GST_VAAPI_DISPLAY_UNLOCK(image->priv->display);
528     if (!vaapi_check_status(status, "vaUnmapBuffer()"))
529         return FALSE;
530
531     image->priv->image_data = NULL;
532     return TRUE;
533 }
534
535 guint
536 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
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
542     return image->priv->image.num_planes;
543 }
544
545 guchar *
546 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
547 {
548     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
549     g_return_val_if_fail(image->priv->is_constructed, FALSE);
550     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
551     g_return_val_if_fail(plane < image->priv->image.num_planes, NULL);
552
553     return image->priv->image_data + image->priv->image.offsets[plane];
554 }
555
556 guint
557 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
558 {
559     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
560     g_return_val_if_fail(image->priv->is_constructed, FALSE);
561     g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
562     g_return_val_if_fail(plane < image->priv->image.num_planes, 0);
563
564     return image->priv->image.pitches[plane];
565 }
566
567 guint
568 gst_vaapi_image_get_data_size(GstVaapiImage *image)
569 {
570     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
571     g_return_val_if_fail(image->priv->is_constructed, FALSE);
572
573     return image->priv->image.data_size;
574 }
575
576 gboolean
577 gst_vaapi_image_update_from_buffer(GstVaapiImage *image, GstBuffer *buffer)
578 {
579     GstVaapiImagePrivate *priv;
580     GstStructure *structure;
581     GstCaps *caps;
582     GstVaapiImageFormat format;
583     gint width, height;
584     guint offsets[3], pitches[3], widths[3], heights[3];
585     guint i, j;
586     guchar *data;
587     guint32 data_size;
588
589     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
590     g_return_val_if_fail(image->priv->is_constructed, FALSE);
591     g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
592
593     priv      = image->priv;
594     data      = GST_BUFFER_DATA(buffer);
595     data_size = GST_BUFFER_SIZE(buffer);
596     caps      = GST_BUFFER_CAPS(buffer);
597
598     if (!caps)
599         return FALSE;
600
601     format = gst_vaapi_image_format_from_caps(caps);
602     if (format != priv->format)
603         return FALSE;
604
605     structure = gst_caps_get_structure(caps, 0);
606     gst_structure_get_int(structure, "width",  &width);
607     gst_structure_get_int(structure, "height", &height);
608     if (width != priv->width || height != priv->height)
609         return FALSE;
610
611     if (!gst_vaapi_image_map(image))
612         return FALSE;
613
614     if (priv->is_linear && data_size == priv->image.data_size)
615         memcpy(priv->image_data, data, data_size);
616     else {
617         /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
618         const guint width2  = (width  + 1) / 2;
619         const guint height2 = (height + 1) / 2;
620         guint size2;
621         switch (format) {
622         case GST_VAAPI_IMAGE_NV12:
623             offsets[0] = 0;
624             pitches[0] = GST_ROUND_UP_4(width);
625             widths [0] = width;
626             heights[0] = height;
627             offsets[1] = offsets[0] + height * pitches[0];
628             pitches[1] = pitches[0];
629             widths [1] = width2 * 2;
630             heights[1] = height2;
631             size2      = offsets[1] + height2 * pitches[1];
632             break;
633         case GST_VAAPI_IMAGE_YV12:
634         case GST_VAAPI_IMAGE_I420:
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] = GST_ROUND_UP_4(GST_ROUND_UP_2(width) / 2);
641             widths [1] = width2;
642             heights[1] = height2;
643             offsets[2] = offsets[1] + height2 * pitches[1];
644             pitches[2] = pitches[1];
645             widths [2] = width2;
646             heights[2] = height2;
647             size2      = offsets[2] + height2 * pitches[2];
648             break;
649         case GST_VAAPI_IMAGE_ARGB:
650         case GST_VAAPI_IMAGE_RGBA:
651         case GST_VAAPI_IMAGE_ABGR:
652         case GST_VAAPI_IMAGE_BGRA:
653             offsets[0] = 0;
654             pitches[0] = width * 4;
655             widths [0] = width * 4;
656             heights[0] = height;
657             size2      = offsets[0] + height * pitches[0];
658             break;
659         default:
660             g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
661                     GST_FOURCC_ARGS(format));
662             break;
663         }
664         if (size2 != data_size)
665             g_error("data_size mismatch %d / %u", size2, data_size);
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 }