《ffplay分析(从启动到读取线程的操作)》
《ffplay分析(视频解码线程的操作)》
《ffplay分析(音频解码线程的操作)》
《ffplay 分析(音频从Frame(解码后)队列取数据到SDL输出)》
《ffplay分析 (视频从Frame(解码后)队列取数据到SDL输出)》
《ffplay分析 (音视频同步:主时钟为音频)》
《ffplay分析 (暂停 / 播放处理)》
《ffplay分析 (seek操作处理)》
ffplay的数据结构分析 (版本:ffmpeg-4.2.1)
- struct VideoState(ffplay中最大的一个封装结构,所有信息被包含在内)
- struct Clock(时间封装)
- struct MyAVPacketList(解码前数据,PacketQueue的一个节点)
- struct PacketQueue(解码前的Packet队列)
- struct Frame(解码后数据,FrameQueue队列的元素)
- struct FrameQueue(解码后的Frame队列)
- struct AudioParams(音频参数)
- struct Decoder(解码器数据封装)
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 + 当前帧的Durationdouble 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_AVFILTERstruct 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_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//保留最近一次的相应audio、video、subtitle流的steam indexint 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;//一个采样单元占用的字节数(channels * fmt)int frame_size;//一秒中音频占用字节数(channels * fmt * freq)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 timeint64_t start_pts;//初始化时stream的time baseAVRational start_pts_tb;//记录最后一次解码的frame的pts,如果解码出来的帧是无效的pts就用这个值来//推算int64_t next_pts;//next_pts的单位AVRational next_pts_tb;//解码线程SDL_Thread *decoder_tid;
} Decoder;