Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  interop_sw.c   Sprache: C

 

/*****************************************************************************
 * converter_sw.c: OpenGL converters for software video formats
 *****************************************************************************
 * Copyright (C) 2016,2017 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.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <assert.h>
#include <limits.h>
#include <stdlib.h>

#include <vlc_common.h>
#include <vlc_arrays.h>
#include <vlc_plugin.h>
#include <vlc_opengl.h>
#include <vlc_opengl_interop.h>

#include "gl_util.h"
#include "interop.h"

#define PBO_DISPLAY_COUNT 2 /* Double buffering */
typedef struct
{
    PFNGLDELETEBUFFERSPROC DeleteBuffers;
    GLuint      buffers[PICTURE_PLANE_MAX];
    size_t      bytes[PICTURE_PLANE_MAX];
} picture_sys_t;

struct priv
{
    bool   has_gl_3;
    bool   has_texture_rg;
    bool   has_texture_integer;
    bool   has_unpack_subimage;
    bool   unsigned_sampler;
    void * texture_temp_buf;
    size_t texture_temp_buf_size;
    struct {
        picture_t *display_pics[PBO_DISPLAY_COUNT];
        size_t display_idx;
    } pbo;

#define OPENGL_VTABLE_F(X) \
        X(PFNGLGETERRORPROC,        GetError) \
        X(PFNGLGETINTEGERVPROC,     GetIntegerv) \
        X(PFNGLGETSTRINGPROC,       GetString) \
        \
        X(PFNGLACTIVETEXTUREPROC,   ActiveTexture) \
        X(PFNGLBINDTEXTUREPROC,     BindTexture) \
        X(PFNGLTEXIMAGE2DPROC,      TexImage2D) \
        X(PFNGLTEXSUBIMAGE2DPROC,   TexSubImage2D) \
        \
        X(PFNGLBINDBUFFERPROC,      BindBuffer) \
        X(PFNGLBUFFERDATAPROC,      BufferData) \
        X(PFNGLBUFFERSUBDATAPROC,   BufferSubData) \
        X(PFNGLDELETEBUFFERSPROC,   DeleteBuffers) \
        X(PFNGLGENBUFFERSPROC,      GenBuffers) \
        X(PFNGLPIXELSTOREIPROC,     PixelStorei) \
        X(PFNGLTEXPARAMETERIPROC,   TexParameteri)
    struct {
#define DECLARE_SYMBOL(type, name) type name;
        OPENGL_VTABLE_F(DECLARE_SYMBOL)
    } gl;
};

static void
pbo_picture_destroy(picture_t *pic)
{
    picture_sys_t *picsys = pic->p_sys;

    picsys->DeleteBuffers(pic->i_planes, picsys->buffers);

    free(picsys);
}

static picture_t *
pbo_picture_create(const struct vlc_gl_interop *interop)
{
    const struct priv *priv = interop->priv;
    picture_sys_t *picsys = calloc(1, sizeof(*picsys));
    if (unlikely(picsys == NULL))
        return NULL;

    picture_resource_t rsc = {
        .p_sys = picsys,
        .pf_destroy = pbo_picture_destroy,
    };
    picture_t *pic = picture_NewFromResource(&interop->fmt_out, &rsc);
    if (pic == NULL)
    {
        free(picsys);
        return NULL;
    }

    priv->gl.GenBuffers(pic->i_planes, picsys->buffers);
    picsys->DeleteBuffers = priv->gl.DeleteBuffers;

    /* XXX: needed since picture_NewFromResource override pic planes */
    if (picture_Setup(pic, &interop->fmt_out))
    {
        picture_Release(pic);
        return NULL;
    }

    assert(pic->i_planes > 0
        && (unsigned) pic->i_planes <= interop->tex_count);

    for (int i = 0; i < pic->i_planes; ++i)
    {
        const plane_t *p = &pic->p[i];

        if( p->i_pitch < 0 || p->i_lines <= 0 ||
            (size_t)p->i_pitch > SIZE_MAX/p->i_lines )
        {
            picture_Release(pic);
            return NULL;
        }
        picsys->bytes[i] = p->i_pitch * p->i_lines;
    }
    return pic;
}

static int
pbo_data_alloc(const struct vlc_gl_interop *interop, picture_t *pic)
{
    const struct priv *priv = interop->priv;
    picture_sys_t *picsys = pic->p_sys;

    priv->gl.GetError();

    for (int i = 0; i < pic->i_planes; ++i)
    {
        priv->gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, picsys->buffers[i]);
        priv->gl.BufferData(GL_PIXEL_UNPACK_BUFFER, picsys->bytes[i], NULL,
                                GL_DYNAMIC_DRAW);

        if (priv->gl.GetError() != GL_NO_ERROR)
        {
            msg_Err(interop->gl, "could not alloc PBO buffers");
            priv->gl.DeleteBuffers(i, picsys->buffers);
            return VLC_EGENERIC;
        }
    }
    return VLC_SUCCESS;
}

static int
pbo_pics_alloc(const struct vlc_gl_interop *interop)
{
    struct priv *priv = interop->priv;
    for (size_t i = 0; i < PBO_DISPLAY_COUNT; ++i)
    {
        picture_t *pic = priv->pbo.display_pics[i] =
            pbo_picture_create(interop);
        if (pic == NULL)
            goto error;

        if (pbo_data_alloc(interop, pic) != VLC_SUCCESS)
            goto error;
    }

    /* turn off pbo */
    priv->gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

    return VLC_SUCCESS;
error:
    for (size_t i = 0; i < PBO_DISPLAY_COUNT && priv->pbo.display_pics[i]; ++i)
        picture_Release(priv->pbo.display_pics[i]);
    return VLC_EGENERIC;
}

static int
tc_pbo_update(const struct vlc_gl_interop *interop, uint32_t textures[],
              const int32_t tex_width[], const int32_t tex_height[],
              picture_t *pic, const size_t *plane_offset)
{
    (void) plane_offset; assert(plane_offset == NULL);
    struct priv *priv = interop->priv;

    picture_t *display_pic = priv->pbo.display_pics[priv->pbo.display_idx];
    picture_sys_t *p_sys = display_pic->p_sys;
    priv->pbo.display_idx = (priv->pbo.display_idx + 1) % PBO_DISPLAY_COUNT;

    for (int i = 0; i < pic->i_planes; i++)
    {
        GLsizeiptr size = pic->p[i].i_lines * pic->p[i].i_pitch;
        const GLvoid *data = pic->p[i].p_pixels;
        priv->gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER,
                           p_sys->buffers[i]);
        priv->gl.BufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, data);

        priv->gl.ActiveTexture(GL_TEXTURE0 + i);
        priv->gl.BindTexture(interop->tex_target, textures[i]);

        //for YUV with interleaved UV, pixel pitch is reported as 1
        if (pic->i_planes == 2 && interop->tex_count == 2 && i == 1)
            priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, pic->p[i].i_pitch / (pic->p[i].i_pixel_pitch * 2));
        else
            priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, pic->p[i].i_pitch / pic->p[i].i_pixel_pitch);

        priv->gl.TexSubImage2D(interop->tex_target, 0, 0, 0, tex_width[i], tex_height[i],
                                   interop->texs[i].format, interop->texs[i].type, NULL);
        priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    }

    if (pic->i_planes == 1 && interop->tex_count == 2)
    {
        /* For YUV 4:2:2 formats, a single plane is uploaded into 2 textures */
        priv->gl.ActiveTexture(GL_TEXTURE1);
        priv->gl.BindTexture(interop->tex_target, textures[1]);
        priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, (pic->p[0].i_pitch / pic->p[0].i_pixel_pitch) >> 1); /* yuv[Y] */
        priv->gl.TexSubImage2D(interop->tex_target, 0, 0, 0, tex_width[1], tex_height[1],
                               interop->texs[1].format, interop->texs[1].type, NULL);
        priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    }
    GL_ASSERT_NOERROR(&priv->gl);

    /* turn off pbo */
    priv->gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

    return VLC_SUCCESS;
}

static int
tc_common_allocate_textures(const struct vlc_gl_interop *interop, uint32_t textures[],
                            const int32_t tex_width[], const int32_t tex_height[])
{
    const struct priv *priv = interop->priv;
    for (unsigned i = 0; i < interop->tex_count; i++)
    {
        priv->gl.BindTexture(interop->tex_target, textures[i]);
        priv->gl.TexImage2D(interop->tex_target, 0, interop->texs[i].internal,
                                tex_width[i], tex_height[i], 0, interop->texs[i].format,
                                interop->texs[i].type, NULL);

        /* Integer textures cannot be filtered using GL_LINEAR. They must use
         * GL_NEAREST. */

        if (priv->unsigned_sampler)
        {
            priv->gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            priv->gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        }
        GL_ASSERT_NOERROR(&priv->gl);
    }
    return VLC_SUCCESS;
}

static int
upload_plane(const struct vlc_gl_interop *interop, unsigned tex_idx,
             int32_t width, int32_t height, size_t pitch, size_t pixel_size,
             const void *pixels, unsigned pixel_pack)
{
    struct priv *priv = interop->priv;
    GLenum tex_format = interop->texs[tex_idx].format;
    GLenum tex_type = interop->texs[tex_idx].type;

    /* This unpack alignment is the default, but setting it just in case. */
    priv->gl.PixelStorei(GL_UNPACK_ALIGNMENT, 4);

    assert(height > 0);
    assert(width > 0);
    assert(pixel_size);
    assert(pitch % pixel_size == 0);
    assert((size_t) width * pixel_size <= pitch);

    size_t width_bytes = width * pixel_size * pixel_pack;

    if (!priv->has_unpack_subimage)
    {
        if (pitch != width_bytes)
        {
            size_t aligned_row_len = vlc_align(width_bytes, 4);
            size_t buf_size = aligned_row_len * height;
            const uint8_t *source = pixels;
            uint8_t *destination;
            if (priv->texture_temp_buf_size < buf_size)
            {
                void *r_texture_buf = realloc(priv->texture_temp_buf, buf_size);
                if (r_texture_buf == NULL)
                {
                    free(priv->texture_temp_buf);
                    priv->texture_temp_buf = NULL;
                    priv->texture_temp_buf_size = 0;
                    return VLC_ENOMEM;
                }
                priv->texture_temp_buf = r_texture_buf;
                priv->texture_temp_buf_size = buf_size;
            }
            destination = priv->texture_temp_buf;

            for (GLsizei h = 0; h < height ; h++)
            {
                memcpy(destination, source, width_bytes);
                source += pitch;
                destination += aligned_row_len;
            }
            priv->gl.TexSubImage2D(interop->tex_target, 0, 0, 0, width, height,
                                   tex_format, tex_type, priv->texture_temp_buf);
        }
        else
        {
            priv->gl.TexSubImage2D(interop->tex_target, 0, 0, 0, width, height,
                                   tex_format, tex_type, pixels);
        }
    }
    else
    {
        priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, pitch / (pixel_size * pixel_pack ));

        priv->gl.TexSubImage2D(interop->tex_target, 0, 0, 0, width, height,
                               tex_format, tex_type, pixels);
        priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    }

    GL_ASSERT_NOERROR(&priv->gl);

    return VLC_SUCCESS;
}

static int
tc_common_update(const struct vlc_gl_interop *interop, uint32_t textures[],
                 const int32_t tex_width[], const int32_t tex_height[],
                 picture_t *pic, const size_t *plane_offset)
{
    const struct priv *priv = interop->priv;
    int ret = VLC_SUCCESS;
    for (int i = 0; i < pic->i_planes && ret == VLC_SUCCESS; i++)
    {
        assert(textures[i] != 0);
        priv->gl.ActiveTexture(GL_TEXTURE0 + i);
        priv->gl.BindTexture(interop->tex_target, textures[i]);
        const void *pixels = plane_offset != NULL ?
                             &pic->p[i].p_pixels[plane_offset[i]] :
                             pic->p[i].p_pixels;

        //are we uploading packed UV plane
        unsigned pixel_pack = (pic->i_planes == 2 && i == 1) ? 2 : 1;
        ret = upload_plane(interop, i, tex_width[i], tex_height[i],
                           pic->p[i].i_pitch, pic->p[i].i_pixel_pitch, pixels, pixel_pack);
    }

    if (pic->i_planes == 1 && interop->tex_count == 2)
    {
        /* For YUV 4:2:2 formats, a single plane is uploaded into 2 textures */
        assert(textures[1] != 0);
        priv->gl.ActiveTexture(GL_TEXTURE1);
        priv->gl.BindTexture(interop->tex_target, textures[1]);
        const void *pixels = plane_offset != NULL ?
                             &pic->p[0].p_pixels[plane_offset[0]] :
                             pic->p[0].p_pixels;

        ret = upload_plane(interop, 1, tex_width[1], tex_height[1],
                           pic->p[0].i_pitch, pic->p[0].i_pixel_pitch, pixels, 2);
    }

    return ret;
}

static inline void
DivideRationalByTwo(vlc_rational_t *r) {
    if (r->num % 2 == 0)
        r->num /= 2;
    else
        r->den *= 2;
}

static bool fixGLFormat(struct vlc_gl_interop *interop, unsigned pixel_size,
                        GLint* intfmt, GLint* fmt, GLint *type)
{
    struct priv *priv = interop->priv;
    if (*intfmt == 0)
        return true;

    bool gles2, gles3, gl2, gl3;
    if (priv->has_gl_3)
    {
        gl3 = interop->gl->api_type == VLC_OPENGL;
        gl2 = false;
        gles3 = interop->gl->api_type == VLC_OPENGL_ES2;
        gles2 = false;
    }
    else
    {
        gl3 = false;
        gl2 = interop->gl->api_type == VLC_OPENGL;
        gles3 = false;
        gles2 = interop->gl->api_type == VLC_OPENGL_ES2;
    }
    assert(gl3 || gl2 || gles3 || gles2);

    if (gles3)
    {
        switch (*intfmt) {
        case GL_R8:
        case GL_RG8:
            break;
        case GL_R16:
            if (priv->has_texture_integer)
            {
                msg_Dbg(interop->gl, "fallback to GL_R16UI");
                *intfmt = GL_R16UI;
                *fmt = GL_RED_INTEGER;
            }

            /* Android is unlikely to support GL_R16UI or GL_R16: */
            if (vlc_gl_interop_GetTexFormatSize(interop, GL_TEXTURE_2D, *fmt,
                                                *intfmt, *type) != 16)
            {
                /* XXX: Quality loss with 12 and 16bits since half float has
                 * 10bits of mantissa */

                msg_Dbg(interop->gl, "fallback to GL_R16F");
                *intfmt = GL_R16F;
                *fmt = GL_RED;
                *type = GL_HALF_FLOAT;
            }
            break;
        case GL_RG16:
            if (priv->has_texture_integer)
            {
                msg_Dbg(interop->gl, "fallback to GL_R1G6UI ?");
                *intfmt = GL_RG16UI;
                *fmt = GL_RED_INTEGER;
            }
            /* XXX Is GL_RG16F possible with semi planar ? */
            break;
        default:
            vlc_assert_unreachable();
        }
    }
    else if (gles2 && priv->has_texture_rg)
    {
        //for GLES2 GL_EXT_texture_rg we need to use GL_RED/GL_RG as internal format
        switch (*intfmt) {
        case GL_R8:
            msg_Dbg(interop->gl, "fallback to GL_GED");
            *intfmt = GL_RED;
            *fmt = GL_RED;
            break;
        case GL_RG8:
            msg_Dbg(interop->gl, "fallback to GL_RG8");
            *intfmt = GL_RG;
            *fmt = GL_RG;
            break;
        case GL_R16:
        case GL_RG16:
            return false;
        default:
            vlc_assert_unreachable();
        }
    }
    else if (gles2 || (gl2 && !priv->has_texture_rg))
    {
        //fallback to GL_LUMINANCE / GL_LUMINANCE_ALPHA
        switch (*intfmt) {
        case GL_R8:
            msg_Dbg(interop->gl, "fallback to GL_LUMINANCE");
            *intfmt = GL_LUMINANCE;
            *fmt = GL_LUMINANCE;
            break;
        case GL_R16:
            if (gles2)
                return false;

            msg_Dbg(interop->gl, "fallback to GL_LUMINANCE16");
            *intfmt = GL_LUMINANCE16;
            *fmt = GL_LUMINANCE;
            break;
        case GL_RG8:
            msg_Dbg(interop->gl, "fallback to GL_LUMINANCE_ALPHA");
            *intfmt = GL_LUMINANCE_ALPHA;
            *fmt = GL_LUMINANCE_ALPHA;
            break;
        case GL_RG16:
            if (gles2)
                return false;

            msg_Dbg(interop->gl, "fallback to GL_LUMINANCE16_ALPHA16");
            *intfmt = GL_LUMINANCE16_ALPHA16;
            *fmt = GL_LUMINANCE_ALPHA;
            break;
        default:
            vlc_assert_unreachable();
        }
    }
    else
    {
        //OpenGL 3.0 and OpenGL with GL_ARB_texture_rg
        //don't need transformations
        assert(gl3 || (gl2 && priv->has_texture_rg));
    }

    if (pixel_size == 2
     && vlc_gl_interop_GetTexFormatSize(interop, GL_TEXTURE_2D, *fmt,
                                        *intfmt, *type) != 16)
        return false;
    return true;
}

static int
interop_yuv_base_init(struct vlc_gl_interop *interop,
                      const vlc_chroma_description_t *desc)
{
    struct interop_formats
    {
        GLint intfmt;
        GLint fmt;
        GLint plane2_intfmt;
        GLint plane2_fmt;
        GLint type;
        GLint plane2_type;
    } formats[] = {
        // 3 and 4 planes
            // 8 bits pixels
            { GL_R8, GL_RED, 0, 0, GL_UNSIGNED_BYTE, 0 },
            // 16 bits pixels
            { GL_R16, GL_RED, 0, 0, GL_UNSIGNED_SHORT, 0 },
        // 2 planes
            // 8 bits pixels
            { GL_R8, GL_RED, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE },
            // 16 bits pixels
            { GL_R16, GL_RED, GL_RG16, GL_RG, GL_UNSIGNED_SHORT, GL_UNSIGNED_SHORT },
        // 1 plane is a special case that will be handled explicitly
    };

    if (desc->plane_count == 1)
    {
        if (desc->fcc == VLC_CODEC_VUYA)
        {
            interop->tex_count = 1;
            interop->texs[0] = (struct vlc_gl_tex_cfg) {
                { 1, 1 }, { 1, 1 },
                GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE
            };
            return VLC_SUCCESS;
        }
        else if (desc->pixel_size != 2)
        {
            msg_Warn(interop->gl, "unsupported chroma %.4s", (char*)&desc->fcc);
            return VLC_EGENERIC;
        }

        /* Only YUV 4:2:2 formats */
        /* The pictures have only 1 plane, but it is uploaded twice, once to
         * access the Y components, once to access the UV components. See
         * #26712. */

        GLint intfmt = GL_RG8;
        GLint fmt = GL_RG;
        GLint type = GL_UNSIGNED_BYTE;
        if (!fixGLFormat(interop, desc->pixel_size, &intfmt, &fmt, &type))
            return VLC_EGENERIC;

        interop->tex_count = 2;
        interop->texs[0] = (struct vlc_gl_tex_cfg) {
            { 1, 1 }, { 1, 1 },
            intfmt, fmt, type
        };
        interop->texs[1] = (struct vlc_gl_tex_cfg) {
            { 1, 2 }, { 1, 1 },
            GL_RGBA, GL_RGBA, type
        };
        return VLC_SUCCESS;
    }

    uint8_t format_index = 0;
    switch (desc->plane_count)
    {
    case 4:
    case 3:
        break;
    case 2:
        format_index += 2;
        break;
    default:
        vlc_assert_unreachable();
        break;
    }

    switch (desc->pixel_size)
    {
    case 1:
        break;
    case 2:
        format_index += 1;
        break;
    default:
        return VLC_EGENERIC;
    }

    assert(format_index < ARRAY_SIZE(formats));

    struct interop_formats* format = &formats[format_index];

    GLint plane1_intfmt = format->intfmt;
    GLint plane1_fmt = format->fmt;
    GLint plane1_type = format->type;
    if (!fixGLFormat(interop, desc->pixel_size, &plane1_intfmt, &plane1_fmt,
                     &plane1_type))
        return VLC_EGENERIC;

    GLint plane2_intfmt = format->plane2_intfmt;
    GLint plane2_fmt = format->plane2_fmt;
    GLint plane2_type = format->plane2_type;
    if (!fixGLFormat(interop, desc->pixel_size, &plane2_intfmt, &plane2_fmt,
                     &plane2_type))
        return VLC_EGENERIC;

    msg_Dbg(interop, "Using format at index %u", format_index);
    msg_Dbg(interop, "Plane1: fmt=%#x intfmt=%#x type=%#x", plane1_fmt,
            plane1_intfmt, plane1_type);
    msg_Dbg(interop, "Plane2: fmt=%#x intfmt=%#x type=%#x", plane2_fmt,
            plane2_intfmt, plane2_type);

    if (desc->plane_count >= 3)
    {
        interop->tex_count = desc->plane_count;
        for (unsigned i = 0; i < interop->tex_count; ++i )
        {
            interop->texs[i] = (struct vlc_gl_tex_cfg) {
                { desc->p[i].w.num, desc->p[i].w.den },
                { desc->p[i].h.num, desc->p[i].h.den },
                plane1_intfmt, plane1_fmt, plane1_type
            };
        }
    }
    else if (desc->plane_count == 2)
    {
        interop->tex_count = 2;

        interop->texs[0] = (struct vlc_gl_tex_cfg) {
            { desc->p[0].w.num, desc->p[0].w.den },
            { desc->p[0].h.num, desc->p[0].h.den },
            plane1_intfmt, plane1_fmt, plane1_type
        };
        interop->texs[1] = (struct vlc_gl_tex_cfg) {
            { desc->p[1].w.num, desc->p[1].w.den },
            { desc->p[1].h.num, desc->p[1].h.den },
            plane2_intfmt, plane2_fmt, plane2_type
        };

        /*
         * If plane_count == 2, then the chroma is semiplanar: the U and V
         * planes are packed in the second plane. As a consequence, the
         * horizontal scaling, as reported in the vlc_chroma_description_t, is
         * doubled.
         *
         * But once imported as an OpenGL texture, both components are stored
         * in a single texel (the two first components of the vec4).
         * Therefore, from OpenGL, the width is not doubled, so the horizontal
         * scaling must be divided by 2 to compensate.
         */

         DivideRationalByTwo(&interop->texs[1].w);
    }

    return VLC_SUCCESS;
}

static int
interop_rgb_base_init(struct vlc_gl_interop *interop, vlc_fourcc_t chroma)
{
    switch (chroma)
    {
        case VLC_CODEC_RGB24:
            interop->texs[0] = (struct vlc_gl_tex_cfg) {
                { 1, 1 }, { 1, 1 }, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE
            };
            break;
#ifdef GL_BGR
        case VLC_CODEC_BGR24:
            interop->texs[0] = (struct vlc_gl_tex_cfg) {
                { 1, 1 }, { 1, 1 }, GL_RGB, GL_BGR, GL_UNSIGNED_BYTE
            };
            break;
#endif

        case VLC_CODEC_RGBA:
            interop->texs[0] = (struct vlc_gl_tex_cfg) {
                { 1, 1 }, { 1, 1 }, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE
            };
            break;
        case VLC_CODEC_BGRA: {
            if (vlc_gl_interop_GetTexFormatSize(interop, GL_TEXTURE_2D, GL_BGRA, GL_RGBA,
                                                GL_UNSIGNED_BYTE) != 32)
                return VLC_EGENERIC;
            interop->texs[0] = (struct vlc_gl_tex_cfg) {
                { 1, 1 }, { 1, 1 }, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE
            };
            break;
        }
        default:
            return VLC_EGENERIC;
    }
    interop->tex_count = 1;
    return VLC_SUCCESS;
}

static void
interop_xyz12_init(struct vlc_gl_interop *interop)
{
    interop->tex_count = 1;
    interop->tex_target = GL_TEXTURE_2D;
    interop->texs[0] = (struct vlc_gl_tex_cfg) {
        { 1, 1 }, { 1, 1 }, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT
    };
}

static int
opengl_interop_init(struct vlc_gl_interop *interop,
                    vlc_fourcc_t chroma, video_color_space_t yuv_space)
{
    const vlc_chroma_description_t *desc =
        vlc_fourcc_GetChromaDescription(chroma);
    if (!desc || desc->plane_count == 0)
        return VLC_EGENERIC;

    assert(!interop->fmt_out.p_palette);
    interop->fmt_out.i_chroma = chroma;
    interop->fmt_out.space = yuv_space;
    interop->tex_target = GL_TEXTURE_2D;

    if (chroma == VLC_CODEC_XYZ_12B||
        chroma == VLC_CODEC_XYZ_12L)
    {
        interop_xyz12_init(interop);
        return VLC_SUCCESS;
    }

    if (vlc_fourcc_IsYUV(chroma))
        return interop_yuv_base_init(interop, desc);

    return interop_rgb_base_init(interop, chroma);
}

static void
opengl_interop_generic_deinit(struct vlc_gl_interop *interop)
{
    struct priv *priv = interop->priv;
    for (size_t i = 0; i < PBO_DISPLAY_COUNT && priv->pbo.display_pics[i]; ++i)
        picture_Release(priv->pbo.display_pics[i]);
    free(priv->texture_temp_buf);
    free(priv);
}

static int
opengl_interop_generic_init(struct vlc_gl_interop *interop, bool allow_dr)
{

    struct priv *priv = calloc(1, sizeof(struct priv));
    if (unlikely(priv == NULL))
        return VLC_ENOMEM;

    interop->priv = priv;

#define LOAD_SYMBOL(type, name) \
    priv->gl.name = vlc_gl_GetProcAddress(interop->gl, "gl" # name); \
    assert(priv->gl.name != NULL);

    OPENGL_VTABLE_F(LOAD_SYMBOL);

    struct vlc_gl_extension_vt extension_vt;
    vlc_gl_LoadExtensionFunctions(interop->gl, &extension_vt);

    /* OpenGL or OpenGL ES2 with GL_EXT_unpack_subimage ext */
    priv->has_unpack_subimage = interop->gl->api_type == VLC_OPENGL
        || vlc_gl_HasExtension(&extension_vt, "GL_EXT_unpack_subimage");

    /* RG textures are available natively since OpenGL 3.0 and OpenGL ES 3.0 */
    priv->has_gl_3 = vlc_gl_GetVersionMajor(&extension_vt) >= 3;

    priv->has_texture_rg =
        (interop->gl->api_type == VLC_OPENGL
            && vlc_gl_HasExtension(&extension_vt, "GL_ARB_texture_rg"))
        || (interop->gl->api_type == VLC_OPENGL_ES2
            && vlc_gl_HasExtension(&extension_vt, "GL_EXT_texture_rg"));

    priv->has_texture_integer = priv->has_gl_3 &&
        (interop->gl->api_type == VLC_OPENGL
        || vlc_gl_HasExtension(&extension_vt, "GL_EXT_texture_integer"));

    video_color_space_t space;
    const bool is_yup = vlc_fourcc_IsYUV(interop->fmt_in.i_chroma);

    if (is_yup)
    {
        GLint max_texture_units = 0;
        priv->gl.GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units);
        if (max_texture_units < 3)
            goto error;

        space = interop->fmt_in.space;
    }
    else
        space = COLOR_SPACE_UNDEF;

    /* The pictures are uploaded upside-down */
    video_format_TransformBy(&interop->fmt_out, TRANSFORM_VFLIP);

    /* Check whether the given chroma is translatable to OpenGL. */
    vlc_fourcc_t i_chroma = interop->fmt_in.i_chroma;
    int ret = opengl_interop_init(interop, i_chroma, space);
    if (ret == VLC_SUCCESS)
        goto interop_init;

    if (!is_yup)
    {
        i_chroma = VLC_CODEC_RGBA;
        ret = opengl_interop_init(interop, i_chroma, space);
        if (ret == VLC_SUCCESS)
            goto interop_init;
    }

    vlc_fourcc_t *list = NULL;
    if (is_yup)
        list = vlc_fourcc_GetYUVFallback(interop->fmt_in.i_chroma);
    else if (interop->fmt_in.i_chroma != VLC_CODEC_XYZ_12B)
        list = vlc_fourcc_GetRGBFallback(interop->fmt_in.i_chroma);

    if (list == NULL)
        goto error;

    /* Check whether any fallback for the chroma is translatable to OpenGL. */
    for (const vlc_fourcc_t *fcc = list; *fcc; fcc++)
    {
        ret = opengl_interop_init(interop, *fcc, space);
        if (ret == VLC_SUCCESS)
        {
            i_chroma = *fcc;
            msg_Warn(interop->gl, "direct rendering failed for %4.4s, "
                     "fallback to %4.4s",
                     (const char *)&interop->fmt_in.i_chroma,
                     (const char *)&i_chroma);
            goto interop_init;
        }
    }
    free(list);

    goto error;

interop_init:
    /* We found a chroma with matching parameters for OpenGL. The interop can
     * be created. */


    interop->fmt_in.i_chroma = i_chroma;

    static const struct vlc_gl_interop_ops ops = {
        .allocate_textures = tc_common_allocate_textures,
        .update_textures = tc_common_update,
        .close = opengl_interop_generic_deinit,
    };
    interop->ops = &ops;

    switch (interop->texs[0].format)
    {
        case GL_RED_INTEGER:
        case GL_RG_INTEGER:
            priv->unsigned_sampler = true;
            break;
        default:
            priv->unsigned_sampler = false;
            break;
    }

    if (allow_dr && priv->has_unpack_subimage)
    {
        /* Ensure we do direct rendering / PBO with OpenGL 3.0 or higher. */
        const unsigned char *ogl_version = priv->gl.GetString(GL_VERSION);
        const bool glver_ok = strverscmp((const char *)ogl_version, "3.0") >= 0;

        const bool has_pbo = glver_ok &&
            (vlc_gl_HasExtension(&extension_vt, "GL_ARB_pixel_buffer_object") ||
             vlc_gl_HasExtension(&extension_vt, "GL_EXT_pixel_buffer_object"));

        const bool supports_pbo = has_pbo && priv->gl.BufferData
            && priv->gl.BufferSubData;
        if (supports_pbo && pbo_pics_alloc(interop) == VLC_SUCCESS)
        {
            static const struct vlc_gl_interop_ops pbo_ops = {
                .allocate_textures = tc_common_allocate_textures,
                .update_textures = tc_pbo_update,
                .close = opengl_interop_generic_deinit,
            };
            interop->ops = &pbo_ops;
            msg_Dbg(interop->gl, "PBO support enabled");
        }
    }

    return VLC_SUCCESS;

error:
    free(priv);
    interop->priv = NULL;
    return VLC_EGENERIC;
}

static int OpenInteropSW(struct vlc_gl_interop *interop)
{
    return opengl_interop_generic_init(interop, false);
}

static int OpenInteropDirectRendering(struct vlc_gl_interop *interop)
{
    return opengl_interop_generic_init(interop, true);
}

vlc_module_begin ()
    set_description("Software OpenGL interop")
    set_capability("opengl sw interop", 1)
    set_callback(OpenInteropSW)
    set_subcategory(SUBCAT_VIDEO_VOUT)
    add_shortcut("sw")

    add_submodule()
    set_callback(OpenInteropDirectRendering)
    set_capability("opengl sw interop", 2)
    add_shortcut("pbo")
vlc_module_end ()

Messung
CHZG
83959189

¤ Dauer der Verarbeitung: 0.5 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104856
#Domains=417808