从学龄前开始解读FFMPEG代码 之 avformat_open_input函数read_probe和read_head
从学龄前开始解读FFMPEG代码 之 avformat_open_input函数 read_probe和read_head
- 开始学习前想说的话
- avi格式视频要如何probe
- FLV格式视频是如何probe的
- FLV文件read_header做了什么
- 结束时要说的话
开始学习前想说的话
在之前的open_input函数一二三的学习中,学龄前课程以一个代码运行的时序来进行了代码讲解,但是毕竟没有用一个较好的实例说明,而且在av_probe_input_format3函数中,学龄前学习也提到了,对于需要推测文件格式的数据源,会调用文件格式列表里面每种文件格式的read_probe函数去给一个评分,那么在avformat_open_input整个过程中会用到的read_probe和read_head具体要怎么做呢?本文以avi和flv两种视频封装格式为例子进行一下学龄前讨论学习
avi格式视频要如何probe
首先来看一下avi格式视频是怎么封装的,该格式的封装代码函数在libavformat/avidec.c中,拉到代码最后一行可以看到,该封装格式定义如下:
AVInputFormat ff_avi_demuxer = {.name = "avi",.long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"),.priv_data_size = sizeof(AVIContext),.extensions = "avi",.read_probe = avi_probe,.read_header = avi_read_header,.read_packet = avi_read_packet,.read_close = avi_read_close,.read_seek = avi_read_seek,.priv_class = &demuxer_class,
};
可以看到哈,avi格式设置了自己的read_probe函数,函数名字叫avi_probe,就在同一个文件里,如下所示:
static int avi_probe(const AVProbeData *p)
{int i;/* check file header */for (i = 0; avi_headers[i][0]; i++)if (AV_RL32(p->buf ) == AV_RL32(avi_headers[i] ) &&AV_RL32(p->buf + 8) == AV_RL32(avi_headers[i] + 4))return AVPROBE_SCORE_MAX;return 0;
}
看上去非常简短哈,但是这里涉及到了一个比较重要的变量是avi_headers,一个二位数组,记录了一些headers信息,这个数组在同文件中定义了,如下所示:
static const char avi_headers[][8] = {{ 'R', 'I', 'F', 'F', 'A', 'V', 'I', ' ' },{ 'R', 'I', 'F', 'F', 'A', 'V', 'I', 'X' },{ 'R', 'I', 'F', 'F', 'A', 'V', 'I', 0x19 },{ 'O', 'N', '2', ' ', 'O', 'N', '2', 'f' },{ 'R', 'I', 'F', 'F', 'A', 'M', 'V', ' ' },{ 0 }
};
那么在probe里的这个*AV_RL32(p- >buf) == AV_RL32(avi_headers[i])*是在比较什么呢?我们知道,文件以二进制数据的方式存储,而文件头,即文件head中一般存储了关于这个文件的各种相关信息;实际上,AVI 文件是一种RIFF 文件格式,所以其文件头部的组成为 ‘RIFF’(4字节) + RIFF文件大小(4字节) + ‘AVIx’(文件类型,4字节,x可能为不同的类型,对应不同的字符)共12字节,如果要推测检查buf中是不是avi的文件头,就是将读入的文件头中的byte 0 ~ Byte 3(32位所以用了FFmpeg中的宏,是AV_RL32), byte 8 ~ Byte 11 与 avi_header 进行比较,如果对比成功,那么就证明该文件是avi文件,直接一伯分通过!否则,直接给予一个0的评分,推测条件非常严格,没有其他的中间分数。
从这个函数来看,avi的probe函数还是较为简单的,下面来看一下flv的probe函数是怎么样的
FLV格式视频是如何probe的
flv的probe函数写在了libavformat/flvdec.c文件中,该文件里有flv,有flv_live以及kux(优酷所使用的一种格式)等flv解码的格式,那么来看一下flv的probe吧
static int flv_probe(const AVProbeData *p)
{return probe(p, 0);
}
直接指向了该文件内的probe函数,所以实际上再跳转到probe函数中
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;
}
在flv的flv_probe中跳转到了同文件中的函数probe,而在该函数中可以看到,flv采用了跟avi差不多的文件头数据的字节对比的方法,首先用AV_RB32(d + 5)这样一个宏定义获取了byte 5~byte 8的数据(从byte0开始算的话),并使用AV_RB32进行了大小端转换,转换为小端并存到变量offset中。
之后,检查开头3个字符(代表signature)是否为“FLV”,再检查第四个字符(代表flv的version)是否小于5,再判断第6个字节(Headersize的第1个字节)为0,以及offset取值大于8.进入if判断后,是live flv和flv种类的判断,通过之后直接返回一伯分!同样的,如果没有通过检查,那么直接没有余地,评分为零。
FLV文件read_header做了什么
继续上面的flv分析,flv文件里的read_header又要做什么呢?
flv的read_header也在libavformat/flvdec.c文件中,如下所示:
static int flv_read_header(AVFormatContext *s)
{int flags;FLVContext *flv = s->priv_data;int offset;int pre_tag_size = 0;/* Actual FLV data at 0xe40000 in KUX file */if(!strcmp(s->iformat->name, "kux"))avio_skip(s->pb, 0xe40000);avio_skip(s->pb, 4);flags = avio_r8(s->pb);flv->missing_streams = flags & (FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO);s->ctx_flags |= AVFMTCTX_NOHEADER;offset = avio_rb32(s->pb);avio_seek(s->pb, offset, SEEK_SET);/* Annex E. The FLV File Format* E.3 TheFLVFileBody* Field Type Comment* PreviousTagSize0 UI32 Always 0* */pre_tag_size = avio_rb32(s->pb);if (pre_tag_size) {av_log(s, AV_LOG_WARNING, "Read FLV header error, input file is not a standard flv format, first PreviousTagSize0 always is 0\n");}s->start_time = 0;flv->sum_flv_tag_size = 0;flv->last_keyframe_stream_index = -1;return 0;
}
在代码中可以看见,声明好需要使用到的变量之后,代码流程中首先对kux格式(优酷的一种视频文件格式)做好skip(从代码来看实际上只是做了一个位移嘛…感觉针对这文件类型youku是不是也没有使用什么特别的视频算法编码之类的…)之后,将pb跳过4个字节,并读取设置好头部存储的flag位。
之后,检查数据中是否有视频和音频文件并设置标记位missing_streams,然后在ctx_flags中设置好noheader位置表示头部已经移动处理了。
在此之后,设置好位移offset,再对s的pb进行重定位,判断好pretagsize,之后设置好开始时间等等变量,完成整个过程。
结束时要说的话
为什么不分析avi的read_header函数呢?因为实在是太长了,看到头疼。总体上来说,每种视频格式使用到的函数也由它们自身视频文件特点来决定,所以本文只通过几个简单举例,来了解这两个函数的主要功能大概是做什么的就可以了。
不过接下来最为头疼的还是接下来几篇学习中即将要拿来分析的avformat_find_stream_info函数,该函数用于在打开解码器之前,提取文件数据,尝试读取一些视频的packet包数据,做好文件中stream等等的各项设置,功能比较重要,总共有五百多行,预计要分成三到四篇才能讲得完…而且也是个函数挖坑重灾区,慢慢走起吧
从学龄前开始解读FFMPEG代码 之 avformat_open_input函数read_probe和read_head相关推荐
- 从学龄前开始解读FFMPEG代码 之 AVDictionary结构体以及av_dict_set()相关函数
从学龄前开始解读FFMPEG代码 之 AVDictionary结构体以及av_dict_set相关函数 开始AVDictionary以及相关函数学习前想说的一些话 从AVDictionary开始 av ...
- FFmpeg代码导读——基础篇
从事音视频技术开发对FFmpeg都不会感到陌生,通过它可以完成音视频采集.编解码.转码.后处理以及流媒体服务等诸多的功能,可以说涵盖了音视频开发中绝大多数的领域.金山云多媒体SDK团队在移动直播.短视 ...
- CVPR 2020 Oral 文章汇总,包括论文解读与代码实现
点击上方,选择星标或置顶,不定期资源大放送! 阅读大概需要10分钟 Follow小博主,每天更新前沿干货 [导读]本文为大家整理了10篇CVPR2020上被评为Oral的论文解读和代码汇总. 1.Ra ...
- vins 解读_代码解读 | VINS 视觉前端
AI 人工智能 代码解读 | VINS 视觉前端 本文作者是计算机视觉life公众号成员蔡量力,由于格式问题部分内容显示可能有问题,更好的阅读体验,请查看原文链接:代码解读 | VINS 视觉前端 v ...
- 融媒发展背景下学龄前儿童交通安全主题绘本创作启示
学龄前儿童,特别是3-6岁的儿童,是良好习惯.素养以及行为规范形成的关键时期,是生命教育和安全教育的黄金时期.<幼儿园教育指导纲要(试行)>中明确要求"把保护幼儿的生命和促进幼儿 ...
- FFmpeg代码实现视频剪切
有几天没写FFmpeg代码了,今天趁着有空闲来撸下FFmpeg剪切视频代码,我也是边学习边写,如果有错误,请在评论中指出,互相学习. 思路 说起来这个功能的实现也很简单,给定一个起始时间.一个结束时间 ...
- 时空上下文视觉跟踪(STC)算法的解读与代码复现
时空上下文视觉跟踪(STC)算法的解读与代码复现 zouxy09@qq.com http://blog.csdn.net/zouxy09 本博文主要是关注一篇视觉跟踪的论文.这篇论文是Kaihua Z ...
- C++main函数的参数介绍以及如何在main函数前执行一段代码
C++中参数介绍:https://www.cnblogs.com/jisongxie/p/7892366.html C++中主函数的执行过程以及如何在main函数前执行一段代码:https://www ...
- 台式电脑键盘字母乱了_键盘侠的育儿经利用键盘引导学龄前儿童正确使用电脑、学习英文字母和拼音...
点击上方"总想做自己"关注我 可以订阅哦 一点资讯邀约作者:方游 专注探讨个人成长与正向激励的话题, 有干货,不再错过,快来点击关注吧! 微信公众号:apple_seeworld ...
最新文章
- [导入]解决“Internet Explorer 无法打开 Internet站点已终止操作”问题
- 世界很大,先从这几个公众号看起!
- python中collections_Python中collections模块的基本使用教程
- 实用代码-C#之IP地址和整数的互转
- 带你全面了解QinQ
- String.getBytes()和new String()
- phpcms能做什么呢?有什么作用呢?
- devexpress html编辑器,DevExpress 通用控件系列:TextEdit(2)
- qt中将数据库中的数据显示
- 简历 skill ps html,ps个人简历制作教程
- u3d canvas设置
- 数据表底层的B+树的叶子结点为啥用类似双链表连接起来
- [转载]刘光斗-刘晚苍系武学传人概况
- python学习——函数
- 同个网络计算机之间怎么共享,在局域网环境下的多台电脑之间如何实现文件共享需求呢?...
- andorid pppoe拨号上网
- Python | 人脸识别系统 — 活体检测
- 区块链1——区块链基础
- pageoffice使用笔记
- mysql与其他连接需要的框架_VS2013与MySql建立连接;您的项目引用了最新实体框架;但是,找不到数据链接所需的与版本兼容的实体框架数据库 EF6使用Mysql的技巧...