ffplay-主要数据结构分析
数据结构分析
VideoState
其实主要作用和800行代码写一个播放器作用中一样 就是一个主要封装类
保存 其中各项的主要内容
个人觉得比较需要注意到的 几个非重点常用对象
1 abort_request 就是判断这个时候有没有 退出播放 如果退出了 那就一系列退出操作
2 force_refresh 视频播放时候用的
3 av_sync_type ffplay 支持3种同步方式
typedef struct VideoState {SDL_Thread *read_tid; // 读线程句柄AVInputFormat *iformat; // 指向demuxerint 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 masterdouble audio_clock; // 当前音频帧的PTS+当前帧Durationint audio_clock_serial; // 播放序列,seek可改变此值// 以下4个参数 非audio master同步方式使用double audio_diff_cum; // used for AV difference average computationdouble audio_diff_avg_coef;double audio_diff_threshold;int audio_diff_avg_count;// endAVStream *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_sizeint audio_write_buf_size;int audio_volume; // 音量int muted; // =1静音,=0则正常struct AudioParams audio_src; // 音频frame的参数
#if CONFIG_AVFILTERstruct AudioParams audio_filter_src;
#endifstruct AudioParams audio_tgt; // SDL支持的音频参数,重采样转换:audio_src->audio_tgtstruct SwrContext *swr_ctx; // 音频重采样contextint 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; // 音频TextureSDL_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 discontinuitystruct 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_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;SDL_cond *continue_read_thread; // 当读取数据队列满了后进入休眠时,可以通过该condition唤醒读线程
} VideoState;
MyAVPacketList
和名字一样 就是 解封装出来的packet 用链表的形式开始保存 啦啦啦啦
可以理解为是队列的⼀个节点。可以通过其 next 字段访问下⼀个节点
重点!!! 1 serial
ffplay中多处⽤到serial的概念,主要⽤来区分是否连续 数据,每做⼀次seek,该serial都会做+1的递增,以区分不同的播放序列。
说人话就是: 就是 seek 一次以后 以前的队列中的东西当然就不能用了呗 应该放弃的放弃 要free 的 free
typedef struct MyAVPacketList {AVPacket pkt; //解封装后的数据struct MyAVPacketList *next; //下一个节点int serial; //播放序列
} MyAVPacketList;
PacketQueue
这里这个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;
PacketQueue的函数们
太多了 写了一篇新的 连接在这里
Frame
真正存储解码后⾳视频数据的结构体为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/1int uploaded; // 用来记录该帧是否已经显示过?int flip_v; // =1则垂直翻转, = 0则正常播放
} Frame;
FrameQueue
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
在这里插入代码片
AudioParams
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;
struct Decoder解码器封装
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 timeAVRational start_pts_tb; // 初始化时是stream的time_baseint64_t next_pts; // 记录最近一次解码后的frame的pts,当解出来的部分帧没有有效的pts时则使用next_pts进行推算AVRational next_pts_tb; // next_pts的单位SDL_Thread *decoder_tid; // 线程句柄
} Decoder;
ffplay-主要数据结构分析相关推荐
- ffplay的数据结构分析
<ffplay分析(从启动到读取线程的操作)> <ffplay分析(视频解码线程的操作)> <ffplay分析(音频解码线程的操作)> <ffplay 分析( ...
- 【Android 逆向】函数拦截 ( GOT 表数据结构分析 | 函数根据 GOT 表进行跳转的流程 )
文章目录 一.GOT 表数据结构分析 二.函数根据 GOT 表进行跳转的流程 一.GOT 表数据结构分析 GOT 表分为 222 部分 , 一部分在 调用者部分 ( 可执行文件 ) 中 , 一部分在 ...
- 硬盘扇区数据结构分析
来自 http://hi.baidu.com/qtycr/blog/item/f6cc9b2b74e2d7fee7cd40da.html 硬盘扇区数据结构分析 初买来一块硬盘,我们是没有办法使用的,你 ...
- 音视频从入门到精通——FFmpeg数据结构分析
FFmpeg数据结构分析 FFmpeg解码流程 重要结构体之间的关系 AVFormatContext iformat:输入媒体的AVInputFormat,比如指向AVInputFormat ff_f ...
- 数据结构分析:红黑树、B+树
数据结构分析:红黑树.B+树 前言 常见的数据结构大概分为以下8种,作为一个开发人员,数据结构是内功之一. 本文参考了网络上相关知识,加之自己的理解.简单说明红黑树.B+树的特性. 1. 二叉搜索树( ...
- MYSEE:Sp数据结构分析初稿
以前写了MYSEE 服务器端源码分析文档,主要是对其中的消息机制(sp,cp,ts,之间的交互)的分析.因为CU抽风,发表的文章无故给我删除了 ,所以这个不是完整的 Sp 数据结构分析(初稿) 一:准 ...
- 商品分类目录数据结构分析
商品分类目录数据结构分析 说明:一般电商网站的商品分类信息,都是3级标题. 划分: 1级标题-2级标题-3级标题 标题是有所属关系的 /1.查询一级商品分类信息/ SELECT * FROM tb_i ...
- SICK LMS511开发及数据结构分析、坐标转换
SICK LMS511开发及数据结构分析.坐标转换 最近公司有新上的项目,开始接触激光扫描仪,型号SICK LMS511. 获取数据 在网上看了一些资料,有通过SICK自带的软件看了一下激光扫描仪的配 ...
- 【JAVA进阶】java中的集合(番外篇3)- HashMap源码底层数据结构分析
写在前面的话 脑子是个好东西,可惜的是一直没有搞懂脑子的内存删除机制是什么,所以啊,入行多年,零零散散的文章看了无数,却总是学习了很多也忘了很多. 痛定思痛的我决定从今天开始系统的梳理下知识架构,记录 ...
- 【Apache OFBiz 系列】OFBiz的数据结构分析(五)
前面一系列的数据结构分析分别整理了ofbiz的通用模块.网站信息模块.内容模块.调查问卷模块,但是一直未呈现信息化中的主体--人员,这篇文章全面整理了ofbiz的会员管理模块数据模型,ofbiz对会员 ...
最新文章
- 数据结构——树、二叉树、森林、哈夫曼树、字符串模式匹配
- Java异常简单介绍
- echarts如何获取后端的值_Echarts 获取后台数据 使用后台数据展示 柱形图
- 自建Hive数据仓库跨版本迁移到阿里云Databricks数据洞察
- iScroll学习笔记
- 中点坐标公式 矩形_压轴题必备|中考数学“动点坐标”问题,这个万能解法人人都能学会!...
- 使用 Xunit.DependencyInjection 改造测试项目
- 一文教你使用java开发一款推箱子游戏
- python分析服务器日志_python实现web服务器日志分析脚本
- java中sql语句怎么把开始和结束时间作为参数写sql查询_JDBC数据库连接怎么操作?...
- 分区字段不在SQL过滤中,悲剧
- 量化交易策略matlab交易方案,【策略分享】Matlab量化交易策略源码分享
- samba add new smbpasswd recycle
- mysql之为表添加一个字段并设定默认值
- loadrunner 及 QTP 下载地址
- 线性代数学习心得(二)矩阵的逆和矩阵变换
- XJTU_选课小助手
- 今日头条推荐算法原理全文详解之六
- ae合成设置快捷键_这些AE技巧,相见恨晚
- ffmpeg 合并音乐+视频报错之[mp4 @ 000002b9bcfa1ec0] Non-monotonous DTS in output stream 0:1; previous: 1136042
热门文章
- 基于java的飞机大战雷电游戏的开发与设计#毕业设计
- 【转】WIFI-Direct(Wifi直连)、AirPlay、DLAN、Miracast功能介绍
- 利用EDA365 SKILLS 生成gerber和手动生成
- 3dsmax动画六、骨骼调整及蒙皮。
- 算法分析与设计课程总结
- 利用ENVI绘制土地利用图
- 游戏音效是用什么软件做的?
- java网上购物系统_Java Web 应用教程——网上购物系统的实现
- 2020-09-24L1-016 查验身份证 (15分) 一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下: 首先对前17位数字加权求和,权重分配为:{7,
- Linux GCC编译详细