ffplay是小而全的一个播放器,据说是音视频领域中适合新手入门之最,没有之一。
本篇咱就来拜读拜读,谈谈个人的一些理解吧!
代码中的主干函数
1、stream_open
创建大管家VideoState结构体
2、read_thread
读视频流线程
3、stream_component_open
打开不同类型的解码器
4、audio_thread
音频解码线程
5、video_thread
视频解码线程
6、subtitle_thread
字幕解码线程
7、event_loop
事件循环处理
8、video_refresh
视频刷新及同步处理
9、video_display
显示视频、字幕
10、sdl_audio_callback
音频播放
代码中的主干结构体
VideoState
大管家,贯穿全代码。
Clock
时钟,音视频同步的基石
FrameQueue
缓冲帧队列
PacketQueue
缓冲包队列
依赖库
FFmpeg + SDL2
从main开始读代码
首先需要说明的是:ffmpeg API一般都支持AVDictionary的参数设置,我们读代码时直接忽略掉,也不会给它传任何参数,代码逻辑才是我们的主要关注点。
main代码中概括起来就是:
1、解析程序入参
2、初始化SDL2
3、创建Windows窗口
4、创建大管家VideoState结构体。
5、进入事件循环
1、解析程序入参
ret = parse_options(NULL, argc, argv, options, opt_input_file);这个艰巨的任务由它完成,它具体是怎么解析参数的嘛,who cares?
2、初始化SDL2
flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
if (SDL_Init (flags)){...}
SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
SDL_EventState(SDL_USEREVENT, SDL_IGNORE);SDL2初始化视频子系统、音频子系统、定时器,并忽略掉SYSWMEVENT和USEREVENT事件。
SDL_SYSWMEVENT系统窗口事件基本是一直触发的,程序中属于是杂音的存在,所以忽略掉减少干扰。
SDL_USEREVENT用户自定义事件,ffplay中几乎就没用到,这里就是个声明式忽略。但是我们是发现ffplay起始是定义了自定义事件的:
#define FF_QUIT_EVENT (SDL_USEREVENT + 2)用来退出程序。这里可能有个疑问,明明有SDL_QUIT事件,为什么还要定义FF_QUIT_EVENT呢?因为SDL_QUIT由系统层面的事件,代码中退出使用FF_QUIT_EVENT是在语义上明确区分是系统让关的还是自己让关的。
3、创建Windows窗口
// 创建窗口
window = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags);
// 创建渲染器
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);关键就这两行,创建一个窗口再创建一个渲染器。源码中还涉及一些vulken的使用,有点太高级反而影响整体逻辑的阅读理解,所以也忽略掉吧。
创建渲染器时,使用SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC,表示使用硬件加速,并且同步显示。
4、创建大管家VideoState结构体。
ret = stream_open(is, filename);stream_open函数会创建管家的,这里只要判断管家是正常的家伙的就行了。
5、进入事件循环
event_loop(is);
/* never returns */
return0;函数event_loop会进入一个不会主动结束的死循环,处理用户事件等等。
最后
main函数实际就这点东西,本质就是做初始化的操作,其它的主干函数就放到后面文章慢慢拆解。
夜雨聆风