2 * gstvaapiimage.c - VA image abstraction
4 * gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
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.
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.
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
23 #include "gstvaapiutils.h"
24 #include "gstvaapiimage.h"
25 #include <va/va_backend.h>
28 #include "gstvaapidebug.h"
30 G_DEFINE_TYPE(GstVaapiImage, gst_vaapi_image, G_TYPE_OBJECT);
32 #define GST_VAAPI_IMAGE_GET_PRIVATE(obj) \
33 (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
34 GST_VAAPI_TYPE_IMAGE, \
35 GstVaapiImagePrivate))
37 struct _GstVaapiImagePrivate {
38 GstVaapiDisplay *display;
39 gboolean is_constructed;
42 GstVaapiImageFormat internal_format;
43 GstVaapiImageFormat format;
58 #define SWAP_UINT(a, b) do { \
65 gst_vaapi_image_destroy(GstVaapiImage *image)
67 GstVaapiImagePrivate * const priv = image->priv;
68 VADisplay dpy = gst_vaapi_display_get_display(priv->display);
71 gst_vaapi_image_unmap(image);
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;
81 g_object_unref(priv->display);
87 _gst_vaapi_image_create(GstVaapiImage *image, GstVaapiImageFormat format)
89 GstVaapiImagePrivate * const priv = image->priv;
90 const VAImageFormat *va_format;
93 if (!gst_vaapi_display_has_image_format(priv->display, format))
96 va_format = gst_vaapi_image_format_get_va_format(format);
100 status = vaCreateImage(
101 gst_vaapi_display_get_display(priv->display),
102 (VAImageFormat *)va_format,
107 return (status == VA_STATUS_SUCCESS &&
108 priv->image.format.fourcc == va_format->fourcc);
112 gst_vaapi_image_create(GstVaapiImage *image)
114 GstVaapiImagePrivate * const priv = image->priv;
116 if (_gst_vaapi_image_create(image, priv->format)) {
117 priv->internal_format = priv->format;
121 switch (priv->format) {
122 case GST_VAAPI_IMAGE_I420:
123 priv->internal_format = GST_VAAPI_IMAGE_YV12;
125 case GST_VAAPI_IMAGE_YV12:
126 priv->internal_format = GST_VAAPI_IMAGE_I420;
129 priv->internal_format = 0;
132 if (!priv->internal_format)
134 if (!_gst_vaapi_image_create(image, priv->internal_format))
137 GST_DEBUG("image 0x%08x", priv->image.image_id);
142 gst_vaapi_image_finalize(GObject *object)
144 gst_vaapi_image_destroy(GST_VAAPI_IMAGE(object));
146 G_OBJECT_CLASS(gst_vaapi_image_parent_class)->finalize(object);
150 gst_vaapi_image_set_property(
157 GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
158 GstVaapiImagePrivate * const priv = image->priv;
162 priv->display = g_object_ref(g_value_get_object(value));
165 priv->format = g_value_get_uint(value);
168 priv->width = g_value_get_uint(value);
171 priv->height = g_value_get_uint(value);
174 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
180 gst_vaapi_image_get_property(
187 GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
191 g_value_set_pointer(value, gst_vaapi_image_get_display(image));
194 g_value_set_uint(value, gst_vaapi_image_get_id(image));
197 g_value_set_uint(value, gst_vaapi_image_get_format(image));
200 g_value_set_uint(value, gst_vaapi_image_get_width(image));
203 g_value_set_uint(value, gst_vaapi_image_get_height(image));
206 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
212 gst_vaapi_image_constructed(GObject *object)
214 GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
215 GObjectClass *parent_class;
217 image->priv->is_constructed = gst_vaapi_image_create(image);
219 parent_class = G_OBJECT_CLASS(gst_vaapi_image_parent_class);
220 if (parent_class->constructed)
221 parent_class->constructed(object);
225 gst_vaapi_image_class_init(GstVaapiImageClass *klass)
227 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
229 g_type_class_add_private(klass, sizeof(GstVaapiImagePrivate));
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;
236 g_object_class_install_property
239 g_param_spec_object("display",
241 "GStreamer Va display",
242 GST_VAAPI_TYPE_DISPLAY,
243 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
245 g_object_class_install_property
248 g_param_spec_uint("id",
251 0, G_MAXUINT32, VA_INVALID_ID,
254 g_object_class_install_property
257 g_param_spec_uint("width",
261 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
263 g_object_class_install_property
266 g_param_spec_uint("height",
270 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
272 g_object_class_install_property
275 g_param_spec_uint("format",
279 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
283 gst_vaapi_image_init(GstVaapiImage *image)
285 GstVaapiImagePrivate *priv = GST_VAAPI_IMAGE_GET_PRIVATE(image);
288 priv->display = NULL;
289 priv->image_data = NULL;
294 memset(&priv->image, 0, sizeof(priv->image));
295 priv->image.image_id = VA_INVALID_ID;
296 priv->image.buf = VA_INVALID_ID;
301 GstVaapiDisplay *display,
302 GstVaapiImageFormat format,
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);
311 GST_DEBUG("format %" GST_FOURCC_FORMAT ", size %ux%u",
312 GST_FOURCC_ARGS(format), width, height);
314 return g_object_new(GST_VAAPI_TYPE_IMAGE,
323 gst_vaapi_image_get_id(GstVaapiImage *image)
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);
328 return image->priv->image.image_id;
332 gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
334 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
335 g_return_val_if_fail(image->priv->is_constructed, FALSE);
340 *va_image = image->priv->image;
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))
348 SWAP_UINT(va_image->offsets[1], va_image->offsets[2]);
349 SWAP_UINT(va_image->pitches[1], va_image->pitches[2]);
355 gst_vaapi_image_get_display(GstVaapiImage *image)
357 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
358 g_return_val_if_fail(image->priv->is_constructed, FALSE);
360 return image->priv->display;
364 gst_vaapi_image_get_format(GstVaapiImage *image)
366 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
367 g_return_val_if_fail(image->priv->is_constructed, FALSE);
369 return image->priv->format;
373 gst_vaapi_image_get_width(GstVaapiImage *image)
375 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
376 g_return_val_if_fail(image->priv->is_constructed, FALSE);
378 return image->priv->width;
382 gst_vaapi_image_get_height(GstVaapiImage *image)
384 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
385 g_return_val_if_fail(image->priv->is_constructed, FALSE);
387 return image->priv->height;
391 gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
393 g_return_if_fail(GST_VAAPI_IS_IMAGE(image));
394 g_return_if_fail(image->priv->is_constructed);
397 *pwidth = image->priv->width;
400 *pheight = image->priv->height;
404 gst_vaapi_image_is_linear(GstVaapiImage *image)
407 guint i, width, height, width2, height2, data_size;
409 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
410 g_return_val_if_fail(image->priv->is_constructed, FALSE);
412 if (!gst_vaapi_image_get_image(image, &va_image))
415 for (i = 1; i < va_image.num_planes; i++)
416 if (va_image.offsets[i] < va_image.offsets[i - 1])
419 width = image->priv->width;
420 height = image->priv->height;
421 width2 = (width + 1) / 2;
422 height2 = (height + 1) / 2;
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;
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;
437 g_error("FIXME: incomplete formats");
440 return va_image.data_size == data_size;
443 static inline gboolean
444 _gst_vaapi_image_is_mapped(GstVaapiImage *image)
446 return image->priv->image_data != NULL;
450 gst_vaapi_image_is_mapped(GstVaapiImage *image)
452 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
453 g_return_val_if_fail(image->priv->is_constructed, FALSE);
455 return _gst_vaapi_image_is_mapped(image);
459 gst_vaapi_image_map(GstVaapiImage *image)
464 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
465 g_return_val_if_fail(image->priv->is_constructed, FALSE);
467 if (_gst_vaapi_image_is_mapped(image))
470 status = vaMapBuffer(
471 gst_vaapi_display_get_display(image->priv->display),
472 image->priv->image.buf,
475 if (!vaapi_check_status(status, "vaMapBuffer()"))
478 image->priv->image_data = image_data;
483 gst_vaapi_image_unmap(GstVaapiImage *image)
487 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
488 g_return_val_if_fail(image->priv->is_constructed, FALSE);
490 if (!_gst_vaapi_image_is_mapped(image))
493 status = vaUnmapBuffer(
494 gst_vaapi_display_get_display(image->priv->display),
495 image->priv->image.buf
497 if (!vaapi_check_status(status, "vaUnmapBuffer()"))
500 image->priv->image_data = NULL;
505 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
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);
511 return image->priv->image.num_planes;
515 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
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);
522 return image->priv->image_data + image->priv->image.offsets[plane];
526 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
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);
533 return image->priv->image.pitches[plane];
537 gst_vaapi_image_get_data_size(GstVaapiImage *image)
539 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
540 g_return_val_if_fail(image->priv->is_constructed, FALSE);
542 return image->priv->image.data_size;
546 gst_vaapi_image_update_from_buffer(GstVaapiImage *image, GstBuffer *buffer)
548 GstVaapiImagePrivate *priv;
549 GstStructure *structure;
551 GstVaapiImageFormat format;
553 guint offsets[3], pitches[3], widths[3], heights[3];
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);
564 data = GST_BUFFER_DATA(buffer);
565 data_size = GST_BUFFER_SIZE(buffer);
566 caps = GST_BUFFER_CAPS(buffer);
571 format = gst_vaapi_image_format_from_caps(caps);
572 if (format != priv->format)
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)));
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)
587 if (!gst_vaapi_image_map(image))
590 if (format == priv->internal_format && data_size == priv->image.data_size)
591 memcpy(priv->image_data, data, data_size);
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;
598 case GST_VAAPI_IMAGE_NV12:
600 pitches[0] = GST_ROUND_UP_4(width);
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];
609 case GST_VAAPI_IMAGE_YV12:
610 case GST_VAAPI_IMAGE_I420:
612 pitches[0] = GST_ROUND_UP_4(width);
615 offsets[1] = offsets[0] + height * pitches[0];
616 pitches[1] = GST_ROUND_UP_4(GST_ROUND_UP_2(width) / 2);
618 heights[1] = height2;
619 offsets[2] = offsets[1] + height2 * pitches[1];
620 pitches[2] = pitches[1];
622 heights[2] = height2;
623 size2 = offsets[2] + height2 * pitches[2];
625 case GST_VAAPI_IMAGE_ARGB:
626 case GST_VAAPI_IMAGE_RGBA:
627 case GST_VAAPI_IMAGE_ABGR:
628 case GST_VAAPI_IMAGE_BGRA:
630 pitches[0] = width * 4;
631 widths [0] = width * 4;
633 size2 = offsets[0] + height * pitches[0];
636 g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
637 GST_FOURCC_ARGS(format));
640 if (size2 != data_size)
641 g_error("data_size mismatch %d / %u", size2, data_size);
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];
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]);
662 dst += priv->image.pitches[i];
667 if (!gst_vaapi_image_unmap(image))