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

一、引言

通过FFmpeg命令:

cpp 复制代码
./ffmpeg -i XXX.flv

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

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

二、flv_probe函数的定义

flv_probe函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/flvdec.c中,可以看到flv_probe函数内部调用了probe函数:

cpp 复制代码
static int flv_probe(const AVProbeData *p)
{
    return probe(p, 0);
}

三、probe函数的定义

probe函数也定义在libavformat/flvdec.c中:

cpp 复制代码
static int probe(const AVProbeData *p, int live)
{
    const uint8_t *d = p->buf;
    unsigned offset = AV_RB32(d + 5);

    if (d[0] == 'F' &&
        d[1] == 'L' &&
        d[2] == 'V' &&
        d[3] < 5 && d[5] == 0 &&
        offset + 100 < p->buf_size &&
        offset > 8) {
        int is_live = !memcmp(d + offset + 40, "NGINX RTMP", 10);

        if (live == is_live)
            return AVPROBE_SCORE_MAX;
    }
    return 0;
}

probe函数的作用就是检测某个文件是否为flv文件或flv格式的直播流。

形参pd:输入型参数,为AVProbeData类型的指针。

AVProbeData结构体声明在libavformat/avformat.h中:

cpp 复制代码
/**
 * 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的FLV文件/直播流中读取出来的二进制数据"的缓冲区。

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

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

返回值:返回一个类型为整形的分值。返回0表示该文件/直播流完全不符合FLV格式。返回AVPROBE_SCORE_MAX(100)表示该文件/直播流完全符合FLV格式。

形参live:值为1表示需要被推测格式的是直播流,值为0表示需要被推测格式的是本地媒体文件。

四、probe函数的内部实现原理

下面以需要被推测格式的是FLV文件为例,讲解probe函数的内部实现原理。FLV文件对应的解析函数是flv_probe函数,flv_probe函数内部调用了probe函数,这时probe函数的形参live的值为0。

probe函数内部,由于FLV文件的开头就是FLV header,所以指针d指向FLV header:

cpp 复制代码
    const uint8_t *d = p->buf;

通过AV_RB32宏定义读取FLV header中的DataOffset属性,得到整个FLV header的以字节为单位的长度,赋值给局部变量offset。关于AV_RB32宏定义的用法可以参考:《FFmpeg源码:AV_RB32、AV_RB16、AV_RB8宏定义分析》:

cpp 复制代码
    unsigned offset = AV_RB32(d + 5);

从《音视频入门基础:FLV专题(3)------FLV header简介》可以知道,FLV header的前3个字节固定为"FLV",所有判断d[0] == 'F' && d[1] == 'L' && d[2] == 'V';FLV header的第4个字节为Version,对于FLV格式,值必须为1,所以判断d[3] < 5;整个FLV header的长度固定为9,所以判断offset > 8。不满足条件probe函数返回0,表示完全不符合FLV格式:

cpp 复制代码
    if (d[0] == 'F' &&
        d[1] == 'L' &&
        d[2] == 'V' &&
        d[3] < 5 && d[5] == 0 &&
        offset + 100 < p->buf_size &&
        offset > 8) {
    //...
    }
    return 0;

判断是否为直播流,变量is_live的值为1表示是直播流,值为0表示是FLV文件:

cpp 复制代码
        int is_live = !memcmp(d + offset + 40, "NGINX RTMP", 10);

返回AVPROBE_SCORE_MAX(100)表示该文件/直播流完全符合FLV格式。

cpp 复制代码
        if (live == is_live)
            return AVPROBE_SCORE_MAX;
相关推荐
EasyNTS1 小时前
无插件直播流媒体音视频播放器EasyPlayer.js播放器的g711系列的音频,听起来为什么都是杂音
音视频·g711
weixin_452600694 小时前
【青牛科技】电流模式PWM控制器系列--D4870
科技·单片机·嵌入式硬件·音视频·智能电表·白色家电电源·机顶盒电源
LNTON羚通8 小时前
摄像机视频分析软件下载LiteAIServer视频智能分析平台玩手机打电话检测算法技术的实现
算法·目标检测·音视频·监控·视频监控
小屁孩大帅-杨一凡13 小时前
Python-flet实现个人视频播放器
开发语言·python·音视频
EasyCVR15 小时前
私有化部署视频平台EasyCVR宇视设备视频平台如何构建视频联网平台及升级视频转码业务?
大数据·网络·音视频·h.265
天空中的野鸟15 小时前
Android音频采集
android·音视频
计算机毕设孵化场16 小时前
计算机毕设-基于springboot的高校网上缴费综合务系统视频的设计与实现(附源码+lw+ppt+开题报告)
java·spring boot·计算机外设·音视频·课程设计·高校网上缴费综合务系统视频·计算机毕设ppt
lxkj_202421 小时前
使用线程局部存储解决ffmpeg中多实例调用下自定义日志回调问题
ffmpeg
简鹿办公1 天前
如何提取某站 MV 视频中的音乐为 MP3 音频
音视频·简鹿视频格式转换器·视频提取mp3音频
yufengxinpian1 天前
集成了高性能ARM Cortex-M0+处理器的一款SimpleLink 2.4 GHz无线模块-RF-BM-2340B1
单片机·嵌入式硬件·音视频·智能硬件