FFMPEG转换为OpenGL纹理。

5
我是来询问如何将AVFrame转换为OpenGL纹理的。实际上,我创建了一个渲染器来输出音频(音频正常工作),以及视频,但视频没有输出。以下是我的代码:
纹理创建:
glGenTextures(1,&_texture);
glBindTexture(GL_TEXTURE_2D,_texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); 
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

代码信息: _texture变量是一个GLuint,用于保存纹理ID。

将AVFrame转换为OpenGL纹理的函数:

int VideoGL::NextVideoFrame(){
// Get a packet from the queue
AVPacket *videopacket = this->DEQUEUE(VIDEO);
int frameFinished;
if(videopacket!=0){
    avcodec_decode_video2(_codec_context_video, _std_frame,&frameFinished,videopacket);

    if(frameFinished){

        sws_scale(sws_ctx, _std_frame->data, _std_frame->linesize, 0, _codec_context_video->height, _rgb_frame->data, _rgb_frame->linesize);

        if(_firstrendering){
        glBindTexture(GL_TEXTURE_2D,_texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _codec_context_video->width,_codec_context_video->height,0,GL_RGB,GL_UNSIGNED_BYTE,_rgb_frame->data[0]);

        _firstrendering = false;

        }else{

            glActiveTexture(_texture);
            glBindTexture(GL_TEXTURE_2D,_texture);
            glTexSubImage2D(GL_TEXTURE_2D,0,0,0,_codec_context_video->width,_codec_context_video->height,GL_RGB,GL_UNSIGNED_BYTE,_rgb_frame->data[0]);

        }
        av_free_packet(videopacket);
        return 0;
    }else{

        av_free_packet(videopacket);
        return -1;
    }

}else{
    return -1;
}
return 0;
}

代码信息:有一个队列,其中线程存储AVFrames,此函数经常被调用以获取AVFrames,直到它获得NULL时才停止被调用。
但实际上这并不起作用。(我尝试在stackoverflow中寻找一些问题,仍然无法解决)是否有示例或者有人能帮助我纠正其中的任何错误?
附加数据:我尝试将GL_RGB更改为GL_RGBA并开始处理格式,但无论如何当我尝试GL_RGBA时它都会崩溃(因为宽度和高度非常大,尝试将它们调整大小)。 我已尝试将大小更改为2的幂,但仍然无法解决。
第1次编辑:
线程函数:
DWORD WINAPI VideoGL::VidThread(LPVOID myparam){

VideoGL * instance = (VideoGL*) myparam;
instance->wave_audio->Start();

int quantity=0;

AVPacket packet;
while(av_read_frame(instance->_format_context,&packet) >= 0){
    if(packet.stream_index==instance->videoStream){
        instance->ENQUEUE(VIDEO,&packet);
    }
    if(packet.stream_index==instance->audioStream){
        instance->ENQUEUE(AUDIO,&packet);
    }
}

instance->ENQUEUE(AUDIO,NULL);
instance->ENQUEUE(VIDEO,NULL);

return 0;
}

线程创建函数:

CreateThread(NULL, 0, VidThread, this, NULL, NULL);

这里的“this”是指包含NextVideoFrame和_texture成员的类。

解决:

我按照dantenwolf的建议进行了一些调整,现在视频正常显示并且带有音频/视频:

截图


NextVideoFrame() 运行在你提到的辅助线程上还是主线程上? - genpfault
我会发布CreateThread和线程,最好查看代码以更好地理解它。 - Spamdark
次要线程仅将AVFrame存储到队列中,NextVideoFrame在另一个地方被调用,这是一个更新纹理并将其绘制到屏幕(opengl)的函数。 - Spamdark
1个回答

7

使用多线程的OpenGL有一个小技巧。每个线程只能有一个活动的OpenGL上下文,也可以在同一时间内只有一个给定的OpenGL上下文处于活动状态。迁移上下文到另一个线程是可能的,但你不应该在不同线程间随意地转移OpenGL上下文。

有两种可能的方法:

  • 使用映射内存的像素缓冲对象(在一个线程中映射,在另一个线程中更新)

  • 使用第二个OpenGL上下文,与其他上下文共享其纹理。然后将一个上下文绑定到每个线程(在Windows上可以使用相同的可绘制区域,即HDC)。然后你可以在一个线程中更新纹理,而在另一个线程中绘制它。纹理更新会在线程间引入同步点,因此你不会意外地写入正在被访问进行绘制的纹理。


太棒了!我最近在stackoverflow问题中看到了你的一个答案,对我帮助很大。这是一张截图!现在它运行得很好!截图 - Spamdark
你能分享一下你修复后的代码吗? - Mike Versteeg
@MikeVersteeg:这是针对我还是Spamdark的?如果是后者,你必须在用户名前面加上“@”来通知他/她。 - datenwolf
@Spamdark,你现在修好了,能和我们分享一下你的代码吗? - Mike Versteeg
@datenwolf 感谢您的提示! - Mike Versteeg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接