音视频入门基础:MPEG2-TS专题(5)——FFmpeg源码中,判断某文件是否为TS文件的实现
一、引言
通过FFmpeg命令:
./ffmpeg -i XXX.ts
可以判断出某个文件是否为TS文件:

所以FFmpeg是怎样判断出某个文件是否为TS文件呢?它内部其实是通过mpegts_probe函数来判断的。从《FFmpeg源码:av_probe_input_format3函数和AVInputFormat结构体分析(FFmpeg源码5.0.3版本)》和《7.0.1版本的FFmpeg源码中av_probe_input_format3函数和AVInputFormat结构体的改变》中可以知道:FFmpeg源码中实现容器格式检测的函数是av_probe_input_format3函数,其内部通过循环while ((fmt1 = av_demuxer_iterate(&i))) 拿到所有容器格式对应的AVInputFormat结构,然后通过score = fmt1->read_probe(&lpd)语句执行不同容器格式对应的解析函数,根据是否能被解析,以及匹配程度,来判断出这是哪种容器格式。而TS文件对应的解析函数就是mpegts_probe函数。
二、mpegts_probe函数的定义
mpegts_probe函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/mpegts.c中:
static int mpegts_probe(const AVProbeData *p)
{const int size = p->buf_size;int maxscore = 0;int sumscore = 0;int i;int check_count = size / TS_FEC_PACKET_SIZE;
#define CHECK_COUNT 10
#define CHECK_BLOCK 100if (!check_count)return 0;for (i = 0; i<check_count; i+=CHECK_BLOCK) {int left = FFMIN(check_count - i, CHECK_BLOCK);int score = analyze(p->buf + TS_PACKET_SIZE *i, TS_PACKET_SIZE *left, TS_PACKET_SIZE , 1);int dvhs_score = analyze(p->buf + TS_DVHS_PACKET_SIZE*i, TS_DVHS_PACKET_SIZE*left, TS_DVHS_PACKET_SIZE, 1);int fec_score = analyze(p->buf + TS_FEC_PACKET_SIZE *i, TS_FEC_PACKET_SIZE *left, TS_FEC_PACKET_SIZE , 1);score = FFMAX3(score, dvhs_score, fec_score);sumscore += score;maxscore = FFMAX(maxscore, score);}sumscore = sumscore * CHECK_COUNT / check_count;maxscore = maxscore * CHECK_COUNT / CHECK_BLOCK;ff_dlog(0, "TS score: %d %d\n", sumscore, maxscore);if (check_count > CHECK_COUNT && sumscore > 6) {return AVPROBE_SCORE_MAX + sumscore - CHECK_COUNT;} else if (check_count >= CHECK_COUNT && sumscore > 6) {return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT;} else if (check_count >= CHECK_COUNT && maxscore > 6) {return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT;} else if (sumscore > 6) {return 2;} else {return 0;}
}
该函数的作用就是检测某个文件是否为TS文件。
形参p:输入型参数,为AVProbeData类型的指针。
AVProbeData结构体声明在libavformat/avformat.h中:
/*** This structure contains the data a format has to probe a file.*/
typedef struct AVProbeData {const char *filename;unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */int buf_size; /**< Size of buf except extra allocated bytes */const char *mime_type; /**< mime_type, when known. */
} AVProbeData;
p->filename为:需要被推测格式的文件的路径。
p->buf:指向“存放从路径为p->filename的TS文件中读取出来的二进制数据”的缓冲区。
p->buf_size:缓冲区p->buf的大小,单位为字节。注:FFmpeg判断某个文件的格式时不会读取完整个文件,只会读取它前面的一部分,比如最开始的2048个字节。只要根据前面的这些字节就足够判断出它的格式了,所以p->buf_size的值一般就是2048。
p->mime_type:一般为NULL,可忽略。
返回值:返回一个类型为整形的分值。返回0表示该文件完全不符合TS格式。返回的值越接近100表示该文件越符合TS格式。
三、analyze函数的定义
mpegts_probe函数中,会调用analyze函数,analyze函数定义如下:
static int analyze(const uint8_t *buf, int size, int packet_size,int probe)
{int stat[TS_MAX_PACKET_SIZE];int stat_all = 0;int i;int best_score = 0;memset(stat, 0, packet_size * sizeof(*stat));for (i = 0; i < size - 3; i++) {if (buf[i] == 0x47) {int pid = AV_RB16(buf+1) & 0x1FFF;int asc = buf[i + 3] & 0x30;if (!probe || pid == 0x1FFF || asc) {int x = i % packet_size;stat[x]++;stat_all++;if (stat[x] > best_score) {best_score = stat[x];}}}}return best_score - FFMAX(stat_all - 10*best_score, 0)/10;
}
该函数的作用是:检测buf指向的码流的前size个字节,检测其是否符合每个transport packet(又称TS包,TS分组、传输流报文)的长度固定为packet_size个字节的TS格式。返回一个类型为整形的分值,返回的值越接近100表示越符合对应的TS格式。
从《音视频入门基础:MPEG2-TS专题(3)——TS Header简介》可以知道,TS格式有三种:分别为transport packet长度固定为188、192和204字节。
analyze函数中首先会定义一个元素个数为TS_MAX_PACKET_SIZE(值为204)的数组stat。因为加上了FEC前向纠错的情况下,一个transport packet长度为204字节;而普通的MPEG2-TS传输流中,一个transport packet长度固定为188字节。所以一个transport packet的最大长度为204字节,所以定义数组stat的元素个数为TS_MAX_PACKET_SIZE(值为204字节):
int stat[TS_MAX_PACKET_SIZE];int stat_all = 0;int i;int best_score = 0;memset(stat, 0, packet_size * sizeof(*stat));
判断是否读取到了值为0x47的同步字节:
if (buf[i] == 0x47)
如果读取到了同步字节,读取TS Header中的PID属性,赋值给变量pid;读取TS Header中的adaptation_field_control属性,将该属性的值经过运算,赋值给变量asc:
int pid = AV_RB16(buf+1) & 0x1FFF;int asc = buf[i + 3] & 0x30;
如果不是探测格式(!probe)或该transport packet为空包(pid == 0x1FFF)或适配域存在标志大于0(asc),通过取余运算,判断对应的二进制数据是否符合transport packet长度为packet_size个字节的TS格式:
if (!probe || pid == 0x1FFF || asc) {int x = i % packet_size;stat[x]++;stat_all++;if (stat[x] > best_score) {best_score = stat[x];}}
不断循环,每符合一次“transport packet长度为packet_size个字节”的条件时,就让分值累加。最后返回最终得到的分值,该分值表示符合对应的TS格式的程度:
for (i = 0; i < size - 3; i++) {//...}return best_score - FFMAX(stat_all - 10*best_score, 0)/10;
四、mpegts_probe函数的内部实现分析
宏TS_FEC_PACKET_SIZE、TS_DVHS_PACKET_SIZE、TS_PACKET_SIZE定义如下,分别对应transport packet长度固定为188、192和204字节的TS格式:
#define TS_FEC_PACKET_SIZE 204
#define TS_DVHS_PACKET_SIZE 192
#define TS_PACKET_SIZE 188
#define TS_MAX_PACKET_SIZE 204
mpegts_probe函数中会调用analyze函数。从上面对analyze函数的分析,我们可以知道:
1.语句int score = analyze(p->buf + TS_PACKET_SIZE *i, TS_PACKET_SIZE *left, TS_PACKET_SIZE , 1)的作用是:检测“p->buf + TS_PACKET_SIZE*i”指向的码流符合transport packet长度固定为188字节的TS格式的程度,将对应的分数赋值给变量score。
2.语句int dvhs_score = analyze(p->buf + TS_DVHS_PACKET_SIZE*i, TS_DVHS_PACKET_SIZE*left, TS_DVHS_PACKET_SIZE, 1)的作用是:检测“p->buf + TS_DVHS_PACKET_SIZE*i”指向的码流符合transport packet长度固定为192字节的TS格式的程度,将对应的分数赋值给变量dvhs_score 。
3.语句int fec_score = analyze(p->buf + TS_FEC_PACKET_SIZE *i, TS_FEC_PACKET_SIZE *left, TS_FEC_PACKET_SIZE , 1)的作用是:检测“p->buf + TS_FEC_PACKET_SIZE *i”指向的码流符合transport packet长度固定为204字节的TS格式的程度,将对应的分数赋值给变量fec_score :
int score = analyze(p->buf + TS_PACKET_SIZE *i, TS_PACKET_SIZE *left, TS_PACKET_SIZE , 1);int dvhs_score = analyze(p->buf + TS_DVHS_PACKET_SIZE*i, TS_DVHS_PACKET_SIZE*left, TS_DVHS_PACKET_SIZE, 1);int fec_score = analyze(p->buf + TS_FEC_PACKET_SIZE *i, TS_FEC_PACKET_SIZE *left, TS_FEC_PACKET_SIZE , 1);
取变量score、dvhs_score、fec_score的最大值,即该码流最符合的那种TS格式的分数,赋值给变量score:
score = FFMAX3(score, dvhs_score, fec_score);sumscore += score;maxscore = FFMAX(maxscore, score);
返回最终表示符合程度的分数:
sumscore = sumscore * CHECK_COUNT / check_count;maxscore = maxscore * CHECK_COUNT / CHECK_BLOCK;ff_dlog(0, "TS score: %d %d\n", sumscore, maxscore);if (check_count > CHECK_COUNT && sumscore > 6) {return AVPROBE_SCORE_MAX + sumscore - CHECK_COUNT;} else if (check_count >= CHECK_COUNT && sumscore > 6) {return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT;} else if (check_count >= CHECK_COUNT && maxscore > 6) {return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT;} else if (sumscore > 6) {return 2;} else {return 0;}
五、总结
从上面我们可以知道,FFmpeg检测某个文件是否为TS文件,是通过判断是否读取到了同步字节,以及同步字节之间的transport packet长度是否固定为188或192或204个字节实现的。
相关文章:
音视频入门基础:MPEG2-TS专题(5)——FFmpeg源码中,判断某文件是否为TS文件的实现
一、引言 通过FFmpeg命令: ./ffmpeg -i XXX.ts 可以判断出某个文件是否为TS文件: 所以FFmpeg是怎样判断出某个文件是否为TS文件呢?它内部其实是通过mpegts_probe函数来判断的。从《FFmpeg源码:av_probe_input_format3函数和AVI…...
每天10个vue面试题(九)
1、如何在组件中批量使用Vuex的getter属性? 使用mapGetters辅助函数, 利用对象展开运算符将getter混入computed 对象中computed:{ ...mapGetters([total,discountTotal]) } 2、vue2和vue3的区别? 双向数据绑定不同:vue2 的双向数据绑定…...
Jenkins的环境部署
day22 回顾 Jenkins 简介 官网Jenkins Jenkins Build great things at any scale The leading open source automation server, Jenkins provides hundreds of plugins to support building, deploying and automating any project. 用来构建一切 其实就是用Java写的一个项目…...
八、鸿蒙开发-网络请求、应用级状态管理
提示:本文根据b站尚硅谷2024最新鸿蒙开发HarmonyOS4.0鸿蒙NEXT星河版零基础教程课整理 链接指引 > 尚硅谷2024最新鸿蒙开发HarmonyOS4.0鸿蒙NEXT星河版零基础教程 文章目录 一、网络请求1.1 申请网络访问权限1.2 安装axios库1.2.1 配置环境变量1.2.2 第二步&…...
经验笔记:Git 中的远程仓库链接及上下游关系管理
Git 中的远程仓库链接及上下游关系管理 1. 远程仓库的链接信息 当你克隆一个远程仓库时,Git 会在本地仓库中记录远程仓库的信息。这些信息包括远程仓库的 URL、默认的远程名称(通常是 origin),以及远程仓库中的所有分支和标签。…...
Paint 学习笔记
目录 ippaint 外扩对象 LCM_inpaint_Outpaint_Comfy: 不支持文字引导 ippaint https://github.com/Sanster/IOPaint 外扩对象 https://www.iopaint.com/models/diffusion/powerpaint_v2 GitHub - open-mmlab/PowerPaint: [ECCV 2024] PowerPaint, a versatile …...
Jenkins修改LOGO
重启看的LOGO和登录页面左上角的LOGO 进入LOGO存在的目录 [roottest-server01 svgs]# pwd /opt/jenkins_data/war/images/svgs [roottest-server01 svgs]# ll logo.svg -rw-r--r-- 1 jenkins jenkins 29819 Oct 21 10:58 logo.svg #jenkins_data目录是我挂载到了/opt目录&…...
kafka是如何做到高效读写
消息持久化: Kafka 将消息存储在磁盘上,并且通过顺序写入的方式提高写入性能。 消息被追加到日志文件的尾部,避免了随机写操作,从而提高了写入速度。零拷贝技术:利用操作系统的零拷贝特性,数据可以从磁盘直…...
Intern大模型训练营(九):XTuner 微调实践微调
本节课程的视频和教程都相当清晰,尤其是教程,基本只要跟着文档,在开发机上把指令都相同地输出一遍,就可以完成任务(大赞),相当顺利。因此,这里的笔记就不重复赘述步骤,更…...
从一次java.io.StreamCorruptedException: invalid stream header: 48656C6C 错误中学到的调试思路
问题场景: 在项目中,我试图使用 Java 的 ObjectInputStream 反序列化一个对象。代码逻辑看似简单:读取字节流,将其转为 Java 对象。然而,程序抛出了以下异常: java.io.StreamCorruptedException: invalid…...
树莓派的发展历史
树莓派(Raspberry Pi)是由英国的树莓派基金会开发的一系列单板计算机,其目标是为了促进计算机科学教育,同时提供廉价的计算机硬件平台。 1. 诞生背景与初代模型(2006-2012) 背景:树莓派的概念起…...
K8S containerd拉取harbor镜像
前言 接前面的环境 K8S 1.24以后开始启用docker作为CRI,这里用containerd拉取 参考文档 正文 vim /etc/containerd/config.toml #修改内容如下 #sandbox_image "registry.aliyuncs.com/google_containers/pause:3.10" systemd_cgroup true [plugins.…...
Ubuntu 环境下通过 Apt-get 安装软件
操作场景 为提升用户在云服务器上的软件安装效率,减少下载和安装软件的成本,腾讯云提供了 Apt-get 下载源。在 Ubuntu 环境下,用户可通过 Apt-get 快速安装软件。对于 Apt-get 下载源,不需要添加软件源,可以直接安装软…...
vue使用List.forEach遍历集合元素
需要遍历集合对其每个元素进行操作时,可以使用forEach方法 1.语法:集合.forEach ( 定义每一项 > 定义每一项都要进行的逻辑 ) 2、使用场景: //例如需要给每个员工的工资数量加1000this.personList.forEach(item>item.salary100…...
ROM修改进阶教程------安卓14去除修改系统应用后导致的卡logo验证步骤 适用安卓13 14 安卓15可借鉴参考
上期的博文解析了安卓14 安卓15去除系统应用签名验证的步骤解析。我们要明白。修改系统应用后有那些验证。其中签名验证 去卡logo验证 与可降级安装应用验证等等的区别。有些要相互结合使用。今天的博文将对修改系统应用后卡logo验证做个步骤解析。 通过博文了解💝💝�…...
苹果macbook,MacOS 11,12,13,14,15 跳过监管锁(配置锁)
第一步:进入恢复模式 长按电源键关机,再长按开机进入恢复模式。(M,Intel芯片方法不同) 第二步:复制代码 右上角联网,打开Safari,地址栏输入http://i7q.cn/61NWfQ。复制以下命令&am…...
【YOLOv8】安卓端部署-2-项目实战
文章目录 1 准备Android项目文件1.1 解压文件1.2 放置ncnn模型文件1.3 放置ncnn和opencv的android文件1.4 修改CMakeLists.txt文件 2 手机连接电脑并编译软件2.1 编译软件2.2 更新配置及布局2.3 编译2.4 连接手机 3 自己数据集训练模型的部署4 参考 1 准备Android项目文件 1.1…...
第二十四章 Spring之源码阅读——AOP篇
Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…...
Linux配置MySQL自动备份
Linux配置MySQL自动备份 配置MySQL的自动备份首先要编辑一个备份脚本然后配置开启Linux定时任务即可,下面是具体配置 1、配置备份脚本并测试执行 1.1 编写备份脚本 #这里创建脚本名为mysql_backups.sh mkdir ~/mysqlmulu touch ~/mysqlmulu/mysql_backups.sh#!/…...
qt 之 QDockWidget设置不可拖动
在Qt中,可以通过设置QDockWidget的属性来禁止它被拖动。你可以使用QDockWidget::setFeatures方法并传递QDockWidget::DockWidgetMovable作为参数来禁用拖动功能。 以下是一个简单的示例代码,展示了如何设置QDockWidget为不可拖动: #include …...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
