FLV 封装格式解析

news2025/5/21 11:01:40

1、FLV 简介

FLV(Flash Video) 是 Adobe 公司推出的一种流媒体格式,由于其封装后的音视频文件体积小、封装简单等特点,非常适合于互联网上使用。目前主流的视频网站基本都支持FLV。采用 FLV 格式封装的文件后缀为.flv。直播场景下拉流比较常见的是 http-flv 直播流,具有延时低,易传输等特点。

2、FLV 格式

总体上看,FLV 由文件头(file header)和(file body)组成,其中 FLV body 由一对对的 Tag 和 Previous Tag Size(占用4个字节,记录前一个 Tag 的大小,用于逆向读取处理,FLV header 后的第一个 Previous Tag Size 为 0)组成。而 Tag 一般分为三种类型:脚本数据、视频数据、音频数据,有 Tag header 和 Tag Data 组成。FLV 数据以大端序进行存储,在解析时需要注意。一个标准 FLV 文件结构如下图:

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

2.1 FLV header(文件头)

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

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

2.2 FLV Body(文件体)

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

back-pointer

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

Tag

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

3、FLV Tag 详解

3.1 Tag 数据结构

flv_tag_header + flv_tag_data,其中 flv_tag_header 占 11 个字节。

3.2 FLV_tag_header

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

3.3 FLV_Tag_Data

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

3.3.1 Script Tag Data(脚本类型、帧类型)

脚本 Tag 一般只有一个,是 flv 的第一个 Tag,跟在 flv header 后,用于存放 flv 视频和音频的元信息,比如 duration、audiodatarate、creator、width 等。一般来说,Script Tag Data结构包含两个 AMF 包(AMF(Action Message Format)是 Adobe 设计的一种通用数据封装格式,在 Adobe 的很多产品中应用,简单来说,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 个字节表示元素值的类型。后面跟着为对应值,占用字节数取决于值的类型。

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

【学习地址】: FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发
【文章福利】:免费领取更多音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击 1079654574 加群领取哦~

3.3.2 Video Tag Data(视频类型)

视频 Tag Data 开始的第一个字节包含视频数据的参数信息,从第二个字节开始为视频流数据。结构如下:

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

第二个字节开始为视频数据

AVC VIDEO PACKET

视频的格式(CodecID)是 AVC(H.264)的话,VideoTagHeader 会多出 4 个字节的信息,AVCPacketType 和 CompositionTime,所以是 H264 编码的情况下 VideoHeader 长度是 5 个字节。 AVC VIDEO PACKET 结构如下:

AVCDecoderConfigurationRecord

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

sps pps

第一个 video tag 一般存放的是 sps 和 pps。存储的格式: 0x01 + sps[1] + sps[2] + sps[3] + 0xFF + 0xE1 + sps size + sps + 01 + pps size + pps 其中 sps size 和 pps siz e各占两个字节。 data 数据结构:

关于 CTS:需要和 pts,dts 配合一起理解。 pts:presentation time stamps 显示时间,也就是接收方在显示器显示这帧的时间。单位为1/90000 秒; dts:decoder timestamps 解码时间,也就是rtp包中传输的时间戳,表明解码的顺序。单位为1/90000 秒。——根据后面的理解,pts 就是标准中的 CompositionTime; cts偏移:cts = (pts - dts) / 90。cts 的单位是毫秒; pts 和 dts 的时间不一样,应该只出现在含有 B 帧的情况下,也就是 profile main 以上。baseline 是没有这个问题的,baseline 的 pts 和 dts 一直相同,所以 cts 一直为 0 。

3.3.3 Audio Tag Data(音频类型)

音频 Tag Data 区域开始的第一个字节包含了音频数据的参数信息,从第二个字节开始为音频流数据。结构如下:

第一个字节包含音频信息:前 4bit 表示音频格式;第 5、6bit 表示采样率;第 7bit 表示采样精度;第 8bit 表示音频声道。

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

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

  • AAC sequence header 也就是包含了 AudioSpecificConfig,AudioSpecificConfig 包含着一些更加详细音频的信息。
  • AAC raw 这种包含的就是音频 ES 流了,也就是 audio payload。

4、举例子

根据真实数据来解析 FLV 格式。

4.1 FLV header

<464c5601 05000000 09000000 00120001 17000000 00000000 02000a6f 
6e4d6574 61446174 61030005 77696474 68004084 00000000 00000006 
68656967 68740040 7e000000 00000000 0c736f75 7263655f 77696474 
6800409e 00000000 0000000d 736f7572 63655f68 65696768 74004090 
e0000000 00000009 6672616d 65726174 6500402e 00000000 0000000c 
76696465 6f636f64 65636964 00401c00 00000000 00000f61 7564696f 
73616d70 6c657261 74650040 bf400000 00000000 0f617564 696f7361 
6d706c65 73697a65 00403000 00000000 00000673 74657265 6f010000 
0c617564 696f636f 64656369 64004024 00000000 0000000c 63726561 
74696f6e 64617465 02001732 3032312d 31312d30 33203037 3a32373a 
33302055 54430006 61757468 6f720200 034c4c4c 00000900 000122>

46 4C 56 01 05 00 00 00 09:表示 FLV header(9 字节),音视频都有; 0x 00 00 00 00 表示第一个 back-pointers(前一个 tag 的 size)。因为前面没有 tag,所以为 0。

4.2 脚本 Tag

12 00 01 17 00 00 00 00 00 00 00 (tag header 11 字节) 0x12 表示这是一个 scripts tag, 00 01 17 三子节表示 tag data 长度为 279 个字节,Timestreamp、TimestampExtended、stream id 均为0。 下一个 back-pointers,表示该 scripts tag 的 size,即 279 + 11 = 290 = 0x122,即 00 00 01 22,可以看到最后4字节刚好是的。 02 00 0a 6f 6e 4d 65 74 61 44 61 74 61 表示第一个 AMF 包,02 表示类型为 string type,后面两个字节 00 0A 表示长度是 10,值 onMetaData; 03 表示 ObjectType,此处应该一般是 08 才对,表示数组类型;表示有8个键值对。 00 05 表示键长度,77 69 64 74 68 表示 width,00 表示类型为 Number,后面 8 字节表示值; 00 06 表示键长度, 68 65 69 67 68 74 表示 height,00 表示类型为 Number,后面 8 字节表示值。 以此类推解析到最后 end marker 00 00 09,表示解析完毕。

4.3 第一个 video tag

一般包含 sps、pps

<09000022 00000000 00000017 00000000 014d401e ffe1000e 674d401e 
a680a03d a6e02020 20400100 0468ee3c 80000000 2d>

09 00 00 22 00 00 00 00 00(tag header 11 字节) 09 表示视频 tag; 00 00 22 表示长度为 0x22 = 34,加上头部长度 11 字节,为 45。下一个 back-pointers 是 00 00 00 2D(上面数据的最后的四字节), Timestreamp、TimestampExtended、stream id 均为 0; 下面就是 tag data 数据: 0x17 即 0001 0111,前 4 位表示帧类型,1 为关键帧;后 4 位表示编码 ID,7 表示 AVC。 视频格式是AVC(H.264)类型的话,后面 1 个字节表示 AVCPacketType,再后三个字节表示 CompositionTime: 00 AVCPacketType 为 0,表示是 AVCDecoderConfigurationRecord。就表示包含着 sps 和 pps 了。这个东西要第一个发给解码器,要不然不能正常解码。 00 00 00 CompositionTime 为 0。接下来就是 sps 和 pps 的信息了。 按照 0x01 + sps[1] + sps[2] + sps[3] + 0xFF + 0xE1 + sps size + sps + 01 + pps size + pps 的格式,参照二进制数据: sps[1] = 4d;sps[2] = 40;sps[3] = 1e;sps size = 000e,表示 sps 的长度为 14,14 字节的数据读取完,读到 01;pps size = 0004,表示 pps 的长度为 4,读取完后该 tag 结束。下一个back-pointers。

4.4 第二个 video tag 以及后续的 video tag

<09002374 00000000 00000017 01000000 0000236b 65b82129 7f7ae073 
74574ffe 0fd1ebcc 6851014a d25aa986 6daee3a1 6f0d7b77 734e17a9 
0fe00e56 a92b864b 3d423839 c229a2ab 23208467 ... ...00 00237f>

下一个 video 长度为 9076 的视频数据,0x17 后一位 AVCPacketType 01,表示 NALU 数据,三个 CompositionTime 字节过后,就是 NALU 数据了。

4.5 第一个 audio tag 以及后续的 audio tag

<08000004 00005d00 000000ae 00158800 00000f>

08 00 00 04 00 00 5d 00 00 00 00 0x08 表示音频,00 00 04 表示长度为 4,时间戳为 93;加上头部长度 11,为 15,下一个 back-pointers 是 0x 00 00 00 0F(最后四子节);接下来就是 tag data: AE: 即 0b10101110,前 4 位为 10,表示音频格式是 AAC;10 会多一个字节 AACPacketType,表示该 AACAUDIODATA 的类型;第 5、6 位为 11,十进制 3,表示采样率为 44kHz;第 7 位为1,表示 16 位采样精度;第 8 位为 0,表示 sndMono 单声道。 00: 表示 AACPacketType,为 0 表示该 Tag 是 AAC sequence header。接下来两个字节表示 AudioSpecificConfig,包含更加详细音频的信息。读取完后该 tag 结束。下一个 back-pointers。

4.6 第二个 audio tag 以及后续的 audio tag

<080002fb 00005d00 000000ae 01010e34 14564a2d 92154484 d0602e72 
f7cab6d6 af82156c cae24cba a6f8d2b9 d2d512d3 db8b5632 ef76c8a9 
95225c3b 69e84d7d 03a6fa66 7b475759 0ead71f5 37c73379 fd124cd5 
1ba64d95 48d7aa3b c558863e 090fc9ae ... ...0000 0306>

下一个长度为 752 的音频数据,时间戳为 93,0xae 后一位为 01,表示 ACC raw,即音频 NALU 数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/944165.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

git学习笔记(重实践) | 版本管理 - 分支管理 - 常见场景

文章目录 git学习笔记Git是什么仓库常见的命令commit 备注规范在文件下设置git忽略文件 .gitignore 版本管理git log | git reflog 查看提交日志/历史版本版本穿梭 git resetgit reset HEAD <file> git checkout -- fileName 丢弃工作区的修改git revertGit恢复之前版本的…

成功项目风险预防可控的5个重点

成功的项目往往重视项目风险的预防和管控&#xff0c;这样有利于可能风险的及时控制和解决&#xff0c;将其不利影响降到最小。如果不重视对风险的预防和管控&#xff0c;不及时发现和处理项目风险&#xff0c;那么项目风险往往会为我们带来意想不到的不利后果&#xff0c;往往…

【LeetCode-中等题】146. LRU 缓存

文章目录 题目方法一&#xff1a;直接继承LinkedHashMap调用api方法二&#xff1a;自定义LinkedHashMap HashMap ListNode LinkedHashMap 题目 LRU缓存是什么&#xff1a;LRU缓存机制&#xff0c;你想知道的这里都有 实现 LRU 缓存算法 方法一&#xff1a;直接继承Linked…

深度图相关评测网站

文章目录 1 单目/Stereo相关测评网站介绍12 单目/Stereo相关测评网站介绍23 单目/Stereo相关测评网站介绍3 1 单目/Stereo相关测评网站介绍1 https://vision.middlebury.edu/stereo/eval3/ 2 单目/Stereo相关测评网站介绍2 http://www.cvlibs.net/datasets/kitti/eval_stereo…

Vscode画流程图

1.下载插件 Draw.id Integration 2.桌面新建文件&#xff0c;后缀名改为XXX.drawio 在vscode打开此文件 &#xff0c;就可以进行绘制流程图啦

无涯教程-Android - Broadcast Receivers

Broadcast Receivers 仅响应来自其他应用程序或系统本身的广播消息&#xff0c;这些消息有时称为events或intents。例如&#xff0c;应用程序还可以启动广播&#xff0c;以使其他应用程序知道某些数据已下载到设备并可供他们使用&#xff0c;因此广播接收器将拦截此通信并启动适…

做区块链卡牌游戏有什么好处?

区块链卡牌游戏是一种基于区块链技术的创新性游戏形式&#xff0c;它将传统的卡牌游戏与区块链技术相结合&#xff0c;实现了去中心化、数字化资产的交易和收集。这种新型游戏形式正逐渐在游戏行业引起了广泛的关注和热潮。本文将深入探讨区块链卡牌游戏的定义、特点以及其在未…

自定义node-red节点中,如何编写节点的配置信息弹窗

前言 最近有读者通过博客向我咨询,在自定义node-red节点时,如何编写该节点的配置页面,就是我们通常见到的,双节节点打开的信息弹窗。如下图: 上面两张图,展示了inject节点与mqtt in 节点的配置弹窗。 在弹窗中,除了上面的删除,取消,完成,和下面的失效按钮。 中间…

以“迅”防“汛”!5G视频快线筑牢防汛“安全堤”

近期&#xff0c;西安多地突发山洪泥石流灾害。防洪救灾刻不容缓&#xff0c;为进一步做好防汛工作&#xff0c;加强防洪调度监管&#xff0c;切实保障群众的生命财产安全&#xff0c;当地政府管理部门亟需拓展智能化技术&#xff0c;通过人防技防双保障提升防灾救灾应急处置能…

模型的保存加载、模型微调、GPU使用及Pytorch常见报错

序列化与反序列化 序列化就是说内存中的某一个对象保存到硬盘当中&#xff0c;以二进制序列的形式存储下来&#xff0c;这就是一个序列化的过程。 而反序列化&#xff0c;就是将硬盘中存储的二进制的数&#xff0c;反序列化到内存当中&#xff0c;得到一个相应的对象&#xff…

深度学习论文: Segment Any Anomaly without Training via Hybrid Prompt Regularization

深度学习论文: Segment Any Anomaly without Training via Hybrid Prompt Regularization Segment Any Anomaly without Training via Hybrid Prompt Regularization PDF: https://arxiv.org/pdf/2305.10724.pdf PyTorch代码: https://github.com/shanglianlm0525/CvPytorch Py…

【计算机系统概论Yale.patt】第二章

文章目录 2.数据的表示与计算2.1 bit2.1.1 信号的编码表示2.1.2 计算机采用二进制的原因2.1.3 数据类型无符号整数有符号整数原码反码补码编码方式与范围移码4-bit的不同编码方式 2.1.4 IEEE754浮点数尾数指数0000 0000 含义1111 1111含义 例 2.2 进制转换2.2.1 二转十2.2.2 十…

python面试:使用cProfile剖析程序性能

我们需要安装tuna&#xff1a;pip install tuna 程序执行完毕后&#xff0c;我们会得到一个results.prof&#xff0c;在CMD中输入指令&#xff1a;“tuna results.prof”。 import time import cProfile import pstatsdef add(x, y):resulting_sum 0resulting_sum xresulti…

mysql数据表Table is marked as crashed and should be repaired 的解决办法

错误原因 网上查了一下&#xff0c;错误的产生原因&#xff0c;有网友说是频繁查询和更新XXXX表造成的索引错误&#xff0c;还有说法是Mysql数据库因某种原因而受到了损坏。 【如&#xff1a;数据库服务器突发性断电&#xff0c;在数据表提供服务时对表的源文件进行某种操作都…

Java程序生成可执行exe文件及可安装程序

Java程序生成可执行exe文件及可安装程序 文章目录 Java程序生成可执行exe文件及可安装程序整体流程Maven项目打成jar包打成可执行文件准备工作&#xff1a;exe4j的下载、安装与激活使用exe4j打包 打成可安装文件参考 整体流程 将项目打包成可正常运行的jar包&#xff08;使用命…

涉及结构体的排序问题

简单举一个例子来介绍涉及结构体的排序问题。 例&#xff1a;输入若干学生姓名、语文成绩、数学成绩、英语成绩&#xff0c;根据三科成绩总分由高到低进行排序。 输入数据&#xff1a; 小明 78 89 90 小红 87 88 77 小华 91 92 96 输出样例&#xff1a; 小华 91 92 96 279 小明…

通义千问本地化部署不调用GPU只调用CPU的检查方法

今天部署本地版通义千问的时候遇到一个问题。 启动他的cli_demo.py调用的一直都是CPU模式的。 检查cuda已经正确安装&#xff0c;后面发现是torch即PyTorch的安装问题。 我安装torch的时候&#xff0c;用的是默认指令&#xff0c;没有增加别的参数。 检测一下&#xff0c;输出…

正中优配:创业板指大涨3.47%!减速器等概念板块掀涨停潮!

周二&#xff08;8月29日)&#xff0c;三大股指团体涨超1%。截至上午收盘&#xff0c;上证指数涨1.39%&#xff0c;报3141.82点&#xff1b;深证成指和创业板指别离涨2.41%和3.47%&#xff1b;沪深两市算计成交额6264.51亿元&#xff0c;总体来看&#xff0c;两市个股涨多跌少&…

教会你怎么玩转 文件下载

&#x1f600;前言 教会你怎么玩转 文件下载 &#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x1f609;&#x1f609; 在csdn获奖荣誉: …

使用apifox前置数据base64编码并添加一个字段

具体前置脚本如下&#xff1a; // pm.request.body.update 处理 body 参数里的变量 let bodyStr pm.request.body.raw; // base64 编码数据 let bodyEncode btoa(bodyStr); console.log(bodyEncode) let newBody {"data": bodyEncode,"sendTime": &qu…