FFmpeg5.0源码阅读——格式检测

  摘要:在拿到一个新的格式后,FFmpeg总是能够足够正确的判断格式的内容并进行相应的处理。本文在描述FFmpeg如何进行格式检测来确认正在处理的媒体格式类型,并进行相应的处理。
  关键字:FFmpeg,format,probe

  在调用FFmpeg的APIavformat_open_input之后就能在对应的AVFormatContext看到具体的媒体信息。为了获取媒体信息,首先需要打开流文件,然后读取HEAD检测媒体格式。

//打开文件
if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
    return ret;

if (s->iformat)
    return 0;
//探测文件格式
return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                s, 0, s->format_probesize);

1 打开文件

流协议
  为了打开文件首先需要确认输入的流协议。由于FFMpeg支持很多种协议,比如文件、RTMP、HTTP等,为了正确的读取数据首先需要确认输入的流是哪一种协议。对于输入文件,FFMpeg中确认具体是那种流协议,需要使用哪种protcol打开文件读取数据。
  FFmpeg中很多场景对于支持格式的检测都是对当前支持的格式列表的遍历来选择最匹配的那一个作为最终匹配的结果。协议选择也是一样。如果外界调用API时指定了打开的协议的类型则不会进行检测,没有的花会按照下面的调用链进行匹配。

init_input->io_open_default->ffio_open_whitelist->ffurl_open_whitelist->ffurl_alloc->url_find_protocol

  Protcol的核心代码如下,通过遍历内部维护的全局静态url表格匹配每一个url的name,最终选择能够匹配到的URLProtocol作为最终的流协议。一旦确认了流协议后续就是调用对应的open和read函数指针打开和读文件。

protocols = ffurl_get_protocols(NULL, NULL);
if (!protocols)
    return NULL;
for (i = 0; protocols[i]; i++) {
        const URLProtocol *up = protocols[i];
    if (!strcmp(proto_str, up->name)) {
        av_freep(&protocols);
        return up;
    }
    if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
        !strcmp(proto_nested, up->name)) {
        av_freep(&protocols);
        return up;
    }
}

2 检测媒体格式

确认iformat
  媒体格式探测其实就是确认使用哪种AVInputFormat解析(该成员是AVFormatContext->iformat字段)。如上面的代码所示这里的流协议检测是调用av_probe_input_format2来实现的,而该函数也只是转调了av_probe_input_format3而已。

  av_probe_input_format3检测文件格式的方式就是遍历FFmpeg内部的iformat静态表格,选择匹配度个最高的那个作为最终的格式。核心代码如下。

while ((fmt1 = av_demuxer_iterate(&i))) {
    if (fmt1->flags & AVFMT_EXPERIMENTAL)
        continue;
    if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))
        continue;
    score = 0;
    if (ffifmt(fmt1)->read_probe) {
        score = ffifmt(fmt1)->read_probe(&lpd);
        if (score)
            av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);
        if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
            switch (nodat) {
            case NO_ID3:
                score = FFMAX(score, 1);
                break;
            case ID3_GREATER_PROBE:
            case ID3_ALMOST_GREATER_PROBE:
                score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
                break;
            case ID3_GREATER_MAX_PROBE:
                score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
                break;
            }
        }
    } else if (fmt1->extensions) {
        if (av_match_ext(lpd.filename, fmt1->extensions))
            score = AVPROBE_SCORE_EXTENSION;
    }
    if (av_match_name(lpd.mime_type, fmt1->mime_type)) {
        if (AVPROBE_SCORE_MIME > score) {
            av_log(NULL, AV_LOG_DEBUG, "Probing %s score:%d increased to %d due to MIME type\n", fmt1->name, score, AVPROBE_SCORE_MIME);
            score = AVPROBE_SCORE_MIME;
        }
    }
    if (score > score_max) {
        score_max = score;
        fmt       = fmt1;
    } else if (score == score_max)
        fmt = NULL;
}

  从上面的流程中可以看到FFmpeg媒体信息探测会给每个格式一个分数,将根据输入流和对应的格式确认当前的分数,分数越高则匹配度越高,最后选择分数最高的格式作为选中的格式。而分数计算主要参考read_probe返回的分数值、后缀名和ID3元数据。
  从代码中也能够看到read_probe给的分数相比比较优先,而输入路径的后缀名会作为兜底策略,最低50分。这样是为什么对于一个不是媒体文件的格式个更改后缀名后却有可能检测成功的原因。那为什么需要保留后缀名检测呢?原因是并不是所有格式都能够通过文件内容判断其格式,比如相机的raw格式往往更改后缀名后就无法正常解析,这是因为raw格式只包含数据不包含任何其他多余的信息。

read_probe
  FFmpeg内部read_probe其实就是就是调用对应AVFormatInputread_probe函数,每个格式都有自己的实现。一般情况下分为两种,一种是文件开头有tag直接表明当前文件类型的,比如GIF等;另一种是需要读取部分文件内容来确认格式信息的,比如mov、mp4格式。

  第一种比较简单直接读取文件内容然后匹配tag即可,比如gif的格式检测:

static int gif_probe(const AVProbeData *p){
    /* check magick */
    if (memcmp(p->buf, gif87a_sig, 6) && memcmp(p->buf, gif89a_sig, 6))
        return 0;

    /* width or height contains zero? */
    if (!AV_RL16(&p->buf[6]) || !AV_RL16(&p->buf[8]))
        return 0;

    return AVPROBE_SCORE_MAX;
}

  另一种需要度却文件内容,比如mp4格式检测需要读取其中的box来确认部分文件信息。

static int mov_probe(const AVProbeData *p)
{
    int64_t offset;
    uint32_t tag;
    int score = 0;
    int moov_offset = -1;

    /* check file header */
    offset = 0;
    for (;;) {
        int64_t size;
        int minsize = 8;
        /* ignore invalid offset */
        if ((offset + 8ULL) > (unsigned int)p->buf_size)
            break;
        size = AV_RB32(p->buf + offset);
        if (size == 1 && offset + 16 <= (unsigned int)p->buf_size) {
            size = AV_RB64(p->buf+offset + 8);
            minsize = 16;
        } else if (size == 0) {
            size = p->buf_size - offset;
        }
        if (size < minsize) {
            offset += 4;
            continue;
        }
        tag = AV_RL32(p->buf + offset + 4);
        switch(tag) {
        /* check for obvious tags */
        case MKTAG('m','o','o','v'):
            moov_offset = offset + 4;
        case MKTAG('m','d','a','t'):
        case MKTAG('p','n','o','t'): /* detect movs with preview pics like ew.mov and april.mov */
        case MKTAG('u','d','t','a'): /* Packet Video PVAuthor adds this and a lot of more junk */
        case MKTAG('f','t','y','p'):
            if (tag == MKTAG('f','t','y','p') &&
                       (   AV_RL32(p->buf + offset + 8) == MKTAG('j','p','2',' ')
                        || AV_RL32(p->buf + offset + 8) == MKTAG('j','p','x',' ')
                        || AV_RL32(p->buf + offset + 8) == MKTAG('j','x','l',' ')
                    )) {
                score = FFMAX(score, 5);
            } else {
                score = AVPROBE_SCORE_MAX;
            }
            break;
        /* those are more common words, so rate then a bit less */
        case MKTAG('e','d','i','w'): /* xdcam files have reverted first tags */
        case MKTAG('w','i','d','e'):
        case MKTAG('f','r','e','e'):
        case MKTAG('j','u','n','k'):
        case MKTAG('p','i','c','t'):
            score  = FFMAX(score, AVPROBE_SCORE_MAX - 5);
            break;
        case MKTAG(0x82,0x82,0x7f,0x7d):
            score  = FFMAX(score, AVPROBE_SCORE_EXTENSION - 5);
            break;
        case MKTAG('s','k','i','p'):
        case MKTAG('u','u','i','d'):
        case MKTAG('p','r','f','l'):
            /* if we only find those cause probedata is too small at least rate them */
            score  = FFMAX(score, AVPROBE_SCORE_EXTENSION);
            break;
        }
        if (size > INT64_MAX - offset)
            break;
        offset += size;
    }
    if (score > AVPROBE_SCORE_MAX - 50 && moov_offset != -1) {
        /* moov atom in the header - we should make sure that this is not a
         * MOV-packed MPEG-PS */
        offset = moov_offset;

        while (offset < (p->buf_size - 16)) { /* Sufficient space */
               /* We found an actual hdlr atom */
            if (AV_RL32(p->buf + offset     ) == MKTAG('h','d','l','r') &&
                AV_RL32(p->buf + offset +  8) == MKTAG('m','h','l','r') &&
                AV_RL32(p->buf + offset + 12) == MKTAG('M','P','E','G')) {
                av_log(NULL, AV_LOG_WARNING, "Found media data tag MPEG indicating this is a MOV-packed MPEG-PS.\n");
                /* We found a media handler reference atom describing an
                 * MPEG-PS-in-MOV, return a
                 * low score to force expanding the probe window until
                 * mpegps_probe finds what it needs */
                return 5;
            } else {
                /* Keep looking */
                offset += 2;
            }
        }
    }

    return score;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python自动化运维--DNS处理模块dnspython

1.dnspython介绍 dnspython是Pyhton实现的一个DNS工具包&#xff0c;他几乎支持所有的记录类型&#xff0c;可以用于查询、传输并动态更新ZONE信息&#xff0c;同事支持TSIG&#xff08;事物签名&#xff09;验证消息和EDNS0&#xff08;扩展DNS&#xff09;。在系统管理方面&a…

EVE-NG网络仿真平台搭建

现在目前实验都是使用华为的Ensp模拟器&#xff0c;但是有时候一些功能模拟器无法实现&#xff0c;要不就是使用真机进行实验&#xff0c;第二个就是换个支持相关命令的模拟器了&#xff0c;今天来简单学习下EVE-NG这个模拟器。 一、EVE-NG简介 EVE-NG&#xff08;Emulated Vir…

【深度学习】注意力机制

https://blog.csdn.net/weixin_43334693/article/details/130189238 https://blog.csdn.net/weixin_47936614/article/details/130466448 https://blog.csdn.net/qq_51320133/article/details/138305880 注意力机制&#xff1a;在处理信息的时候&#xff0c;会将注意力放在需要…

HarmonyOS开发实战:UDP通讯示例规范

1. UDP简介 UDP协议是传输层协议的一种&#xff0c;它不需要建立连接&#xff0c;是不可靠、无序的&#xff0c;相对于TCP协议报文更简单&#xff0c;在特定场景下有更高的数据传输效率&#xff0c;在现代的网络通讯中有广泛的应用&#xff0c;以最新的HTTP/3为例&#xff0c;…

2024年6月29日 (周六) 叶子游戏新闻

老板键工具来唤去: 它可以为常用程序自定义快捷键&#xff0c;实现一键唤起、一键隐藏的 Windows 工具&#xff0c;并且支持窗口动态绑定快捷键&#xff08;无需设置自动实现&#xff09;。 喜马拉雅下载工具: 字面意思 《星刃》性感女主私密部位细节逼真 让玩家感到惊讶《星刃…

探索NVIDIA A100 显卡 如何手搓A100显卡

NVIDIA A100 显卡&#xff08;GPU&#xff09;是基于NVIDIA的Ampere架构设计的高性能计算和人工智能任务的处理器。 A100显卡主要由以下几种关键芯片和组件组成&#xff1a; 1. GPU芯片 NVIDIA GA100 GPU&#xff1a; 核心组件&#xff0c;是整个显卡的核心处理单元。GA100芯…

Ubuntu24.04 Isaacgym的安装

教程1 教程2 教程3 1.下载压缩包 link 2. 解压 tar -xvf IsaacGym_Preview_4_Package.tar.gz3. 从源码安装 Ubuntu24.04还需首先进入虚拟环境 python -m venv myenv # 创建虚拟环境&#xff0c;已有可跳过 source myenv/bin/activate # 激活虚拟环境python编译 cd isaa…

Python容器 之 字符串--字符串的常用操作方法

1.字符串查找方法 find() 说明&#xff1a;被查找字符是否存在于当前字符串中。 格式&#xff1a;字符串.find(被查找字符) 结果&#xff1a;如果存在则返回第一次出现 被查找字符位置的下标 如果不存在则返回 -1 需求&#xff1a; 1. 现有字符串数据: 我是中国人 2. 请设计程序…

Python 作业题1 (猜数字)

题目 你要根据线索猜出一个三位数。游戏会根据你的猜测给出以下提示之一&#xff1a;如果你猜对一位数字但数字位置不对&#xff0c;则会提示“Pico”&#xff1b;如果你同时猜对了一位数字及其位置&#xff0c;则会提示“Fermi”&#xff1b;如果你猜测的数字及其位置都不对&…

网络爬虫基础知识

文章目录 网络爬虫基础知识爬虫的定义爬虫的工作流程常用技术和工具爬虫的应用1. 抓取天气信息2. 抓取新闻标题3. 抓取股票价格4. 抓取商品价格5. 抓取博客文章标题 网络爬虫基础知识 爬虫的定义 网络爬虫&#xff08;Web Crawler 或 Spider&#xff09;是一种自动化程序&…

算法训练营day24--93.复原IP地址 +78.子集 +90.子集II

一、93.复原IP地址 题目链接&#xff1a;https://leetcode.cn/problems/restore-ip-addresses/ 文章讲解&#xff1a;https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1fA4y1o715 1.1 初…

MyBatis入门案例

实施前的准备工作&#xff1a; 1.准备数据库表2.创建一个新的springboot工程&#xff0c;选择引入对应的起步依赖&#xff08;mybatis、mysql驱动、lombok&#xff09;3.在application.properties文件中引入数据库连接信息4.创建对应的实体类Emp&#xff08;实体类属性采用驼峰…

终身免费的Navicat数据库,不需要破解,官方支持

终身免费的Navicat数据库&#xff0c;不需要破解&#xff0c;官方支持 卸载了Navicat&#xff0c;很不爽上干货&#xff0c;Navicat免费版下载地址 卸载了Navicat&#xff0c;很不爽 公司不让用那些破解的数据库软件&#xff0c;之前一直使用Navicat。换了几款其他的数据库试了…

WebStorm 2024 for Mac JavaScript前端开发工具

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff08;适合自己的M芯片版或Intel芯片版&#xff09;&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功3、打开访达&#xff0c;点击【文…

web权限到系统权限 内网学习第一天 权限提升 使用手工还是cs???msf可以不??

现在开始学习内网的相关的知识了&#xff0c;我们在拿下web权限过后&#xff0c;我们要看自己拿下的是什么权限&#xff0c;可能是普通的用户权限&#xff0c;这个连添加用户都不可以&#xff0c;这个时候我们就要进行权限提升操作了。 权限提升这点与我们后门进行内网渗透是乘…

代码查重软件-自力更生

为了减轻工作量&#xff0c;自研了简单实用的代码查重工具&#xff0c;可以对若干文件之间进行查重。通过调试&#xff0c;相似度大于80%的没有一个是冤枉的。好用。去掉雷同的&#xff0c;其他的代码再慢慢看。

pads layout 脚本导出不能运行excle解决办法

在一台新的电脑上安装好PADS&#xff0c;打开PCB文件导出坐标文件时&#xff1a; 出现“ActiveX Automation: server could not be found.”的问题,导致无法成功导出文件,错误提示截图如下&#xff1a; 导致上述问题的原因是在我们配置导出带坐标的脚本时,默认使用的是微软…

服务器连接不上

记录今天2024/07/02的问题&#xff1a; 我今天真的是非常无语&#xff0c;今天在连服务器的时候&#xff0c;突然发现连不上了。 后来才意识到&#xff0c;原来是我笔记本先是开了全局代理&#xff0c;然后再用easy connected连接。当时还跳出了一个窗口如下&#xff0c;我当时…

2024 MWC上海:创新力量驱动未来先行,移远智慧点亮数字蓝海

6月26日&#xff0c;2024年世界移动通信大会&#xff08;MWC上海&#xff09;如期举行&#xff0c;今年的展会以“未来先行”为主题&#xff0c;涵盖“超越 5G、数智制造和人工智能经济”三大技术主题。移远通信作为全球物联网行业的引领者之一&#xff0c;今年不仅在展示内容上…

性能调优 性能监控

1.影响性能考虑点包括&#xff1a; 数据库、应用程序、中间件(tomcat、nginx)、网络和操作系统等方面。 首先考虑自己的应用属于 CPU密集型 还是 IO密集型 cpu密集型 计算&#xff0c;排序&#xff0c;分组查询&#xff0c;各种算法 IO密集型 网络传输&#xff0c;磁盘读…