ffmpeg视频到opengl纹理

时间:2011-06-27 15:53:57

标签: opengl ffmpeg textures video-processing render-to-texture

我正在尝试使用ffmpeg渲染帧并将其从视频转换为OpenGL纹理以放置在四边形上。我已经筋疲力尽了谷歌并没有找到答案,我找到了答案,但似乎没有一个有效。

基本上,我使用avcodec_decode_video2()解码帧,然后sws_scale()将帧转换为RGB,然后glTexSubImage2D()从中创建一个openGL纹理,但似乎无法得到任何工作。

我已确保“目标”AVFrame在SWS上下文设置中具有2维功能。这是我的代码:

SwsContext *img_convert_ctx = sws_getContext(pCodecCtx->width,
                pCodecCtx->height, pCodecCtx->pix_fmt, 512,
                256, PIX_FMT_RGB24, SWS_BICUBIC, NULL,
                NULL, NULL);

//While still frames to read
while(av_read_frame(pFormatCtx, &packet)>=0) {
    glClear(GL_COLOR_BUFFER_BIT);

    //If the packet is from the video stream
    if(packet.stream_index == videoStream) {
        //Decode the video
        avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

        //If we got a frame then convert it and put it into RGB buffer
        if(frameFinished) {
            printf("frame finished: %i\n", number);
            sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

            glBindTexture(GL_TEXTURE_2D, texture);
            //gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pCodecCtx->width, pCodecCtx->height, GL_RGB, GL_UNSIGNED_INT, pFrameRGB->data);
            glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, 512, 256, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB->data[0]);
            SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, number);
            number++;
        }
    }

    glColor3f(1,1,1);
    glBindTexture(GL_TEXTURE_2D, texture);
    glBegin(GL_QUADS);
        glTexCoord2f(0,1);
        glVertex3f(0,0,0);

        glTexCoord2f(1,1);
        glVertex3f(pCodecCtx->width,0,0);

        glTexCoord2f(1,0);
        glVertex3f(pCodecCtx->width, pCodecCtx->height,0);

        glTexCoord2f(0,0);
        glVertex3f(0,pCodecCtx->height,0);

    glEnd();

正如您在该代码中看到的那样,我还将帧保存到.ppm文件只是为了确保它们实际呈现,它们是。

正在使用的文件是845x480的.wmv,这可能是问题吗?事实上,我只是告诉它去512x256?

P.S。我看过这个Stack Overflow question,但没有用。

另外,我也有glEnable(GL_TEXTURE_2D),只是加载正常的bmp进行测试。

修改

我现在正在屏幕上看到一个图像,但这是一个乱码,我猜测将事物更改为2的幂(在解码中,swscontext和{{1} }如我的代码所示)。我通常与上面显示的代码完全相同,只是我已将gluBuild2DMipmaps更改为glTexSubImage2D并将类型更改为gluBuild2DMipmaps

这是框架的样子:

Ffmpeg as OpenGL Texture garbled

再次编辑

刚才意识到我没有展示如何设置pFrameRGB的代码:

GL_RGBA

现在我已将//Allocate video frame for 24bit RGB that we convert to. AVFrame *pFrameRGB; pFrameRGB = avcodec_alloc_frame(); if(pFrameRGB == NULL) { return -1; } //Allocate memory for the raw data we get when converting. uint8_t *buffer; int numBytes; numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); buffer = (uint8_t *) av_malloc(numBytes*sizeof(uint8_t)); //Associate frame with our buffer avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); 中的PixelFormat更改为avgpicture_get_size,我也在PIX_FMT_RGB24中完成了此操作,并将SwsContext更改为{{ 1}}然后我得到一个稍微好一点的图像,但看起来我仍然缺少线条而且它仍然有点拉伸:

Ffmpeg Garbled OpenGL Texture 2

另一个编辑

在遵循Macke的建议并将实际分辨率传递给OpenGL之后,我得到的框架几乎是合适的,但仍然有点偏斜,黑白分明,现在只有6fps而不是110fps:

enter image description here

P.S。

我有一个功能,可以在GluBuild2DMipmaps之后将帧保存到图像中,并且它们的颜色很好,所以OGL中的所有内容都使它成为B& W.

最后编辑

工作!好吧,我现在有它工作,基本上我没有将纹理填充到2的幂,只是使用视频的分辨率。

我在正确的glPixelStorei()

中得到了正确显示的纹理
GL_RGB

此外,如果其他人sws_scale()显示像我这样的空白问题,则必须至少使用glPixelStorei(GL_UNPACK_ALIGNMENT, 2); 填充纹理一次,因此我在循环中使用它一次,然后使用{{ 1}}之后。

感谢Macke和datenwolf的所有帮助。

2 个答案:

答案 0 :(得分:8)

  

当你使用时,纹理是否已初始化   致电glTexSubImage2D?你需要   致电glTexImage2D(不是 Sub )   时间来初始化纹理对象。   对数据指针OpenGL使用NULL   然后将在没有的情况下初始化纹理   复制数据。   回答

修改

您不提供mipmaping级别。那你禁用了mipmaping吗?

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILER, linear_interpolation ? GL_LINEAR : GL_NEAREST);

编辑2 图像是颠倒的并不令人惊讶,因为大多数图像格式的原点位于左上角,而OpenGL将纹理图像的原点放在左下角。你看到的那条带似乎是错误的行间距。

编辑3

一年前我自己做过这种事。我给fqmpeg写了一个小包装器,我称之为aveasy https://github.com/datenwolf/aveasy

这是一些将使用aveasy获取的数据放入OpenGL纹理的代码:

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <GL/glew.h>

#include "camera.h"
#include "aveasy.h"

#define CAM_DESIRED_WIDTH 640
#define CAM_DESIRED_HEIGHT 480

AVEasyInputContext *camera_av;
char const *camera_path = "/dev/video0";
GLuint camera_texture;

int open_camera(void)
{
    glGenTextures(1, &camera_texture);

    AVEasyInputContext *ctx;

    ctx = aveasy_input_open_v4l2(
        camera_path,
        CAM_DESIRED_WIDTH,
        CAM_DESIRED_HEIGHT,
        CODEC_ID_MJPEG,
        PIX_FMT_BGR24 );
    camera_av = ctx;

    if(!ctx) {
        return 0;
    }

    /* OpenGL-2 or later is assumed; OpenGL-2 supports NPOT textures. */
    glBindTexture(GL_TEXTURE_2D, camera_texture[i]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(
        GL_TEXTURE_2D,  
        0,
        GL_RGB, 
        aveasy_input_width(ctx),
        aveasy_input_height(ctx),
        0,
        GL_BGR,
        GL_UNSIGNED_BYTE,
        NULL );

    return 1;
}

void update_camera(void)
{
    glPixelStorei( GL_UNPACK_SWAP_BYTES, GL_FALSE );
    glPixelStorei( GL_UNPACK_LSB_FIRST,  GL_TRUE  );
    glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
    glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0);
    glPixelStorei( GL_UNPACK_SKIP_ROWS, 0);
    glPixelStorei( GL_UNPACK_ALIGNMENT, 1);

    AVEasyInputContext *ctx = camera_av;
    void *buffer;

    if(!ctx)
        return;

    if( !( buffer = aveasy_input_read_frame(ctx) ) )
        return;

    glBindTexture(GL_TEXTURE_2D, camera_texture);
    glTexSubImage2D(
        GL_TEXTURE_2D,
        0,
        0,
        0,
        aveasy_input_width(ctx),
        aveasy_input_height(ctx),
        GL_BGR,
        GL_UNSIGNED_BYTE,
        buffer );
}


void close_cameras(void)
{
    aveasy_input_close(camera_av);
    camera_av=0;
}

我在一个项目中使用它并且它在那里工作,所以这个代码经过测试,等等。

答案 1 :(得分:5)

  

正在使用的文件是.wmv at   854x480,这可能是问题吗?   我只是告诉它去的事实   512x256?

是的!

条纹图案明显表明您的数据大小不匹配(行大小)。 (由于颜色是正确的,RGB与BGR对比BGRA和n分量是正确的。)

你告诉OpenGL你上传的纹理是512x256(它不是,AFAICT)。使用真实尺寸(NPOT,如果它不古老,你的卡应该支持它。)

否则,请在之前调整/填充数据,然后将其作为1024x512纹理上传。

更新

我对OpenGL更熟悉你正在调用的其他功能。

sxs_scale可能符合您的要求(即将图像缩小到底池大小)。 但是,缩放每个帧可能会很慢。

我会使用填充(这意味着,将一个小图像(您的视频)复制到一个大纹理的一部分(opengl)

其他一些提示:

  • 你真的需要mipmap吗?只有在你需要平滑地缩小纹理时才生成它们(通常只有当它在某个三维几何体上时才需要)。
  • 如果你正在渲染视频,请避免在运行时生成mipmap(特别是,不要使用gluBuildMipMaps2D,因为它可能在软件中运行。还有其他方法更快,如果你需要mipmapping (例如使用GL_GENERATE_MIPMAP纹理参数)。有关详细信息,请参见this thread
  • 避免重复调用glTexImage,因为这会创建一个新纹理。 glTexSubImage只是更新纹理的一部分,这对你来说可能更好。
  • 如果您想在一个步骤中上传纹理(出于性能原因而优选),但数据不太合适,请查看glPixelStore以设置像素和行步幅。我怀疑从sxs_scale / wmw给出的数据在每行的末尾(黑线)有一些填充。可能是因为每行开始于偶数8-16-32字节的边界。