ffmpeg视频到opengl纹理

Inf*_*izz 40 opengl textures ffmpeg 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();
Run Code Online (Sandbox Code Playgroud)

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

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

PS我看过这个Stack Overflow问题,但它没有帮助.

此外,我也有glEnable(GL_TEXTURE_2D),并通过加载正常的bmp测试它.

编辑

我现在在屏幕上获得的图像,但它是一个乱码一团糟,我猜是与不断变化的事物2的幂(在解码,swscontext并且gluBuild2DMipmaps如在我的代码).我平时几乎一模一样的代码,如上图所示,只有我变glTexSubImage2DgluBuild2DMipmaps,改变了类型GL_RGBA.

这是框架的样子:

Ffmpeg作为OpenGL纹理乱码

再次编辑

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

//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);
Run Code Online (Sandbox Code Playgroud)

现在,我ahve改变了PixelFormatavgpicture_get_sizePIX_FMT_RGB24,我已经做了,在SwsContext也和改变GluBuild2DMipmapsGL_RGB和我得到一个稍微好一点的形象,但它看起来像我仍然失踪线,它仍然是一个有点捉襟见肘:

Ffmpeg乱码OpenGL纹理2

另一个编辑

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

在此输入图像描述

PS

我有一个功能,可以将帧保存到图像之后sws_scale(),它们的颜色和一切都很好,因此OGL中的某些内容使它成为B&W.

最后的编辑

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

在正确的glPixelStorei()中,我得到了正确显示的纹理和幸运的猜测

glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
Run Code Online (Sandbox Code Playgroud)

此外,如果其他人有subimage()像我这样的显示空白问题,你必须至少填充纹理一次,glTexImage2D()所以我在循环中使用它一次,然后使用glTexSubImage2D()它.

感谢Macke和datenwolf的所有帮助.

dat*_*olf 9

你打电话时是否初始化了纹理glTexSubImage2D?您需要调用glTexImage2D(而不是Sub)一次来初始化纹理对象.对数据指针使用NULL,然后OpenGL将初始化纹理而不复制数据. 回答

编辑

你没有提供mipmaping级别.所以你禁用了mipmaping吗?

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILER, linear_interpolation ? GL_LINEAR : GL_NEAREST);
Run Code Online (Sandbox Code Playgroud)

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

编辑3

一年前我自己做过这种事.我写了一个ffmpeg的小包装器,我称之为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;
}
Run Code Online (Sandbox Code Playgroud)

我在一个项目中使用它,它在那里工作,所以这个代码是经过测试的.


Mac*_*cke 5

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

是!

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

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

否则,请将数据上传为1024x512纹理之前调整大小/填充数据.

更新

我对OpenGL更熟悉你正在调用的其他函数.

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

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

其他一些提示:

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