[vlc-devel] [PATCH v2 5/6] video_filter: add opengl filter executor

Romain Vimont rom1v at videolabs.io
Fri Feb 26 17:10:35 UTC 2021


From: Alexandre Janniaux <ajanni at videolabs.io>

Add a video filter to execute OpenGL filters.

It can be used as follow:

    ./vlc file.mkv --video-filter=opengl --opengl-filter=FILTERNAME
    ./vlc file.mkv --video-filter=opengl{filter=FILTERNAME}

Co-authored-by: Romain Vimont <rom1v at videolabs.io>
---
 modules/video_filter/Makefile.am |  29 +++
 modules/video_filter/opengl.c    | 327 +++++++++++++++++++++++++++++++
 2 files changed, 356 insertions(+)
 create mode 100644 modules/video_filter/opengl.c

diff --git a/modules/video_filter/Makefile.am b/modules/video_filter/Makefile.am
index 1c856b393b..68383eb3fc 100644
--- a/modules/video_filter/Makefile.am
+++ b/modules/video_filter/Makefile.am
@@ -124,6 +124,35 @@ libci_filters_plugin_la_LDFLAGS += -Wl,-framework,OpenGLES
 video_filter_LTLIBRARIES += libci_filters_plugin.la
 endif
 
+libopengl_filter_plugin_la_SOURCES = video_filter/opengl.c
+
+if HAVE_LINUX
+if HAVE_ANDROID
+libopengl_filter_plugin_la_LIBADD = libvlc_opengles.la
+libopengl_filter_plugin_la_CFLAGS = -DUSE_OPENGL_ES2=1
+else
+libopengl_filter_plugin_la_LIBADD = libvlc_opengl.la
+endif
+video_filter_LTLIBRARIES += libopengl_filter_plugin.la
+endif
+
+if HAVE_IOS
+libopengl_filter_plugin_la_LIBADD = libvlc_opengles.la
+libopengl_filter_plugin_la_CFLAGS = -DUSE_OPENGL_ES2=1
+video_filter_LTLIBRARIES += libopengl_filter_plugin.la
+endif
+
+if HAVE_TVOS
+libopengl_filter_plugin_la_LIBADD = libvlc_opengles.la
+libopengl_filter_plugin_la_CFLAGS = -DUSE_OPENGL_ES2=1
+video_filter_LTLIBRARIES += libopengl_filter_plugin.la
+endif
+
+if HAVE_OSX
+libopengl_filter_plugin_la_LIBADD = libvlc_opengl.la
+video_filter_LTLIBRARIES += libopengl_filter_plugin.la
+endif
+
 libdeinterlace_common_la_SOURCES = video_filter/deinterlace/common.c video_filter/deinterlace/common.h
 libdeinterlace_common_la_LDFLAGS = -static
 noinst_LTLIBRARIES += libdeinterlace_common.la
diff --git a/modules/video_filter/opengl.c b/modules/video_filter/opengl.c
new file mode 100644
index 0000000000..dab1645fa9
--- /dev/null
+++ b/modules/video_filter/opengl.c
@@ -0,0 +1,327 @@
+/*****************************************************************************
+ * opengl.c: OpenGL filter
+ *****************************************************************************
+ * Copyright (C) 2020 Videolabs
+ *
+ * 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 <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_modules.h>
+#include <vlc_picture.h>
+#include <vlc_filter.h>
+#include <vlc_opengl.h>
+#include <vlc_vout_window.h>
+#include <vlc_vout_display.h>
+#include <vlc_atomic.h>
+#include "../video_output/opengl/vout_helper.h"
+#include "../video_output/opengl/filters.h"
+#include "../video_output/opengl/gl_api.h"
+#include "../video_output/opengl/gl_common.h"
+#include "../video_output/opengl/interop.h"
+
+#define OPENGL_CFG_PREFIX "opengl-"
+static const char *const opengl_options[] = { "filter", NULL };
+
+typedef struct
+{
+    vlc_gl_t *gl;
+    struct vlc_gl_filters *filters;
+    struct vlc_gl_interop *interop;
+    struct vlc_gl_api api;
+} filter_sys_t;
+
+static picture_t *Filter(filter_t *filter, picture_t *input)
+{
+    filter_sys_t *sys = filter->p_sys;
+
+    if (vlc_gl_MakeCurrent(sys->gl) != VLC_SUCCESS)
+        return NULL;
+
+    int ret = vlc_gl_filters_UpdatePicture(sys->filters, input);
+    if (ret != VLC_SUCCESS)
+    {
+        vlc_gl_ReleaseCurrent(sys->gl);
+        return NULL;
+    }
+
+    ret = vlc_gl_filters_Draw(sys->filters);
+    if (ret != VLC_SUCCESS)
+    {
+        vlc_gl_ReleaseCurrent(sys->gl);
+        return NULL;
+    }
+
+    picture_t *output = vlc_gl_SwapOffscreen(sys->gl);
+    vlc_gl_ReleaseCurrent(sys->gl);
+
+    if (output == NULL)
+        goto end;
+
+    output->date = input->date;
+    output->b_force = input->b_force;
+    output->b_still = input->b_still;
+
+    output->format.i_frame_rate =
+        filter->fmt_out.video.i_frame_rate;
+    output->format.i_frame_rate_base =
+        filter->fmt_out.video.i_frame_rate_base;
+
+end:
+    picture_Release(input);
+    return output;
+}
+
+static int
+AppendVFlip(struct vlc_gl_filters *filters)
+{
+    config_chain_t *cfg = NULL;
+    char *name;
+    char *leftover = config_ChainCreate(&name, &cfg, "draw{vflip}");
+    free(leftover);
+    free(name);
+
+    /* The OpenGL filter will do the real job, this file is just a filter_t
+     * wrapper */
+    struct vlc_gl_filter *glfilter =
+        vlc_gl_filters_Append(filters, "draw", cfg);
+    config_ChainDestroy(cfg);
+    if (!glfilter)
+        return VLC_EGENERIC;
+
+    return VLC_SUCCESS;
+}
+
+static int
+LoadFilters(filter_sys_t *sys, const char *glfilters_config)
+{
+    struct vlc_gl_filters *filters = sys->filters;
+    assert(glfilters_config);
+
+    const char *string = glfilters_config;
+    char *next_module = NULL;
+    do
+    {
+        char *name;
+        config_chain_t *config = NULL;
+        char *leftover = config_ChainCreate(&name, &config, string);
+
+        free(next_module);
+        next_module = leftover;
+        string = next_module; /* const view of next_module */
+
+        if (name)
+        {
+            struct vlc_gl_filter *filter =
+                vlc_gl_filters_Append(filters, name, config);
+            config_ChainDestroy(config);
+            if (!filter)
+            {
+                msg_Err(sys->gl, "Could not load GL filter: %s", name);
+                free(name);
+                return VLC_EGENERIC;
+            }
+
+            free(name);
+        }
+    } while (string);
+
+    return VLC_SUCCESS;
+}
+
+static void Close( filter_t *filter )
+{
+    filter_sys_t *sys = filter->p_sys;
+
+    if (sys != NULL)
+    {
+        vlc_gl_MakeCurrent(sys->gl);
+        vlc_gl_filters_Delete(sys->filters);
+        vlc_gl_interop_Delete(sys->interop);
+        vlc_gl_ReleaseCurrent(sys->gl);
+
+        vlc_gl_Release(sys->gl);
+        free(sys);
+    }
+}
+
+static int Open( vlc_object_t *obj )
+{
+    filter_t *filter = (filter_t *)obj;
+
+    filter_sys_t *sys
+        = filter->p_sys
+        = malloc(sizeof *sys);
+    if (sys == NULL)
+        return VLC_ENOMEM;
+
+    unsigned width
+        = filter->fmt_out.video.i_visible_width
+        = filter->fmt_in.video.i_visible_width;
+
+    unsigned height
+        = filter->fmt_out.video.i_visible_height
+        = filter->fmt_in.video.i_visible_height;
+
+    // TODO: other than BGRA format ?
+#ifdef USE_OPENGL_ES2
+# define VLCGLAPI VLC_OPENGL_ES2
+#else
+# define VLCGLAPI VLC_OPENGL
+#endif
+
+    struct vlc_decoder_device *device = filter_HoldDecoderDevice(filter);
+    sys->gl = vlc_gl_CreateOffscreen(obj, device, width, height, VLCGLAPI,
+                                     NULL);
+
+    /* The vlc_gl_t instance must have hold the device if it needs it. */
+    if (device)
+        vlc_decoder_device_Release(device);
+
+    if (sys->gl == NULL)
+    {
+        msg_Err(obj, "Failed to create opengl context\n");
+        goto gl_create_failure;
+    }
+
+    if (vlc_gl_MakeCurrent (sys->gl) != VLC_SUCCESS)
+    {
+        msg_Err(obj, "Failed to gl make current");
+        assert(false);
+        goto make_current_failure;
+    }
+
+    struct vlc_gl_api *api = &sys->api;
+    if (vlc_gl_api_Init(api, sys->gl) != VLC_SUCCESS)
+    {
+        msg_Err(obj, "Failed to init vlc_gl_api");
+        goto gl_api_failure;
+    }
+
+    sys->interop = vlc_gl_interop_New(sys->gl, api, filter->vctx_in,
+                                      &filter->fmt_in.video);
+    if (!sys->interop)
+    {
+        msg_Err(obj, "Could not create interop");
+        goto gl_interop_failure;
+    }
+
+    config_ChainParse(filter, OPENGL_CFG_PREFIX, opengl_options, filter->p_cfg);
+
+    char *glfilters_config =
+        var_InheritString(filter, OPENGL_CFG_PREFIX "filter");
+    if (!glfilters_config)
+    {
+        msg_Err(obj, "No filters requested");
+        goto filter_config_failure;
+    }
+
+
+    sys->filters = vlc_gl_filters_New(sys->gl, api, sys->interop);
+    if (!sys->filters)
+    {
+        msg_Err(obj, "Could not create filters");
+        goto filters_new_failure;
+    }
+
+    int ret;
+
+    ret = LoadFilters(sys, glfilters_config);
+    if (ret != VLC_SUCCESS)
+    {
+        msg_Err(obj, "Could not load filters: %s", glfilters_config);
+        free(glfilters_config);
+        goto filter_config_failure;
+    }
+    free(glfilters_config);
+
+    if (sys->gl->offscreen_vflip)
+    {
+        /* OpenGL renders upside-down, add a filter to get the pixels in the
+         * normal orientation */
+        ret = AppendVFlip(sys->filters);
+        if (ret != VLC_SUCCESS)
+            return VLC_EGENERIC;
+    }
+
+    ret = vlc_gl_filters_InitFramebuffers(sys->filters);
+    if (ret != VLC_SUCCESS)
+    {
+        msg_Err(obj, "Could not init filters framebuffers");
+        goto init_framebuffer_failure;
+    }
+
+    vlc_gl_filters_SetViewport(sys->filters, 0, 0, filter->fmt_out.video.i_visible_width, filter->fmt_out.video.i_visible_height);
+
+    vlc_gl_ReleaseCurrent(sys->gl);
+
+    static const struct vlc_filter_operations ops = {
+        .filter_video = Filter,
+        .close = Close,
+    };
+    filter->ops = &ops;
+    filter->fmt_out.video.orientation = ORIENT_NORMAL;
+
+    filter->fmt_out.video.i_chroma
+        = filter->fmt_out.i_codec
+        = sys->gl->offscreen_chroma_out;
+
+    filter->fmt_out.video.i_frame_rate =
+        filter->fmt_in.video.i_frame_rate;
+
+    filter->fmt_out.video.i_frame_rate_base =
+        filter->fmt_in.video.i_frame_rate_base;
+
+    assert(filter->fmt_out.video.i_frame_rate_base != 0);
+
+    filter->vctx_out = sys->gl->offscreen_vctx_out;
+
+    return VLC_SUCCESS;
+
+init_framebuffer_failure:
+filters_new_failure:
+    vlc_gl_filters_Delete(sys->filters);
+
+filter_config_failure:
+    vlc_gl_interop_Delete(sys->interop);
+
+gl_interop_failure:
+gl_api_failure:
+    vlc_gl_ReleaseCurrent(sys->gl);
+
+make_current_failure:
+    vlc_gl_Release(sys->gl);
+
+gl_create_failure:
+    free(sys);
+
+    return VLC_EGENERIC;
+}
+
+vlc_module_begin()
+    set_shortname( N_("opengl") )
+    set_description( N_("Opengl filter executor") )
+    set_category( CAT_VIDEO )
+    set_subcategory( SUBCAT_VIDEO_VFILTER )
+    set_capability( "video filter", 0 )
+    add_shortcut( "opengl" )
+    set_callback( Open )
+    add_module_list( "opengl-filter", "opengl filter", NULL,
+                     "opengl filter", "List of OpenGL filters to execute" )
+vlc_module_end()
-- 
2.30.1



More information about the vlc-devel mailing list