ffmpegvideo到opengl纹理
我试图渲染帧抓取和转换使用ffmpegvideo到OpenGL纹理放在一个四。 我已经非常疲惫的谷歌,没有find答案,以及我find答案,但他们似乎没有工作。
基本上,我使用avcodec_decode_video2()
解码帧,然后sws_scale()
将帧转换为RGB,然后glTexSubImage2D()
创build一个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文件,以确保它们实际上是渲染,它们是。
正在使用的文件是854×480的.wmv,这可能是问题吗? 事实上,我只是告诉它去512×256?
PS我看过这个堆栈溢出的问题,但没有帮助。
另外,我也有glEnable(GL_TEXTURE_2D)
,并通过加载一个正常的bmp来testing它。
编辑
我现在在屏幕上得到一个图像,但它是一个乱七八糟的乱七八糟,我猜是要做的事情变化的权力2(在解码, swscontext
和gluBuild2DMipmaps
如我的代码所示)。 我通常几乎是完全相同的代码,如上所示,只是我已经将glTexSubImage2D
更改为gluBuild2DMipmaps
并将types更改为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);
现在我已经把PIX_FMT_RGB24
的PixelFormat
avgpicture_get_size
了PIX_FMT_RGB24
,我也在SwsContext
做了这个,把SwsContext
改成了GL_RGB
,我得到了一个稍微好一点的图像,但是看起来我仍然缺less线条,并且还有点延伸:
另一个编辑
在遵循Macke的build议并将实际的分辨率传递给OpenGL后,我得到的帧几乎是正确的,但仍然有点偏斜,而且在黑白模式下,现在只能获得6fps而不是110fps:
PS
我有一个函数来保存sws_scale()
后的图像帧,他们出来的颜色和一切都很好,所以在OGL的东西使它B&W。
最后编辑
加工! 好吧,我现在正在工作,基本上我没有将纹理填充到2的幂,只是使用video的分辨率。
我在正确的glPixelStorei()中得到了正确的纹理,
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
此外,如果其他人有像我这样的subimage()
显示空白问题,你必须用glTexImage2D()
至less填充一次纹理,所以我在循环中使用它一次,然后使用glTexSubImage2D()
。
感谢Macke和datenwolf的帮助。
当你调用glTexSubImage2D
时,纹理是否被初始化?你需要调用glTexImage2D
(不是Sub )来初始化纹理对象。使用NULL作为数据指针,然后OpenGL将初始化纹理而不复制数据。回答
编辑
你不提供成像级别。 那么你是否禁用了mipmaping?
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILER, linear_interpolation ? GL_LINEAR : GL_NEAREST);
编辑2图像是颠倒的是没有惊喜,因为大多数图像格式有左上angular的起源,而OpenGL放置纹理图像的原点在左下angular。 你看到的那条带看起来像是错误的横行。
编辑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; }
我在一个项目中使用它,它在那里工作,所以这个代码被testing,sorting。
正在使用的文件是854×480的.wmv,这可能是问题吗? 事实上,我只是告诉它去512×256?
是!
条纹图案显然表明您的数据大小不匹配(行大小)。 (由于颜色是正确的,RGB与BGR对BGRA和N分量是正确的。)
你告诉OpenGL你上传的纹理是512×256(不是AFAICT)。 使用真实的尺寸(NPOT,你的卡应该支持它,如果它不古老)。
否则,请在将其上传为1024×512纹理之前调整/填充数据。
更新
我更熟悉OpenGL,你正在调用的其他function。
sxs_scale可能是你想要的(即缩放图像到一个锅大小)。 但是,缩放每一帧可能会很慢。
我会用填充(也就是说,将一个小图像(您的video)复制到一个大纹理的一部分(opengl)
其他一些提示:
- 你真的需要mipmap吗? 只有在需要平滑缩放纹理时才能生成这些纹理(通常只有在某些三维几何体上才需要)。
- 如果你正在渲染一个video,避免在运行时产生mipmap(特别是不要使用gluBuildMipMaps2D,因为它可能在软件中运行)。还有其他的方法, 如果你需要mipmapping(比如使用GL_GENERATE_MIPMAP纹理参数),速度会更快。 这个线程的更多信息。
- 避免重复调用glTexImage,因为这将创build一个新的纹理。 glTexSubImage只是更新纹理的一部分,这可能对你更好。
- 如果你想在一个步骤中上传纹理(这是出于性能的原因),但数据不太合适,请查看
glPixelStore
来设置像素和行的步幅。 我怀疑从sxs_scale / wmw给出的数据在每一行(黑线)的末尾都有一些填充。 大概是这样,每一行在8-16-32字节的边界上开始。