ffmpeg-ffplay代码架构简述
全局变量
/* Minimum SDL audio buffer size, in samples. */
// 最小音频缓冲
#define SDL_AUDIO_MIN_BUFFER_SIZE 512
/* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */
// 计算实际音频缓冲大小,并不需要太频繁回调,这里设置的是最大音频回调次数是每秒30次
#define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30/* Step size for volume control in dB */
// 音频控制 以db为单位的步进
#define SDL_VOLUME_STEP (0.75)/* no AV sync correction is done if below the minimum AV sync threshold */
// 最低同步阈值,如果低于该值,则不需要同步校正
#define AV_SYNC_THRESHOLD_MIN 0.04
/* AV sync correction is done if above the maximum AV sync threshold */
// 最大同步阈值,如果大于该值,则需要同步校正
#define AV_SYNC_THRESHOLD_MAX 0.1
/* If a frame duration is longer than this, it will not be duplicated to compensate AV sync */
// 帧补偿同步阈值,如果帧持续时间比这更长,则不用来补偿同步
#define AV_SYNC_FRAMEDUP_THRESHOLD 0.1
/* no AV correction is done if too big error */
// 同步阈值。如果误差太大,则不进行校正
#define AV_NOSYNC_THRESHOLD 10.0/* maximum audio speed change to get correct sync */
// 正确同步的最大音频速度变化值(百分比)
#define SAMPLE_CORRECTION_PERCENT_MAX 10/* external clock speed adjustment constants for realtime sources based on buffer fullness */
// 根据实时码流的缓冲区填充时间做外部时钟调整
// 最小值
#define EXTERNAL_CLOCK_SPEED_MIN 0.900
// 最大值
#define EXTERNAL_CLOCK_SPEED_MAX 1.010
// 步进
#define EXTERNAL_CLOCK_SPEED_STEP 0.001/* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
// 使用差值来实现平均值
#define AUDIO_DIFF_AVG_NB 20/* polls for possible required screen refresh at least this often, should be less than 1/fps */
// 刷新频率 应该小于 1/fps
#define REFRESH_RATE 0.01/* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
/* TODO: We assume that a decoded and resampled frame fits into this buffer */
// 采样大小
#define SAMPLE_ARRAY_SIZE (8 * 65536)#define CURSOR_HIDE_DELAY 1000000#define USE_ONEPASS_SUBTITLE_RENDER 1// 冲采样标志
static unsigned sws_flags = SWS_BICUBIC;// 包列表结构
typedef struct MyAVPacketList {AVPacket pkt;struct MyAVPacketList *next;int serial;
} MyAVPacketList;// 待解码包队列
typedef struct PacketQueue {MyAVPacketList *first_pkt, *last_pkt;int nb_packets;int size;int64_t duration;int abort_request;int serial;SDL_mutex *mutex;SDL_cond *cond;
} PacketQueue;#define VIDEO_PICTURE_QUEUE_SIZE 3
#define SUBPICTURE_QUEUE_SIZE 16
#define SAMPLE_QUEUE_SIZE 9
#define FRAME_QUEUE_SIZE FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))// 音频参数
typedef struct AudioParams {int freq; // 频率int channels; // 声道数int64_t channel_layout; // 声道设计,单声道,双声道还是立体声enum AVSampleFormat fmt; // 采样格式int frame_size; // 采样大小int bytes_per_sec; // 每秒多少字节
} AudioParams;// 时钟
typedef struct Clock {double pts; // 时钟基准 /* clock base */double pts_drift; // 更新时钟的差值 /* clock base minus time at which we updated the clock */double last_updated; // 上一次更新的时间double speed; // 速度int serial; // 时钟基于使用该序列的包 /* clock is based on a packet with this serial */int paused; // 停止标志int *queue_serial; // 指向当前数据包队列序列的指针,用于过时的时钟检测 /* pointer to the current packet queue serial, used for obsolete clock detection */
} Clock;/* Common struct for handling all types of decoded data and allocated render buffers. */
// 解码帧结构
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; // 帧的高度int format; // 格式AVRational sar; // 额外参数int uploaded; // 上载int flip_v; // 反转
} Frame;// 解码后的帧队列
typedef struct FrameQueue {Frame queue[FRAME_QUEUE_SIZE]; // 队列数组int rindex; // 读索引int windex; // 写索引int size; // 大小int max_size; // 最大大小int keep_last; // 保持上一个int rindex_shown; // 读显示SDL_mutex *mutex; // 互斥变量SDL_cond *cond; // 条件变量PacketQueue *pktq;
} FrameQueue;// 时钟同步类型
enum {AV_SYNC_AUDIO_MASTER, // 音频作为同步,默认以音频同步 /* default choice */AV_SYNC_VIDEO_MASTER, // 视频作为同步AV_SYNC_EXTERNAL_CLOCK, // 外部时钟作为同步 /* synchronize to an external clock */
};// 解码器结构
typedef struct Decoder {AVPacket pkt; // 包AVPacket pkt_temp; // 中间包PacketQueue *queue; // 包队列AVCodecContext *avctx; // 解码上下文int pkt_serial; // 包序列int finished; // 是否已经结束int packet_pending; // 是否有包在等待SDL_cond *empty_queue_cond; // 空队列条件变量int64_t start_pts; // 开始的时间戳AVRational start_pts_tb; // 开始的额外参数int64_t next_pts; // 下一帧时间戳AVRational next_pts_tb; // 下一帧的额外参数SDL_Thread *decoder_tid; // 解码线程
} Decoder;// 视频状态结构
typedef struct VideoState {SDL_Thread *read_tid; // 读取线程AVInputFormat *iformat; // 输入格式int abort_request; 终止int force_refresh; 强制刷新int paused; // 停止int last_paused; // 最后停止int queue_attachments_req; // 队列附件请求int seek_req; // 查找请求int seek_flags; // 查找标志int64_t seek_pos; // 查找位置int64_t seek_rel; // int read_pause_return; // 读停止返回AVFormatContext *ic; // 解码格式上下文int realtime; // 是否实时码流Clock audclk; // 音频时钟Clock vidclk; // 视频时钟Clock extclk; // 外部时钟FrameQueue pictq; // 视频队列FrameQueue subpq; // 字幕队列FrameQueue sampq; // 音频队列Decoder auddec; // 音频解码器Decoder viddec; // 视频解码器Decoder subdec; // 字幕解码器int audio_stream; // 音频码流Idint av_sync_type; // 同步类型double audio_clock; // 音频时钟int audio_clock_serial; // 音频时钟序列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; // 音频码流PacketQueue audioq; // 音频包队列int audio_hw_buf_size; // 硬件缓冲大小uint8_t *audio_buf; // 音频缓冲区uint8_t *audio_buf1; // 音频缓冲区1unsigned int audio_buf_size; // 音频缓冲大小 /* in bytes */unsigned int audio_buf1_size; // 音频缓冲大小1int audio_buf_index; // 音频缓冲索引 /* in bytes */int audio_write_buf_size; // 音频写入缓冲大小int audio_volume; // 音量int muted; // 是否静音struct AudioParams audio_src; // 音频参数
#if CONFIG_AVFILTER struct AudioParams audio_filter_src; // 音频过滤器
#endifstruct AudioParams audio_tgt; // 音频参数struct SwrContext *swr_ctx; // 音频转码上下文int frame_drops_early; // 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; // 音频TextureSDL_Texture *sub_texture; // 字幕TextureSDL_Texture *vid_texture; // 视频Textureint subtitle_stream; // 字幕码流IdAVStream *subtitle_st; // 字幕码流PacketQueue subtitleq; // 字幕包队列double frame_timer; // 帧计时器double frame_last_returned_time; // 上一次返回时间double frame_last_filter_delay; // 上一个过滤器延时int video_stream; // 视频码流IdAVStream *video_st; // 视频码流PacketQueue videoq; // 视频包队列double max_frame_duration; // 最大帧显示时间 // maximum duration of a frame - above this, we consider the jump a timestamp discontinuitystruct SwsContext *img_convert_ctx; // 视频转码上下文struct SwsContext *sub_convert_ctx; // 字幕转码上下文int eof; // 结束标志char *filename; // 文件名int width, height, xleft, ytop; // 宽高,其实坐标int step; // 步进#if CONFIG_AVFILTERint vfilter_idx; // 过滤器索引AVFilterContext *in_video_filter; // 第一个视频滤镜 // the first filter in the video chainAVFilterContext *out_video_filter; // 最后一个视频滤镜 // the last filter in the video chainAVFilterContext *in_audio_filter; // 第一个音频过滤器 // the first filter in the audio chainAVFilterContext *out_audio_filter; // 最后一个音频过滤器 // the last filter in the audio chainAVFilterGraph *agraph; // 音频过滤器 // audio filter graph
#endif// 上一个视频码流Id、上一个音频码流Id、上一个字幕码流Idint last_video_stream, last_audio_stream, last_subtitle_stream;SDL_cond *continue_read_thread; // 连续读线程
} VideoState;/* options specified by the user */
static AVInputFormat *file_iformat; // 文件输入格式
static const char *input_filename; // 输入文件名
static const char *window_title; // 标题
static int default_width = 640; // 默认宽度
static int default_height = 480; // 默认高度
static int screen_width = 0; // 屏幕宽度
static int screen_height = 0; // 屏幕高度
static int audio_disable; // 是否禁止播放声音
static int video_disable; // 是否禁止播放视频
static int subtitle_disable; // 是否禁止播放字幕
static const char* wanted_stream_spec[AVMEDIA_TYPE_NB] = {0};
static int seek_by_bytes = -1; //
static int display_disable; // 显示禁止
static int borderless; //
static int startup_volume = 100; // 起始音量
static int show_status = 1; // 显示状态
static int av_sync_type = AV_SYNC_AUDIO_MASTER; // 同步类型
static int64_t start_time = AV_NOPTS_VALUE; // 开始时间
static int64_t duration = AV_NOPTS_VALUE; // 间隔
static int fast = 0; // 快速
static int genpts = 0; //
static int lowres = 0; // 慢速
static int decoder_reorder_pts = -1; // 解码器重新排列时间戳
static int autoexit; // 否自动退出
static int exit_on_keydown; // 是否按下退出
static int exit_on_mousedown; // 是否鼠标按下退出
static int loop = 1; // 循环
static int framedrop = -1; // 舍弃帧
static int infinite_buffer = -1; // 缓冲区大小限制 =1表示不限制(is->realtime实时传输时不限制
static enum ShowMode show_mode = SHOW_MODE_NONE; // 显示类型
static const char *audio_codec_name; // 音频解码器名称
static const char *subtitle_codec_name; // 字幕解码器名称
static const char *video_codec_name; // 视频解码器名称
double rdftspeed = 0.02; // 自适应滤波器的速度
static int64_t cursor_last_shown; // 上一次显示光标
static int cursor_hidden = 0; // 光标隐藏
#if CONFIG_AVFILTER
static const char **vfilters_list = NULL; // 视频滤镜
static int nb_vfilters = 0; // 视频滤镜数量
static char *afilters = NULL; // 音频滤镜
#endif
static int autorotate = 1; // 是否自动旋转/* current context */
static int is_full_screen; // 是否全屏
static int64_t audio_callback_time; // 音频回调时间static AVPacket flush_pkt; // 刷新的包#define FF_QUIT_EVENT (SDL_USEREVENT + 2)static SDL_Window *window; // 窗口
static SDL_Renderer *renderer; // 渲染器
设计的流程
包含文件读取、解封装、解码、音视频输出、音视频同步,流程如下图所示:
ffplay中使用的线程
(1)读线程。读取文件、解封装
(2)音频解码线程。解码音频压缩数据为PCM数据。
(3)视频解码线程。解码视频压缩数据为图像数据。
(4)音频输出线程。基于SDL播放,该线程实际上是SDL的内部线程。
(5)视频输出线程。基于SDL播放,该线程为程序主线程。