音视频入门基础:MPEG2-PS专题(4)——FFmpeg源码中,判断某文件是否为PS文件的实现

一、引言

通过FFmpeg命令:

./ffmpeg -i XXX.ps

可以判断出某个文件是否为PS文件:

所以FFmpeg是怎样判断出某个文件是否为PS文件呢?它内部其实是通过mpegps_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)语句执行不同容器格式对应的解析函数,根据是否能被解析,以及匹配程度,来判断出这是哪种容器格式。而PS文件对应的解析函数就是mpegps_probe函数。

二、mpegps_probe函数的定义

mpegps_probe函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/mpeg.c中:

static int mpegps_probe(const AVProbeData *p)
{uint32_t code = -1;int i;int sys = 0, pspack = 0, priv1 = 0, vid = 0;int audio = 0, invalid = 0, score = 0;int endpes = 0;for (i = 0; i < p->buf_size; i++) {code = (code << 8) + p->buf[i];if ((code & 0xffffff00) == 0x100) {int len  = p->buf[i + 1] << 8 | p->buf[i + 2];int pes  = endpes <= i && check_pes(p->buf + i, p->buf + p->buf_size);int pack = check_pack_header(p->buf + i);if (code == SYSTEM_HEADER_START_CODE)sys++;else if (code == PACK_START_CODE && pack)pspack++;else if ((code & 0xf0) == VIDEO_ID && pes) {endpes = i + len;vid++;}// skip pes payload to avoid start code emulation for private// and audio streamselse if ((code & 0xe0) == AUDIO_ID &&  pes) {audio++; i+=len;}else if (code == PRIVATE_STREAM_1  &&  pes) {priv1++; i+=len;}else if (code == 0x1fd             &&  pes) vid++; //VC1else if ((code & 0xf0) == VIDEO_ID && !pes) invalid++;else if ((code & 0xe0) == AUDIO_ID && !pes) invalid++;else if (code == PRIVATE_STREAM_1  && !pes) invalid++;}}if (vid + audio > invalid + 1) /* invalid VDR files nd short PES streams */score = AVPROBE_SCORE_EXTENSION / 2;//     av_log(NULL, AV_LOG_ERROR, "vid:%d aud:%d sys:%d pspack:%d invalid:%d size:%d \n",
//            vid, audio, sys, pspack, invalid, p->buf_size);if (sys > invalid && sys * 9 <= pspack * 10)return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2: AVPROBE_SCORE_EXTENSION / 2 + (audio + vid + pspack > 1); // 1 more than mp3if (pspack > invalid && (priv1 + vid + audio) * 10 >= pspack * 9)return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2: AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpgif ((!!vid ^ !!audio) && (audio > 4 || vid > 1) && !sys &&!pspack && p->buf_size > 2048 && vid + audio > invalid) /* PES stream */return (audio > 12 || vid > 6 + 2 * invalid) ? AVPROBE_SCORE_EXTENSION + 2: AVPROBE_SCORE_EXTENSION / 2;// 02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1// mp3_misidentified_2.mp3 has sys:0 priv1:0 pspack:0 vid:0 audio:6// Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618return score;
}

该函数的作用就是检测某个文件是否为PS文件。

形参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的PS文件中读取出来的二进制数据”的缓冲区。

p->buf_size:缓冲区p->buf的大小,单位为字节。注:FFmpeg判断某个文件的格式时不会读取完整个文件,只会读取它前面的一部分,比如最开始的2048个字节。只要根据前面的这些字节就足够判断出它的格式了,所以p->buf_size的值一般就是2048。

p->mime_type:一般为NULL,可忽略。

返回值:返回一个类型为整形的分值。返回0表示该文件完全不符合PS格式。返回的值越接近100表示该文件越符合PS格式。

三、mpegps_probe函数的内部实现分析

mpegps_probe函数中首先会定义整形变量。其中,变量sys表示这段长度为p->buf_size的码流中system header的个数;pspack为这段码流中pack header的个数;priv1为这段码流中私有流的个数;vid为这段码流中视频流的个数:

    int sys = 0, pspack = 0, priv1 = 0, vid = 0;

不断读取出存放在数组p->buf[i]中的二进制码流数据:

    for (i = 0; i < p->buf_size; i++) {code = (code << 8) + p->buf[i];if ((code & 0xffffff00) == 0x100) {//...}}

宏SYSTEM_HEADER_START_CODE定义在libavformat/mpeg.h中,值为0x000001bb,表示system header的起始码(system header的system_header_start_code属性):

#define SYSTEM_HEADER_START_CODE    ((unsigned int)0x000001bb)

根据是否是system header的起始码,判断是否读取到了PS流的system header,如果读取到了,让变量sys的值加1:

            if (code == SYSTEM_HEADER_START_CODE)sys++;

宏PACK_START_CODE定义在libavformat/mpeg.h中,值为0x000001ba,表示pack header的起始码(pack header的pack_start_code属性):

#define PACK_START_CODE             ((unsigned int)0x000001ba)

根据是否是pack header的起始码,判断是否读取到了PS流的pack header,如果读取到了,让变量pspack的值加1:

          int pack = check_pack_header(p->buf + i);  //...else if (code == PACK_START_CODE && pack)pspack++;

检查是否读取到了PES流。如果读取到了,通过PES packet中的stream_id属性判断里面的ES流是否为视频流。如果是视频流,让变量vid的值加1:

            int pes  = endpes <= i && check_pes(p->buf + i, p->buf + p->buf_size);//...else if ((code & 0xf0) == VIDEO_ID && pes) {endpes = i + len;vid++;}

返回最终表示符合程度的分数:

    if (sys > invalid && sys * 9 <= pspack * 10)return (audio > 12 || vid > 3 || pspack > 2) ? AVPROBE_SCORE_EXTENSION + 2: AVPROBE_SCORE_EXTENSION / 2 + (audio + vid + pspack > 1); // 1 more than mp3if (pspack > invalid && (priv1 + vid + audio) * 10 >= pspack * 9)return pspack > 2 ? AVPROBE_SCORE_EXTENSION + 2: AVPROBE_SCORE_EXTENSION / 2; // 1 more than .mpgif ((!!vid ^ !!audio) && (audio > 4 || vid > 1) && !sys &&!pspack && p->buf_size > 2048 && vid + audio > invalid) /* PES stream */return (audio > 12 || vid > 6 + 2 * invalid) ? AVPROBE_SCORE_EXTENSION + 2: AVPROBE_SCORE_EXTENSION / 2;// 02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1// mp3_misidentified_2.mp3 has sys:0 priv1:0 pspack:0 vid:0 audio:6// Have\ Yourself\ a\ Merry\ Little\ Christmas.mp3 0 0 0 5 0 1 len:21618return score;

四、总结

从上面我们可以知道,FFmpeg检测某个文件是否为PS文件,是通过判断这段码流中,读取到的system header、pack header、PES流中视频流的个数等信息实现的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/66111.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Python学习日记-74] 面向对象实战2——选课系统

[Python学习日记-74] 面向对象实战2——选课系统 简介 开发要求 实现&#xff1a;选课系统 简介 在前面的《年会答题系统》当中我们介绍了面向对象软件开发的一些流程&#xff0c;当然这一流程只是涵括了大部分的&#xff0c;目前在业界也没有一个统一的标准&#xff0c;每个…

用户注册模块(芒果头条项目进度4)

1 创建⽤户模块⼦应⽤ 1.1 在项⽬包⽬录下 创建apps的python包。 1.2 在apps包下 创建应⽤userapp $ cd 项⽬包⽬录/apps $ python ../../manage.py startapp userapp 1.3 配置导包路径 默认情况下导包路径指向项⽬根⽬录 # 通过下⾯语句可以打印当前导包路径 print(sys.pa…

Elasticsearch:利用 AutoOps 检测长时间运行的搜索查询

作者&#xff1a;来自 Elastic Valentin Crettaz 了解 AutoOps 如何帮助你调查困扰集群的长期搜索查询以提高搜索性能。 AutoOps 于 11 月初在 Elastic Cloud Hosted 上发布&#xff0c;它通过性能建议、资源利用率和成本洞察、实时问题检测和解决路径显著简化了集群管理。 Au…

关于Flutter应用国际化语言的设置

目录 1. Locale配置 2. 用户切换/启动自动加载缓存里面的locale 由于最近在开发app国际化设置的时候遇到一些问题&#xff0c;所以做出一些总结。 1. Locale配置 具体的初始化配置可以参考文档&#xff1a;i18n | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 值得…

基层医联体医院患者历史检验检查数据的快速Python编程分析

​​​​​​​ 一、引言 1.1 研究背景与意义 在当今数字化医疗时代,医疗数据呈爆炸式增长,涵盖患者的基本信息、病史、检验检查结果、治疗方案等各个维度。这些海量且复杂的数据蕴含着巨大价值,为精准医疗决策提供了关键依据。通过对患者历史检验检查数据的深入对比分析…

如何使用OpenCV进行抓图-多线程

前言 需求&#xff1a; 1、如何使用OpenCV捕抓Windows电脑上USB摄像头的流、 2、采用多线程 3、获知当前摄像头的帧率。 这个需求&#xff0c;之前就有做了&#xff0c;但是由于出现了一个问题&#xff0c;人家摄像头的帧率目前都可以达到60帧/s 了&#xff0c;而我的程序…

数势科技:解锁数据分析 Agent 的智能密码(14/30)

一、数势科技引领数据分析变革 在当今数字化浪潮中&#xff0c;数据已然成为企业的核心资产&#xff0c;而数据分析则是挖掘这一资产价值的关键钥匙。数势科技&#xff0c;作为数据智能领域的领军者&#xff0c;以其前沿的技术与创新的产品&#xff0c;为企业开启了高效数据分析…

C++11编译器优化以及引用折叠

1.左值与右值的意义 1.左值引用和右值引用最终目的是减少拷贝&#xff0c;提高效率 2.左值引用还可以修改参数/返回值 左值引用不足&#xff1a; 部分函数放回场景&#xff0c;只能传值返回&#xff0c;不能引用左值返回 当前函数局部对象&#xff0c;出了当前函数作用域生…

小程序学习06——uniapp组件常规引入和easycom引入语法

目录 一 组件注册 1.1 组件全局注册 1.2 组件全局引入 1.3 组件局部引入 页面引入组件方式 1.3.1 传统vue规范&#xff1a; 1.3.2 通过uni-app的easycom 二 组件的类型 2.1 基础组件列表 一 组件注册 1.1 组件全局注册 &#xff08;a&#xff09;新建compoents文件…

【时时三省】(C语言基础)常见的动态内存错误2

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 对非动态开辟空间内存使用free释放 示例&#xff1a; 这个arr数组是在栈上的 *p指向的就是arr 对非动态空间也用了free ferr只能在动态开辟空间使用 使用free释放一块动态开辟空间的一部分…

3D高斯点云CUDA版本数据制作与demo运行

0. 简介 关于UCloud(优刻得)旗下的compshare算力共享平台 UCloud(优刻得)是中国知名的中立云计算服务商&#xff0c;科创板上市&#xff0c;中国云计算第一股。 Compshare GPU算力平台隶属于UCloud&#xff0c;专注于提供高性价4090算力资源&#xff0c;配备独立IP&#xff0c;…

【游戏设计原理】46 - 魔杖

幻想&#xff0c;人们可以通过多种形式来引发&#xff0c;比如文字&#xff0c;图片&#xff0c;绘画&#xff0c;语言等&#xff0c;但游戏与以上这些形式的区别&#xff0c;正如游戏与其他艺术形式的区别一样&#xff0c;游戏作为一种艺术和娱乐形式&#xff0c;其独特之处在…

基于Java的敬老院管理系统的设计和实现【源码+文档+部署讲解】

基于Java的敬老院管理系统设计和实现 摘 要 新世纪以来,互联网与计算机技术的快速发展,我国也迈进网络化、集成化的信息大数据时代。对于大众而言,单机应用早已成为过去&#xff0c;传统模式早已满足不了当下办公生活等多种领域的需求,在一台电脑上不联网的软件少之又少&#x…

Git快速入门(一)·Git软件的安装以及GitHubDesktop客户端的安装

目录 1. 概述 1.1 版本控制介绍 1.1.1 集中式版本控制 1.1.2 分布式版本控制 1.1.3 多人协作开发 2. 安装Git 3. 安装GitHubDesktop客户端 1. 概述 Git 是一个免费的、开源的分布式版本控制系统。它能够快速高效地处理从小型到大型的各种项目。Git 具有易于学习…

数据挖掘——神经网络分类

神经网络分类 神经网络分类人工神经网络多层人工神经网络 误差反向传播&#xff08;BP&#xff09;网络后向传播算法 神经网络分类 人工神经网络 人工神经网络主要由大量的神经元以及它们之间的有向连接构成。包含三个方面&#xff1a; 神经元的激活规则 主要是指神经元输入…

PDF文件提示-文档无法打印-的解决办法

背景信息 下载了几个签名的PDF文件&#xff0c;想要打印纸质版&#xff0c;结果打印时 Adobe Acrobat Reader 提示【文档无法打印】: 解决办法 网上的方案是使用老版本的PDF阅读器&#xff0c; 因为无法打印只是一个标识而已。 PDF文件不能打印的五种解决方案-zhihu 这些方…

docker容器间基于bridge双向通信

前面介绍了通过link实现容器间的单向通信&#xff1a; docker容器间基于Link单向通信-CSDN博客 情景概述 通过前面已经知道了设置link来达到容器间通过容器名称双向通信&#xff0c;那是不是可以通过每个容器都设置link来达到双向通信&#xff0c;这种方式实现起来太麻烦&…

前端如何判断多个请求完毕

在前端开发中&#xff0c;经常会遇到需要同时发起多个异步请求&#xff0c;并在所有请求都完成后再进行下一步操作的情况。 这里有几个常用的方法来实现这一需求&#xff1a; 使用 Promise.all() Promise.all() 方法接收一个 Promise 对象的数组作为参数&#xff0c;当所有的…

云备份项目--服务端编写

文章目录 7. 数据管理模块7.1 如何设计7.2 完整的类 8. 热点管理8.1 如何设计8.2 完整的类 9. 业务处理模块9.1 如何设计9.2 完整的类9.3 测试9.3.1 测试展示功能 完整的代码–gitee链接 7. 数据管理模块 TODO: 读写锁&#xff1f;普通锁&#xff1f; 7.1 如何设计 需要管理…

exam0-试卷整理

exam0-试卷整理 2010&#xff0c;2013是梦开始的地方&#xff0c;大概率会出原题的 2010 2013 2015 大题 manchester RIP更新 说出ISO与TCP/IP模型的相同点和不同点&#xff08;8分&#xff09; 相似&#xff1a; 两者都有层次&#xff0c;网络专业人员都需要知道二者&a…