html - 在 Qt 中渲染 OpenGL 场景并将其流式传输到 HTML5 界面

标签 html qt opengl

我想知道是否可以在 Qt 中渲染 OpenGL 场景并将其实时流式传输到 HTML5 界面(我的意思是场景是当场生成的)。

我一直在努力寻找相关信息以及如何做,但我没有成功...

如果存在,是否存在任何类型的现有机制来压缩图像和优化带宽使用。我正在考虑 Citrix 之类的解决方案,但使用 HTML5 客户端。

最佳答案

此答案解释了如何使用 OpenGL、Qt 和 GStreamer 完成此任务.但在我开始之前,有两个问题需要立即解决:

  • Streaming video to HTML5 is still problematic .我建议使用 Ogg 进行编码,因为它比 h264 更受现代浏览器的支持;
  • 编码视频并将其流式传输到 HTTP 如果没有第 3 方库来帮助您,这将是一个相当大的挑战。好好看看GStreamer (用于处理多媒体文件的跨平台库)。这就是我在这里用来对来自 OpenGL 的帧缓冲区的帧进行编码和流式传输的工具;

实现此类内容的路线图是什么样的?

首先从帧缓冲区捕获帧。有different methods可用于 this purpose ,以及 opengl 离屏渲染 的谷歌搜索将返回几个有趣的帖子和文档。我不会深入讨论技术细节,因为这个主题已经被广泛涉及,但出于教育目的,我将分享下面的代码来演示如何检索帧并将其保存为磁盘上的 jpg:

// GLWidget is a class based on QGLWidget.
void GLWidget::paintGL()
{
    /* Setup FBO and RBO */

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fb);

    glGenFramebuffersEXT(1, &_fb);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fb);

    glGenRenderbuffersEXT(1, &_color_rb);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _color_rb);

    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_BGRA, viewport[2], viewport[3]);
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, _color_rb);

    /* Draw the scene (with transparency) */

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glLoadIdentity();
    glTranslatef(-2.0f, 0.0f, -7.0f);
    glRotatef(45, 1.0f, 1.0f, 0.0f);
    _draw_cube();

    glLoadIdentity();
    glTranslatef(2.0f, 0.0f, -7.0f);
    glRotatef(30, 0.5f, 1.0f, 0.5f);
    _draw_cube();

    glFlush();

    /* Retrieve pixels from the framebuffer */

    int imgsize = viewport[2] * viewport[3];
    std::cout << "* Viewport size: " << viewport[2] << "x" << viewport[3] << std::endl;

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glReadBuffer(GL_COLOR_ATTACHMENT0);

    unsigned char* pixels = new unsigned char[sizeof(unsigned char) * imgsize * 4];
    glReadPixels(0, 0, viewport[2], viewport[3], GL_BGRA, GL_UNSIGNED_BYTE, pixels);

    // Use fwrite to dump data:
    FILE* fp = fopen("dumped.bin","w");
    fwrite(pixels, sizeof(unsigned char) * imgsize * 4, 1, fp);
    fclose(fp);

    // or use QImage to encode the raw data to jpg:
    QImage image((const unsigned char*)pixels, viewport[2], viewport[3], QImage::Format_RGB32);
    QImage flipped = image.mirrored();
    flipped.save("output2.jpg");

    // Disable FBO and RBO
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

    // Delete resources
    glDeleteRenderbuffersEXT(1, &_color_rb);
    glDeleteFramebuffersEXT(1, &_fb);
    delete[] pixels;
}

QImage 用于将原始GL_BGRA 帧转换为jpg 文件。 draw_scene() 方法简单地绘制了一个透明的彩色立方体:

下一步是对帧进行编码并通过 HTTP 流式传输。但是,您可能不希望在能够流式传输之前将帧缓冲区中的每一帧都保存到磁盘。你是对的,你不必! GStreamer 提供了一个 C API,您可以在您的应用程序中使用它来执行由 gst-launch(下面介绍)完成的操作。这个库甚至有一个名为 QtGstreamer 的 Qt 包装器让事情变得更简单。

GStreamer 1.0 提供了一个名为 gst-launch-1.0 的命令行应用程序,可用于在您开始编码之前测试其功能。开发人员通常会使用它来组装一条指令流水线,从而在开始编码之前让奇迹发生。

以下命令显示了如何使用它来解码 jpg、将其编码为 Ogg theora 并以 HTML5 页面可以播放的方式将该单个图像流式传输到 HTTP:

gst-launch-1.0.exe -v filesrc location=output.jpg ! decodebin ! imagefreeze ! clockoverlay shaded-background=true font-desc="Sans 38" ! theoraenc ! oggmux ! tcpserversink host=127.0.0.1 port=8080

第三步也是最后一步是打开一个用于显示流的 HTML5 页面。此步骤必须在 gst-launch 运行时执行,因此将下面的代码复制并粘贴到一个文件中,然后在浏览器中打开该页面(我在 Chrome 上测试过)。该页面连接到本地主机,端口 8080 并开始接收流。您可能已经注意到 gst-launch 管道在原始图像上覆盖了一个时钟:

<html>
    <title>A simple HTML5 video test</title>
</html>
<body> 
    <video autoplay controls width=320 height=240>    
    <source src="http://localhost:8080" type="video/ogg">
       You browser doesn't support element <code>video</code>.
    </video>
</body>

enter image description here

I'm just trying to figure out exactly how GStreamer can convert a raw BGRA frame to jpg (or other formats)在流式传输之前。

更新:

问题解决了!有可能 encode a raw BGRA frame to jpg*Ogg 并直接流式传输,而无需在磁盘上创建中间文件。我冒昧地将 FPS 限制设置为 15,并将 theoraenc 的标准质量降低了 50%:

gst-launch-1.0.exe -v filesrc location=dumped.bin blocksize=1920000 ! video/x-raw,format=BGRA,width=800,height=600,framerate=1/1 ! videoconvert ! video/x-raw,format=RGB,framerate=1/1 ! videoflip method=vertical-flip ! imagefreeze ! videorate ! video/x-raw,format=RGB,framerate=30/2 ! videoconvert ! clockoverlay shaded-background=true font-desc="Sans 38" ! theoraenc quality=24 ! oggmux ! queue ! tcpserversink host=127.0.0.1 port=8080 sync-method=2

此管道上有一些您并不真正需要的操作。尽管如此,您可以采取一些措施来优化带宽,例如将帧缩放到更小的尺寸 (400x300)、设置 FPS 的下限、降低编码帧的质量等:

gst-launch-1.0.exe -v filesrc location=dumped.bin blocksize=1920000 ! video/x-raw,format=BGRA,width=800,height=600,framerate=1/1 ! videoconvert ! video/x-raw,format=RGB,framerate=1/1 ! videoflip method=vertical-flip ! videoscale ! video/x-raw,width=400,height=300! imagefreeze ! videorate ! video/x-raw,format=RGB,framerate=30/2 ! videoconvert ! clockoverlay shaded-background=true font-desc="Sans 38" ! theoraenc quality=24 ! oggmux ! tcpserversink host=127.0.0.1 port=8080 sync-method=2

关于html - 在 Qt 中渲染 OpenGL 场景并将其流式传输到 HTML5 界面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23929462/

相关文章:

javascript - HTML5 视频嵌入在播放结束时返回到海报

PHP,从 MySQL 填充的下拉列表中获取 MySQL 查询

java - 在 Java 中使用 Qt?

C++/OpenGL : How does Tesselation work?

c++ - 最好是更新一个小的顶点缓冲区,还是发送一个制服?

c - 在 OpenGL 中绘制二维纹理

html - 删除 Wordpress 菜单之一上的悬停红线

html - 垂直对齐固定宽度容器中的可变高度元素

c++ - QtAddon SerialPort 从 Qt4 到 Qt5

c++ - Qt5 应用程序不运行