《ffplay分析(从启动到读取线程的操作)》
《ffplay分析(视频解码线程的操作)》
《ffplay分析(音频解码线程的操作)》
ffplay的数据结构分析
ffplay的播放显示是使用SDL(Simple DirectMedia Layer 跨平台多媒体开发库)处理的。
ffplay结构体内的元素也是和ffmpeg一样,一个结构体内的元素不是对应单一的功能点,比如同一个结构体就会有视频、音频、字幕的信息,因为都是用同一个结构体来存储,在函数调用时就可以用一个函数接口。
struct VideoState(ffplay中最大的一个封装结构,所有信息被包含在内)
typedef struct VideoState {
//读线程SDL线程句柄
SDL_Thread *read_tid;
//指向输入的封装格式
AVInputFormat *iformat;
//退出请求,(1 = 请求退出)
int abort_request;
//立即刷新请求(1 = 请求刷新)
int force_refresh;
//播放暂停状态(1 = 暂停, 0 = 播放)
int paused;
//暂存播放暂停状态(最近一次的状态)
int last_paused;
int queue_attachments_req;
//标识一次seek的请求
int seek_req;
//seek标志(AVSEEK_FLAG_BYTE等)
int seek_flags;
//请求seek的目标位置(当前位置 + 增量 )
int64_t seek_pos;
//本次请求seek的位置增量
int64_t seek_rel;
int read_pause_return;
//iformat 输入封装格式的上下文
AVFormatContext *ic;
//标志是否为实时流数据(1 = 实时流)
int realtime;
//音频时钟
Clock audclk;
//视频时钟
Clock vidclk;
//外部时钟
Clock extclk;
//视频Frame队列(解码后)
FrameQueue pictq;
//字幕Frame队列(解码后)
FrameQueue subpq;
//音频Frame队列(解码后)
FrameQueue sampq;
//音频解码器
Decoder auddec;
//视频解码器
Decoder viddec;
//字幕解码器
Decoder subdec;
//音频流索引(比如有国语、粤语就是不同音频流的)
int audio_stream;
//音视频同步类型(默认audio master)
int av_sync_type;
//当前音频帧的PTS + 当前帧的Duration
double audio_clock;
//播放序列,seek可改变这个值
int audio_clock_serial;
//当av_sync_type != audio master时使用
double audio_diff_cum; /* used for AV difference average computation */
double audio_diff_avg_coef;
double audio_diff_threshold;
int audio_diff_avg_count;
//音频流
AVStream *audio_st;
//音频Packet队列(解码前)
PacketQueue audioq;
//SDL音频缓冲区的大小(字节)
int audio_hw_buf_size;
//指向待播放的一帧音频数据,指向的数据区将被拷贝到SDL音频缓冲区
//重采样后就是audio_buf1
//需要重采样的数据
uint8_t *audio_buf;
//重采样后的数据
uint8_t *audio_buf1;
//audio_buf的大小
unsigned int audio_buf_size; /* in bytes */
//audio_buf1的大小
unsigned int audio_buf1_size;
//更新拷贝位置 当前音频帧中已拷入SDL音频缓冲区
// 的位置索引(指向第一个待拷贝字节,因为拷贝到SDL可能不是完整一帧音频数据拷贝的)
int audio_buf_index; /* in bytes */
//当前音频帧中尚未拷入SDL音频缓冲区的数据量
int audio_write_buf_size;
//音量
int audio_volume;
//是否静音(1 = 静音, 0 = 正常)
int muted;
//音频参数
struct AudioParams audio_src;
#if CONFIG_AVFILTER
struct AudioParams audio_filter_src;
#endif
//SDL支持的音频参数,重采样转换(文件的音频参数SDL不支持就要转:audio_src->audio_tgt)
struct AudioParams audio_tgt;
//音频重采样的上下文
struct SwrContext *swr_ctx;
//丢弃视频Packet计数
int frame_drops_early;
//丢弃视频Frame计数
int frame_drops_late;
enum ShowMode {
SHOW_MODE_NONE = -1, SHOW_MODE_VIDEO = 0, SHOW_MODE_WAVES, SHOW_MODE_RDFT, SHOW_MODE_NB
} show_mode;
//音频波形显示时使用
int16_t sample_array[SAMPLE_ARRAY_SIZE];
int sample_array_index;
int last_i_start;
RDFTContext *rdft;
int rdft_bits;
FFTSample *rdft_data;
int xpos;
double last_vis_time;
SDL_Texture *vis_texture;
//字幕显示
SDL_Texture *sub_texture;
//视频显示
SDL_Texture *vid_texture;
//字幕流索引
int subtitle_stream;
//字幕流索引
AVStream *subtitle_st;
//字幕Packet队列(解码前)
PacketQueue subtitleq;
//记录最后一帧播放的时间
double frame_timer;
double frame_last_returned_time;
double frame_last_filter_delay;
//视频流索引
int video_stream;
//视频流
AVStream *video_st;
//视频Packet队列(解码前)
PacketQueue videoq;
//一帧最大间隔
double max_frame_duration; // maximum duration of a frame - above this, we consider the jump a timestamp discontinuity
//视频格式尺寸转换
struct SwsContext *img_convert_ctx;
//字幕格式尺寸转换
struct SwsContext *sub_convert_ctx;
//是否读取结束
int eof;
//文件名
char *filename;
//宽、高、x坐标起始、y坐标起始
int width, height, xleft, ytop;
//1 = 步进模式播放 ,0 = 其他模式
int step;
#if CONFIG_AVFILTER
int vfilter_idx;
AVFilterContext *in_video_filter; // the first filter in the video chain
AVFilterContext *out_video_filter; // the last filter in the video chain
AVFilterContext *in_audio_filter; // the first filter in the audio chain
AVFilterContext *out_audio_filter; // the last filter in the audio chain
AVFilterGraph *agraph; // audio filter graph
#endif
//保留最近一次的相应audio、video、subtitle流的steam index
int last_video_stream, last_audio_stream, last_subtitle_stream;
//条件变量,当读取数据队列满了后进入休眠时,可以通过该condition唤醒读线程
SDL_cond *continue_read_thread;
} VideoState;
struct Clock(时间封装)
typedef struct Clock {
//当前帧(待播放)显示时间戳, 播放后当前帧变成上一帧
double pts; /* clock base */
//当前pts与当前系统时钟的差值,audio、video对于该值是独立
double pts_drift; /* clock base minus time at which we updated the clock */
//最后一次更新的系统时钟
double last_updated;
//时钟速度控制,用于控制播放速度
double speed;
//播放序列,就是一段连续的播放动作,一个seek操作会启动一段新的播放序列
int serial; /* clock is based on a packet with this serial */
//播放标识 (1 = 暂停状态)
int paused;
//指向当前 PacketQueue 的序列
int *queue_serial; /* pointer to the current packet queue serial, used for obsolete clock detection */
} Clock;
struct MyAVPacketList(解码前数据,PacketQueue的一个节点)
typedef struct MyAVPacketList {
//解封装后的数据(解码前)
AVPacket pkt;
//下一个节点
struct MyAVPacketList *next;
//播放序列
int serial;
} MyAVPacketList;
struct PacketQueue(解码前的Packet队列)
typedef struct PacketQueue {
//队列头,队列尾
MyAVPacketList *first_pkt, *last_pkt;
//包数量,队列元素数量
int nb_packets;
//队列所有元素的数据大小总和
int size;
//队列所有元素的数据播放持续时间总和
int64_t duration;
//用户退出标志
int abort_request;
//播放序列号
int serial;
//维护PacketQueue的互斥量
SDL_mutex *mutex;
//条件变量,读、写相互通知
SDL_cond *cond;
} PacketQueue;
struct Frame(解码后数据,FrameQueue队列的元素)
typedef struct Frame {
//数据帧
AVFrame *frame;
//字幕
AVSubtitle sub;
//播放序列
int serial;
//显示时间戳
double pts; /* presentation timestamp for the frame */
//该帧持续时间
double duration; /* estimated duration of the frame */
//该帧在文件中的字节位置
int64_t pos; /* byte position of the frame in the input file */
//宽
int width;
//高
int height;
// 对于图像为(enum AVPixelFormat),
// 对于声音则为(enum AVSampleFormat)
int format;
//图像宽高比
AVRational sar;
//记录该帧是否已经显示过
int uploaded;
//垂直翻转(1 = 180度 , 0 = 正常 )
int flip_v;
} Frame;
struct FrameQueue(解码后的Frame队列)
typedef struct FrameQueue {
//Frame数组
Frame queue[FRAME_QUEUE_SIZE];
//读索引
int rindex;
//写索引
int windex;
//当前总帧数
int size;
//可存储最大帧数
int max_size;
// 等于 1 时,表示队列里保持最后一帧的数据不释放,只有在销毁队列在释放
int keep_last;
//初始化为0 ,和keep_last一起使用
int rindex_shown;
//互斥量
SDL_mutex *mutex;
//条件变量
SDL_cond *cond;
//数据包缓冲队列(解码前队列)
PacketQueue *pktq;
} FrameQueue;
struct AudioParams(音频参数)
typedef struct AudioParams {
//采样率
int freq;
//通道数
int channels;
//通道布局(比如:立体声)
int64_t channel_layout;
//采样格式(比如:AV_SAMPLE_FMT_S16)
enum AVSampleFormat fmt;
//一个采样单元占用的字节数
int frame_size;
//一秒中音频占用字节数
int bytes_per_sec;
} AudioParams;
struct Decoder(解码器数据封装)
typedef struct Decoder {
AVPacket pkt;
//数据包队列(解码前)
PacketQueue *queue;
//解码器上下文
AVCodecContext *avctx;
//包序列
int pkt_serial;
//解码器工作状态(0 = 工作, !0= 空闲)
int finished;
//解码器异常状态(0 = 异常,1 = 正常)
int packet_pending;
//条件变量
SDL_cond *empty_queue_cond;
//初始化时stream的start time
int64_t start_pts;
//初始化时stream的time base
AVRational start_pts_tb;
//记录最后一次解码的frame的pts,如果解码出来的帧是无效的pts就用这个值来
//推算
int64_t next_pts;
//next_pts的单位
AVRational next_pts_tb;
//解码线程
SDL_Thread *decoder_tid;
} Decoder;