从学龄前开始解读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相关推荐

  1. 从学龄前开始解读FFMPEG代码 之 AVDictionary结构体以及av_dict_set()相关函数

    从学龄前开始解读FFMPEG代码 之 AVDictionary结构体以及av_dict_set相关函数 开始AVDictionary以及相关函数学习前想说的一些话 从AVDictionary开始 av ...

  2. FFmpeg代码导读——基础篇

    从事音视频技术开发对FFmpeg都不会感到陌生,通过它可以完成音视频采集.编解码.转码.后处理以及流媒体服务等诸多的功能,可以说涵盖了音视频开发中绝大多数的领域.金山云多媒体SDK团队在移动直播.短视 ...

  3. CVPR 2020 Oral 文章汇总,包括论文解读与代码实现

    点击上方,选择星标或置顶,不定期资源大放送! 阅读大概需要10分钟 Follow小博主,每天更新前沿干货 [导读]本文为大家整理了10篇CVPR2020上被评为Oral的论文解读和代码汇总. 1.Ra ...

  4. vins 解读_代码解读 | VINS 视觉前端

    AI 人工智能 代码解读 | VINS 视觉前端 本文作者是计算机视觉life公众号成员蔡量力,由于格式问题部分内容显示可能有问题,更好的阅读体验,请查看原文链接:代码解读 | VINS 视觉前端 v ...

  5. 融媒发展背景下学龄前儿童交通安全主题绘本创作启示

    学龄前儿童,特别是3-6岁的儿童,是良好习惯.素养以及行为规范形成的关键时期,是生命教育和安全教育的黄金时期.<幼儿园教育指导纲要(试行)>中明确要求"把保护幼儿的生命和促进幼儿 ...

  6. FFmpeg代码实现视频剪切

    有几天没写FFmpeg代码了,今天趁着有空闲来撸下FFmpeg剪切视频代码,我也是边学习边写,如果有错误,请在评论中指出,互相学习. 思路 说起来这个功能的实现也很简单,给定一个起始时间.一个结束时间 ...

  7. 时空上下文视觉跟踪(STC)算法的解读与代码复现

    时空上下文视觉跟踪(STC)算法的解读与代码复现 zouxy09@qq.com http://blog.csdn.net/zouxy09 本博文主要是关注一篇视觉跟踪的论文.这篇论文是Kaihua Z ...

  8. C++main函数的参数介绍以及如何在main函数前执行一段代码

    C++中参数介绍:https://www.cnblogs.com/jisongxie/p/7892366.html C++中主函数的执行过程以及如何在main函数前执行一段代码:https://www ...

  9. 台式电脑键盘字母乱了_键盘侠的育儿经利用键盘引导学龄前儿童正确使用电脑、学习英文字母和拼音...

    点击上方"总想做自己"关注我 可以订阅哦 一点资讯邀约作者:方游 专注探讨个人成长与正向激励的话题, 有干货,不再错过,快来点击关注吧! 微信公众号:apple_seeworld ...

最新文章

  1. [导入]解决“Internet Explorer 无法打开 Internet站点已终止操作”问题
  2. 世界很大,先从这几个公众号看起!
  3. python中collections_Python中collections模块的基本使用教程
  4. 实用代码-C#之IP地址和整数的互转
  5. 带你全面了解QinQ
  6. String.getBytes()和new String()
  7. phpcms能做什么呢?有什么作用呢?
  8. devexpress html编辑器,DevExpress 通用控件系列:TextEdit(2)
  9. qt中将数据库中的数据显示
  10. 简历 skill ps html,ps个人简历制作教程
  11. u3d canvas设置
  12. 数据表底层的B+树的叶子结点为啥用类似双链表连接起来
  13. [转载]刘光斗-刘晚苍系武学传人概况
  14. python学习——函数
  15. 同个网络计算机之间怎么共享,在局域网环境下的多台电脑之间如何实现文件共享需求呢?...
  16. andorid pppoe拨号上网
  17. Python | 人脸识别系统 — 活体检测
  18. 区块链1——区块链基础
  19. pageoffice使用笔记
  20. mysql与其他连接需要的框架_VS2013与MySql建立连接;您的项目引用了最新实体框架;但是,找不到数据链接所需的与版本兼容的实体框架数据库 EF6使用Mysql的技巧...

热门文章

  1. 阿里云云盘根目录扩容
  2. Android WiFi开发教程
  3. electron安装后运行报错解决方法
  4. 常见的web漏洞及其防范
  5. spring cloud sream 统一集成mq中间件
  6. linux修图软件 知乎,推荐几款适合大学生用的电脑!
  7. 软件测试与质量保证 - 复习与面试题库(from hitwh)
  8. apache中使用php
  9. JavaScript基础之缓存机制:浏览器缓存机制
  10. Evosuite用maven构建(内附详细过程)