我正在尝试使用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
。
这是框架的样子:
再次编辑
刚才意识到我没有展示如何设置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}}然后我得到一个稍微好一点的图像,但看起来我仍然缺少线条而且它仍然有点拉伸:
另一个编辑
在遵循Macke的建议并将实际分辨率传递给OpenGL之后,我得到的框架几乎是合适的,但仍然有点偏斜,黑白分明,现在只有6fps而不是110fps:
P.S。
我有一个功能,可以在GluBuild2DMipmaps
之后将帧保存到图像中,并且它们的颜色很好,所以OGL中的所有内容都使它成为B& W.
最后编辑
工作!好吧,我现在有它工作,基本上我没有将纹理填充到2的幂,只是使用视频的分辨率。
我在正确的glPixelStorei()
中得到了正确显示的纹理GL_RGB
此外,如果其他人sws_scale()
显示像我这样的空白问题,则必须至少使用glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
填充纹理一次,因此我在循环中使用它一次,然后使用{{ 1}}之后。
感谢Macke和datenwolf的所有帮助。
答案 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)
其他一些提示:
glPixelStore
以设置像素和行步幅。我怀疑从sxs_scale / wmw给出的数据在每行的末尾(黑线)有一些填充。可能是因为每行开始于偶数8-16-32字节的边界。