在上篇文章中介绍了如果搭建一个基于ffmpeg的播放器框架
本文在上篇文章的基础上,继续讨论如何将ffmpeg解码出的视频帧进行渲染显示
1、视频帧渲染
上篇文章中介绍了如何基于ffmpeg搭建一个视频播放器框架,运行程序后可以看到,除了生成几张图片外,程序好像什么也做不了。
这是因为ffmpeg通过其封装的api及组件,为我们屏蔽了不同视频封装格式及编码格式的差异,以统一的api接口提供给开发者使用,开发者不需要了解每种编码方式及封装方式具体的技术细节,只需要调用ffmpeg提供的api就可以完成解封装和解码的操作了。
至于视频帧的渲染及音频帧的播放,ffmpeg就无能为力了,因此需要借助类似sdl库等其他第三方组件来完成。
这里讲述如何使用sdl库完成视频帧的渲染,sdl在底层封装了opengl图形库,sdl提供的api简化了opengl的绘图操作,为开发者提供了很多便利的操作,当然,你也可以采用其他系统支持的图形库来绘制视频帧。
sdl库的编译安装详见[公众号:断点实验室]的前述文章 [ffmpeg播放器实现详解 - 框架搭建]。
1.1 渲染环境搭建
一个视频帧在显示前,需要准备一个用于显示视频的窗口对象,以及附着在窗口上的画布对象
创建SDL窗口,并指定图像尺寸
// 创建SDL窗口,并指定图像尺寸
screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);
创建画布对象
// 创建画布对象
bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen);
1.2 视频帧渲染
在窗口和画布对象创建完成后,就可以开始视频帧的渲染显示了。
在对画布对象操作前,需要对其加线程锁保护,避免其他线程对画布中的内容进行竞争性访问(后面的内容很快会涉及到多线程环境的开发)。对线程操作不熟悉的同学可以了解一下在多线程环境下,多个线程对临界区资源的竞争性访问与线程同步操作。
SDL_LockYUVOverlay(bmp);//locks the overlay for direct access to pixel data
向画布注入解码后的视频帧
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pict.data, pict.linesize);
在画布对象的视频帧填充操作完成后,释放sdl线程锁。
//Unlocks a previously locked overlay. An overlay must be unlocked before it can be displayed
SDL_UnlockYUVOverlay(bmp);
对视频帧的渲染
SDL_DisplayYUVOverlay(bmp, &rect);//图像渲染
可以看到,由于借助了sdl封装的api绘图接口,视频帧的渲染还是非常容易的,如果直接采用opengl绘图,绘制过程会相对复杂些,例程主要的目的是为了介绍ffmpeg的使用,因此,这里采用sdl简化了渲染流程。
1.3 项目源码编译
本例程和上篇文章中用到的编译方法完全一样
tutorial02: tutorial02.c
gcc -o tutorial02 -g3 tutorial02.c -I${FFMPEG_INCLUDE} -I${SDL_INCLUDE} \
-L${FFMPEG_LIB} -lavutil -lavformat -lavcodec -lswscale -lswresample -lz -lm \
`sdl-config --cflags --libs`
clean:
rm -rf tutorial02
执行make命令开始编译,编译完成后,可在源码目录生成名为[tutorial02]的可执行文件。
可通过ldd命令查询当前可执行文件所有依赖的动态库。
1.4 验证
执行[tutorial02 url]命令,可以看到有画面输出了。
./tutorial02 rtmp://58.200.131.2:1935/livetv/hunantv