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