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(在解码, swscontextgluBuild2DMipmaps如我的代码所示)。 我通常几乎是完全相同的代码,如上所示,只是我已经将glTexSubImage2D更改为gluBuild2DMipmaps并将types更改为GL_RGBA

这是框架的样子:

作为OpenGL纹理Ffmpeg乱码

再次编辑

刚才意识到我没有展示如何设置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_RGB24PixelFormat avgpicture_get_sizePIX_FMT_RGB24 ,我也在SwsContext做了这个,把SwsContext改成了GL_RGB ,我得到了一个稍微好一点的图像,但是看起来我仍然缺less线条,并且还有点延伸:

Ffmpeg乱码OpenGL的纹理2

另一个编辑

在遵循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字节的边界上开始。