ffmpeg-ffplay代码架构简述
全局变量
/* Minimum SDL audio buffer size, in samples. */
// 最小音频缓冲
#define SDL_AUDIO_MIN_BUFFER_SIZE 512
/* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */
// 计算实际音频缓冲大小,并不需要太频繁回调,这里设置的是最大音频回调次数是每秒30次
#define SDL_AUDIO_MAX_CALLBACKS_PER_SEC 30/* Step size for volume control in dB */
// 音频控制 以db为单位的步进
#define SDL_VOLUME_STEP (0.75)/* no AV sync correction is done if below the minimum AV sync threshold */
// 最低同步阈值,如果低于该值,则不需要同步校正
#define AV_SYNC_THRESHOLD_MIN 0.04
/* AV sync correction is done if above the maximum AV sync threshold */
// 最大同步阈值,如果大于该值,则需要同步校正
#define AV_SYNC_THRESHOLD_MAX 0.1
/* If a frame duration is longer than this, it will not be duplicated to compensate AV sync */
// 帧补偿同步阈值,如果帧持续时间比这更长,则不用来补偿同步
#define AV_SYNC_FRAMEDUP_THRESHOLD 0.1
/* no AV correction is done if too big error */
// 同步阈值。如果误差太大,则不进行校正
#define AV_NOSYNC_THRESHOLD 10.0/* maximum audio speed change to get correct sync */
// 正确同步的最大音频速度变化值(百分比)
#define SAMPLE_CORRECTION_PERCENT_MAX 10/* external clock speed adjustment constants for realtime sources based on buffer fullness */
// 根据实时码流的缓冲区填充时间做外部时钟调整
// 最小值
#define EXTERNAL_CLOCK_SPEED_MIN 0.900
// 最大值
#define EXTERNAL_CLOCK_SPEED_MAX 1.010
// 步进
#define EXTERNAL_CLOCK_SPEED_STEP 0.001/* we use about AUDIO_DIFF_AVG_NB A-V differences to make the average */
// 使用差值来实现平均值
#define AUDIO_DIFF_AVG_NB 20/* polls for possible required screen refresh at least this often, should be less than 1/fps */
// 刷新频率 应该小于 1/fps
#define REFRESH_RATE 0.01/* NOTE: the size must be big enough to compensate the hardware audio buffersize size */
/* TODO: We assume that a decoded and resampled frame fits into this buffer */
// 采样大小
#define SAMPLE_ARRAY_SIZE (8 * 65536)#define CURSOR_HIDE_DELAY 1000000#define USE_ONEPASS_SUBTITLE_RENDER 1// 冲采样标志
static unsigned sws_flags = SWS_BICUBIC;// 包列表结构
typedef struct MyAVPacketList {AVPacket pkt;struct MyAVPacketList *next;int serial;
} MyAVPacketList;// 待解码包队列
typedef struct PacketQueue {MyAVPacketList *first_pkt, *last_pkt;int nb_packets;int size;int64_t duration;int abort_request;int serial;SDL_mutex *mutex;SDL_cond *cond;
} PacketQueue;#define VIDEO_PICTURE_QUEUE_SIZE 3
#define SUBPICTURE_QUEUE_SIZE 16
#define SAMPLE_QUEUE_SIZE 9
#define FRAME_QUEUE_SIZE FFMAX(SAMPLE_QUEUE_SIZE, FFMAX(VIDEO_PICTURE_QUEUE_SIZE, SUBPICTURE_QUEUE_SIZE))// 音频参数
typedef struct AudioParams {int freq; // 频率int channels; // 声道数int64_t channel_layout; // 声道设计,单声道,双声道还是立体声enum AVSampleFormat fmt; // 采样格式int frame_size; // 采样大小int bytes_per_sec; // 每秒多少字节
} AudioParams;// 时钟
typedef struct Clock {double pts; // 时钟基准 /* clock base */double pts_drift; // 更新时钟的差值 /* clock base minus time at which we updated the clock */double last_updated; // 上一次更新的时间double speed; // 速度int serial; // 时钟基于使用该序列的包 /* clock is based on a packet with this serial */int paused; // 停止标志int *queue_serial; // 指向当前数据包队列序列的指针,用于过时的时钟检测 /* pointer to the current packet queue serial, used for obsolete clock detection */
} Clock;/* Common struct for handling all types of decoded data and allocated render buffers. */
// 解码帧结构
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; // 帧的高度int format; // 格式AVRational sar; // 额外参数int uploaded; // 上载int flip_v; // 反转
} Frame;// 解码后的帧队列
typedef struct FrameQueue {Frame queue[FRAME_QUEUE_SIZE]; // 队列数组int rindex; // 读索引int windex; // 写索引int size; // 大小int max_size; // 最大大小int keep_last; // 保持上一个int rindex_shown; // 读显示SDL_mutex *mutex; // 互斥变量SDL_cond *cond; // 条件变量PacketQueue *pktq;
} FrameQueue;// 时钟同步类型
enum {AV_SYNC_AUDIO_MASTER, // 音频作为同步,默认以音频同步 /* default choice */AV_SYNC_VIDEO_MASTER, // 视频作为同步AV_SYNC_EXTERNAL_CLOCK, // 外部时钟作为同步 /* synchronize to an external clock */
};// 解码器结构
typedef struct Decoder {AVPacket pkt; // 包AVPacket pkt_temp; // 中间包PacketQueue *queue; // 包队列AVCodecContext *avctx; // 解码上下文int pkt_serial; // 包序列int finished; // 是否已经结束int packet_pending; // 是否有包在等待SDL_cond *empty_queue_cond; // 空队列条件变量int64_t start_pts; // 开始的时间戳AVRational start_pts_tb; // 开始的额外参数int64_t next_pts; // 下一帧时间戳AVRational next_pts_tb; // 下一帧的额外参数SDL_Thread *decoder_tid; // 解码线程
} Decoder;// 视频状态结构
typedef struct VideoState {SDL_Thread *read_tid; // 读取线程AVInputFormat *iformat; // 输入格式int abort_request; 终止int force_refresh; 强制刷新int paused; // 停止int last_paused; // 最后停止int queue_attachments_req; // 队列附件请求int seek_req; // 查找请求int seek_flags; // 查找标志int64_t seek_pos; // 查找位置int64_t seek_rel; // int read_pause_return; // 读停止返回AVFormatContext *ic; // 解码格式上下文int realtime; // 是否实时码流Clock audclk; // 音频时钟Clock vidclk; // 视频时钟Clock extclk; // 外部时钟FrameQueue pictq; // 视频队列FrameQueue subpq; // 字幕队列FrameQueue sampq; // 音频队列Decoder auddec; // 音频解码器Decoder viddec; // 视频解码器Decoder subdec; // 字幕解码器int audio_stream; // 音频码流Idint av_sync_type; // 同步类型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; // 音频包队列int audio_hw_buf_size; // 硬件缓冲大小uint8_t *audio_buf; // 音频缓冲区uint8_t *audio_buf1; // 音频缓冲区1unsigned int audio_buf_size; // 音频缓冲大小 /* in bytes */unsigned int audio_buf1_size; // 音频缓冲大小1int audio_buf_index; // 音频缓冲索引 /* in bytes */int audio_write_buf_size; // 音频写入缓冲大小int audio_volume; // 音量int muted; // 是否静音struct AudioParams audio_src; // 音频参数
#if CONFIG_AVFILTER struct AudioParams audio_filter_src; // 音频过滤器
#endifstruct AudioParams audio_tgt; // 音频参数struct SwrContext *swr_ctx; // 音频转码上下文int frame_drops_early; // 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; // 音频TextureSDL_Texture *sub_texture; // 字幕TextureSDL_Texture *vid_texture; // 视频Textureint subtitle_stream; // 字幕码流IdAVStream *subtitle_st; // 字幕码流PacketQueue subtitleq; // 字幕包队列double frame_timer; // 帧计时器double frame_last_returned_time; // 上一次返回时间double frame_last_filter_delay; // 上一个过滤器延时int video_stream; // 视频码流IdAVStream *video_st; // 视频码流PacketQueue videoq; // 视频包队列double max_frame_duration; // 最大帧显示时间 // maximum duration of a frame - 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; // 宽高,其实坐标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// 上一个视频码流Id、上一个音频码流Id、上一个字幕码流Idint last_video_stream, last_audio_stream, last_subtitle_stream;SDL_cond *continue_read_thread; // 连续读线程
} VideoState;/* options specified by the user */
static AVInputFormat *file_iformat; // 文件输入格式
static const char *input_filename; // 输入文件名
static const char *window_title; // 标题
static int default_width = 640; // 默认宽度
static int default_height = 480; // 默认高度
static int screen_width = 0; // 屏幕宽度
static int screen_height = 0; // 屏幕高度
static int audio_disable; // 是否禁止播放声音
static int video_disable; // 是否禁止播放视频
static int subtitle_disable; // 是否禁止播放字幕
static const char* wanted_stream_spec[AVMEDIA_TYPE_NB] = {0};
static int seek_by_bytes = -1; //
static int display_disable; // 显示禁止
static int borderless; //
static int startup_volume = 100; // 起始音量
static int show_status = 1; // 显示状态
static int av_sync_type = AV_SYNC_AUDIO_MASTER; // 同步类型
static int64_t start_time = AV_NOPTS_VALUE; // 开始时间
static int64_t duration = AV_NOPTS_VALUE; // 间隔
static int fast = 0; // 快速
static int genpts = 0; //
static int lowres = 0; // 慢速
static int decoder_reorder_pts = -1; // 解码器重新排列时间戳
static int autoexit; // 否自动退出
static int exit_on_keydown; // 是否按下退出
static int exit_on_mousedown; // 是否鼠标按下退出
static int loop = 1; // 循环
static int framedrop = -1; // 舍弃帧
static int infinite_buffer = -1; // 缓冲区大小限制 =1表示不限制(is->realtime实时传输时不限制
static enum ShowMode show_mode = SHOW_MODE_NONE; // 显示类型
static const char *audio_codec_name; // 音频解码器名称
static const char *subtitle_codec_name; // 字幕解码器名称
static const char *video_codec_name; // 视频解码器名称
double rdftspeed = 0.02; // 自适应滤波器的速度
static int64_t cursor_last_shown; // 上一次显示光标
static int cursor_hidden = 0; // 光标隐藏
#if CONFIG_AVFILTER
static const char **vfilters_list = NULL; // 视频滤镜
static int nb_vfilters = 0; // 视频滤镜数量
static char *afilters = NULL; // 音频滤镜
#endif
static int autorotate = 1; // 是否自动旋转/* current context */
static int is_full_screen; // 是否全屏
static int64_t audio_callback_time; // 音频回调时间static AVPacket flush_pkt; // 刷新的包#define FF_QUIT_EVENT (SDL_USEREVENT + 2)static SDL_Window *window; // 窗口
static SDL_Renderer *renderer; // 渲染器
设计的流程
包含文件读取、解封装、解码、音视频输出、音视频同步,流程如下图所示:

ffplay中使用的线程
(1)读线程。读取文件、解封装
(2)音频解码线程。解码音频压缩数据为PCM数据。
(3)视频解码线程。解码视频压缩数据为图像数据。
(4)音频输出线程。基于SDL播放,该线程实际上是SDL的内部线程。
(5)视频输出线程。基于SDL播放,该线程为程序主线程。

相关文章:
ffmpeg-ffplay代码架构简述
全局变量 /* Minimum SDL audio buffer size, in samples. */ // 最小音频缓冲 #define SDL_AUDIO_MIN_BUFFER_SIZE 512 /* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */ // 计算实际音频缓冲大小,并不需要太频繁…...
⛳ 多线程面试-什么是多线程上下文切换?
目录 ⛳ 多线程面试-什么是多线程上下文切换?🎁 Java中用到的线程调度算法是什么?🎨 什么是线程饥饿 ?你对线程优先级的理解是什么? ⛳ 多线程面试-什么是多线程上下文切换ÿ…...
vb+SQL车辆管理系统设计与实现
摘 要 随着信息时代的到来,信息高速公路的兴起,全球信息化进入了一个新的发展时期。人们越来越认识到计算机强大的信息模块处理功能,使之成为信息产业的基础和支柱。 我国经济的快速发展,汽车已经成为人们不可缺少的交通工具。对于拥有大量车辆的机关企事业来说,车辆的…...
java的枚举类
枚举类的概念和使用 1.枚举类的理解:类的对象只有有限个,确定的。我们称此为枚举类。 2.当需要定义一组常量时,强烈建议使用枚举类。对象便是所指的常量。 3.如果枚举类中只有一个对象,则可以作为单例模式的实现方式。 定义枚举类…...
基于java早餐店点餐系统源码设计与实现
摘 要 多姿多彩的世界带来了美好的生活,行业的发展也是形形色色的离不开技术的发展。作为时代进步的发展方面,信息技术至始至终都是成就行业发展的重要秘密。不论何种行业,大到国家、企业,小到团体、个人都在多方位的结合信息化技…...
ODOO16如何处理采购运输正常损耗的成本价核算?
《会计准则》规定:商品流通企业在采购商品过程中发生的运输费、装卸费、运输途中的合理损耗都归为采购存货成本中。 例如:采购A产品1000个,单价10元/个,途中运输正常损耗率是5%,因此实际入库是950个,入库金…...
【数据预测】基于白鲸优化算法BWO的VMD-KELM光伏发电功率预测 短期功率预测【Matlab代码#54】
文章目录 【可更换其他算法,获取资源请见文章第6节:资源获取】1. 白鲸优化算法BWO2. 变分模态分解VMD3. 核极限学习机KELM4. 部分代码展示5. 仿真结果展示6. 资源获取 【可更换其他算法,获取资源请见文章第6节:资源获取】 1. 白鲸…...
函数式编程-将过程作为返回值的应用:分步过程
之前的文章提到函数式编程的一等函数(First-class Function)四个性质中有“可以将过程作为返回值”这一点,但这一点在实际使用中不如“将过程作为参数”(高阶函数)用得多。本文介绍一种这个性质用于分步函数的应用。 …...
Mysql-学习笔记
文章目录 1. 数据库1.1 Mysql安装及常用代码1.2 SQL介绍1.3 SQL分类1. DDL-操作数据库,表2. DML-对表中的数据进行增删改3. DQL-对表中的数据进行查询条件查询模糊查询排序查询分组查询分页查询 4. DCL-对数据库进行权限控制外键约束表关系-多对多多表查询事务 1. 数…...
【雕爷学编程】Arduino动手做(187)---1.3寸OLED液晶屏模块2
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&#x…...
Windows用户如何安装新版本cpolar内网穿透
Windows用户如何安装新版本cpolar内网穿透 文章目录 Windows用户如何安装新版本cpolar内网穿透 在科学技术高度发达的今天,我们身边充斥着各种电子产品,这些电子产品不仅为我们的工作带来极大的便利,也让生活变得丰富多彩。我们可以使用便携的…...
MacBookPro安装Win10,Wifi不能用了,触控板不能用了(2)
一、问题 去年在MacBookPro上装过Win10,当初只分配了60G空间。各方面原因需要重装系统,上个月装了一晚上,也无法连接Wifi,触控板只能当鼠标左键用。 后来发现是没有相关驱动造成的,于是从Mac系统联网找到网卡驱动&am…...
理解C++中变量的作用域
理解C中变量的作用域 常规变量(如前面定义的所有变量)的作用域很明确,只能在作用域内使用它们,如果您在作用域外使用它们,编译器将无法识别,导致程序无法通过编译。在作用域外面,变量是未定义的…...
vue+element-ui给全局请求设置一个loading样式
老项目后台管理,要在每个页面请求的时候都添加一个loading,为了统一和防止一个页面多次请求页面出现闪烁的情况同意在request.js中添加了一个全局loading。 想要的效果: 1.在请求的时候创建一个loading样式,请求结束是关闭。 2…...
传球游戏
题目描述 上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。 游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个…...
智能卡通用安全检测指南 思度文库
范围 本标准规定了智能卡类产品进行安全性检测的一般性过程和方法。 本标准适用于智能卡安全性检测评估和认证。 规范性引用文件 下列文件对于本文件的应用是必不可少的。凡是注日期的引用文件,仅注日期的版本适用于本文件。凡是不注日期的引用文件,…...
Maven设置阿里云路径(防止加载过慢)
<?xml version"1.0" encoding"UTF-8"?><!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding …...
JavaScript原型链污染漏洞复现与防范
目录 什么是原型链污染漏洞? 复现原型链污染漏洞 防范原型链污染漏洞 什么是原型链污染漏洞? 原型链污染是JavaScript中的一种安全漏洞,利用该漏洞可以修改对象的原型,从而影响对象及其属性的行为。攻击者可以通过修改原型链来…...
初识MySQL数据库之用户管理
目录 一、用户管理 二、用户 1. 用户信息 2. 创建用户 3. 用户登录测试 4. 删除用户 5. 设置用户远端登录 6. 修改密码 6.1 修改当前用户的密码 6.2 root用户修改指定用户的密码 三、权限 1. 数据库中的各个权限含义 2. 给用户授权 3. 查看用户拥有权限 4. 授权…...
JVM 类文件结构(class文件)
JVM 本文链接:https://blog.csdn.net/feather_wch/article/details/132116849 类文件结构 1、class文件的组成 无符号数:基本数据类型 u1 u2 u3 u4 描述 数字字符串索引引用 表:复合数据类型,无符号数 表组, _inf…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
Redis上篇--知识点总结
Redis上篇–解析 本文大部分知识整理自网上,在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库,Redis 的键值对中的 key 就是字符串对象,而 val…...
Java高级 |【实验八】springboot 使用Websocket
隶属文章:Java高级 | (二十二)Java常用类库-CSDN博客 系列文章:Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…...
