[vlc-devel] [PATCH v2 22/22] opengl: add mock filter

Alexandre Janniaux ajanni at videolabs.io
Thu Jul 2 17:10:09 CEST 2020


Hi,

I'm fine with the module, but it needs a bit more setup in
the current state of the OpenGL implementation.

In particular, it will only work on OpenGL platform, not
OpenGL ES currently, because libvlc_opengl and
libvlc_opengles are still different.

I'm ok with merging it like this for now since it doesn't
break anything, but the commit message should probably signal
this.

Regards,
--
Alexandre Janniaux
Videolabs

On Wed, Jul 01, 2020 at 12:30:23PM +0200, Romain Vimont wrote:
> This mock filter simplifies testing of the OpenGL filter engine.
>
> Co-authored-by: Alexandre Janniaux <ajanni at videolabs.io>
> ---
>  modules/video_output/opengl/Makefile.am |   7 +
>  modules/video_output/opengl/mock.c      | 398 ++++++++++++++++++++++++
>  2 files changed, 405 insertions(+)
>  create mode 100644 modules/video_output/opengl/mock.c
>
> diff --git a/modules/video_output/opengl/Makefile.am b/modules/video_output/opengl/Makefile.am
> index ce6509529b..3bb49cc64e 100644
> --- a/modules/video_output/opengl/Makefile.am
> +++ b/modules/video_output/opengl/Makefile.am
> @@ -76,6 +76,13 @@ libgl_plugin_la_CFLAGS += -DHAVE_GL_CORE_SYMBOLS
>  libgl_plugin_la_LIBADD += $(GL_LIBS)
>  endif
>
> +libglfilter_mock_plugin_la_SOURCES = video_output/opengl/mock.c
> +libglfilter_mock_plugin_la_LIBADD = $(LIBM) libvlc_opengl.la
> +libglfilter_mock_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)'
> +if HAVE_GL
> +noinst_LTLIBRARIES += libglfilter_mock_plugin.la
> +endif
> +
>  if HAVE_GL
>  vout_LTLIBRARIES += libgl_plugin.la
>  endif # HAVE_GL
> diff --git a/modules/video_output/opengl/mock.c b/modules/video_output/opengl/mock.c
> new file mode 100644
> index 0000000000..b59b078ca1
> --- /dev/null
> +++ b/modules/video_output/opengl/mock.c
> @@ -0,0 +1,398 @@
> +/*****************************************************************************
> + * mock.c: mock OpenGL filter
> + *****************************************************************************
> + * Copyright (C) 2020 VLC authors and VideoLAN
> + *
> + * 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.
> + *****************************************************************************/
> +
> +/**
> + * This mock draws a triangle, by default in blend mode:
> + *
> + *     ./vlc file.mkv --gl-filter='mock'
> + *
> + * It can be configured as a non-blend filter, to mask the video with the
> + * triangle instead:
> + *
> + *     ./vlc file.mkv --gl-filter='mock{mask}'
> + *
> + * The triangle may be rotated (the value is in degrees):
> + *
> + *     ./vlc file.mkv --gl-filter='mock{angle=45}'
> + *     ./vlc file.mkv --gl-filter='mock{mask,angle=45}'
> + *
> + * If a speed is specified, the triangle automatically rotates with the video
> + * timestamps:
> + *
> + *     ./vlc file.mkv --gl-filter='mock{speed=1}'
> + *
> + * Several instances may be combined:
> + *
> + *     ./vlc file.mkv --gl-filter='mock{mask,speed=1}:mock{angle=180,speed=-1}'
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +# include "config.h"
> +#endif
> +
> +#include <assert.h>
> +
> +#include <vlc_common.h>
> +#include <vlc_plugin.h>
> +#include <vlc_modules.h>
> +#include <vlc_opengl.h>
> +
> +#include <math.h>
> +
> +#include "filter.h"
> +#include "gl_api.h"
> +#include "gl_common.h"
> +#include "gl_util.h"
> +
> +#ifdef USE_OPENGL_ES2
> +# define SHADER_VERSION "#version 100\n"
> +# define FRAGMENT_SHADER_PRECISION "precision mediump float;\n"
> +#else
> +# define SHADER_VERSION "#version 120\n"
> +# define FRAGMENT_SHADER_PRECISION
> +#endif
> +
> +#define MOCK_CFG_PREFIX "mock-"
> +
> +static const char *const filter_options[] = { "angle", "mask", "speed", NULL };
> +
> +struct sys {
> +    GLuint program_id;
> +
> +    GLuint vbo;
> +
> +    struct {
> +        GLint vertex_pos;
> +        GLint rotation_matrix;
> +        GLint vertex_color; // blend (non-mask) only
> +    } loc;
> +
> +    float theta0;
> +    float speed;
> +
> +    float rotation_matrix[16];
> +    float ar;
> +};
> +
> +static void
> +InitMatrix(struct sys *sys, vlc_tick_t pts)
> +{
> +    float time_sec = secf_from_vlc_tick(pts);
> +    /* Full cycle in 60 seconds if speed = 1 */
> +    float theta = sys->theta0 + sys->speed * time_sec * 2 * 3.141592f / 60;
> +    float cos_theta = cos(theta);
> +    float sin_theta = sin(theta);
> +    float ar = sys->ar;
> +
> +    /* Defined in column-major order */
> +    memcpy(sys->rotation_matrix, (float[16]) {
> +        cos_theta,        sin_theta * ar,  0,          0,
> +        -sin_theta / ar,  cos_theta,       0,          0,
> +        0,                0,               0,          0,
> +        0,                0,               0,          1,
> +    }, sizeof(sys->rotation_matrix));
> +}
> +
> +static int
> +DrawBlend(struct vlc_gl_filter *filter, const struct vlc_gl_input_meta *meta)
> +{
> +    struct sys *sys = filter->sys;
> +
> +    const opengl_vtable_t *vt = &filter->api->vt;
> +
> +    vt->UseProgram(sys->program_id);
> +
> +    vt->Enable(GL_BLEND);
> +    vt->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
> +
> +    /*
> +     * The VBO data contains, for each vertex, 2 floats for the vertex position
> +     * followed by 3 floats for the associated color:
> +     *
> +     *  |     vertex 0      |     vertex 1      | ...
> +     *  | x | y | R | G | B | x | y | R | G | B | x | ...
> +     *   \-----/ \---------/
> +     * vertex_pos vertex_color
> +     */
> +
> +    const GLsizei stride = 5 * sizeof(float);
> +
> +    vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
> +
> +    vt->EnableVertexAttribArray(sys->loc.vertex_pos);
> +    vt->VertexAttribPointer(sys->loc.vertex_pos, 2, GL_FLOAT, GL_FALSE, stride,
> +                            (const void *) 0);
> +
> +    intptr_t offset = 2 * sizeof(float);
> +    vt->EnableVertexAttribArray(sys->loc.vertex_color);
> +    vt->VertexAttribPointer(sys->loc.vertex_color, 3, GL_FLOAT, GL_FALSE,
> +                            stride, (const void *) offset);
> +
> +    InitMatrix(sys, meta->pts);
> +    vt->UniformMatrix4fv(sys->loc.rotation_matrix, 1, GL_FALSE,
> +                         sys->rotation_matrix);
> +
> +    vt->DrawArrays(GL_TRIANGLES, 0, 3);
> +
> +    vt->Disable(GL_BLEND);
> +
> +    return VLC_SUCCESS;
> +}
> +
> +static int
> +DrawMask(struct vlc_gl_filter *filter, const struct vlc_gl_input_meta *meta)
> +{
> +    struct sys *sys = filter->sys;
> +
> +    const opengl_vtable_t *vt = &filter->api->vt;
> +
> +    vt->UseProgram(sys->program_id);
> +
> +    struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
> +    vlc_gl_sampler_Load(sampler);
> +
> +    vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
> +    vt->EnableVertexAttribArray(sys->loc.vertex_pos);
> +    vt->VertexAttribPointer(sys->loc.vertex_pos, 2, GL_FLOAT, GL_FALSE, 0,
> +                            (const void *) 0);
> +
> +    InitMatrix(sys, meta->pts);
> +    vt->UniformMatrix4fv(sys->loc.rotation_matrix, 1, GL_FALSE,
> +                         sys->rotation_matrix);
> +
> +    vt->Clear(GL_COLOR_BUFFER_BIT);
> +    vt->DrawArrays(GL_TRIANGLES, 0, 3);
> +
> +    return VLC_SUCCESS;
> +}
> +
> +static void
> +Close(struct vlc_gl_filter *filter)
> +{
> +    struct sys *sys = filter->sys;
> +
> +    const opengl_vtable_t *vt = &filter->api->vt;
> +    vt->DeleteProgram(sys->program_id);
> +    vt->DeleteBuffers(1, &sys->vbo);
> +
> +    free(sys);
> +}
> +
> +static int
> +InitBlend(struct vlc_gl_filter *filter)
> +{
> +    struct sys *sys = filter->sys;
> +    const opengl_vtable_t *vt = &filter->api->vt;
> +
> +    static const char *const VERTEX_SHADER =
> +        SHADER_VERSION
> +        "attribute vec2 vertex_pos;\n"
> +        "attribute vec3 vertex_color;\n"
> +        "uniform mat4 rotation_matrix;\n"
> +        "varying vec3 color;\n"
> +        "void main() {\n"
> +        "  gl_Position = rotation_matrix * vec4(vertex_pos, 0.0, 1.0);\n"
> +        "  color = vertex_color;\n"
> +        "}\n";
> +
> +    static const char *const FRAGMENT_SHADER =
> +        SHADER_VERSION
> +        FRAGMENT_SHADER_PRECISION
> +        "varying vec3 color;\n"
> +        "void main() {\n"
> +        "  gl_FragColor = vec4(color, 0.5);\n"
> +        "}\n";
> +
> +    GLuint program_id =
> +        vlc_gl_BuildProgram(VLC_OBJECT(filter), vt,
> +                            1, (const char **) &VERTEX_SHADER,
> +                            1, (const char **) &FRAGMENT_SHADER);
> +
> +    if (!program_id)
> +        return VLC_EGENERIC;
> +
> +    sys->program_id = program_id;
> +
> +    sys->loc.vertex_pos = vt->GetAttribLocation(sys->program_id, "vertex_pos");
> +    assert(sys->loc.vertex_pos != -1);
> +
> +    sys->loc.rotation_matrix = vt->GetUniformLocation(sys->program_id,
> +                                                      "rotation_matrix");
> +    assert(sys->loc.rotation_matrix != -1);
> +
> +    sys->loc.vertex_color = vt->GetAttribLocation(program_id, "vertex_color");
> +    assert(sys->loc.vertex_color != -1);
> +
> +    vt->GenBuffers(1, &sys->vbo);
> +
> +    static const GLfloat data[] = {
> +      /* x   y      R  G  B */
> +         0,  1,     1, 0, 0,
> +        -1, -1,     0, 1, 0,
> +         1, -1,     0, 0, 1,
> +    };
> +
> +    vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
> +    vt->BufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
> +    vt->BindBuffer(GL_ARRAY_BUFFER, 0);
> +
> +    filter->config.blend = true;
> +    filter->config.msaa_level = 4;
> +
> +    static const struct vlc_gl_filter_ops ops = {
> +        .draw = DrawBlend,
> +        .close = Close,
> +    };
> +    filter->ops = &ops;
> +
> +    return VLC_SUCCESS;
> +}
> +
> +static int
> +InitMask(struct vlc_gl_filter *filter)
> +{
> +    struct sys *sys = filter->sys;
> +    const opengl_vtable_t *vt = &filter->api->vt;
> +
> +    struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
> +
> +    static const char *const VERTEX_SHADER =
> +        SHADER_VERSION
> +        "attribute vec2 vertex_pos;\n"
> +        "uniform mat4 rotation_matrix;\n"
> +        "varying vec2 tex_coords;\n"
> +        "void main() {\n"
> +        "  vec4 pos = rotation_matrix * vec4(vertex_pos, 0.0, 1.0);\n"
> +        "  tex_coords = vec2((pos.x + 1.0) / 2.0,\n"
> +        "                    (pos.y + 1.0) / 2.0);\n"
> +        "  gl_Position = pos\n;"
> +        "}\n";
> +
> +    static const char *const FRAGMENT_SHADER_TEMPLATE =
> +        SHADER_VERSION
> +        "%s\n" /* extensions */
> +        FRAGMENT_SHADER_PRECISION
> +        "%s\n" /* vlc_texture definition */
> +        "varying vec2 tex_coords;\n"
> +        "void main() {\n"
> +        "  gl_FragColor = vlc_texture(tex_coords);\n"
> +        "}\n";
> +
> +    const char *extensions = sampler->shader.extensions
> +                           ? sampler->shader.extensions : "";
> +
> +    char *fragment_shader;
> +    int ret = asprintf(&fragment_shader, FRAGMENT_SHADER_TEMPLATE, extensions,
> +                       sampler->shader.body);
> +    if (ret < 0)
> +        return VLC_EGENERIC;
> +
> +    GLuint program_id =
> +        vlc_gl_BuildProgram(VLC_OBJECT(filter), vt,
> +                            1, (const char **) &VERTEX_SHADER,
> +                            1, (const char **) &fragment_shader);
> +    free(fragment_shader);
> +    if (!program_id)
> +        return VLC_EGENERIC;
> +
> +    sys->program_id = program_id;
> +
> +    vlc_gl_sampler_FetchLocations(sampler, program_id);
> +
> +    sys->loc.vertex_pos = vt->GetAttribLocation(sys->program_id, "vertex_pos");
> +    assert(sys->loc.vertex_pos != -1);
> +
> +    sys->loc.rotation_matrix = vt->GetUniformLocation(sys->program_id,
> +                                                      "rotation_matrix");
> +    assert(sys->loc.rotation_matrix != -1);
> +
> +    vt->GenBuffers(1, &sys->vbo);
> +
> +    static const GLfloat data[] = {
> +      /* x   y */
> +         0,  1,
> +        -1, -1,
> +         1, -1,
> +    };
> +
> +    vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
> +    vt->BufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
> +    vt->BindBuffer(GL_ARRAY_BUFFER, 0);
> +
> +    filter->config.blend = false;
> +    filter->config.msaa_level = 4;
> +
> +    static const struct vlc_gl_filter_ops ops = {
> +        .draw = DrawMask,
> +        .close = Close,
> +    };
> +    filter->ops = &ops;
> +
> +    return VLC_SUCCESS;
> +}
> +
> +static vlc_gl_filter_open_fn Open;
> +static int
> +Open(struct vlc_gl_filter *filter, const config_chain_t *config,
> +     struct vlc_gl_tex_size *size_out)
> +{
> +    (void) config;
> +
> +    config_ChainParse(filter, MOCK_CFG_PREFIX, filter_options, config);
> +
> +    bool mask = var_InheritBool(filter, MOCK_CFG_PREFIX "mask");
> +    float angle = var_InheritFloat(filter, MOCK_CFG_PREFIX "angle");
> +    float speed = var_InheritFloat(filter, MOCK_CFG_PREFIX "speed");
> +
> +    struct sys *sys = filter->sys = malloc(sizeof(*sys));
> +    if (!sys)
> +        return VLC_EGENERIC;
> +
> +    int ret;
> +    if (mask)
> +        ret = InitMask(filter);
> +    else
> +        ret = InitBlend(filter);
> +
> +    if (ret != VLC_SUCCESS)
> +        goto error;
> +
> +    sys->ar = (float) size_out->width / size_out->height;
> +    sys->theta0 = angle * M_PI / 180; /* angle in degrees, theta0 in radians */
> +    sys->speed = speed;
> +
> +    return VLC_SUCCESS;
> +
> +error:
> +    free(sys);
> +    return VLC_EGENERIC;
> +}
> +
> +vlc_module_begin()
> +    set_shortname("mock")
> +    set_description("mock OpenGL filter")
> +    set_category(CAT_VIDEO)
> +    set_subcategory(SUBCAT_VIDEO_VFILTER)
> +    set_capability("opengl filter", 0)
> +    set_callback(Open)
> +    add_float(MOCK_CFG_PREFIX "angle", 0.f, NULL, NULL, false)
> +    add_float(MOCK_CFG_PREFIX "speed", 0.f, NULL, NULL, false) /* in rotation per minute */
> +    add_bool(MOCK_CFG_PREFIX "mask", false, NULL, NULL, false)
> +vlc_module_end()
> --
> 2.27.0
>


More information about the vlc-devel mailing list