【问题标题】:Create a video from OpenGL rendered frames从 OpenGL 渲染帧创建视频
【发布时间】:2014-09-27 16:09:29
【问题描述】:

我正在尝试从 OpenGL 中的动画创建视频文件。 我一直在阅读如何做到这一点,据我了解,有两种选择:

  1. 将 OpenGL 中的每个渲染帧保存到一个图像文件中,然后从中创建一个视频文件
  2. 使用glReadPixels() 获取帧数据,并即时将其写入视频文件

我认为第二种方法最适合我,但是,我找不到有关如何实现第二部分(写入视频文件)的信息。

谁能指出我学习如何做到这一点的一些网站?我可以使用哪些类型的库来编码(?)我在 OpenGL 中渲染的帧中的视频?

编辑

在对此进行了更多搜索之后,我相信 ffmpeg 是要走的路。我发现 this blog 的代码显然可以在 Windows 上运行。

我已经从网站上下载了 ffmpeg,这样我就可以像示例中一样执行命令了。不幸的是,我的应用程序崩溃并且没有创建视频。我检查了文件指针是否有效但它不是,所以我认为错误来自函数popen的执行。

我传递了与命令完全相同的参数,但仍然没有有效的文件指针,你知道会发生什么吗?

问题是,我不想花太多时间编码视频编码,因为我还有其他项目要处理。

【问题讨论】:

  • 我在编码/解码视频文件方面并没有真正的经验,但我认为你最好的选择是寻找一个为你做这件事的库,你只需将数据传递给它。如果它的任何部分与我想象的音频(哪个)相同,那将不是微不足道的。不幸的是,我不知道任何图书馆。
  • 问题是,您并没有寻求具体的帮助。有很多库能够将一系列 RGB 图像编码为视频流(ffmpeg、gstreamer,我很确定 OSX 有一些原生的东西)。除非您询问有关这些库的特定信息,否则这个问题没有意义。

标签: opengl osx-mavericks qt5 ubuntu-14.04


【解决方案1】:

由于我无法直接从我的 c++ 代码中使用ffmpeg,因此可能的解决方案如下。在 Qt5 中,您可以使用函数 paintGL 更新要渲染的帧。之后,使用 glReadPixels 获取像素,然后使用 QImage 将帧保存为 png 图像

void OpenGLViewer::paintGL()
{
    // Clear screen
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Update array attached to OpenGL

    glDrawArrays(GL_POINTS, 0, _points);

    update();

    glReadPixels(0, 0, this->width(), this->height(), GL_RGBA, GL_UNSIGNED_BYTE, _buffer);

    std::stringstream name;
    name << "Frame" << _frame++ << ".png";
    QString filename(name.str().c_str());
    QImage imagen(_buffer, this->width(), this->height(), QImage::Format_ARGB32);
    imagen.save(filename, "PNG");
}

这将在您的工作目录中留下一堆图像,您可以使用控制台中的以下命令在视频中进行编码

ffmpeg -framerate 30 -start_number 0 -i Frame%d.png -vcodec mpeg4 -vf vflip test.avi

我仍然需要检查为什么颜色会被反转,但现在这工作正常,因为动画是重要的东西,而不是颜色。

【讨论】:

    【解决方案2】:

    安装libpng后可以这样操作: uint8_t 像素 = 新 uint8_t[wh*3]; glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *) 像素);

    for (int j = 0; j * 2 < h; ++j) {
    int x = j * w * 3;
    int y = (h - 1 - j) * w * 3;
    for (int i = w * 3; i > 0; --i) {
        uint8_t tmp = pixels[x];
        pixels[x] = pixels[y];
        pixels[y] = tmp;
        ++x;
        ++y;
        }
    }
    

    png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 如果 (!png) 返回错误;

    png_infop info = png_create_info_struct(png);
    if (!info) {
        png_destroy_write_struct(&png, &info);
        return false;
    }
    std::string s = "IMAGE/"+string(filename);
    FILE *fp = fopen(s.c_str(), "wb");
    if (!fp) {
        png_destroy_write_struct(&png, &info);
        return false;
    }
    
    png_init_io(png, fp);
    png_set_IHDR(png, info, w, h, 8 , PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
        PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
    png_colorp palette = (png_colorp)png_malloc(png, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
    if (!palette) {
        fclose(fp);
        png_destroy_write_struct(&png, &info);
        return false;
    }
    png_set_PLTE(png, info, palette, PNG_MAX_PALETTE_LENGTH);
    png_write_info(png, info);
    png_set_packing(png);
    
    png_bytepp rows = (png_bytepp)png_malloc(png, h * sizeof(png_bytep));
    for (int i = 0; i < h; ++i)
        rows[i] = (png_bytep)(pixels + (h - i - 1) * w * 3);
    
    png_write_image(png, rows);
    png_write_end(png, info);
    png_free(png, palette);
    png_destroy_write_struct(&png, &info);
    
    fclose(fp);
    delete[] rows;
    

    【讨论】:

    • ffmpeg -framerate 30 -start_number 0 -i Frame%d.png -vcodec mpeg4 -vf vflip test.avi