ffplay的源码地址:http://ffmpeg.org/doxygen/trunk/ffplay_8c_source.html
ffplay中有两个队列一个PacketQueue, 一个FrameQueue,下面我们对队列中AVPacket和AVFrame关系进行分析和说明。
一、AVPacket 和 AVFrame 结构含义
AVPacket
用于存储压缩的数据,分别包括有音频压缩数据,视频压缩数据和字幕压缩数据。它通常在解复用操作后存储压缩数据,然后作为输入传给解码器。或者由编码器输出然后传递给复用器。对于视频压缩数据,一个AVPacket通常包括一个视频帧。对于音频压缩数据,可能包括几个压缩的音频帧。
AVFrame
用于存储解码后的音频或者视频数据。AVFrame必须通过av_frame_alloc进行分配,通过av_frame_free释放。
两者之间的关系
av_read_frame得到压缩的数据包AVPacket,一般有三种压缩的数据包(视频、音频和字幕),都用AVPacket表示。
然后调用avcodec_send_packet 和 avcodec_receive_frame对AVPacket进行解码得到AVFrame。
注:从 FFmpeg 3.x 开始,avcodec_decode_video2 就被废弃了,取而代之的是 avcodec_send_packet 和 avcodec_receive_frame。
二、ffplay 队列关系
ffplay中有三种PacketQueue,分别为视频包队列、音频包队列和字幕包队列。
相应地也有三种FrameQueue,视频帧队列、音频帧队列和字幕帧队列。
队列的初始化工作是在stream_open函数中进行,分别通过packet_queue_init和frame_queue_init执行初始化操作。需要注意的是,初始化中PacketQueue没有手动分配AVPacket结构,而是直接使用解码过程中的AVPacket。FrameQueue中则是通过av_frame_alloc手动分配了AVFrame结构。
在read_thread函数中,通过av_read_frame函数读取数据包,然后调用packet_queue_put将AVPacket添加到PacketQueue中。
在video_thread函数中,通过get_video_frame函数读取数据帧,然后调用queue_picture将AVFrame添加到FrameQueue中。
那么两个队列是怎么联系起来的呢?通过分析read_thread函数可以知晓:
首先,创建解复用和解码所需要的数据结构。然后,分别通过stream_component_open函数打开三种数据流。最后,通过av_read_frame将解复用后的数据包分别添加到对应的PacketQueue中。在stream_component_open函数主要负责解码工作,ffplay中为解码工作专门设置了一个数据结构Decoder,Decoder结构中有一个成员queue,这个queue就是指的输入的PacketQueue,通过decoder_init函数来指定PacketQueue。这个工作就是在stream_component_open中执行的。指定PacketQueue之后通过get_video_frame函数从PacketQueue中解码出AVFrame结构,最后通过queue_picture函数将解码得到的帧添加到FrameQueue。