陆小曼超详细解析FFplay之音视频解码线程


1.解码线程
ffplay的解码线程独?于数据读线程 , 并且每种类型的流(AVStream)都有其各?的解码线程 , 如:
(1)video_thread?于解码video stream 。
(2)audio_thread?于解码audio stream 。
(3)subtitle_thread?于解码subtitle stream 。
为?便阅读 , 先列?张表格 , 梳理各个变量、函数名称 。
陆小曼超详细解析FFplay之音视频解码线程
本文插图
其中PacketQueue?于存放从read_thread取到的各?播放时间内的AVPacket 。 FrameQueue?于存放各?解码后的AVFrame 。 Clock?于同步?视频 。 解码线程负责将PacketQueue数据解码为AVFrame , 并存?FrameQueue 。 对于不同流 , 其解码过程?同?异 。
/** * 解码器封装 */typedef struct Decoder {AVPacket pkt;// 数据包队列PacketQueue *queue;// 解码器上下?AVCodecContext *avctx;// 包序列int pkt_serial;// =0 , 解码器处于?作状态;=?0 , 解码器处于空闲状态int finished;// =0 , 解码器处于异常状态 , 需要考虑重置解码器;=1 , 解码器处于正常状 态int packet_pending;// 检查到packet队列空时发送 signal缓存 , read_thread读取数据SDL_cond *empty_queue_cond;// 初始化时是stream的start timeint64_t start_pts;// 初始化时是stream的time_baseAVRational start_pts_tb;// 记录最近?次解码后的frame的pts , 当解出来的部分帧没有有效的pts 时则使?next_pts进?推算int64_t next_pts;// next_pts的单位AVRational next_pts_tb;// 线程句柄SDL_Thread *decoder_tid; } Decoder;解码器相关的函数
decoder我们ffplay?定义 , 重新封装的 。avcodec才是ffmpeg的提供 。
初始化解码器 。
void decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue,SDL_cond *empty_queue_cond);
启动解码器
int decoder_start(Decoder *d, int (*fn)(void *), const char *thread_name, void* arg)
解帧
int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub);
终?解码器
void decoder_abort(Decoder *d, FrameQueue *fq);
销毁解码器
void decoder_destroy(Decoder *d);
使??法如下:
(1)启动解码线程
decoder_init() 。
decoder_start() 。
(2)解码线程具体流程
decoder_decode_frame()
(3)退出解码线程
decoder_abort()
decoder_destroy()
2.视频解码线程
数据来源:从read_thread线程?来 。
数据处理:在video_thread进?解码 , 具体调?get_video_frame 。
数据出?:在video_refresh读取frame进?显示 。
video_thread() , 先看video_thead , 对于滤镜部分(CONFIG_AVFILTER定义部分) , 暂时不做分析, 需要知道有这个功能就可以 , 简化后的代码如下:
static int video_thread(void *arg){VideoState *is = arg;// 分配解码帧AVFrame *frame = av_frame_alloc();// ptsdouble pts;// 帧持续时间double duration;int ret;// 1 获取stream timebaseAVRational tb = is->video_st->time_base;// 2 获取帧率 , 以便计算每帧picture的durationAVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st , NULL);if (!frame)return AVERROR(ENOMEM);for (;;) {// 循环取出视频解码的帧数据// 3 解码获取?帧视频画?ret = get_video_frame(is, frame);if (ret < 0)goto the_end; //解码结束, 什么时候会结束if (!ret) //没有解码得到画?, 什么情况下会得不到解后的帧continue;// 4 计算帧持续时间和换算pts值为秒//没有帧率时则设置为0, 有帧率帧计算出帧间隔duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRat ional){frame_rate.den, 27 frame_r ate.num}) : 0);// 根据AVStream timebase计算出pts值, 单位为秒pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av _q2d(tb);// 5 将解码后的视频帧插?队列ret = queue_picture(is, frame, pts, duration, frame->pkt_pos , is->viddec.pkt_serial);// 6 释放frame对应的数据// 正常情况下frame对应的buf以被av_frame_move_refav_frame_unref(frame);if (ret < 0) // 返回值?于0则退出线程goto the_end;}the_end:av_frame_free(&frame); // 释放framereturn 0; }


推荐阅读