OpenGL+ffmpeg在全屏模式下速度较慢
OpenGL + ffmpeg slow in fullscreen mode
我正在尝试使用ffmpeg和OpenGL+SDL播放视频文件。播放速度很慢,而且闪烁。代码是从不同的博客/网站积累而来的,我真的不太确定发生了什么。很抱歉发布这么长的代码,但这是最小化的版本。我的实际代码在窗口模式下也不能很好地运行。不知怎的,下面的版本在窗口模式下播放得很流畅。
#ifndef INT64_C
#define INT64_C(c) (int64_t)(c)
#define UINT64_C(c) (uint64_t)(c)
#endif
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
#include <SDL.h>
#include <GL/gl.h>
int fullscreen = 1, videoStream = -1, frameFinished=0;
const PixelFormat CONV_FORMAT = PIX_FMT_RGB24;
const char *fname = "moviesample.mp4";
AVFormatContext *pFormatCtx = NULL;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = 0, *pFrameRGB = 0;
AVPacket packet;
AVDictionary *optionsDict = NULL;
struct SwsContext *sws_ctx = NULL;
GLuint texture_video;
void av_init();
void draw_frame();
int main(int argc, const char **argv) {
SDL_Event event;
av_init();
uint16_t width = fullscreen ? 1600 : pCodecCtx->width;
uint16_t height = fullscreen ? 900 : pCodecCtx->height;
SDL_Init(SDL_INIT_EVERYTHING);
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
SDL_SetVideoMode(width, height, 32,
SDL_OPENGL | SDL_HWPALETTE | SDL_HWSURFACE | SDL_HWACCEL |
(fullscreen ? SDL_FULLSCREEN : 0)
);
glEnable(GL_TEXTURE_2D);
glClearColor(0.0f, 0.4f, 0.4f, 0.0f);
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );
glShadeModel( GL_SMOOTH );
glGenTextures(1, &texture_video);
glBindTexture(GL_TEXTURE_2D, texture_video);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, pCodecCtx->width, pCodecCtx->height,
0, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB->data[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
sws_ctx = sws_getCachedContext(sws_ctx, pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, CONV_FORMAT,
SWS_BICUBIC, NULL, NULL, NULL);
while (1) {
draw_frame();
SDL_GL_SwapBuffers();
SDL_PollEvent(&event);
switch(event.type) {
case SDL_QUIT:
SDL_Quit();
exit(0);
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE) {
SDL_Quit();
exit(0);
}
break;
default:
break;
}
}
return 0;
}
void draw_frame() {
if (av_read_frame(pFormatCtx, &packet)>=0) {
if(packet.stream_index==videoStream) {
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if(frameFinished) {
sws_scale (sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
pFrameRGB->linesize);
glBindTexture( GL_TEXTURE_2D, texture_video );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pCodecCtx->width,
pCodecCtx->height, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB->data[0]);
}
glClear(GL_COLOR_BUFFER_BIT);
glScalef(1.0f, -1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, 0.0f);
glEnd();
glScalef(1.0f, -1.0f, 1.0f);
}
av_free_packet(&packet);
} else {
av_seek_frame(pFormatCtx, videoStream, 0, AVSEEK_FLAG_FRAME);
}
}
void av_init() {
av_register_all();
avformat_open_input(&pFormatCtx, fname, NULL, NULL);
avformat_find_stream_info(pFormatCtx, NULL);
for(uint8_t i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
videoStream=i;
break;
}
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
avcodec_open2(pCodecCtx, pCodec, &optionsDict);
pFrame = avcodec_alloc_frame();
pFrameRGB = avcodec_alloc_frame();
int bytes = avpicture_get_size(CONV_FORMAT, pCodecCtx->width,
pCodecCtx->height);
uint8_t *video_buffer = (uint8_t*)av_malloc( bytes * sizeof(uint8_t) );
avpicture_fill((AVPicture *)pFrameRGB, video_buffer, CONV_FORMAT,
pCodecCtx->width, pCodecCtx->height);
}
在全屏中,您可能会获得vsync,这意味着SDL_GL_SwapBuffers()
每帧将阻塞16ms左右。
要模拟窗口模式下的效果,请在主while(1)
循环的末尾添加SDL_Delay(16)
。
重写draw_frame()
,使其泵浦libav
,直到它得到下一帧,而不是每个主循环只泵浦一次,希望你得到一帧:
// g++ main.cpp `pkg-config sdl gl libswscale libavcodec libavformat --libs --cflags` && SDL_VIDEO_FULLSCREEN_HEAD=0 ./a.out
#ifndef INT64_C
#define INT64_C(c) (int64_t)(c)
#define UINT64_C(c) (uint64_t)(c)
#endif
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
#include <SDL.h>
#include <GL/gl.h>
int fullscreen = 1, videoStream = -1, frameFinished=0;
const PixelFormat CONV_FORMAT = PIX_FMT_RGB24;
const char *fname = "/home/genpfault/vid.mpg";
AVFormatContext *pFormatCtx = NULL;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = 0, *pFrameRGB = 0;
AVPacket packet;
AVDictionary *optionsDict = NULL;
struct SwsContext *sws_ctx = NULL;
GLuint texture_video;
void av_init();
void next_frame();
int main(int argc, const char **argv) {
SDL_Event event;
av_init();
uint16_t width = fullscreen ? 1920 : pCodecCtx->width;
uint16_t height = fullscreen ? 1200 : pCodecCtx->height;
SDL_Init(SDL_INIT_EVERYTHING);
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
SDL_SetVideoMode(width, height, 32,
SDL_OPENGL |
(fullscreen ? SDL_FULLSCREEN : 0)
);
glEnable(GL_TEXTURE_2D);
glClearColor(0.0f, 0.4f, 0.4f, 0.0f);
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );
glShadeModel( GL_SMOOTH );
glGenTextures(1, &texture_video);
glBindTexture(GL_TEXTURE_2D, texture_video);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, pCodecCtx->width, pCodecCtx->height,
0, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB->data[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
sws_ctx = sws_getCachedContext(sws_ctx, pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, CONV_FORMAT,
SWS_BICUBIC, NULL, NULL, NULL);
while (1) {
while( SDL_PollEvent(&event) )
{
switch(event.type) {
case SDL_QUIT:
SDL_Quit();
exit(0);
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE) {
SDL_Quit();
exit(0);
}
break;
default:
break;
}
}
next_frame();
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture( GL_TEXTURE_2D, texture_video );
glScalef(1.0f, -1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, 0.0f);
glEnd();
glScalef(1.0f, -1.0f, 1.0f);
SDL_GL_SwapBuffers();
}
return 0;
}
void next_frame()
{
while( true )
{
if( av_read_frame(pFormatCtx, &packet) >= 0 )
{
if( packet.stream_index == videoStream )
{
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if(frameFinished)
{
sws_scale (sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
pFrameRGB->linesize);
glBindTexture( GL_TEXTURE_2D, texture_video );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, pCodecCtx->width,
pCodecCtx->height, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB->data[0]);
break;
}
}
av_free_packet(&packet);
}
else
{
av_seek_frame(pFormatCtx, videoStream, 0, AVSEEK_FLAG_FRAME);
}
}
}
void av_init() {
av_register_all();
avformat_open_input(&pFormatCtx, fname, NULL, NULL);
avformat_find_stream_info(pFormatCtx, NULL);
for(uint8_t i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
videoStream=i;
break;
}
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
avcodec_open2(pCodecCtx, pCodec, &optionsDict);
pFrame = avcodec_alloc_frame();
pFrameRGB = avcodec_alloc_frame();
int bytes = avpicture_get_size(CONV_FORMAT, pCodecCtx->width,
pCodecCtx->height);
uint8_t *video_buffer = (uint8_t*)av_malloc( bytes * sizeof(uint8_t) );
avpicture_fill((AVPicture *)pFrameRGB, video_buffer, CONV_FORMAT,
pCodecCtx->width, pCodecCtx->height);
}
相关文章:
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 为什么在保护模式下继承升级不起作用
- 如何在全屏模式下(在OpenGL中)使背景透明
- 为什么在读取文件大小时文件IO速度会发生变化
- 为什么使用__LINE_的代码在发布模式下在MSVC下编译,而不是在调试模式下
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 此模式的C++RegEx
- avrogencpp能为模式中的每种类型生成单独的头文件吗
- 使用可变模板的Broadcaster/Listener模式
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- 文件系统:复制功能的速度秘诀是什么
- c++方法参数只能在linux的发布模式下自行更改
- 资源管理设计模式
- 学习多线程C++:添加线程不会使执行速度更快,即使它看起来应该
- 使用 mod_gsoap 部署服务时,如何在 Gsoap 中更改 soap 上下文的模式?
- C++ 无法在字符数组中使用 for 循环打印字母模式
- 小字符串优化(调试与发布模式)
- 是与调试模式或发布模式相关的程序的运行速度
- 重复发送http请求,速度更快(异步模式)
- OpenGL+ffmpeg在全屏模式下速度较慢