FLV格式详解

目录

一、文件结构概述

二、内容描述

2.1 字段描述

2.2 Tag

2.2.1 Tag Header

2.2.2 Tag Data

三、解析过程

四、知识点汇总

五、FLV使用场景及优缺点

六、QA

1、previous tag size是否有存在必要?因为tag header固定是11字节,tag header中还有有一个字段DataSize描述TagData的大小,Tag的大小 = 11(TagHeader大小)+TagHeader->DataSize,此时就不需要previous tag size了,而且previous tag size描述的是前一个tag的大小,tag都读完之后才会读取到previous tag size,才能知道tag的大小

2、如何识别出这一段数据是previous tag size呢,有什么标识别吗?

3、AAC/AVC sequence header都有什么作用,包含哪些内容?

4、script tag、AAC/AVC sequence header 在什么情况会有多个?

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

6、video tag中”相对时间戳“有什么用?

7、tag data中的数据是一帧数据吗?

一、文件结构概述

1、 写在前面:学习的时候一定要对照官方文档、结合分析工具,才能深入理解

(1)官方文档:http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf

(2)推荐FLV的分析工具:

2、FLV(Flash Video)封装格式是由一个文件头(flv header)和一个文件体(flv body)组成。

3、FLV的body由一对对的(previous tag size + tag)组成。previous tag size记录了前一个tag的大小,用于逆向读取处理,previous tag size本身的大小是固定的占4个字节。一般一个flv文件由一个头部信息,一个script tag,以及若干个video tag和audio tag组成。

4、Tag一般可以分为3种类型:脚本(帧)数据类型(script)、⾳频数据类型(audio)、视频数据(video)。每个tag都由header和data组成;(比如tag是script类型,那tag header 就是 script header,tag data 就是 script data)一个tag只能是三种类型的其中一种(不是一个tag同时包含三种数据类型),tag header的字段都是一样,tag data的内容是不同的,通过tag header来判断tag是哪一种类型。(每一种类型tag的内容:FLV格式详解_狗蛋儿l的博客-CSDN博客

5、各个数据关系图

 说明:

Tag 有三种类型:audio,video和script。一个tag只能是其中一种类型,通过tag header区分哪种类型。不同类型tag的tag data内容也有所不同,所以图中在tag data后面标注了一个“或”字。

Video Tag Body、Audio Tag Body也有两种类型,通过Video Tag Header区分是哪种类型,所以后面都有一个“或”字。标注灰色是因为视频在H.264时才会有AVC sequence header、AVC NALU其它视频编码格式没有这两种内容。音频只有在AAC时才会有AAC sequence header、AAC raw其它视频编码格式没有这两种内容。

6、文件结构图:各种数据在flv内部真实的排列顺序

二、内容描述

2.1 字段描述

2.2 Tag

1、 tag的类型有三种:audio,video和script。

2、每一个tag也是由tag header和tag data组成。tag header里存放的是当前tag的类型、数据区的长度等信息,tag data存放的内容根据tag类型的不同而有所不同。

2.2.1 Tag Header

1、tag header包含tag的基本信息

2、从TagType中可以获取tag是哪种类型,音频(8)、视频(9)、脚本(18)

3、从DataSize中可以获取tag data的大小(是tag data的大小,不是整个tag的大小)

2.2.2 Tag Data

1、包含元信息和真正音视频数据

2.2.2.1 Script Tag Data

1、tag header中TagType==0x12时,tag的类型是scrpit tag

2、一般第一个tag的类型就是script tag

3、以不同类型的键值对的形式保存了若干媒体文件的参数数据(metadata)(《FFmpeg音视频开发基础与实战》,78页)

4、 一般来说,该script tag data结构包含两个AMF包。AMF(Action Message Format)是Adobe设计的一种通用数据封装格式,在Adobe的很多产品中应用,简单来说,AMF将不同类型的数据用统一的格式来描述。 (FLV format分析 - 简书)AMF包的第一个字节表示包类型,如下所示:

 第一个AMF包:封装字符串类型数据。
        第1个字节表示AMF包类型,一般总是0x02,表示字符串。第2-3个字节为UI16类型值,标识字符串的长度,一般总是0x000A(字符串“onMetaData”的长度)。后面字节为具体的字符串,一般总为“onMetaData”(6F,6E,4D,65,74,61,44,61,74,61)。

第二个AMF包:封装一个数组类型,这个数组中包含了音视频信息项的名称和值。

        第1个字节表示AMF包类型,一般总是0x08,表示数组。第2-5个字节为UI32类型值,表示数组元素的个数。后面即为各数组元素的封装。常见的数组元素如下所示。

        上面提到的数组元素,为元素名称和值组成的对。第1-2个字节表示元素名称的长度,假设为L。后面跟着为长度为L的字符串。第L+3个字节表示元素值的类型。后面跟着为对应值,占用字节数取决于值的类型;以不同类型的键值对的形式保存了若干媒体文件的参数数据(metadata)(《FFmpeg音视频开发基础与实战》,78页)。通过mediaInfo分析可以直观看到

        上图中一个数据有4个字段描述,key和value分别有两个字段来描述,以duration为例进行说明:

        StringLength:key的长度,即“duration”这个字符串的长度

        StringData:key的内容,即“duration”

        Type:value的类型,即double

        Value:value的值,即5.120s

通过ffmpeg获取metadata,我们看到的duration是5.120s,其实在flv的script tag中有4个字段来描述。

2.2.2.3 Audio Tag Data

1、音频audio tag data又分为audio tag header和audio tag body数据区;编码格式如果不是AAC则audio tag header是1个字节,如果是AAC,则audio tag header是2个字节,多出来一个AACPacketType,用来表示audio tag body的类型;

2、audio tag 结构如下

Field

Type

Comment

Audio Tag Header

音频格式

SoundFormat

UB4

0 = Linear PCM, platform endian

1 =ADPCM

2 = MP3

3 = Linear PCM, little endian

4 = Nellymoser 16-kHz mono

5 = Nellymoser 8-kHz mono

6 = Nellymoser

7 = G.711 A-law logarithmic PCM

8 = G.711 mu-law logarithmic PCM

9 = reserved

10 = AAC

11 = Speex

14 = MP3 8-Khz

15 = Device-specific sound

flv是不支持g711a的,如果要用,可能要用线性音频。

采样率

SoundRate

UB2

0 = 5.5-kHz

1 = 11-kHz

2 = 22-kHz

3 = 44-kHz

对于AAC总是3

采样精度

SoundSize

UB1

0 = snd8Bit

1 = snd16Bit

音频声道

SoundType

UB1

0 = sndMono 单声道

1 = sndStereo 立体声,双声道

对于AAC总是1

AAC包类型

AACPacketType

UB8

只有在AAC时才有此字段

0 = AAC sequence header

1 = AAC raw

Audio Tag Body

音频数据

AudioData

UI[8*n]

如果是PCM线性数据,存储的时候每个16bit小端存储,有符号。

如果音频格式是AAC,则存储的数据是AAC AUDIO DATA,否则为线性数组

 

3、AAC tag body的结构如下,两种类型AAC sequence header、AAC raw都是相同的存储结构,如上表所示

4、AACPacketTYpe中的AAC sequence header存放的是AudioSpecificConfig,它存放了解码AAC音频所需要的详细信息,用于初始化编码器,包含了更加详细的音频信息,比如采样率、声道数等;AudioSpecificConfig详细内容见《ISO-14496-3 Audio》中的1.6.2.1 章节。而且在ffmpeg中有对AudioSpecificConfig解析的函数,ff_mpeg4audio_get_config(),可以对比的看一下,理解更深刻。(FLV视频封装格式详解_51CTO博客_flv 格式

5、通常情况下,AAC sequence header这种tag在flv文件中只出现1次,并且在第一个audio tag中。如果采样率、声道数等信息发生变化,则需要重新发送AAC sequence header。

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

因为当SoundFormat为AAC时,audio tag header中的SoundType须设置为1(立体声),SoundRate须设置为3(44KHZ),但这并不意味着FLV文件中AAC编码的音频必须是44KHZ的立体声。播放器在播放AAC音频时,应忽略audio tag header中的参数,并根据AudioSpecificConfig来配置正确的解码参数。(FFmpeg代码导读——基础篇 - 腾讯云开发者社区-腾讯云)

6、AAC raw 存放的是真正的音频数据

2.2.2.4 Video Tag Data

1、视频video tag data又分为video tag header 和video tag body数据区;编码格式如果不是H.264则vide tag header是1个字节,如果是H.264,则video tag header是4个字节,多出来AVCPacketType和CompositionTime

  • AVCPacketType用来表示VIDEODATA的内容
  • CompositonTime相对时间戳,如果AVCPacketType=0x01,为相对时间戳,其它均为0;

2、video tag 结构如下

Field

Type

Comment

Video Tag Header

帧类型 

FrameType

UB4

1 = keyframe (for AVC, a seekable frame)——h264的IDR,关键帧,可重入帧。

2 = inter frame (for AVC, a non- seekable frame)——h264的普通帧

3 = d keyframe (reserved for server use only)

5 = video info/command frame

编码ID 

CodecID

UB4

使用哪种编码类型

1 = JPEG (currently unused)

2 = Sorenson H.263

3 = Screen video4: On2 VP6

5 = On2 VP6 with alpha channel

6 = Screen video version 2

7 = AVC

AVC包类型

AVCPacketType

UB8

只有在H.264时才有此字段

0 = AVC sequence header

1 = AVC NALU

2 = AVC end of sequence(lower level NALU sequence ender is not required or supported)

相对时间戳

CompositionTime

UB24

只有在H.264时才有此字段

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

Video Tag Body

视频数据

VideoData

UI[8*n]

AVCPacketType=0:数据部分为AVCDecoderConfigurationRecord;

AVCPacketType=1:数据部分为1个或多个NALU

AVCPacketType=2:数据部分为空

 

3、AVC sequence header中存放的是AVCDecoderConfigurationRecord,包含着是H.264解码相关比较重要信息,比如sps和pps信息,用于初始化编码器;详细信息见《ISO-14496-15 AVC file format》。它存放的是AVC的编码参数,解码时需设置给解码器后方可正确解码。

4、通常情况下,AVC sequence header这种Tag在FLV文件中只出现1次,并且在第一个video tag中。如果码率、分辨率等信息发生变化,则需要重新发送AVC sequence header。

5、ACV NALU存放的是真正的视频数据

两种类型ACV sequence header、AAC NALU都是相同的存储结构,如上表所示。

6、CompositionTime(相对时间戳)

相对时间戳的概念需要和PTS、DTS一起理解(FFmpeg代码导读——基础篇 - 腾讯云开发者社区-腾讯云

  • 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的概念。

三、解析过程

1、通过解析tag header中的TagType的内容获取tag的类型,通过DataSize获取tag data大小

2、读取tag data中的Audio/Video Tag Header获取音视频的编码信息

3、读取真正的音视频数据

四、知识点汇总

1、flv header通常是是9字节。

2、previous tag size固定是4字节,flv header后的第一个previous tag size的值为0。

3、tag header的大小是都是固定的11字节,tag data的大小则不一定,因为tag有三种类型(audio,video,script),不同类型data大小不同,不同的video tag也不一定相同。

4、tag header中的tagtype判断出tag是哪种类型,音频(tagtype==8)、视频(tagtype==9)、脚本(tagtype==18)。

5、script tag通常是flv文件的第一个tag,并且只有一个,跟在flv header后面。

script tag数量不是1的情况:如果有字幕,则script tag就不是一个了,因为字幕的tag类型也是script tag,字幕的tag数量是不固定的,和audio/video一样,字幕的script也贯穿整个flv文件,字幕的AMF包类型是“OnTextData”。读取和写入过程详见ffmpeg代码。

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

AAC sequence header数量不是1的情况:如果采样率、声道数等信息变化时,ffmpeg会新写一个AAC sequence header,因为要重新初始化编码器,读文件时,读到中间的AAC sequence header,会将信息重新写入编码器。

7、AVC sequence header是AVC tag body 的一种类型;通常情况下,AVC sequence header这种tag在flv文件中只出现1次,并且在第一个Video Tag中,它存放的是AVC的编码参数,解码时需设置给解码器后方可正确解码。

AVC sequence header数量不是1的情况:如果分辨率、码率等信息变化时,ffmpeg会新写一个AVC sequence header,因为要重新初始化编码器,读文件时,读到中间的AVC sequence header,会将信息重新写入编码器。

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

五、FLV使用场景及优缺点

1、使用场景:目前主流的视频网站基本都支持FLV,直播、点播都有广泛的应用。

2、优点:

(1)封装后的音视频文件体积小、封装简单等特点,非常适合于互联网上使用。

(2)它的出现有效地解决了视频文件导入Flash后,使导出的SWF文件体积庞大,不能在网络上很好的使用等问题。

3、缺点:

(1)FLV 参考标准协议中没有定义可以存储 H.265 视频压缩数据,如果我们自己将 H.265 的视频数据存储到 FLV 容器中,其他播放器不一定能够很好地播放这个视频。所以在我们将视频流、音频流写入到一个封装容器中之前,需要先弄清楚这个容器是否支持我们当前的视频流、音频流数据。(03|如何做音视频的封装与转码?-极客时间

(2)不适合多音轨:flv只能有一条音轨和一条视频轨。

六、QA

1、previous tag size是否有存在必要?因为tag header固定是11字节,tag header中还有有一个字段DataSize描述TagData的大小,Tag的大小 = 11(TagHeader大小)+TagHeader->DataSize,此时就不需要previous tag size了,而且previous tag size描述的是前一个tag的大小,tag都读完之后才会读取到previous tag size,才能知道tag的大小

参考一位网友的回答:(Why flv file body use PreviousTagSize rather than NextTagSize?)

I think flv tag list in flv body is designed as double linked list. The PreviousTagSize represents the back node point. Actually, the 'NextTagSize' is already included in current node (DataSize syntax in tag header), and it can point to the next node.

With double direction linked points, it will be easy and fast to seek previous or latter media packets.

2、如何识别出这一段数据是previous tag size呢,有什么标识别吗?

没有标识,没有字段来描述这一段数据就是previous tag size;只能通过tag末尾再偏移4个字节来找到PreviousTagSize

3、AAC/AVC sequence header都有什么作用,包含哪些内容?

AAC sequence header:详见“2.2.2.3 Audio Tag Data -> 4”

AVC sequence header:详见"2.2.2.4 Video Tag Data -> 3"

4、script tag、AAC/AVC sequence header 在什么情况会有多个?

详见“2.3知识汇总 -> 5、6、7”

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

详见“2.2.2.3 Audio Tag Data -> 5”

6、video tag中”相对时间戳“有什么用?

详见"2.2.2.4 Video Tag Data -> 6"

7、tag data中的数据是一帧数据吗?

绝大多数情况下,一般正常的文件一个tag是一帧,但也不排除个例,一个tag包含多帧,或者不满一帧,或者连续的任意数据(这个是特定的音视频格式才行,有帧头能够查找的,比如MPV(FLV没有),H264(0x00000001头的)MP3等),对于这种情况,解码前就多处理一步,查找一下帧(常用的MPV,MP3解码器不需要查找,内部自己实现了,只需要不断送数据就行了)。(关于flv中tag的问题

从ffmpeg解析tag的过程也可以看出,一个tag data对应一个pkt,一个pkt就是一帧数据,所以一个tag就是一帧数据

猜你喜欢

转载自blog.csdn.net/weixin_39399492/article/details/129986667