首页 > 其他分享 >FLV 封装格式详解

FLV 封装格式详解

时间:2024-04-10 09:04:39浏览次数:18  
标签:Tag 封装 字节 FLV header tag 类型 详解

FLV 封装格式详解

FLV 封装格式详解

简介

FLV(Flash Video)是 Adobe 公司设计开发的一种流行的流媒体格式,由于其视频文件体积轻巧、封装简单等特点,使其很适合在互联网上进行应用。此外,FLV可以使用 Flash Player 进行播放,而 Flash Player 插件已经安装在全世界绝大部分浏览器上,这使得通过网页播放 FLV 视频十分容易。目前主流的视频网站如优酷网,土豆网,乐视网等网站无一例外地使用了 FLV 格式。FLV 封装格式的文件后缀通常为“.flv”。直播场景下拉流比较常见的是 http-flv 直播流,具有延时低,易传输等特点。

FLV 格式

总体上看,FLV 由文件头(file header)和(file body)组成,其中 FLV body 由一对对的 Tag 和 Previous Tag Size 组成。

在这里插入图片描述

其中,Previous Tag Size 占用4个字节,记录前一个 Tag 的大小,用于逆向读取处理。

FLV header 后的第一个 Previous Tag Size 为 0。

Tag 由 Tag Header 和 Tag Body 组成。Tag Header 占 11 字节。

而 Tag Body 一般分为三种类型:脚本(Script)数据、视频数据、音频数据。其中 Video Tag 和 Audio Tag 由 Tag header 和 Tag Data 组成;Script Tag 只有 Tag Data。

FLV 数据以大端序进行存储,在解析时需要注意。

FLV 文件的详细内容结构如下:

在这里插入图片描述

FLV header

从上图中可以看到,FLV 头占用 9 个字节,用来标识文件类型为 FLV 类型,以及后续的音视频标识。一个 FLV 文件,每种类型的 tag 都属于一个流,也就是一个 flv 文件最多只有一个音频流,一个视频流,不存在多个独立的音视频流在一个文件的情况。FLV 头的结构如下:

在这里插入图片描述

上面的 UI 表示无符号整型,后面跟着的数字代表其长度是多少位;UB 表示位域,表示一个字节的多少位。可以参考C语言中的结构体位域。

所有 FLV 格式文件都以 FLV Header 开头。FLV Header 类型是 FLVHEADER,FLVHEADER 定义如下:

typedef struct {
    UI8 Signature; // 'F' (0x46)
    UI8 Signature; // 'L' (0x4C)
    UI8 Signature; // 'V' (0x56)
    UI8 Version; // FLV 版本。例如,0x01 表示 FLV 版本 1
    UI8 TypeFlags; // b0 是否存在视频流,b2 是否存在音频流,其他字段保留,值为 0
    UI32 DataOffset; // FLV Header 长度,单位:字节
} FLVHEADER;

在 FLV 版本 1 中,“数据偏移”字段值为 9。在 FLV 未来版本中,此字段可兼容更大尺寸的 FLV Header。

FLV body

FLV Header之后,就是 FLV File Body。FLV File Body 是由一连串的 back-pointers + tags 构成。

在这里插入图片描述

FLV Body 类型是 FLVBODY,FLVBODY 定义如下:

typedef struct {
    UI32 PreviousTagSize0;
    FLVTAG Tag1;
    UI32 PreviousTagSize1;
    FLVTAG Tag2;
    ...
    UI32 PreviousTagSizeN-1;
    FLVTAG TagN;
    UI32 PreviousTagSizeN;
} FLVBODY;

back-pointer

back-pointer 表示 Previous Tag Size(前一个 tag 的字节数据长度),占4个字节,第一个 back-pointer 数据为 0。

FLV tag

tag 表示音视频数据,每一个 tag 由两部分组成:tag header 和 tag data。

FLV Tag 类型是 FLVTAG,FLVTAG 定义如下:

typedef struct {
    UB[2] Reserved; // 用于 FMS 的保留字段, 值为 0
    UB[1] Filter; // 指示 packet 是否需要预处理。0 = 不需要预处理。1 = packet 在渲染前需要预处理。未加密文件中此值为0,加密文件中此值为1。
    UB[5] TagType; // 8 = 音频,9 = 视频,18 = 脚本数据
    UI24 DataSize; // tag data 的长度。等于 tag 总长度 – 11
    UI24 Timestamp; // 当前tag的解码时间戳 (DTS),单位是ms。第一个tag的DTS总是0
    UI8 TimestampExtended; // 和Timestamp字段一起构成一个32位值, 此字段为高8位。单位是ms
    UI24 StreamID; // 总为 0
  IF TagType == 8
    AudioTagHeader Header;
  IF TagType == 9
    VideoTagHeader Header;
  IF TagType == 8
    AUDIODATA Data;
  IF TagType == 9
    VIDEODATA Data;
  IF TagType == 18
    SCRIPTDATA Data;
} FLVTAG;

上面 Timestamp 和 TimestampExtended 两个字段拼成一个 32 位的时间戳,是当前 Tag 的解码时间戳 (DTS)。对于音频帧来说,PTS 和 DTS 相同。对于视频帧来说,若含 B 帧,则 PTS 和 DTS 不同,H264 视频帧 PTS = DTS + CTS,CTS 就是 CompositionTime 字段,表示 PTS 与 DTS 的时间偏移值,单位 ms。

FLV tag header

存放当前 tag 的类型、数据区长度、时间戳等信息。tag header 一般占 11 个字节。

在这里插入图片描述

FLV tag 的类型可以是视频、音频和 Script(脚本类型)。

FLV tag data:audio tag

audio tag 包含 audio tag header 和 audio tag body 两部分。

其中,audio tag header 占 1 字节,包含了音频数据的参数信息,从第 2 个字节开始为音频流数据。

在这里插入图片描述

前 4bit 表示音频格式;第 5、6bit 表示采样率;第 7bit 表示采样精度;第 8bit 表示音频声道。

在这里插入图片描述

这里着重强调一下格式10格式:AAC。声音类型应为 1 (立体声) 且采样率应为 3 (44 kHz)。这并不表示 FLV 中的 AAC 音频总是立体声、44 kHz的数据。实际上,Flash 播放器会忽略这两个值,而从已编码的 AAC 位流中提取出声道数和采样率信息。

第二个字节开始为音频数据:

在这里插入图片描述

如果音频格式为 10,即是 AAC 格式。AudioTagHeader 中会多出一个字节 AACPacketType,这个字段来表示 AACAUDIODATA 的类型:0 = AAC sequence header,1 = AAC raw。

  • AAC sequence header 也就是包含了 AudioSpecificConfig,里面有一些更加详细音频的信息。
  • AAC raw 这种包含的就是音频 ES 流了,也就是音频负载(audio payload)。

AudioTagHeader 定义如下:

typedef struct {
    UB [4] SoundFormat;
    UB [2] SoundRate;
    UB [1] SoundSize;
    UB [1] SoundType;
  IF SoundFormat == 10
    UI8 AACPacketType;
} AudioTagHeader;

AudioTagBody/AUDIODATA 定义如下:

typedef struct {
  IF Encrypted
    EncryptedBody Body
  else
    AudioTagBody Body;
} AudioTagBody;

AUDIODATA 包含 Body 字段。如果采用了加密,Body 的类型是 EncryptedBody,可参考规范文档“附件 F. FLV 加密”章节获得详细信息,此处略。如果未采用加密,则 Body 的类型是 AudioTagBody,下面详述。

AudioTagBody 定义如下:

typedef struct {
  IF SoundFormat == 10
    AACAUDIODATA SoundData;
  ELSE
    Varies by format
} AudioTagBody;

Flash 播放器 9.0.115.0 及以上版本支持 AAC 格式。AACAUDIODATA 定义如下:

typedef struct {
  IF AACPacketType == 0
    AudioSpecificConfig;
  ELSE IF AACPacketType == 1
    Raw AAC frame data in UI8;
} AACAUDIODATA;

在这里插入图片描述

FLV tag data:video tag

video tag 包含 video tag header 和 video tag body 两部分。video tag data 开始的第一个字节包含视频数据的参数信息,从第二个字节开始为视频流数据。结构如下:

在这里插入图片描述

第一个字节包含视频信息:前4位表示帧类型;后4位表示编码类型。

在这里插入图片描述

第 2 个字节开始为视频数据。

在这里插入图片描述

如果 CodecID = 7,视频的格式是 AVC(H.264)的话,VideoTagHeader 会多出 4 个字节的信息,AVCPacketType(1 字节) 和 CompositionTime(3 字节),所以是 H.264 编码的情况下 VideoHeader 长度是 5 个字节。

AVC VIDEO PACKET 结构如下:

在这里插入图片描述

VideoTagBody/VIDEODATA 定义如下:

typedef struct {
  IF Encrypted
    EncryptedBody Body
  else
    VideoTagBody Body;
} VIDEODATA;

VIDEODATA 包含 Body 字段。如果采用了加密,Body 的类型是 EncryptedBody,可参考规范文档“附件 F. FLV 加密”章节获得详细信息,此处略。如果未采用加密,则 Body 的类型是 VideoTagBody,下面详述。

VideoTagBody 包含视频帧净荷数据。VideoTagBody 定义如下:

typedef struct {
  IF FrameType == 5
    UI8 VideoData;
  ELSE (
  IF CodecID == 2
    H263VIDEOPACKET VideoData;
  IF CodecID == 3
    SCREENVIDEOPACKET VideoData;
  IF CodecID == 4
    VP6FLVVIDEOPACKET VideoData;
  IF CodecID == 5
    VP6FLVALPHAVIDEOPACKET VideoData;
  IF CodecID == 6
    SCREENV2VIDEOPACKET VideoData;
  IF CodecID == 7
    AVCVIDEOPACKET VideoData;
  )  
} VideoTagBody;

AVCVIDEOPACKET 包含 AVC(H264)视频净荷数据。AVCVIDEOPACKET 定义如下:

typedef struct {
  IF AVCPacketType == 0
    AVCDecoderConfigurationRecord Data;
  IF AVCPacketType == 1
    One or more NALUs
} AVCVIDEOPACKET;

AVCDecoderConfigurationRecord 包含着 H.264 解码相关比较重要的 sps 和 pps 信息,再给 AVC 解码器送数据流之前一定要把 sps 和 pps 信息送出,否则的话解码器不能正常解码。而且在解码器 stop 之后再次 start 之前,如 seek、快进快退状态切换等,都需要重新送一遍 sps 和 pps 的信息。

AVCDecoderConfigurationRecord 在 FLV 文件中一般情况也是出现 1 次,也就是第一个 video tag。

FLV tag data:script tag

script tag 又通常被称为 metadata tag,会放一些关于FLV视频和音频的元数据信息如:duration、width、height等。通常该类型 tag 会跟在 FLV header 后面作为第一个 tag 出现,而且只有一个。

script tag 没有 script tag header,只有script tag body。一般来说,script tag 包含两个 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 个字节表示元素值的类型。后面跟着为对应值,占用字节数取决于值的类型。

常见的数组元素表示如下表:

在这里插入图片描述

AMF:Action Message Format,是 Adobe 设计的一种通用数据封装格式,在 Adobe 的很多产品中应用,简单来说,AMF 将不同类型的数据用统一的格式来描述。

标签:Tag,封装,字节,FLV,header,tag,类型,详解
From: https://blog.csdn.net/ProgramNovice/article/details/137468819

相关文章

  • 最简单的 FLV 封装格式解析程序
    最简单的FLV封装格式解析程序最简单的FLV封装格式解析程序原理源程序结果下载链接参考最简单的FLV封装格式解析程序参考雷霄骅博士的文章:视音频数据处理入门:FLV封装格式解析本文中的程序是一个FLV封装格式解析程序。该程序可以从FLV中分析得到它的基本单元Ta......
  • Linux硬盘故障排除指南:Smartctl、Dmesg和Fsck使用详解
     文章目录引言Smartctl什么是SmartctlSmartctl的作用如何安装和使用SmartctlSmartctl使用示例和解释Dmesg什么是DmesgDmesg的作用如何使用DmesgDmesg使用示例和解释Fsck什么是FsckFsck的作用如何使用FsckFsck使用示例和解释故障......
  • 1-1、Docker命令详解
    Docker命令详解#docker--helpUsage:docker[OPTIONS]COMMAND[arg...]dockerdaemon[--help|...]docker[-h|--help|-v|--version]Aself-sufficientruntimeforcontainers.主要选项:-d:以后台进行方式运行容器-t:提供一个伪终端-......
  • java的封装性
    java的封装性简单说就是用private修饰使之只能在本类中使用。举个例子,现实生活中学生可以查询成绩,老师可以修改成绩。显然在这个现象映射到程序中"学生"是对象,学生具有属性“成绩”,且用private修饰,那么在外界创建一个学生对象时不能直接用学生.成绩体现出来,此时学生的信息......
  • Offer必备算法23_两个数组dp_八道力扣题详解(由易到难)
    目录①力扣1143.最长公共子序列解析代码②力扣1035.不相交的线解析代码③力扣115.不同的子序列解析代码④力扣44.通配符匹配解析代码⑤力扣10.正则表达式匹配解析代码⑥力扣97.交错字符串解析代码⑦力扣712.两个字符串的最小ASCII删除和解析代码⑧力扣71......
  • 【大数据篇】Spark运行时架构详解
    ApacheSpark的运行时架构是设计来高效处理大规模数据的。它包含多个组件,每个组件各司其职,共同协作完成数据处理任务。下面详细介绍这些组件及其职责:主要组件和进程Driver程序(DriverProgram):Driver程序是Spark应用的心脏,它运行应用的main()函数并且创建SparkContext......
  • # C++之STL整理(7)之queue用法(创建、赋值、增删查改)详解
    C++之STL整理(7)之queue用法(创建、赋值、增删查改)详解注:整理一些突然学到的C++知识,随时mark一下例如:忘记的关键字用法,新关键字,新数据结构C++的queue用法整理C++之STL整理(7)之queue用法(创建、赋值、增删查改)详解queue1.queue构造函数2.queue存取、插入和删除操作3.......
  • C语言:指针详解(2)
    目录一、数组名的理解二、使用指针访问数组三、一维数组传参的本质四、冒泡排序五、二级指针六、指针数组正文开始一、数组名的理解在上一篇博客中我们在使用指针访问数组的内容时有这样的代码:intarr[10]={1,2,3,4,5,6,7,8,9,10};int*p=&arr[0];这里我......
  • 【C语言中的操作符详解】
    目录一 C语言中的操作符详解:1.算术操作符2.关系操作符(比较操作符)3.逻辑操作符4.位操作符5.赋值操作符6.条件(三元)操作符7.其他操作符8.递增和递减操作符二c语言操作符,应用场景:算术操作符:位操作符:关系操作符:逻辑操作符:赋值操作符:条件(三元)操作符:其他......
  • antd popover组件二次封装children显示
    importReact,{PropsWithChildren}from'react';import'./index.scss';import{Popover}from'antd';exportinterfaceDictPopoverCompProps{open:boolean;keyWords:string;}//词库联想气泡constDictPopoverComp=(prop......