What is the correct way to bind a QImage to a texture for displaying a video in a QOpenGLWidget

I'm trying to implement an efficient video player, so I started with a Qt texture example.

I have:

void HUD::initializeGL()
{
    initializeOpenGLFunctions();

    makeObject();

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1

    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
    const char *vsrc =
        "attribute highp vec4 vertex;\n"
        "attribute mediump vec4 texCoord;\n"
        "varying mediump vec4 texc;\n"
        "uniform mediump mat4 matrix;\n"
        "void main(void)\n"
        "{\n"
        "    gl_Position = matrix * vertex;\n"
        "    texc = texCoord;\n"
        "}\n";
    vshader->compileSourceCode(vsrc);

    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    const char *fsrc =
        "uniform sampler2D texture;\n"
        "varying mediump vec4 texc;\n"
        "void main(void)\n"
        "{\n"
        "    gl_FragColor = texture2D(texture, texc.st);\n"
        "}\n";
    fshader->compileSourceCode(fsrc);

    program = new QOpenGLShaderProgram;
    program->addShader(vshader);
    program->addShader(fshader);
    program->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE);
    program->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE);
    program->link();

    program->bind();
    program->setUniformValue("texture", 0);
}

void HUD::paintGL()
{
    clock_t begin = clock();
    glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), clearColor.alphaF());
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    static int ctt = 0;
    QMatrix4x4 m;
    m.ortho(-0.2f, +0.2f, +0.2f, -0.2f, 4.0f, 15.0f);
    m.translate(0.0f, 0.0f, -5.0f);

    program->setUniformValue("matrix", m);
    program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
    program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
    program->setAttributeBuffer(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 5 * sizeof(GLfloat));
    program->setAttributeBuffer(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, 3 * sizeof(GLfloat), 2, 5 * sizeof(GLfloat));

    //???//
    //How to bind the QImage into a texture efficiently???
    //???//
    textures->bind();// textures is a QOpenGLTexture
    glDrawArrays(GL_TRIANGLE_FAN, 0 * 4, 4);
    clock_t end = clock();
    double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
    qDebug() << elapsed_secs;

}

      

The example originally used QOpenGLTexture to read from one QImage and bind it to a square drawn with paintGL ().

However, how can I update the content of the texture so that I can display the video at 30Hz?

+3


source to share


2 answers


You should take a look at the Qt video widget example which is the main video player. You can also stream video to a QGraphicsVideoItem , which you can display in a QGraphicsView, which can contain other graphics and can display OpenGL content if you provide it with a QOpenGLWidget viewport.
If you want to use your own, you can use Qt video functionality and implement a class based on QAbstractVideoSurfaceto get video frames from a media player. Take a look at handleType () QVideoFrame. If it's QAbstractVideoBuffer :: GLTextureHandle or QAbstractVideoBuffer :: EGLImageHandle, you can simply draw it to the screen using a quad shader with handle () as the texture identifier. If no data is written to QOpenGLTexture. You can load textures into a thread to prevent blocking the GUI thread.



+2


source


It seems that the QOpenGLTexture class does not allow image data to be updated, see QOpenGLTexture :: allocateStorage .

In this case, you should consider hosting your own OpenGL texture and updating it. This can be done with the following code:

// Keep the ID somewhere
GLuint textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

// First frame
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, <w>, <h>, 0, GL_RGB, GL_UNSIGNED_BYTE, frame.bits());

// Subsequent frames
glTextSubImage2D(GL_TEXTURE_2D, 0, 0, 0, <w>, <h>, GL_RGB, GL_UNSIGNED_BYTE, frame.bits());

// Destruction
glDeleteTextures(1, &textureId);

      



And to prepare your pixel data before loading, you can do something like this:

QImage prepareFrame(const QImage& frame) {
    if(frame.format() != QImage::Format_RGB888) {
        return frame.convertToFormat(QImage::Format_RGB888);
    }
    return frame;
}

      

0


source







All Articles