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处理是一个比较模块化很高的处理部分 , 大致流程包括以下几步:
- 释放旧的AVFilterGraph并创建一个新的:avfilter_graph_free()和avfilter_graph_alloc()
- 配置video filters:configure_video_filters
- 向buffersrc中添加frame:av_buffersrc_add_frame
- 情况原有的frame和packet:av_frame_unref、avcodec_get_frame_defaults和av_free_packet
- 从buffersink中读取处理后的frame:av_buffersink_get_frame_flags
文章插图
将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时;
- 正常播放完成后退出 , 此时会通过get_video_frame读数据失败退出
- 如果是按ESCAPE和Q键退出 , 会直接退出 , 则不会等到 , 直接在queue_picture函数失败
推荐阅读
- UNIX 平台 C 程序开发心得
- WebRTC记录音视频流
- HarmonyOS之手机应用开发体验
- 恶意软件开发者公布密钥并宣布退出江湖
- Android开发精通Framework是真的可以为所欲为
- MyBatis自动生成工具,开发编码好帮手
- HarmonyOS App开发之组件布局类
- 如何在开发中规避硬编码
- 家居装修类的在线接单APP定制开发案例赏析
- 开发者的瑞士军刀「GitHub 热点速览 v.22.04」