乐于分享
好东西不私藏

认真读读ffplay源码(一)

认真读读ffplay源码(一)

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函数实际就这点东西,本质就是做初始化的操作,其它的主干函数就放到后面文章慢慢拆解。