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