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如在我的代码).我平时几乎一模一样的代码,如上图所示,只有我变glTexSubImage2D到gluBuild2DMipmaps,改变了类型GL_RGBA.
这是框架的样子:

再次编辑
刚刚意识到我没有展示如何设置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改变了PixelFormat在avgpicture_get_size给PIX_FMT_RGB24,我已经做了,在SwsContext也和改变GluBuild2DMipmaps来GL_RGB和我得到一个稍微好一点的形象,但它看起来像我仍然失踪线,它仍然是一个有点捉襟见肘:

另一个编辑
在遵循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的所有帮助.
你打电话时是否初始化了纹理回答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)
我在一个项目中使用它,它在那里工作,所以这个代码是经过测试的.
正在使用的文件是845x480的.wmv,这可能是问题吗?事实上,我只是告诉它去512x256?
是!
条纹图案明显表明您的数据大小不匹配(行大小).(由于颜色正确,RGB与BGR对比BGRA和n分量是正确的.)
你告诉OpenGL你上传的纹理是512x256(它不是,AFAICT).使用真实尺寸(NPOT,如果它不古老,你的卡应该支持它).
否则,请在将数据上传为1024x512纹理之前调整大小/填充数据.
更新
我对OpenGL更熟悉你正在调用的其他函数.
sxs_scale可能是您想要的(即将图像缩小到底池大小).但是,缩放每个帧可能会很慢.
我会使用填充(这意味着,将一个小图像(您的视频)复制到一个大纹理的一部分(opengl)
其他一些提示:
glPixelStore设置像素和行步幅.我怀疑从sxs_scale/wmw给出的数据在每行的末尾(黑线)有一些填充.可能使每行开始于偶数8-16-32字节的边界.| 归档时间: |
|
| 查看次数: |
24280 次 |
| 最近记录: |