音视频开发-FFplay视频播放流程( 五 )


video_thread的创建是在read_thread --> stream_component_open函数中:
is->video_tid = SDL_CreateThread(video_thread, is);read_thread线程同样分为三部分:

  • 初始化部分:主要包括AVFrame创建和AVFilterGraph创建 。对应ffplay.c文件中的1881-1895行代码;
  • 循环解码部分:主要包括pause和resume操作处理、读取packet处理、AVFILTER处理、然后是将picture写入视频队列中以及每次解码后的清理动作 。对应ffplay.c文件中的1897-1966行代码;
  • 反初始化部分:主要包括刷新codec中的数据、释放AVFilterGraph、释放AVPacket以及释放AVFrame 。对应ffplay.c文件中的1972-1978行代码;
初始化部分
该线程的初始化就是创建了AVFrame和AVFilterGraph , 其中AVFilterGraph还是和编译宏包含 , 如果没有打开CONFIG_AVFILTER可以直接省略 。
is->video_tid = SDL_CreateThread(video_thread, is);… …AVFrame *frame = av_frame_alloc();#if CONFIG_AVFILTERAVFilterGraph *graph = avfilter_graph_alloc();#endif循环解码部分
主要包括pause和resume操作处理、读取packet处理、AVFILTER处理、然后是将picture写入视频队列中以及每次解码后的清理动作 。
pause和resume操作处理
video_thread中的关于pause和resume的处理比较简单 , 就是如果是pause状态就delay(线程sleep):
while (is->paused && !is->videoq.abort_request)SDL_Delay(10);读取packet处理
avcodec_get_frame_defaults(frame);av_free_packet(&pkt);ret = get_video_frame(is, frame, &pkt, &serial);//关于frame的一些处理av_frame_unref(frame);从上述代码中可以看出 , 一个frame(和packet)的完整生命流程 。
在ffmpeg-tutorial项目中tutorial01.c中的例子是使用avcodec_alloc_frame()来申请并设置default value的操作 , 但是在这里就分成了两步:av_frame_alloc()然后
avcodec_get_frame_defaults(frame) 。
av_free_packet实际上清空上一次get_video_frame中获取的packet数据 , 函数本身是有异常处理的 , 所以连续调用两次av_free_packet是没有问题的 。
get_video_frame函数中主要部分是packet_queue_get然后avcodec_decode_video2 , 即从packet队列中读取数据然后进行解码 , 具体内容有机会另开文章进行讲解 。
AVFILTER处理
AVFILTER处理是一个比较模块化很高的处理部分 , 大致流程包括以下几步:
  1. 释放旧的AVFilterGraph并创建一个新的:avfilter_graph_free()和avfilter_graph_alloc()
  2. 配置video filters:configure_video_filters
  3. 向buffersrc中添加frame:av_buffersrc_add_frame
  4. 情况原有的frame和packet:av_frame_unref、avcodec_get_frame_defaults和av_free_packet
  5. 从buffersink中读取处理后的frame:av_buffersink_get_frame_flags
简单的理解就是:
音视频开发-FFplay视频播放流程

文章插图
 
将picture写入视频队列
如果需要avfilter处理 , 那么处理完后或者不需要avfilter处理 , 解码完成后的frame会调用queue_picture写入到picture队列中 。具体细节不详解 。
解码后的清理动作
使用完packet后 , 必须从frame中释放出来:av_frame_unref 。如api说明:Unreference allthe buffers referenced by frame and reset the frame fields.
for循环跳出条件
有以下几种情况下会break出for循环:
  • get_video_frame读数据失败 , 并且返回<0:该函数失败条件和read_thread其实是一致的 , 即当q->abort_request为true时;
  • configure_video_filters配置filter失败:该函数失败的情况下 , 我遇到的一种就是avfilter_graph_create_filter创建crop filter时失败 , 原因在于在configureffmpeg时没有把filter配置打开 , 导致只有默认的几个filter , 其他一些特性filter都没有添加进行;
  • av_buffersrc_add_frame添加frame失败:该函数属于api , 不详解;
  • queue_picture保存picture失败:该函数的失败条件是当is->videoq.abort_request为true时;
即正常情况下 , 有两种退出模式:
  1. 正常播放完成后退出 , 此时会通过get_video_frame读数据失败退出
  2. 如果是按ESCAPE和Q键退出 , 会直接退出 , 则不会等到 , 直接在queue_picture函数失败


    推荐阅读