其实主要作用和800行代码写一个播放器作用中一样 就是一个主要封装类
保存 其中各项的主要内容
个人觉得比较需要注意到的 几个非重点常用对象
1 abort_request 就是判断这个时候有没有 退出播放 如果退出了 那就一系列退出操作
2 force_refresh 视频播放时候用的
3 av_sync_type ffplay 支持3种同步方式
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
- typedef struct VideoState {
- 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的位置增量
- int read_pause_return;
- AVFormatContext *ic; // iformat的上下文
- 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; // 音视频同步类型, 默认audio master
-
- double audio_clock; // 当前音频帧的PTS+当前帧Duration
- int audio_clock_serial; // 播放序列,seek可改变此值
- // 以下4个参数 非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;
- // end
-
- AVStream *audio_st; // 音频流
- PacketQueue audioq; // 音频packet队列
- int audio_hw_buf_size; // SDL音频缓冲区的大小(字节为单位)
- // 指向待播放的一帧音频数据,指向的数据区将被拷入SDL音频缓冲区。若经过重采样则指向audio_buf1,
- // 否则指向frame中的音频
- uint8_t *audio_buf; // 指向需要重采样的数据
- uint8_t *audio_buf1; // 指向重采样后的数据
- unsigned int audio_buf_size; // 待播放的一帧音频数据(audio_buf指向)的大小
- unsigned int audio_buf1_size; // 申请到的音频缓冲区audio_buf1的实际尺寸
- int audio_buf_index; // 更新拷贝位置 当前音频帧中已拷入SDL音频缓冲区
- // 的位置索引(指向第一个待拷贝字节)
- // 当前音频帧中尚未拷入SDL音频缓冲区的数据量:
- // audio_buf_size = audio_buf_index + audio_write_buf_size
- int audio_write_buf_size;
- int audio_volume; // 音量
- int muted; // =1静音,=0则正常
- struct AudioParams audio_src; // 音频frame的参数
- #if CONFIG_AVFILTER
- struct AudioParams audio_filter_src;
- #endif
- struct AudioParams audio_tgt; // SDL支持的音频参数,重采样转换:audio_src->audio_tgt
- struct SwrContext *swr_ctx; // 音频重采样context
- int frame_drops_early; // 丢弃视频packet计数
- int frame_drops_late; // 丢弃视频frame计数
-
- 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; // 音频Texture
-
- SDL_Texture *sub_texture; // 字幕显示
- SDL_Texture *vid_texture; // 视频显示
-
- int subtitle_stream; // 字幕流索引
- AVStream *subtitle_st; // 字幕流
- PacketQueue subtitleq; // 字幕packet队列
-
- 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; // 一帧最大间隔. above this, we consider the jump a timestamp discontinuity
- struct SwsContext *img_convert_ctx; // 视频尺寸格式变换
- struct SwsContext *sub_convert_ctx; // 字幕尺寸格式变换
- int eof; // 是否读取结束
-
- char *filename; // 文件名
- int width, height, xleft, ytop; // 宽、高,x起始坐标,y起始坐标
- int step; // =1 步进播放模式, =0 其他模式
-
- #if CONFIG_AVFILTER
- int vfilter_idx;
- AVFilterContext *in_video_filter; // the first filter in the video chain
- AVFilterContext *out_video_filter; // the last filter in the video chain
- AVFilterContext *in_audio_filter; // the first filter in the audio chain
- AVFilterContext *out_audio_filter; // the last filter in the audio chain
- AVFilterGraph *agraph; // audio filter graph
- #endif
- // 保留最近的相应audio、video、subtitle流的steam index
- int last_video_stream, last_audio_stream, last_subtitle_stream;
-
- SDL_cond *continue_read_thread; // 当读取数据队列满了后进入休眠时,可以通过该condition唤醒读线程
- } VideoState;
和名字一样 就是 解封装出来的packet 用链表的形式开始保存 啦啦啦啦
可以理解为是队列的⼀个节点。可以通过其 next 字段访问下一个节点
重点!!! 1 serial
ffplay中多处用到serial的概念,主要用来区分是否连续 数据,每做一次seek,该serial都会做+1的递增,以区分不同的播放序列。
说人话就是: 就是 seek 一次以后 以前的队列中的东西当然就不能用了呗 应该放弃的放弃 要free 的 free
- typedef struct MyAVPacketList {
- AVPacket pkt; //解封装后的数据
- struct MyAVPacketList *next; //下一个节点
- int serial; //播放序列
- } MyAVPacketList;
-
这里这个packeQueue 逻辑和前面800行代码播放器 逻辑一样
保存一些基本信息
这个packetqueue 是 同时作用于 视频 音频 和 字幕的
音频、视频、字幕流都有自己独立的PacketQueue
这里也看到了serial字段, MyAVPacketList的serial字段的赋值来自PacketQueue的serial,每个PacketQueue的serial是独立的。
- typedef struct PacketQueue {
- MyAVPacketList *first_pkt, *last_pkt; // 队首,队尾指针
- int nb_packets; // 包数量,也就是队列元素数量
- int size; // 队列所有元素的数据大小总和
- int64_t duration; // 队列所有元素的数据播放持续时间
- int abort_request; // 用户退出请求标志
- int serial; // 播放序列号,和MyAVPacketList的serial作用相同,但改变的时序稍微有点不同
- SDL_mutex *mutex; // 用于维持PacketQueue的多线程安全(SDL_mutex可以按pthread_mutex_t理解)
- SDL_cond *cond; // 用于读、写线程相互通知(SDL_cond可以按pthread_cond_t理解)
- } PacketQueue;
真正存储解码后音视频数据的结构体为AVFrame ,存储字幕则使用AVSubtitle,该Frame的设计是为了音频、视频、字幕帧通用,所以Frame结构体的设计类似AVFrame,部分成员变量只对不同类型有作用,比如sar只对视频有作用。 里面也包含了serial播放序列(每次seek时都切换serial),sar(图像的宽高比(16:9,4:3…),该值来 自AVFrame的sample_aspect_ratio变量。
- typedef struct Frame {
- AVFrame *frame; // 指向数据帧
- AVSubtitle sub; // 用于字幕
- int serial; // 帧序列,在seek的操作时serial会变化
- double pts; // 时间戳,单位为秒
- double duration; // 该帧持续时间,单位为秒
- int64_t pos; // 该帧在输入文件中的字节位置
- int width; // 图像宽度
- int height; // 图像高读
- int format; // 对于图像为(enum AVPixelFormat),
- // 对于声音则为(enum AVSampleFormat)
- AVRational sar; // 图像的宽高比(16:9,4:3...),如果未知或未指定则为0/1
- int uploaded; // 用来记录该帧是否已经显示过?
- int flip_v; // =1则垂直翻转, = 0则正常播放
- } Frame;
FrameQueue是一个环形缓冲区(ring buffer),是用数组实现的一个FIFO。数组方式的环形缓冲区适合于 事先明确了缓冲区的最大容量的情形。 ffplay中创建了三个frame_queue:音频frame_queue,视频frame_queue,字幕frame_queue。每一个frame_queue一个写端一个读端,写端位于解码线程,读端位于播放线程。
- typedef struct FrameQueue {
- Frame queue[FRAME_QUEUE_SIZE]; // FRAME_QUEUE_SIZE 最大size, 数字太大时会占用大量的内存,需要注意该值的设置
- 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;
FrameQueue的设计比如PacketQueue复杂,引入了读取节点但节点不出队列的操作、读取下一节点也不出队列等等的操作,FrameQueue具体操作函数见连接xxx
在这里插入代码片
int bytes_per_sec; // 一秒时间的字节数,比如采样率48Khz,2 channel,16bit,则一秒48000*2*16/8=192000
- typedef struct AudioParams {
- int freq; // 采样率
- int channels; // 通道数
- int64_t channel_layout; // 通道布局,比如2.1声道,5.1声道等
- enum AVSampleFormat fmt; // 音频采样格式,比如AV_SAMPLE_FMT_S16表示为有符号16bit深度,交错排列模式。
- int frame_size; // 一个采样单元占用的字节数(比如2通道时,则左右通道各采样一次合成一个采样单元)
- int bytes_per_sec; // 一秒时间的字节数,比如采样率48Khz,2 channel,16bit,则一秒48000*2*16/8=192000
- } AudioParams;
- typedef struct Decoder {
- AVPacket pkt;
- PacketQueue *queue; // 数据包队列
- AVCodecContext *avctx; // 解码器上下文
- int pkt_serial; // 包序列
- int finished; // =0,解码器处于工作状态;=非0,解码器处于空闲状态
- int packet_pending; // =0,解码器处于异常状态,需要考虑重置解码器;=1,解码器处于正常状态
- SDL_cond *empty_queue_cond; // 检查到packet队列空时发送 signal缓存read_thread读取数据
- int64_t start_pts; // 初始化时是stream的start time
- AVRational start_pts_tb; // 初始化时是stream的time_base
- int64_t next_pts; // 记录最近一次解码后的frame的pts,当解出来的部分帧没有有效的pts时则使用next_pts进行推算
- AVRational next_pts_tb; // next_pts的单位
- SDL_Thread *decoder_tid; // 线程句柄
- } Decoder;
-
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓