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个字节。
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 bytesconst buffer = new ArrayBuffer(16);// Create a couple of viewsconst view1 = new DataView(buffer);const view2 = new DataView(buffer, 12, 4); //from byte 12 for the next 4 bytesview1.setInt8(12, 42); // put 42 in slot 12console.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 writelet 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格局的文档,有了对二进制数据读取的办法,剩下的事件就是依照文档对每一个字段进行解析。
如果感觉有播种请关注微信公众号 前端良文 每周都会分享前端开发中的干货知识点。