FLV(FLASH VIDEO),是一种常用的文件封装格式,目前国内外大部分视频分享网站都是采用的这种格式。其标准定义为《Adobe Flash Video File Format Specification》。RTMP协议也是基于FLV视频格式的。

FLV的文件格式在该规范中已阐述清楚,本章节不再重复描述,而是结合下面的示例具体阐述如何分析FLV文件。

图3. FLV文件结构示例1 

图4. FLV文件结构示例2

FLV文件的分析工具有很多,这里给大家推荐FLV Parser这个小软件,通过它可以很容易的看到文件的组成结构。

3.1 文件结构

从整个文件上看,FLV是由Header和File Body组成,如下图所示:

图5. FLV文件总体结构

1.FLV Header - 长度为9,其结构的标准定义参见标准定义见E.2 The FLV header;

FLV File Body - 由一连串的PreviousTagSize + Tag构成。previousTagSize是4个字节的数据,表示前一个tag的size。标准定义参见E.3 The FLV File Body。

以图3. FLV文件结构示例1为例分析整体结构:

1.位置0x00000000 - 0x00000008, 共9个字节,为FLV Header,其中:

◦0x00000000 - 0x00000002 : 0x46 0x4C 0x56分别表示字符'F''L''V',用来标识这个文件是FLV格式的。在做格式探测的时候,如果发现前3个字节为“FLV”,就认为它是FLV文件;

◦0x00000003 : 0x01, 表示FLV版本号;

◦0x00000004 : 0x05, 转换为2进制是0000 0101,其中第0位为1,表示存在video,第2位为1,表示存在audio;

◦0x00000005 - 0x00000008 : 0x00 0x00 0x00 0x09,转十进制为9,表示FLV header的长度,当FLV 版本号为1时,该值通常为9。

2.位置0x00000009 - ,为FLV File Body:

◦0x00000009 - 0x0000000C : 0x00 0x00 0x00 0x00 PreviousTagSize0,转十进制为0,该值永远为0;

◦0x0000000D - 0x00000209 : 0x12 ... 0x09,共509个字节,为Tag1的具体内容;

◦0x0000020A - 0x0000020D : 0x00 0x00 0x01 0xFD,转十进制为509,表示它前面的Tag,即Tag1的长度为509;

◦0x0000020E - :按照Tag + PreviousTagSize的结构依次递推,此处不再举例说明。

3.2 Tag定义

FLV File Body是由一系列的PreviousTagSize + Tag组成,其中PreviousTagSize的长度为4个字节,用来表示前一个Tag的长度;Tag里面的数据可能是video、audio或者scripts,其定义参见E.4.1 FLV Tag,结构如下:

图6. FLV Tag 结构

以图3. FLV文件结构示例1为例分析Tag结构:

1.位置0x0000020E : 0x08, 二进制为0000 1000,第5位为0, 表示为非加扰文件;低5位01000为8,说明这个Tag包含的数据类型为Audio;

位置0x0000020F - 0x00000211 : 0x00 0x00 0x04,转十进制为4,说明Tag的内容长度为4,与该tag后面的previousTagSize(15) - 11相同;

位置0x00000212 - 0x00000214 : 0x00 0x00 0x00,转十进制为0,说明当前Audio数据的时间戳为0;

位置0x00000215 : 0x00,扩展时间戳为0,如果扩展时间戳不为0,那么该Tag的时间戳应为:Timestamp | TimestampExtended<<24;

位置0x00000216 - 0x00000218 : 0x00 0x00 0x00,StreamID,总是0;

StreamID之后的数据每种格式的情况都不一样,下面会依次进行详细解读。

3.3 Audio Tags

如果TAG包中的TagType等于8,表示该Tag中包含的数据类型为Audio。StreamID之后的数据就是AudioTagHeader,其定义详见E.4.2.1 AUDIODATA。结构如下:

图7. FLV Audio Tag结构

需要说明的是,通常情况下AudioTagHeader之后跟着的就是AUDIODATA数据了,但有个特例,如果音频编码格式为AAC,AudioTagHeader中会多出1个字节的数据AACPacketType,这个字段来表示AACAUDIODATA的类型:

•0 = AAC sequence header

•1 = AAC raw。

以图3. FLV文件结构示例为例分析AudioTag结构:

位置0x00000219 : 0xAF, 二进制表示为1010 1111:

◦高4位为1010,转十进制为10,表示Audio的编码格式为AAC;

◦第3、2位为11,转十进制为3,表示该音频的采样率为44KHZ;

◦第1位为1,表示该音频采样点位宽为16bits;

◦第0位为1,表示该音频为立体声。

位置0x0000021A : 0x00,十进制为0,并且Audio的编码格式为AAC,说明AACAUDIODATA中存放的是AAC sequence header;

位置0x0000021B - 0x0000021C : AUDIODATA数据,即AAC sequence header。

3.3.1 AudioSpecificConfig

AAC sequence header中存放的是AudioSpecificConfig,该结构包含了更加详细的音频信息,《ISO-14496-3 Audio》中的1.6.2.1 章节对此作了详细定义。

通常情况下,AAC sequence header这种Tag在FLV文件中只出现1次,并且是第一个Audio Tag,它存放了解码AAC音频所需要的详细信息。

有关AudioSpecificConfig结构的代码解析,可以参考ffmpeg/libavcodec/mpeg4audio.c中的avpriv_mpeg4audio_get_config方法。

为什么AudioTagHeader中定义了音频的相关参数,我们还需要传递AudioSpecificConfig呢?

因为当SoundFormat为AAC时,SoundType须设置为1(立体声),SoundRate须设置为3(44KHZ),但这并不意味着FLV文件中AAC编码的音频必须是44KHZ的立体声。播放器在播放AAC音频时,应忽略AudioTagHeader中的参数,并根据AudioSpecificConfig来配置正确的解码参数。

3.4 Video Tag

如果TAG包中的TagType等于9,表示该Tag中包含的数据类型为Video。StreamID之后的数据就是VideoTagHeader,其定义详见E.4.3.1 VIDEODATA,结构如下:

图8. FLV Video Tag结构

VideoTagHeader之后跟着的就是VIDEODATA数据了,但是和AAC音频一样,它也存在一个特例,就是当视频编码格式为H.264的时候,VideoTagHeader会多出4个字节的信息,AVCPacketType和CompositionTime。

•AVCPacketType用来表示VIDEODATA的内容

•CompositonTime相对时间戳,如果AVCPacketType=0x01,为相对时间戳,其它均为0;

以图4. FLV文件结构示例2为例分析VideoTagHeader结构:

位置0x0000022C : 0x17, 二进制表示为0001 0111:

◦高4位为0001,转十进制为1,表示当前帧为关键帧;

◦低4位为0111,转十进制为7,说明当前视频的编码格式为AVC。

位置0x0000022D : 0x00,十进制为0,并且Video的编码格式为AVC,说明VideoTagBody中存放的是AVC sequence header;

位置0x0000022E - 0x00000230 : 转十进制为0,表示相对时间戳为0;

位置0x00000231 - 0x0000021C : VIDEODATA数据,即AVC sequence header。

3.4.1 AVCDecoderConfigurationRecord

AVC sequence header中存放的是AVCDecoderConfigurationRecord,《ISO-14496-15 AVC file format》对此作了详细定义。它存放的是AVC的编码参数,解码时需设置给解码器后方可正确解码。

通常情况下,AVC sequence header这种Tag在FLV文件中只出现1次,并且第一个Video Tag。

有关AVCDecoderConfigurationRecord结构的代码解析,可以参考中的ff_isom_write_avcc方法。

3.4.2 CompositionTime(相对时间戳)

相对时间戳的概念需要和PTS、DTS一起理解:

DTS : Decode Time Stamp,解码时间戳,用于告知解码器该视频帧的解码时间;

PTS : Presentation Time Stamp,显示时间戳,用于告知播放器该视频帧的显示时间;

CTS : Composition Time Stamp,相对时间戳,用来表示PTS与DTS的差值。

如果视频里各帧的编码是按输入顺序依次进行的,则解码和显示时间相同,应该是一致的。但在编码后的视频类型中,如果存在B帧,输入顺序和编码顺序并不一致,所以才需要PTS和DTS这两种时间戳。视频帧的解码一定是发生在显示前,所以视频帧的PTS,一定是大于等于DTS的,因此CTS=PTS-DTS。

FLV Video Tag中的TimeStamp,不是PTS,而是DTS,视频帧的PTS需要我们通过DTS + CTS计算得到。

为什么Audio Tag不需要CompositionTime呢?

因为Audio的编码顺序和输入顺序一致,即PTS=DTS,所以它没有CompositionTime的概念。

3.5 Script Data Tags

如果TAG包中的TagType等于18,表示该Tag中包含的数据类型为SCRIPT。

SCRIPTDATA 结构十分复杂,定义了很多格式类型,每个类型对应一种结构,详细可参考E.4.4 Data Tags

onMetaData是SCRIPTDATA中一个非常重要的信息,其结构定义可参考E.5 onMetaData。它通常是FLV文件中的第一个Tag,用来表示当前文件的一些基本信息: 比如视音频的编码类型id、视频的宽和高、文件大小、视频长度、创建日期等。

来自:金山云视频云团队

FFmpeg从入门到出家(FLV文件结构解析)的更多相关文章

  1. FFmpeg从入门到出家(HEVC在RTMP中的扩展)

    由金山云视频云技术团队提供:FFmpeg从入门到出家第三季: 为推进HEVC视频编码格式在直播方案中的落地,经过CDN联盟讨论,并和主流云服务厂商达成一致,规范了HEVC在RTMP/FLV中的扩展,具 ...

  2. Windows下FFmpeg快速入门

    本系列文章导航 Windows下FFmpeg快速入门 ffmpeg参数解释 mencoder和ffmpeg参数详解(Java处理视频) Java 生成视频缩略图(ffmpeg) 使用ffmpeg进行视 ...

  3. Windows下FFmpeg高速入门

    本系列文章导航 Windows下FFmpeg高速入门 ffmpeg參数解释 mencoder和ffmpeg參数具体解释(Java处理视频) Java 生成视频缩略图(ffmpeg) 使用ffmpeg进 ...

  4. Windows下FFmpeg快速入门 <第二篇>

    FFmpeg简介 FFmpeg是什么? FFmpeg是用于录制.转换和流化音频和视频的完整解决方案, 包括 libavcodec ,一套领先的音/视频编解码类库.FFmpeg 在Linux上开发,当可 ...

  5. 【爬虫入门手记03】爬虫解析利器beautifulSoup模块的基本应用

    [爬虫入门手记03]爬虫解析利器beautifulSoup模块的基本应用 1.引言 网络爬虫最终的目的就是过滤选取网络信息,因此最重要的就是解析器了,其性能的优劣直接决定这网络爬虫的速度和效率.Bea ...

  6. MP3文件结构解析(超详细)

    转自:http://blog.csdn.net/u010650845/article/details/53520426 MP3文件结构解析(超详细) 1. MP3文件结构解析 1.1. 概述 1.1. ...

  7. Shiro官方快速入门10min例子源码解析框架2-Session

    Shiro自身维护了一套session管理组件,它可以独立使用,并不单纯依赖WEB/Servlet/EJB容器等环境,使得它的session可以任何应用中使用. 2-Session)主要介绍在quic ...

  8. labview从入门到出家6(进阶篇)--移位寄存器的使用

    前面介绍了如何熟悉和使用Labview自带的库函数以及调试方式,大家后期基本可以凭借这两个方式从入门到出家了,哈哈,后面就靠各位同仁99%的努力了.这篇为啥要讲移位寄存器呢,主要是之前做的项目和经验告 ...

  9. labview从入门到出家5(进阶篇)--程序调试以及labview函数库的运用

    跟了前面几章的操作流程,相信大家对labview有了一定的认识.其实只要了解了labview的编程思路,再熟悉地运用各个变量,函数以及属性,那么我们就可以打开labview的大门了.跟其他编程语言一样 ...

随机推荐

  1. Spring快速复习和总结

    IOC:管理和启动的java类的权力反转到了Spring容器了: 具体说明:Spring容器将配置好的bean放到容器(通过beanfactory,这是一个接口)中,并开始启动(反射)和调用(反射)和 ...

  2. 10个你不得不知的WEB移动端开发的兼容问题

    1.IOS下input设置type=button属性disabled设置true,会出现样式文字和背景异常问题,使用opacity=1来解决 2.一些情况下对非可点击元素如(label,span)监听 ...

  3. HDU-6704 K-th occurrence

    Description You are given a string S consisting of only lowercase english letters and some queries. ...

  4. sh_05_超市买苹果

    sh_05_超市买苹果 # 1. 定义苹果的单价 price = 8.5 # 2. 挑选苹果 weight = 7.5 # 3. 计算付款金额 money = weight * price # 4. ...

  5. 每隔2分钟,div元素顺序淡入

    我们的官网,是游戏网站,需要很多的动画效果,下面就开写一个box有n个元素,这些元素顺序淡入,每隔2分钟,执行一次,代码开始: <div></div> <div>& ...

  6. find查找多种文件后缀

    find命令最常用的是查找某个文件,如: find ./ -name "test.txt" 则会在当前目录及子目录下查找test.txt文件 更常用的是查找某一类型的文件,如: f ...

  7. IDEA设置Ctrl+滚轮调整字体大小

    IDEA设置Ctrl+滚轮调整字体大小(转载)   按Ctrl+Shift+A,出现搜索框 输入mouse: 点击打开这个设置:勾选 点击ok,之后就可以通过Ctrl+滚轮 调整字体大小了.

  8. .bash_profile vs .bashrc

    w http://www.joshstaiger.org/archives/2005/07/bash_profile_vs.html

  9. 《图解 TCP-IP(第 5 版)》

    第一章 网络基础知识 计算机网络根据规模可以分为:广域网(WAN: Wide Area Network)和局域网(LAN: Local Area Network) 协议的标准化: 国际标准化组织(IS ...

  10. python-笔记(六)模块操作以及常用模块简介

    模块.包 什么是模块? 模块实质上就是一个python文件,它是用来组织代码的,意思是说把python代码写到里面,文件名就是模块的名称,例如:model.py model就是模块名称. 什么是包? ...