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 VAImage internal_image;
42 GstVaapiImageFormat internal_format;
43 GstVaapiImageFormat format;
46 guint create_image : 1;
47 guint is_constructed : 1;
62 #define SWAP_UINT(a, b) do { \
69 _gst_vaapi_image_map(GstVaapiImage *image);
72 _gst_vaapi_image_unmap(GstVaapiImage *image);
75 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image);
81 #define VAAPI_TYPE_IMAGE vaapi_image_get_type()
84 vaapi_image_copy(gpointer va_image)
86 return g_slice_dup(VAImage, va_image);
90 vaapi_image_free(gpointer va_image)
92 if (G_LIKELY(va_image))
93 g_slice_free(VAImage, va_image);
97 vaapi_image_get_type(void)
99 static GType type = 0;
101 if (G_UNLIKELY(type == 0))
102 type = g_boxed_type_register_static(
111 vaapi_image_is_linear(const VAImage *va_image)
113 guint i, width, height, width2, height2, data_size;
115 for (i = 1; i < va_image->num_planes; i++)
116 if (va_image->offsets[i] < va_image->offsets[i - 1])
119 width = va_image->width;
120 height = va_image->height;
121 width2 = (width + 1) / 2;
122 height2 = (height + 1) / 2;
124 switch (va_image->format.fourcc) {
125 case VA_FOURCC('N','V','1','2'):
126 case VA_FOURCC('Y','V','1','2'):
127 case VA_FOURCC('I','4','2','0'):
128 data_size = width * height + 2 * width2 * height2;
130 case VA_FOURCC('A','R','G','B'):
131 case VA_FOURCC('R','G','B','A'):
132 case VA_FOURCC('A','B','G','R'):
133 case VA_FOURCC('B','G','R','A'):
134 data_size = 4 * width * height;
137 g_error("FIXME: incomplete formats");
140 return va_image->data_size == data_size;
144 gst_vaapi_image_destroy(GstVaapiImage *image)
146 GstVaapiImagePrivate * const priv = image->priv;
149 _gst_vaapi_image_unmap(image);
151 if (priv->internal_image.image_id != VA_INVALID_ID) {
152 GST_VAAPI_DISPLAY_LOCK(priv->display);
153 status = vaDestroyImage(
154 GST_VAAPI_DISPLAY_VADISPLAY(priv->display),
155 priv->internal_image.image_id
157 GST_VAAPI_DISPLAY_UNLOCK(priv->display);
158 if (!vaapi_check_status(status, "vaDestroyImage()"))
159 g_warning("failed to destroy image 0x%08x\n",
160 priv->internal_image.image_id);
161 priv->internal_image.image_id = VA_INVALID_ID;
165 g_object_unref(priv->display);
166 priv->display = NULL;
171 _gst_vaapi_image_create(GstVaapiImage *image, GstVaapiImageFormat format)
173 GstVaapiImagePrivate * const priv = image->priv;
174 const VAImageFormat *va_format;
177 if (!gst_vaapi_display_has_image_format(priv->display, format))
180 va_format = gst_vaapi_image_format_get_va_format(format);
184 GST_VAAPI_DISPLAY_LOCK(priv->display);
185 status = vaCreateImage(
186 GST_VAAPI_DISPLAY_VADISPLAY(priv->display),
187 (VAImageFormat *)va_format,
190 &priv->internal_image
192 GST_VAAPI_DISPLAY_UNLOCK(priv->display);
193 if (status != VA_STATUS_SUCCESS ||
194 priv->internal_image.format.fourcc != va_format->fourcc)
197 priv->internal_format = format;
202 gst_vaapi_image_create(GstVaapiImage *image)
204 GstVaapiImagePrivate * const priv = image->priv;
205 GstVaapiImageFormat format = priv->format;
206 const VAImageFormat *va_format;
208 if (!priv->create_image)
209 return (priv->image.image_id != VA_INVALID_ID &&
210 priv->image.buf != VA_INVALID_ID);
212 if (!_gst_vaapi_image_create(image, format)) {
214 case GST_VAAPI_IMAGE_I420:
215 format = GST_VAAPI_IMAGE_YV12;
217 case GST_VAAPI_IMAGE_YV12:
218 format = GST_VAAPI_IMAGE_I420;
224 if (!format || !_gst_vaapi_image_create(image, format))
227 priv->image = priv->internal_image;
229 if (priv->format != priv->internal_format) {
230 switch (priv->format) {
231 case GST_VAAPI_IMAGE_YV12:
232 case GST_VAAPI_IMAGE_I420:
233 va_format = gst_vaapi_image_format_get_va_format(priv->format);
236 priv->image.format = *va_format;
237 SWAP_UINT(priv->image.offsets[1], priv->image.offsets[2]);
238 SWAP_UINT(priv->image.pitches[1], priv->image.pitches[2]);
245 GST_DEBUG("image 0x%08x", priv->image.image_id);
246 priv->is_linear = vaapi_image_is_linear(&priv->image);
251 gst_vaapi_image_finalize(GObject *object)
253 gst_vaapi_image_destroy(GST_VAAPI_IMAGE(object));
255 G_OBJECT_CLASS(gst_vaapi_image_parent_class)->finalize(object);
259 gst_vaapi_image_set_property(
266 GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
267 GstVaapiImagePrivate * const priv = image->priv;
271 priv->display = g_object_ref(g_value_get_object(value));
274 const VAImage * const va_image = g_value_get_boxed(value);
276 _gst_vaapi_image_set_image(image, va_image);
280 priv->format = g_value_get_uint(value);
283 priv->width = g_value_get_uint(value);
286 priv->height = g_value_get_uint(value);
289 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
295 gst_vaapi_image_get_property(
302 GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
306 g_value_set_pointer(value, gst_vaapi_image_get_display(image));
309 g_value_set_boxed(value, &image->priv->image);
312 g_value_set_uint(value, gst_vaapi_image_get_id(image));
315 g_value_set_uint(value, gst_vaapi_image_get_format(image));
318 g_value_set_uint(value, gst_vaapi_image_get_width(image));
321 g_value_set_uint(value, gst_vaapi_image_get_height(image));
324 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
330 gst_vaapi_image_constructed(GObject *object)
332 GstVaapiImage * const image = GST_VAAPI_IMAGE(object);
333 GObjectClass *parent_class;
335 image->priv->is_constructed = gst_vaapi_image_create(image);
337 parent_class = G_OBJECT_CLASS(gst_vaapi_image_parent_class);
338 if (parent_class->constructed)
339 parent_class->constructed(object);
343 gst_vaapi_image_class_init(GstVaapiImageClass *klass)
345 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
347 g_type_class_add_private(klass, sizeof(GstVaapiImagePrivate));
349 object_class->finalize = gst_vaapi_image_finalize;
350 object_class->set_property = gst_vaapi_image_set_property;
351 object_class->get_property = gst_vaapi_image_get_property;
352 object_class->constructed = gst_vaapi_image_constructed;
354 g_object_class_install_property
357 g_param_spec_object("display",
359 "GStreamer Va display",
360 GST_VAAPI_TYPE_DISPLAY,
361 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
363 g_object_class_install_property
366 g_param_spec_boxed("image",
370 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
372 g_object_class_install_property
375 g_param_spec_uint("id",
378 0, G_MAXUINT32, VA_INVALID_ID,
381 g_object_class_install_property
384 g_param_spec_uint("width",
388 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
390 g_object_class_install_property
393 g_param_spec_uint("height",
397 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
399 g_object_class_install_property
402 g_param_spec_uint("format",
406 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
410 gst_vaapi_image_init(GstVaapiImage *image)
412 GstVaapiImagePrivate *priv = GST_VAAPI_IMAGE_GET_PRIVATE(image);
415 priv->display = NULL;
416 priv->image_data = NULL;
419 priv->internal_format = 0;
421 priv->create_image = TRUE;
422 priv->is_constructed = FALSE;
423 priv->is_linear = FALSE;
425 memset(&priv->internal_image, 0, sizeof(priv->internal_image));
426 priv->internal_image.image_id = VA_INVALID_ID;
427 priv->internal_image.buf = VA_INVALID_ID;
429 memset(&priv->image, 0, sizeof(priv->image));
430 priv->image.image_id = VA_INVALID_ID;
431 priv->image.buf = VA_INVALID_ID;
436 GstVaapiDisplay *display,
437 GstVaapiImageFormat format,
442 GstVaapiImage *image;
444 g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
445 g_return_val_if_fail(width > 0, NULL);
446 g_return_val_if_fail(height > 0, NULL);
448 GST_DEBUG("format %" GST_FOURCC_FORMAT ", size %ux%u",
449 GST_FOURCC_ARGS(format), width, height);
451 image = g_object_new(
452 GST_VAAPI_TYPE_IMAGE,
462 if (!image->priv->is_constructed) {
463 g_object_unref(image);
470 gst_vaapi_image_new_with_image(GstVaapiDisplay *display, VAImage *va_image)
472 GstVaapiImage *image;
474 g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
475 g_return_val_if_fail(va_image, NULL);
476 g_return_val_if_fail(va_image->image_id != VA_INVALID_ID, NULL);
477 g_return_val_if_fail(va_image->buf != VA_INVALID_ID, NULL);
479 GST_DEBUG("VA image 0x%08x, format %" GST_FOURCC_FORMAT ", size %ux%u",
481 GST_FOURCC_ARGS(va_image->format.fourcc),
482 va_image->width, va_image->height);
484 image = g_object_new(
485 GST_VAAPI_TYPE_IMAGE,
493 if (!image->priv->is_constructed) {
494 g_object_unref(image);
501 gst_vaapi_image_get_id(GstVaapiImage *image)
503 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), VA_INVALID_ID);
504 g_return_val_if_fail(image->priv->is_constructed, VA_INVALID_ID);
506 return image->priv->image.image_id;
510 gst_vaapi_image_get_image(GstVaapiImage *image, VAImage *va_image)
512 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
513 g_return_val_if_fail(image->priv->is_constructed, FALSE);
516 *va_image = image->priv->image;
522 _gst_vaapi_image_set_image(GstVaapiImage *image, const VAImage *va_image)
524 GstVaapiImagePrivate * const priv = image->priv;
525 GstVaapiImageFormat format;
526 VAImage alt_va_image;
527 const VAImageFormat *alt_va_format;
532 format = gst_vaapi_image_format(&va_image->format);
536 priv->create_image = FALSE;
537 priv->internal_image = *va_image;
538 priv->internal_format = format;
539 priv->is_linear = vaapi_image_is_linear(va_image);
540 priv->image = *va_image;
541 priv->format = format;
542 priv->width = va_image->width;
543 priv->height = va_image->height;
545 /* Try to linearize image */
546 if (!priv->is_linear) {
548 case GST_VAAPI_IMAGE_I420:
549 format = GST_VAAPI_IMAGE_YV12;
551 case GST_VAAPI_IMAGE_YV12:
552 format = GST_VAAPI_IMAGE_I420;
559 (alt_va_format = gst_vaapi_image_format_get_va_format(format))) {
560 alt_va_image = *va_image;
561 alt_va_image.format = *alt_va_format;
562 SWAP_UINT(alt_va_image.offsets[1], alt_va_image.offsets[2]);
563 SWAP_UINT(alt_va_image.pitches[1], alt_va_image.pitches[2]);
564 if (vaapi_image_is_linear(&alt_va_image)) {
565 priv->image = alt_va_image;
566 priv->format = format;
567 priv->is_linear = TRUE;
568 GST_DEBUG("linearized image to %" GST_FOURCC_FORMAT " format",
569 GST_FOURCC_ARGS(format));
577 gst_vaapi_image_get_display(GstVaapiImage *image)
579 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
580 g_return_val_if_fail(image->priv->is_constructed, FALSE);
582 return image->priv->display;
586 gst_vaapi_image_get_format(GstVaapiImage *image)
588 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
589 g_return_val_if_fail(image->priv->is_constructed, FALSE);
591 return image->priv->format;
595 gst_vaapi_image_get_width(GstVaapiImage *image)
597 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
598 g_return_val_if_fail(image->priv->is_constructed, FALSE);
600 return image->priv->width;
604 gst_vaapi_image_get_height(GstVaapiImage *image)
606 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
607 g_return_val_if_fail(image->priv->is_constructed, FALSE);
609 return image->priv->height;
613 gst_vaapi_image_get_size(GstVaapiImage *image, guint *pwidth, guint *pheight)
615 g_return_if_fail(GST_VAAPI_IS_IMAGE(image));
616 g_return_if_fail(image->priv->is_constructed);
619 *pwidth = image->priv->width;
622 *pheight = image->priv->height;
626 gst_vaapi_image_is_linear(GstVaapiImage *image)
628 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
629 g_return_val_if_fail(image->priv->is_constructed, FALSE);
631 return image->priv->is_linear;
634 static inline gboolean
635 _gst_vaapi_image_is_mapped(GstVaapiImage *image)
637 return image->priv->image_data != NULL;
641 gst_vaapi_image_is_mapped(GstVaapiImage *image)
643 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
644 g_return_val_if_fail(image->priv->is_constructed, FALSE);
646 return _gst_vaapi_image_is_mapped(image);
650 gst_vaapi_image_map(GstVaapiImage *image)
652 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
653 g_return_val_if_fail(image->priv->is_constructed, FALSE);
655 return _gst_vaapi_image_map(image);
659 _gst_vaapi_image_map(GstVaapiImage *image)
664 if (_gst_vaapi_image_is_mapped(image))
667 GST_VAAPI_DISPLAY_LOCK(image->priv->display);
668 status = vaMapBuffer(
669 GST_VAAPI_DISPLAY_VADISPLAY(image->priv->display),
670 image->priv->image.buf,
673 GST_VAAPI_DISPLAY_UNLOCK(image->priv->display);
674 if (!vaapi_check_status(status, "vaMapBuffer()"))
677 image->priv->image_data = image_data;
682 gst_vaapi_image_unmap(GstVaapiImage *image)
684 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
685 g_return_val_if_fail(image->priv->is_constructed, FALSE);
687 return _gst_vaapi_image_unmap(image);
691 _gst_vaapi_image_unmap(GstVaapiImage *image)
695 if (!_gst_vaapi_image_is_mapped(image))
698 GST_VAAPI_DISPLAY_LOCK(image->priv->display);
699 status = vaUnmapBuffer(
700 GST_VAAPI_DISPLAY_VADISPLAY(image->priv->display),
701 image->priv->image.buf
703 GST_VAAPI_DISPLAY_UNLOCK(image->priv->display);
704 if (!vaapi_check_status(status, "vaUnmapBuffer()"))
707 image->priv->image_data = NULL;
712 gst_vaapi_image_get_plane_count(GstVaapiImage *image)
714 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
715 g_return_val_if_fail(image->priv->is_constructed, FALSE);
716 g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
718 return image->priv->image.num_planes;
722 gst_vaapi_image_get_plane(GstVaapiImage *image, guint plane)
724 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), NULL);
725 g_return_val_if_fail(image->priv->is_constructed, FALSE);
726 g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), NULL);
727 g_return_val_if_fail(plane < image->priv->image.num_planes, NULL);
729 return image->priv->image_data + image->priv->image.offsets[plane];
733 gst_vaapi_image_get_pitch(GstVaapiImage *image, guint plane)
735 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
736 g_return_val_if_fail(image->priv->is_constructed, FALSE);
737 g_return_val_if_fail(_gst_vaapi_image_is_mapped(image), 0);
738 g_return_val_if_fail(plane < image->priv->image.num_planes, 0);
740 return image->priv->image.pitches[plane];
744 gst_vaapi_image_get_data_size(GstVaapiImage *image)
746 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), 0);
747 g_return_val_if_fail(image->priv->is_constructed, FALSE);
749 return image->priv->image.data_size;
753 gst_vaapi_image_update_from_buffer(GstVaapiImage *image, GstBuffer *buffer)
755 GstVaapiImagePrivate *priv;
756 GstStructure *structure;
758 GstVaapiImageFormat format;
760 guint offsets[3], pitches[3], widths[3], heights[3];
765 g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
766 g_return_val_if_fail(image->priv->is_constructed, FALSE);
767 g_return_val_if_fail(GST_IS_BUFFER(buffer), FALSE);
770 data = GST_BUFFER_DATA(buffer);
771 data_size = GST_BUFFER_SIZE(buffer);
772 caps = GST_BUFFER_CAPS(buffer);
777 format = gst_vaapi_image_format_from_caps(caps);
778 if (format != priv->format)
781 structure = gst_caps_get_structure(caps, 0);
782 gst_structure_get_int(structure, "width", &width);
783 gst_structure_get_int(structure, "height", &height);
784 if (width != priv->width || height != priv->height)
787 if (!gst_vaapi_image_map(image))
790 if (priv->is_linear && data_size == priv->image.data_size)
791 memcpy(priv->image_data, data, data_size);
793 /* XXX: copied from gst_video_format_get_row_stride() -- no NV12? */
794 const guint width2 = (width + 1) / 2;
795 const guint height2 = (height + 1) / 2;
798 case GST_VAAPI_IMAGE_NV12:
800 pitches[0] = GST_ROUND_UP_4(width);
803 offsets[1] = offsets[0] + height * pitches[0];
804 pitches[1] = pitches[0];
805 widths [1] = width2 * 2;
806 heights[1] = height2;
807 size2 = offsets[1] + height2 * pitches[1];
809 case GST_VAAPI_IMAGE_YV12:
810 case GST_VAAPI_IMAGE_I420:
812 pitches[0] = GST_ROUND_UP_4(width);
815 offsets[1] = offsets[0] + height * pitches[0];
816 pitches[1] = GST_ROUND_UP_4(GST_ROUND_UP_2(width) / 2);
818 heights[1] = height2;
819 offsets[2] = offsets[1] + height2 * pitches[1];
820 pitches[2] = pitches[1];
822 heights[2] = height2;
823 size2 = offsets[2] + height2 * pitches[2];
825 case GST_VAAPI_IMAGE_ARGB:
826 case GST_VAAPI_IMAGE_RGBA:
827 case GST_VAAPI_IMAGE_ABGR:
828 case GST_VAAPI_IMAGE_BGRA:
830 pitches[0] = width * 4;
831 widths [0] = width * 4;
833 size2 = offsets[0] + height * pitches[0];
836 g_error("could not compute row-stride for %" GST_FOURCC_FORMAT,
837 GST_FOURCC_ARGS(format));
840 if (size2 != data_size)
841 g_error("data_size mismatch %d / %u", size2, data_size);
842 for (i = 0; i < priv->image.num_planes; i++) {
843 guchar *src = data + offsets[i];
844 guchar *dst = priv->image_data + priv->image.offsets[i];
845 for (j = 0; j < heights[i]; j++) {
846 memcpy(dst, src, widths[i]);
848 dst += priv->image.pitches[i];
853 if (!gst_vaapi_image_unmap(image))