在多媒体的世界里,播放器是离用户最近的一环,它将数字编码的音频和视频数据转化为生动的视听体验。ffplay 播放器作为一款强大而备受关注的工具,其背后隐藏着一系列精妙的结构体,它们协同工作,共同完成了从数据读取、解码、渲染到最终呈现的复杂流程。
以下是播放器的一个简单的流程,
当我们启动 ffplay 播放器,它首先会与数据源建立连接,无论是本地文件、网络流还是其他输入源。。紧接着,数据被源源不断地送入解码器,将压缩编码的数据转换为可处理的原始格式。
在这一流程中,各种关键的结构体开始发挥作用。它们像是精心设计的齿轮,紧密咬合,驱动着播放器的每一个环节。分析源码时,结构体就像是源码的目录,可以对整个播放器的运作流程有一个简单的理解。所以看这部分的内容时,不用纠结于每一个字段的意义,有一个大概的印象即可。
ffplay的核心:VideoState
以下是添加了详细注释的结构体 VideoState
:
typedef struct VideoState {
SDL_Thread *read_tid; // 读线程的线程ID
const AVInputFormat *iformat; // 输入格式
int abort_request; // 中断请求标志
int force_refresh; // 强制刷新标志
int paused; // 播放是否暂停标志
int last_paused; // 上一次的暂停状态
int queue_attachments_req; // 是否请求附带图片(如MP3或AAC文件的专辑封面等)
int seek_req; // 是否请求跳转
int seek_flags; // 跳转标志
int64_t seek_pos; // 跳转位置
int64_t seek_rel; // 跳转相对位置
int read_pause_return; // 发送暂停请求的结果(例如RTSP暂停消息是否成功)
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; // 音频流的索引
int av_sync_type; // 音视频同步类型
double audio_clock; // 当前音频时钟值
int audio_clock_serial; // 音频时钟序列号
double audio_diff_cum; // 音频差异累计值(用于计算平均差异)
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; // 重采样音频缓冲区
unsigned int audio_buf_size; // 音频缓冲区大小(字节)
unsigned int audio_buf1_size; // 重采样音频缓冲区大小(字节)
int audio_buf_index; // 音频缓冲区播放位置
int audio_write_buf_size; // 当前音频缓冲区中未播放的数据大小
int audio_volume; // 音频音量
int muted; // 是否静音
struct AudioParams audio_src; // 音频源参数
struct AudioParams audio_filter_src; // 音频滤波源参数
struct 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; // 上一次计算的音频显示起始索引
AVTXContext *rdft; // 快速傅里叶变换上下文
av_tx_fn rdft_fn; // 快速傅里叶变换函数指针
int rdft_bits; // 快速傅里叶变换的位数
float *real_data; // 实部数据
AVComplexFloat *rdft_data; // 复数数据
int xpos; // x 位置
double last_vis_time; // 上一次可视化时间
/*******************渲染纹理相关************************/
SDL_Texture *vis_texture; // 音频可视化纹理
SDL_Texture *sub_texture; // 字幕纹理
SDL_Texture *vid_texture; // 视频纹理
int subtitle_stream; // 字幕流的索引
AVStream *subtitle_st; // 字幕流
PacketQueue subtitleq; // 字幕包队列
double frame_timer; // 帧定时器
double frame_last_returned_time; // 上一帧的显示时间戳
double frame_last_filter_delay; // 上一帧的滤镜延迟
int video_stream; // 视频流的索引
AVStream *video_st; // 视频流
PacketQueue videoq; // 视频包队列
double max_frame_duration; // 最大帧持续时间
struct SwsContext *sub_convert_ctx; // 字幕转换上下文
int eof; // 文件结束标志
char *filename; // 文件名
int width; // 播放窗口宽度
int height; // 播放窗口高度
int xleft; // 显示区域的左偏移
int ytop; // 显示区域的上偏移
int step; // 步进模式(单帧模式)
int vfilter_idx; // 视频滤镜索引
AVFilterContext *in_video_filter; // 视频输入滤镜上下文
AVFilterContext *out_video_filter; // 视频输出滤镜上下文
AVFilterContext *in_audio_filter; // 音频输入滤镜上下文
AVFilterContext *out_audio_filter; // 音频输出滤镜上下文
AVFilterGraph *agraph; // 音频滤镜图
int last_video_stream; // 上一次的视频流索引
int last_audio_stream; // 上一次的音频流索引
int last_subtitle_stream; // 上一次的字幕流索引
SDL_cond *continue_read_thread; // 读线程条件变量
} VideoState;