ijkplayer作为b站开源的播放器,在业界享誉盛名,深受开辟者喜欢,由于底层采取ffmpeg解码,支持主流的流媒体协议,再软件兼容度上非常高;本日我们就针对ijkplayer做一些源码分析,资助那些喜欢ijkplayer但是苦于2w多行代码无从动手的同砚们
系列文章讲授将按照以下次序举行分析,以方便读者明白;
1.重要结构体分析
2.读数据线程剖析
3.音频包剖析和音频播放剖析
4.视频包剖析和视频渲染剖析
5.音视频同步剖析
6.别的功能剖析
ijkplayer 中利用到的重要数据结构体 VideoState 、Clock、MyAVPacketList 、PacketQueue、FrameQueue、AudioParams 、Decoder、Frame、 AVPacket、AVFrame;
为什么第一篇文章要先先容一下这些重要的结构体呢,由于ijkplayer内部的流程比力复杂,如果对这些常用的结构体没有一个清楚的了解,很容易在读源码的时间,迷失方向不知所云;
struct VideoState
VideoState 是一个很重要的结构体,在整个播放过程中,存储了播放器必要的许多参数及状态;
typedef struct VideoState { SDL_Thread *read_tid; //读线程句柄 SDL_Thread _read_tid; AVInputFormat *iformat; //demuxer int abort_request; // ==1 时退出播放 int force_refresh; //==1 时必要立刻革新画面 int paused; // ==1 体现停息,==0 体现播放 int last_paused; //暂存 “播放/停息”状态 int queue_attachments_req; int seek_req; // 标识一次seek 操作 int seek_flags; //seek标识,比方AVSEEK_FLAG_BYTE int64_t seek_pos; //seek的目标位置(位置+增量) int64_t seek_rel; //本次seek的位置增量 AVFormatContext *ic; //输入媒体的上下文 int realtime; // ==1 体现是否是实时流 Clock audclk; //音频时钟 Clock vidclk; //视频时钟 Clock extclk; //外部时钟 FrameQueue pictq; //视频Frame 队列 FrameQueue subpq; //字母Frame 队列 FrameQueue sampq; //音频Frame 队列 Decoder auddec; //音频解码器 Decoder viddec; //视频解码器 Decoder subdec; //字母解码器 int audio_stream; //音频流索引 int av_sync_type; //音视频同步范例 void *handle; 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; //音频AVPacket队列 int audio_hw_buf_size; uint8_t *audio_buf; //指向必要重采样的数据 uint8_t *audio_buf1; //指向重采样后的数据 short *audio_new_buf; /* for soundtouch buf */ unsigned int audio_buf_size; //待播放的一帧音频数据 unsigned int audio_buf1_size; //申请到的音频缓冲区的巨细 unsigned int audio_new_buf_size; int audio_buf_index; /* in bytes */ int audio_write_buf_size; int audio_volume; //音量 int muted; // 静音体现 ,==1 体现静音,==0 体现非静音 struct AudioParams audio_src; //输入媒体的音频参数 struct AudioParams audio_tgt; //SDL 支持的输出音频参数 struct SwrContext *swr_ctx; //音频重采样上下文 int frame_drops_early; int frame_drops_late; int continuous_frame_drops_early; 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; double last_vis_time; int subtitle_stream; AVStream *subtitle_st; //字幕流 PacketQueue subtitleq; //字幕流AVPacket队列 double frame_timer; //记录末了一帧播放的时候 double frame_last_returned_time; double frame_last_filter_delay; int video_stream; //视频流索引 AVStream *video_st; //视频流Stream PacketQueue videoq; //视频AVPacket队列 double max_frame_duration; //一帧最大隔断 struct SwsContext *img_convert_ctx; //视频尺寸转换上下文 int eof; //是否读取竣事标识 char *filename; //媒体文件名 int width, height, xleft, ytop;//宽、高、x起始坐标,y起始坐标 int step; //==1 步进播放模式、 ==0 其他模式 int last_video_stream, last_audio_stream, last_subtitle_stream; SDL_cond *continue_read_thread; //当队列数据满后休眠,可通过condition 唤起读线程 /* extra fields */ SDL_mutex *play_mutex; // only guard state, do not block any long operation SDL_Thread *video_refresh_tid; SDL_Thread _video_refresh_tid; int buffering_on; int pause_req; int dropping_frame; int is_video_high_fps; // above 30fps int is_video_high_res; // above 1080p PacketQueue *buffer_indicator_queue; volatile int latest_video_seek_load_serial; volatile int latest_audio_seek_load_serial; volatile int64_t latest_seek_load_start_at; int drop_aframe_count; int drop_vframe_count; int64_t accurate_seek_start_time; volatile int64_t accurate_seek_vframe_pts; volatile int64_t accurate_seek_aframe_pts; int audio_accurate_seek_req; int video_accurate_seek_req; SDL_mutex *accurate_seek_mutex; SDL_cond *video_accurate_seek_cond; SDL_cond *audio_accurate_seek_cond; volatile int initialized_decoder; int seek_buffering;} VideoState;struct Clock
时钟结构体 Clock,用于音视频同步
typedef struct Clock { double pts; //当前帧(待播放)体现时间戳 double pts_drift; //当前时钟与当前体系时钟的差值 double last_updated; //末了一根刺更新的体系时钟 double speed; //时钟速率(控制播放速率) int serial; //时钟序列号 int paused; // 是否停息 (==1 体现停息) int *queue_serial; /* pointer to the current packet queue serial, used for obsolete clock detection */} Clock;struct MyAVPacketList
MyAVPacketList 可以明白为队列的一个节点,通过next 字段访问下一个节点;
在整个播放器中PacketQueue 队列中存储的就是MyAVPacketList数据,而MyAVPacketList数据中存放的就是未解码的AVPacket结构体(音频/视频/字幕帧);
typedef struct MyAVPacketList { AVPacket pkt; //解封装后的数据 struct MyAVPacketList *next; //下一个节点 int serial; //播放序列号} MyAVPacketList;struct PacketQueue
存储未解码数据AVPacket的Packet 队列;(在整个播放过程中充当生产者的脚色)
在 ffplay.c 函数中,界说了一些PacketQueue的方法
- packet_queue_init (初始化队列)
- packet_queue_destroy (烧毁队列)
- packet_queue_abort (停止队列)
- packet_queue_start (启用队列)
- packet_queue_flush (清空队列内的packet)
- packet_queue_get (获取第一个节点)
- packet_queue_get_or_buffering (去缓冲等候水位后获取第一个节点)
- packet_queue_put (入队列一个节点)
- packet_queue_put_nullpacket (入队列一个空包)
- packet_queue_put_private (存入一个节点,叫醒packet_queue_get 等候锁)
typedef struct PacketQueue { MyAVPacketList *first_pkt, *last_pkt; //队列头、队列尾 int nb_packets; //当前队列元素数量 int size; //当前队列全部元素的巨细总和 int64_t duration; //队列内的数据可播放时间 int abort_request; //用户哀求退出标识 int serial; //序列号 SDL_mutex *mutex; //用于维护PacketQueue的多线程安全 SDL_cond *cond; //用于读、写线程的相互关照 MyAVPacketList *recycle_pkt; int recycle_count; int alloc_count; int is_buffer_indicator;} PacketQueue;struct FrameQueue
解码后的音视频数据结构体是 AVFrame,字幕是AVSubtitle,解码后的数据存放在FrameQueue该结构体内部;FrameQueue的设计是一个环形缓冲区,数组的巨细在初始化的时间设置,每一个FrameQueue有一个写端和一个读端,写端位于解码线程,读端位于播放线程;
在 ffplay.c 函数中,界说了一些FrameQueue的方法
- frame_queue_init (初始化)
- frame_queue_destory (烧毁)
- frame_queue_signal (发送唤起信号)
- frame_queue_peek (获取当前Frame)
- frame_queue_peek_next (获取当前Frame 的下一个Frame)
- frame_queue_peek_last (获取上一个Frame)
- frame_queue_peek_writable (获取一个可写Frame)
- frame_queue_peek_readable (获取一个可读Frame)
- frame_queue_push (更新写索引)
- frame_queue_next (更新读索引)
- frame_queue_nb_remaining (获取队列Frame节点个数)
- frame_queue_last_pos (获取近来播放Frame 在媒体文件的位置)
typedef struct FrameQueue { Frame queue[FRAME_QUEUE_SIZE]; //Frame队列数组 int rindex; //读索引 int windex; //写索引 int size; //当前队列总帧数 int max_size; //可存储的最大帧数 int keep_last; //队列保持末了一针的数据不开释(==1) int rindex_shown; //初始化为0 ,共同 keep_last =1 利用 SDL_mutex *mutex; //互斥量 SDL_cond *cond; //条件变量 PacketQueue *pktq; //数据包缓冲队列} FrameQueue;AudioParams
音频参数,用于SDL 播放大概 音频重采样的设置结构体
typedef struct AudioParams { int freq; //采样率 int channels; //通道数 int64_t channel_layout; //通道结构 enum AVSampleFormat fmt; //音频采样格式(采样深度 + 分列模式) int frame_size; //一个单位占用的字节数 int bytes_per_sec; //一秒时间的字节数} AudioParams;Decoder
- decoder_init (初始化解码器)
- decoder_destroy (烧毁解码器)
- decoder_decode_frame (解码)
- decoder_abort (中断解码器)
typedef struct Decoder { AVPacket pkt; //当前数据包 AVPacket pkt_temp; // PacketQueue *queue; //数据包队列 AVCodecContext *avctx; //解码上下文 int pkt_serial; //包序列号 int finished; //解码器工作是否完成(==0体现工作中,==1 体现空闲) int packet_pending; // 是否非常 (==0体现非常,==1 体现正常) int bfsc_ret; uint8_t *bfsc_data; SDL_cond *empty_queue_cond; int64_t start_pts; //初始化stream的 开始时间 AVRational start_pts_tb; //初始化stream 的time_base int64_t next_pts; //近来一次解码后的frame 的pts(有的frame没有pts,用于推算 pts) AVRational next_pts_tb; /next_pts 的time_base SDL_Thread *decoder_tid; SDL_Thread _decoder_tid; SDL_Profiler decode_profiler; Uint64 first_frame_decoded_time; int first_frame_decoded;} Decoder;struct Frame
typedef struct Frame { AVFrame *frame; //媒体数据帧(音频/视频) AVSubtitle sub; //用于字幕 int serial; //播放序列号 double pts; //展示时间戳,单位s double duration; //该帧连续时间,单位s int64_t pos; //该帧再文件中的字节位置#ifdef FFP_MERGE SDL_Texture *bmp;#else SDL_VoutOverlay *bmp;#endif int allocated; int width; //媒体帧宽 int height; //媒体帧高 int format; //媒体范例 AVRational sar; //宽高比(未知环境则为0/1) int uploaded; //用于记录该帧是否体现过} Frame; |