【问题标题】:What's the proper way to bind a QImage to a texture to display video in QOpenGLWidget将 QImage 绑定到纹理以在 QOpenGLWidget 中显示视频的正确方法是什么
【发布时间】:2015-04-25 12:51:01
【问题描述】:

我正在尝试实现一个高效的视频播放器,所以我从 Qt 的纹理示例开始。

我有:

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;

}

该示例最初使用 QOpenGLTexture 读取单个 QImage 并将其绑定到由 paintGL() 函数绘制的四边形。

但是,如何更新纹理的内容,以便以 30 Hz 显示视频?

【问题讨论】:

    标签: c++ qt opengl


    【解决方案1】:

    你应该看看 Qt video widget example 这是一个基本的视频播放器。您还可以将视频流式传输到QGraphicsVideoItem,您可以在 QGraphicsView 中显示,如果您提供 QOpenGLWidget 视口,它可以包含其他图形项并可以渲染 OpenGL 内容。
    如果你想自己动手,你可能想使用 Qt 视频功能并实现一个基于 QAbstractVideoSurface 的类来接收来自媒体播放器的视频帧。看一下 QVideoFrame 的 handleType()。如果它是 QAbstractVideoBuffer::GLTextureHandle 或 QAbstractVideoBuffer::EGLImageHandle,您也许可以使用四边形着色器将其绘制到屏幕上,并将其 handle() 作为纹理 ID。如果不将数据写入 QOpenGLTexture。您可以在线程中上传纹理以防止阻塞 GUI 线程。

    【讨论】:

      【解决方案2】:

      QOpenGLTexture 类好像不允许更新图片数据,见QOpenGLTexture::allocateStorage

      在这种情况下,您应该考虑分配自己的 OpenGL 纹理并对其进行更新。这可以通过以下代码完成:

      // 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);
      

      为了在上传之前准备像素数据,您可以执行以下操作:

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

      【讨论】:

      • Qt 使用的不可变纹理存储(如果有的话)确实暗示纹理数据是不可变的。只有纹理属性(大小、内部格式、mip 贴图级别数)是固定的。可以随时使用glSubTexImage() 或通过渲染来更新图像数据。
      • 对了,我有点搞混了,而且好像根本不需要使用原生的OpenGL函数来更新数据,可以多次调用OpenGLTexture::setData() .嗯,我喜欢手工制作的东西:)
      • 是不是打错了,Subsequent frames下面的glTextSubImage2D应该是glTexSubImage2D吗?
      • 从性能上看,上传 24bit 数据的效率不如上传 32bit,使用 RGBA 的效率不如 BGRA。详细解释见此贴:community.khronos.org/t/updating-textures-per-frame/75020/3
      猜你喜欢
      • 2015-05-29
      • 1970-01-01
      • 2013-11-08
      • 2018-03-01
      • 2021-04-06
      • 2015-02-22
      • 1970-01-01
      • 1970-01-01
      • 2018-07-22
      相关资源
      最近更新 更多