ijkplayer 源码剖析1(重要结构体先容)

程序员 2024-9-11 06:02:23 90 0 来自 中国
ijkplayer作为b站开源的播放器,在业界享誉盛名,深受开辟者喜欢,由于底层采取ffmpeg解码,支持主流的流媒体协议,再软件兼容度上非常高;本日我们就针对ijkplayer做一些源码分析,资助那些喜欢ijkplayer但是苦于2w多行代码无从动手的同砚们
系列文章讲授将按照以下次序举行分析,以方便读者明白;
1.重要结构体分析
2.读数据线程剖析
3.音频包剖析和音频播放剖析
4.视频包剖析和视频渲染剖析
5.音视频同步剖析
6.别的功能剖析
1.jpg 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;
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 20:26, Processed in 0.117854 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表