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