《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;