FFmpeg的FFplay框架分析

1.框架分析

FFmpeg的FFplay框架分析

文章插图
 
ffplay.c是FFmpeg源码?带的播放器,调?FFmpeg和SDL API实现?个?常有?的播放器 。例如哔哩哔哩著名开源项?ijkplayer也是基于ffplay.c进??次开发 。ffplay实现了播放器的主体功能,掌握其原理对于我们独?开发播放器?常有帮助 。看看整体的框架,如下图:
从整体上,分这几个大的模块,数据读取,AVpacket缓存队列,音频解码,视频解码,AVframe缓存队列,声音和视频输出,音视频同步等模块组成 。
FFmpeg的FFplay框架分析

文章插图
 
首先进入stream_open,stream_open主要的工作是创建音视频解码前和后的数据缓存队列,初始化时钟,包括音频,视频,外部时钟等,初始化数据读取线程read_thread 。源码如下:
static VideoState *stream_open(const char *filename, AVInputFormat *iformat){VideoState *is;is = av_mallocz(sizeof(VideoState));if (!is)return NULL;is->filename = av_strdup(filename);if (!is->filename)goto fail;is->iformat = iformat;is->ytop= 0;is->xleft= 0;/* start video display *///初始化帧队列if (frame_queue_init(&is->pictq, &is->videoq, VIDEO_PICTURE_QUEUE_SIZE, 1) < 0)goto fail;if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) < 0)goto fail;if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) < 0)goto fail;//初始化packet队列if (packet_queue_init(&is->videoq) < 0 ||packet_queue_init(&is->audioq) < 0 ||packet_queue_init(&is->subtitleq) < 0)goto fail;if (!(is->continue_read_thread = SDL_CreateCond())) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %sn", SDL_GetError());goto fail;}//初始化视频时钟init_clock(&is->vidclk, &is->videoq.serial);//初始化音频时钟init_clock(&is->audclk, &is->audioq.serial);//初始化外部时钟init_clock(&is->extclk, &is->extclk.serial);is->audio_clock_serial = -1;if (startup_volume < 0)av_log(NULL, AV_LOG_WARNING, "-volume=%d < 0, setting to 0n", startup_volume);if (startup_volume > 100)av_log(NULL, AV_LOG_WARNING, "-volume=%d > 100, setting to 100n", startup_volume);startup_volume = av_clip(startup_volume, 0, 100);startup_volume = av_clip(SDL_MIX_MAXVOLUME * startup_volume / 100, 0, SDL_MIX_MAXVOLUME);is->audio_volume = startup_volume;is->muted = 0;is->av_sync_type = av_sync_type;//初始化数据读取线程is->read_tid= SDL_CreateThread(read_thread, "read_thread", is);if (!is->read_tid) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateThread(): %sn", SDL_GetError());fail:stream_close(is);return NULL;}return is;} 
看看数据读取线程read_thread做了什么?
大致就是一个解封转过程,然后把解封装的packet放到packet队列 。分别调用avformat_alloc_context(分配解封装上下文),avforamt_open_input(打开文件或网络流,内存数据),avformat_find_stream_info(找到相关流信息),stream_component_open(打开音视频流),av_read_frame(解封转),packet_queue_put(放入到帧队列) 。源码如下,详细请看注释:
typedef struct MyAVPacketList {AVPacket pkt;struct MyAVPacketList *next;int serial;} MyAVPacketList;typedef struct PacketQueue {//链表MyAVPacketList *first_pkt, *last_pkt;//多少个packetint nb_packets;//每个packet大小int size;//每个packet时长int64_t duration;//请求标志int abort_request;int serial;//锁SDL_mutex *mutex;//条件变量SDL_cond *cond;} PacketQueue; 
然后把解封装的数据,放到packet队列 。看看packet的封装结构,实际就是一个链表 。详细源码如下:
typedef struct MyAVPacketList {AVPacket pkt;struct MyAVPacketList *next;int serial;} MyAVPacketList;typedef struct PacketQueue {//链表MyAVPacketList *first_pkt, *last_pkt;//多少个packetint nb_packets;//每个packet大小int size;//每个packet时长int64_t duration;//请求标志int abort_request;int serial;//锁SDL_mutex *mutex;//条件变量SDL_cond *cond;} PacketQueue;视频解码线程,然后就从video的packet 队列取数据,看看做了什么?大致就是使用packet_queue_get去取packet,然后发送解码,avcode_send_packet,avcode_receive_frame,并刷新解码后的frame队列 。详细看看源码,如下:
可以看出ffplay.c还支持过滤器功能,功能还是很全 。
FFmpeg的FFplay框架分析

文章插图
 

FFmpeg的FFplay框架分析

文章插图
 
取数据 。
FFmpeg的FFplay框架分析

文章插图
 
可以看出音视频和subtitle的所调用的函数是不一样 。


推荐阅读