网站首页 > 技术文章 正文
flv.js系列三:FLV格式解析
承接上两篇
这是最后一篇。flv.js中音视频额分离器是FLVDemuxer, 看懂这里的代码之前先要了解FLV文件的数据格式及JS中如何读取指定二进制数据。
1 FLV文件格式
FLV是Flash Video的简称,是Adobe公司推出的流媒体文件格式,我们下面对数据的操作都基于FLV规范文档。整体结构如下图所示,由FLVHeader和FLVBody组成。
1.1 FLVHeader
FLVHeader是固定的9字节,其定义如下图:
对上图做一些说明,Type列定义该字段的长度,UI8代表8位无符号整型,一个字节。UI32代表32位无符号整型,四个字节。UB[5]代表一个字节的5位,UB[1]代表一个字节的1位。
- 第一个字节固定是 0x46 表示 F
- 第二个字节固定是 0x4c 表示 L
- 第三个字节固定是 0x56 表示 V
- 第四个字节 指定FLV格式版本
- 第五个字节中 TypeFlagsAudio 表示是否有音频,TypeFlagsVideo 表示是否有视频
- 最后四个字节表示 FLVHeader的长度 固定是数字 9
1.2 FLVBody与Tag
FLVBody就是由多个Previous TagSize和Tag组成,其中Previous TagSize占4个字节,而且第一个Previous TagSize的值为0。接下来我们看Tag的结构,Tag由Tag Header和Tag Data组成,其定义如下:
可以看到Tag Header的长度是11个字节。
C++音视频开发学习地址:【免费】FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发-学习视频教程-腾讯课堂
C++音视频开发学习资料:点击领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)
2 二进制数据读取API
我们需要把ArrayBuffer类型的数据按照上面的FLV格式进行解析,需要借助于DateView视图类来完成二进制数据的读取和写入,下面看我们会遇到的API。
2.1 构造函数
来自MDN:
new DataView(buffer [, byteOffset [, byteLength]])。 参数说明: buffer 现有对象ArrayBuffer或SharedArrayBuffer用作支持新DataView对象的存储。 byteOffset 可选的 到上述缓冲区中第一个字节的偏移量(以字节为单位),以供新视图引用。如果未指定,则缓冲区视图从第一个字节开始。 byteLength 可选的 字节数组中的元素数。如果未指定,则视图的长度将与缓冲区的长度匹配。
demo:
// create an ArrayBuffer with a size in bytes
const buffer = new ArrayBuffer(16);
// Create a couple of views
const view1 = new DataView(buffer);
const view2 = new DataView(buffer, 12, 4); //from byte 12 for the next 4 bytes
view1.setInt8(12, 42); // put 42 in slot 12
console.log(view2.getInt8(0));
// expected output: 42
2.2 获取数据的API
dataview.getUint8(byteOffset) 参数 byteOffset,从视图开始处读取数据的偏移量(以字节为单位)。 返回值,一个无符号的8位整数。 dataview.getUint32(byteOffset [, littleEndian]) 参数 byteOffset,从视图开始处读取数据的偏移量(以字节为单位)。 littleEndian,可选的指示32位int是按小字节序还是大字节序格式存储的。默认是false。 返回值,一个无符号的32位整数。 这里只讲了两个用到的API,当然Dateview还有设置和读取 有符号或者无符号 8位/16位/32位的API。
2.3 Endian字节序
内存中每一个字节是一个单位,对应一个内存地址,8位最大能表示0-255,如果要表示数字258就需要2个字节,字节序的作用就是规定这2个字节哪个表示高位哪个表示低位,如果内存地址低的表示低位就是小字节序littleEndian,相反就是大字节序。以表示258举例说明:
代码验证如下:
var buf = new ArrayBuffer(2);
var view = new DataView(buf);
view.setInt16(0, 258, true); // little-endian write
let little = view.getUint8(0);
let big = view.getUint8(1);
console.log(little) // 打印 2
console.log(big) // 打印 1
3 位操作
二进制数据的位操作包括 按位非(~)、按位与(&)、按位或(|)、按位异或(^)、左移(<<)、右移(>>)、无符号右移(>>>)。js的API中只提供了读取8、16,32位等固定位的数据,对于其他位数的数据则需要用位操作进行读取。
3.1 读取字节中某一位是0或1
可以利用该字节的数与目标位所在的值进行与操作,这样就只保留字节中目标位的取值。例如读取第6和第8位的值。
let data = new Uint8Array(buffer)[0];
let hasAudio = (data & 4) === 4;
let hasVideo = (data & 1) === 1;
3.2 读取字节中连续几位所表示数值
先与这几位所表示的数值进行与操作,然后在右移到低位。 比如读取一个字节中的前5位所表示的数,前5位的值为248。
let data = new Uint8Array(buffer)[0];
let target = (data & 248) >> 3;
读取字节中其他位置的值也是同理。
3.3 多字节拼接大数据
虽然有获取多字节数据的API,但当不满足需求时,可以通过位移和按位或操作进行大数据的拼接。例如FLV tag中有一个时间戳拼接的地方是 后8位的二进制应该拼接在24位前面 构成32位的数字。
let v = new DataView(chunk, offset);
let ts2 = v.getUint8(0);
let ts1 = v.getUint8(1);
let ts0 = v.getUint8(2);
let ts3 = v.getUint8(3);
// 获取时间戳字段 ts3是 扩展字段 是时间戳的高8位
let timestamp = ts0 | (ts1 << 8) | (ts2 << 16) | (ts3 << 24);
4 总结
有了上面的FLV格式的文档,有了对二进制数据读取的方法,剩下的事情就是按照文档对每一个字段进行解析。
- 上一篇: iG战队让一追三夺得德玛西亚杯西安站冠军
- 下一篇: web前端学到什么程度就可以找工作了?
猜你喜欢
- 2024-11-21 延迟加载图像以提高Web网站性能的五种方法「实践」
- 2024-11-21 web前端学到什么程度就可以找工作了?学到什么程度可以涨薪?
- 2024-11-21 web前端学到什么程度就可以找工作了?
- 2024-11-21 iG战队让一追三夺得德玛西亚杯西安站冠军
- 2024-11-21 前端新趋势?有了Web Component,还用纠结Vue或React?
- 2024-11-21 Python Web全栈之旅11--Web前端●CSS知识点汇总,建议收藏
- 2024-11-21 快速掌握前端开发中的常见错误
- 2024-11-21 用WebRTC和Node.js开发实时视频聊天应用
- 2024-11-21 9 个你可能从未使用过的很棒的 CSS 属性
- 2024-11-21 WebRTC对等连接- 连接不同设备上的浏览器
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-