[vlc-commits] hw: vaapi: add chroma conversion GPU <=> CPU
Victorien Le Couviour--Tuffet
git at videolan.org
Fri Jun 16 16:48:29 CEST 2017
vlc | branch: master | Victorien Le Couviour--Tuffet <victorien.lecouviour.tuffet at gmail.com> | Fri May 19 16:29:32 2017 +0200| [440427044385c129d21174114f4ad20d6d9b1bdc] | committer: Thomas Guillem
hw: vaapi: add chroma conversion GPU <=> CPU
VA_OPAQUE(NV12) <=> I420
VA_OPAQUE(P010) <=> I420_10b (not implemented yet, but planned where the code will
take place)
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=440427044385c129d21174114f4ad20d6d9b1bdc
---
modules/hw/vaapi/Makefile.am | 8 +-
modules/hw/vaapi/chroma.c | 370 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 377 insertions(+), 1 deletion(-)
diff --git a/modules/hw/vaapi/Makefile.am b/modules/hw/vaapi/Makefile.am
index 92eb10c2bd..3f2e446f94 100644
--- a/modules/hw/vaapi/Makefile.am
+++ b/modules/hw/vaapi/Makefile.am
@@ -6,6 +6,12 @@ libvlc_vaapi_instance_la_LIBADD = $(LIBVA_LIBS) $(LIBPTHREAD)
libvlc_vaapi_instance_la_LDFLAGS = -no-undefined \
-version-info 0:0:0
+libvaapi_chroma_plugin_la_SOURCES = hw/vaapi/chroma.c hw/vaapi/vlc_vaapi.c hw/vaapi/vlc_vaapi.h \
+ video_chroma/copy.c video_chroma/copy.h
+libvaapi_chroma_plugin_la_CFLAGS = $(AM_CFLAGS) $(LIBVA_CFLAGS)
+libvaapi_chroma_plugin_la_LIBADD = $(AM_LIBADD) $(LIBVA_LIBS) libvlc_vaapi_instance.la
+
if HAVE_VAAPI
-vaapi_LTLIBRARIES = libvlc_vaapi_instance.la
+vaapi_LTLIBRARIES = libvlc_vaapi_instance.la \
+ libvaapi_chroma_plugin.la
endif
diff --git a/modules/hw/vaapi/chroma.c b/modules/hw/vaapi/chroma.c
new file mode 100644
index 0000000000..605c4deb46
--- /dev/null
+++ b/modules/hw/vaapi/chroma.c
@@ -0,0 +1,370 @@
+/*****************************************************************************
+ * chroma.c: VLC picture to VAAPI surface or vice versa
+ *****************************************************************************
+ * Copyright (C) 2017 VLC authors, VideoLAN and VideoLabs
+ *
+ * Author: Victorien Le Couviour--Tuffet <victorien.lecouviour.tuffet at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+
+#include <va/va.h>
+
+#include <vlc_common.h>
+#include <vlc_filter.h>
+#include <vlc_plugin.h>
+
+#include "../../video_chroma/copy.h"
+#include "vlc_vaapi.h"
+
+# define DEST_PICS_POOL_SZ 3
+
+struct filter_sys_t
+{
+ VADisplay dpy;
+ picture_pool_t * dest_pics;
+ VASurfaceID * va_surface_ids;
+ copy_cache_t cache;
+
+ bool derive_failed;
+ bool image_fallback_failed;
+ VAImage image_fallback;
+};
+
+static int Open(vlc_object_t *obj);
+static void Close(vlc_object_t *obj);
+
+vlc_module_begin()
+ set_shortname(N_("VAAPI"))
+ set_description(N_("VAAPI surface conversions"))
+ set_capability("video converter", 10)
+ set_category(CAT_VIDEO)
+ set_subcategory(SUBCAT_VIDEO_VFILTER)
+ set_callbacks(Open, Close)
+vlc_module_end()
+
+static int CreateFallbackImage(filter_t *filter, picture_t *src_pic)
+{
+ filter_sys_t *const filter_sys = filter->p_sys;
+ VADisplay va_dpy = filter_sys->dpy;
+ int count = vaMaxNumImageFormats(va_dpy);
+
+ VAImageFormat *fmts = malloc(count * sizeof (*fmts));
+ if (unlikely(fmts == NULL))
+ return VLC_ENOMEM;
+
+ if (vaQueryImageFormats(va_dpy, fmts, &count))
+ {
+ free(fmts);
+ return VLC_EGENERIC;
+ }
+
+ int i;
+ for (i = 0; i < count; i++)
+ if (fmts[i].fourcc == VA_FOURCC_NV12)
+ break;
+
+ int ret;
+ if (fmts[i].fourcc == VA_FOURCC_NV12
+ && !vlc_vaapi_CreateImage(VLC_OBJECT(filter), va_dpy, &fmts[i],
+ src_pic->format.i_width, src_pic->format.i_height,
+ &filter_sys->image_fallback))
+ ret = VLC_SUCCESS;
+ else
+ ret = VLC_EGENERIC;
+
+ free(fmts);
+
+ return ret;
+}
+
+static inline void
+FillPictureFromVAImage(picture_t *dest,
+ VAImage *src_img, uint8_t *src_buf, copy_cache_t *cache)
+{
+ switch (src_img->format.fourcc)
+ {
+ case VA_FOURCC_NV12:
+ {
+ uint8_t * src_planes[2] = { src_buf + src_img->offsets[0],
+ src_buf + src_img->offsets[1] };
+ size_t src_pitches[2] = { src_img->pitches[0],
+ src_img->pitches[1] };
+
+ CopyFromNv12ToI420(dest, src_planes, src_pitches,
+ src_img->height, cache);
+ break;
+ }
+ /* TODO
+ * case VA_FOURCC_P010:
+ * break;
+ */
+ default:
+ break;
+ }
+}
+
+static picture_t *
+DownloadSurface(filter_t *filter, picture_t *src_pic)
+{
+ filter_sys_t *const filter_sys = filter->p_sys;
+ VADisplay va_dpy;
+ VAImage src_img;
+ void * src_buf;
+
+ if (filter_sys->dpy == NULL)
+ {
+ /* Get the instance here since the instance may be not created by the
+ * decoder from Open() */
+ va_dpy = filter_sys->dpy = vlc_vaapi_GetInstance();
+ assert(filter_sys->dpy);
+ }
+ else
+ va_dpy = filter->p_sys->dpy;
+
+ picture_t *dest = filter_NewPicture(filter);
+ if (!dest)
+ {
+ msg_Err(filter, "filter_NewPicture failed");
+ goto ret;
+ }
+
+ VASurfaceID surface = vlc_vaapi_PicGetSurface(src_pic);
+ if (vaSyncSurface(va_dpy, surface))
+ goto error;
+
+ if (filter_sys->derive_failed ||
+ vlc_vaapi_DeriveImage(VLC_OBJECT(filter), va_dpy, surface, &src_img))
+ {
+ if (filter_sys->image_fallback_failed)
+ goto error;
+ if (!filter_sys->derive_failed)
+ {
+ filter_sys->derive_failed = true;
+ if (CreateFallbackImage(filter, src_pic))
+ {
+ filter_sys->image_fallback_failed = true;
+ goto error;
+ }
+ }
+
+ if (vaGetImage(va_dpy, surface, 0, 0, src_pic->format.i_width,
+ src_pic->format.i_height,
+ filter_sys->image_fallback.image_id))
+ {
+ filter_sys->image_fallback_failed = true;
+ goto error;
+ }
+ src_img = filter_sys->image_fallback;
+ }
+
+ if (vlc_vaapi_MapBuffer(VLC_OBJECT(filter), va_dpy, src_img.buf, &src_buf))
+ goto error;
+
+ FillPictureFromVAImage(dest, &src_img, src_buf, &filter->p_sys->cache);
+
+ vlc_vaapi_UnmapBuffer(VLC_OBJECT(filter), va_dpy, src_img.buf);
+ if (src_img.image_id != filter_sys->image_fallback.image_id)
+ vlc_vaapi_DestroyImage(VLC_OBJECT(filter), va_dpy, src_img.image_id);
+
+ picture_CopyProperties(dest, src_pic);
+ret:
+ picture_Release(src_pic);
+ return dest;
+
+error:
+ picture_Release(dest);
+ dest = NULL;
+ goto ret;
+}
+
+static inline void
+FillVAImageFromPicture(VAImage *dest_img, uint8_t *dest_buf,
+ picture_t *dest_pic, picture_t *src,
+ copy_cache_t *cache)
+{
+ switch (src->format.i_chroma)
+ {
+ case VLC_CODEC_I420:
+ {
+ uint8_t * src_planes[3] = { src->p[Y_PLANE].p_pixels,
+ src->p[U_PLANE].p_pixels,
+ src->p[V_PLANE].p_pixels };
+ size_t src_pitches[3] = { src->p[Y_PLANE].i_pitch,
+ src->p[U_PLANE].i_pitch,
+ src->p[V_PLANE].i_pitch };
+ void *const tmp[2] = { dest_pic->p[0].p_pixels,
+ dest_pic->p[1].p_pixels };
+
+ dest_pic->p[0].p_pixels = dest_buf + dest_img->offsets[0];
+ dest_pic->p[1].p_pixels = dest_buf + dest_img->offsets[1];
+ dest_pic->p[0].i_pitch = dest_img->pitches[0];
+ dest_pic->p[1].i_pitch = dest_img->pitches[1];
+
+ CopyFromI420ToNv12(dest_pic, src_planes, src_pitches,
+ src->format.i_height, cache);
+
+ dest_pic->p[0].p_pixels = tmp[0];
+ dest_pic->p[1].p_pixels = tmp[1];
+
+ break;
+ }
+ case VLC_CODEC_I420_10L || VLC_CODEC_I420_10B:
+ break;
+ default:
+ break;
+ }
+}
+
+static picture_t *
+UploadSurface(filter_t *filter, picture_t *src)
+{
+ VADisplay const va_dpy = filter->p_sys->dpy;
+ VAImage dest_img;
+ void * dest_buf;
+ picture_t * dest_pic = picture_pool_Wait(filter->p_sys->dest_pics);
+
+ if (!dest_pic)
+ {
+ msg_Err(filter, "cannot retrieve picture from the dest pics pool");
+ goto ret;
+ }
+ vlc_vaapi_PicAttachContext(dest_pic);
+ picture_CopyProperties(dest_pic, src);
+
+ if (vlc_vaapi_DeriveImage(VLC_OBJECT(filter), va_dpy,
+ vlc_vaapi_PicGetSurface(dest_pic), &dest_img)
+ || vlc_vaapi_MapBuffer(VLC_OBJECT(filter), va_dpy,
+ dest_img.buf, &dest_buf))
+ goto error;
+
+ FillVAImageFromPicture(&dest_img, dest_buf, dest_pic,
+ src, &filter->p_sys->cache);
+
+ if (vlc_vaapi_UnmapBuffer(VLC_OBJECT(filter), va_dpy, dest_img.buf)
+ || vlc_vaapi_DestroyImage(VLC_OBJECT(filter),
+ va_dpy, dest_img.image_id))
+ goto error;
+
+ret:
+ picture_Release(src);
+ return dest_pic;
+
+error:
+ picture_Release(dest_pic);
+ dest_pic = NULL;
+ goto ret;
+}
+
+static int Open(vlc_object_t *obj)
+{
+ filter_t *const filter = (filter_t *)obj;
+ filter_sys_t * filter_sys;
+ bool is_upload;
+
+ if (filter->fmt_in.video.orientation != filter->fmt_out.video.orientation)
+ return VLC_EGENERIC;
+
+ if (filter->fmt_in.video.i_chroma == VLC_CODEC_VAAPI_420 &&
+ (filter->fmt_out.video.i_chroma == VLC_CODEC_I420 ||
+ filter->fmt_out.video.i_chroma == VLC_CODEC_I420_10L ||
+ filter->fmt_out.video.i_chroma == VLC_CODEC_I420_10B))
+ {
+ is_upload = false;
+ filter->pf_video_filter = DownloadSurface;
+ }
+ else if ((filter->fmt_in.video.i_chroma == VLC_CODEC_I420 ||
+ filter->fmt_in.video.i_chroma == VLC_CODEC_I420_10L ||
+ filter->fmt_in.video.i_chroma == VLC_CODEC_I420_10B) &&
+ filter->fmt_out.video.i_chroma == VLC_CODEC_VAAPI_420)
+ {
+ is_upload = true;
+ filter->pf_video_filter = UploadSurface;
+ }
+ else
+ return VLC_EGENERIC;
+
+ if (!(filter_sys = calloc(1, sizeof(filter_sys_t))))
+ {
+ msg_Err(obj, "unable to allocate memory");
+ return VLC_ENOMEM;
+ }
+
+ if (is_upload)
+ {
+ filter_sys->dpy = vlc_vaapi_GetInstance();
+ if (filter_sys->dpy == NULL)
+ {
+ free(filter_sys);
+ return VLC_EGENERIC;
+ }
+
+ filter_sys->dest_pics =
+ vlc_vaapi_PoolNew(obj, filter_sys->dpy, DEST_PICS_POOL_SZ,
+ &filter_sys->va_surface_ids,
+ &filter->fmt_out.video, VA_RT_FORMAT_YUV420,
+ VA_FOURCC_NV12);
+ if (!filter_sys->dest_pics)
+ {
+ vlc_vaapi_ReleaseInstance(filter_sys->dpy);
+ free(filter_sys);
+ return VLC_EGENERIC;
+ }
+ }
+ else
+ {
+ /* Don't fetch the vaapi instance since it may be not created yet at
+ * this point (in case of cpu rendering) */
+ filter_sys->dest_pics = NULL;
+ }
+
+ if (CopyInitCache(&filter_sys->cache, filter->fmt_in.video.i_width))
+ {
+ if (is_upload)
+ {
+ picture_pool_Release(filter_sys->dest_pics);
+ vlc_vaapi_ReleaseInstance(filter_sys->dpy);
+ }
+ free(filter_sys);
+ return VLC_EGENERIC;
+ }
+
+ filter->p_sys = filter_sys;
+
+ return VLC_SUCCESS;
+}
+
+static void
+Close(vlc_object_t *obj)
+{
+ filter_sys_t *const filter_sys = ((filter_t *)obj)->p_sys;
+
+ if (filter_sys->image_fallback.image_id)
+ vlc_vaapi_DestroyImage(obj, filter_sys->dpy,
+ filter_sys->image_fallback.image_id);
+ if (filter_sys->dest_pics)
+ picture_pool_Release(filter_sys->dest_pics);
+ if (filter_sys->dpy != NULL)
+ vlc_vaapi_ReleaseInstance(filter_sys->dpy);
+ CopyCleanCache(&filter_sys->cache);
+
+ free(filter_sys);
+}
More information about the vlc-commits
mailing list