ffplay是ffmpeg中的一个播放音视频流的功能,现简要对其进行分析:
1. 图1是ffplay的主干代码流程
1)在stream_open函数之前,主要是对入参的一些分析,所有codec,demux以及相关protocool的注册
注册两个信号量:一个是给中断使用,一个是给终端操作使用(比如键盘,鼠标等的一些动作)
2)在stream_open函数中,先是对video,audio,subtitle三个format的消息队列进行初始化,
再对收发数据的队列进行初始化。然后调用SDL_CreateThread函数开启 read_thread()线程。
3)在创建完线程后,进入event_loop函数中,开始等待来自终端设备的事件,比如,鼠标,键盘的一些操作,以及推出等命令,
具体哪些事件可以详见代码中的switch中每个case的分之。
4)在event_loop函数中,会通过refresh_loop_wait_event()函数等待来自外部一些事件,然后对相应的事件进行响应。
5)其实event_loop就是ffplay的一个线程,它并没有退出,而是一直在这里loop。
2. 图2是read_thread线程的主要流程:
1) 通过调用avformat_open_input()函数来确认要用哪种demux来处理到来的data stream,主要就是读取一段数据流,
对该流进行分析看它与哪种demux最为匹配,具体怎么做没有细看。
2) avformat_find_stream_info()函数根据最前端的一段数据流来分析该流是 视频,音频或字幕,若是音频或者视频是哪种
格式的,是编码还是解码(这个根据输入输出的格式来确认),同时找到对应的编码器或者解码器。不是很清楚为什么需要
在这里调用函数try_decode_frame()对新来的数据流进行解码。
3) av_find_best_stream()函数是找到对应的编码器或解码器。
4) 函数stream_component_open()主要分别打开audio, video, subtitle这三个模式的各自的线程
audio_thread, video_thread, subtitle_thread。
5) 线程read_thread()在完成上述任务后,开始循环处理来自外界的一些信息:
a) abort 退出请求
b) pause 暂停请求
c) seek 跳转请求 (视频的前进后退等的请求)
d) 判断如何缓冲buffer满了,休息等待10ms后再准备向各个模式的缓冲buffer中放数据
e) 调用av_read_frame() 获取data packet后,再通过调用packet_queue_put()函数将数据放倒对应的缓冲buffer中。
调用packet_queue_put函数放置packet,没有经过copy而是直接将packet的指针赋给对应模式的缓冲链表中的队尾,
这样减少了copy,节省了时间。
Notes:
1)结构体VideoState{}开辟的这段内存对于后续的所有线程 read_thread, audio_thread,video_thread以及main_thread
来讲就是全局的内存,可以被他们所共用,所以在使用的时候一定要格外的小心,防止多个线程向同一数据去写操作。
2)后面所有的线程所用的全局内存都是VideoState{}中的一小部分。
第一天开始看ffplay的代码,现在已经离开音频这个行业了,但是还是对这个行业非常感兴趣。
为了不失去自己的技术敏感度,只能自己业余时间来读读代码,锻炼锻炼脑袋了。
很感谢网上哪些技术大牛的博客以及文章,迅速的在自己的mac上搭建好了eclipse的环境以及
ffmpeg编译顺利通过。