RTMP学习笔记(六)FLV文件解析

62 阅读2分钟

欢迎关注公众号:冒泡的肥皂

image.png

1.flv简介

FLV 是FLASH VIDEO的简称。 FLV是一种文件封装格式,它可以封装H264和AAC,其他常见的文件封装格式还有MP4、TS、MKV等等。不同的文件封装格式可以相互转换,只要把一种文件封装格式拆包,解出“裸”的视频流和音频流,再按另一种文件封装格式打包,就可以完成转换,不需要重新编码,因此速度非常快。

2.flv文件格式

2.1头文件

image.png

FLV头文件:(9字节)
1-3: 前3个字节是文件格式标识(FLV 0x46 0x4C 0x56).
4-4: 第4个字节是版本(0x01)
5-5: 第5个字节
      has_video = (b & 0x01)==1 视频
      has_audio = (b & 0x04)==4 音频
                       0x05既有视频也有音频
6-9: 第6-9的四个字节长度.其数据为 00000009 .
整个文件头的长度,一般是9(3+1+1+4)
2.2 tag格式

除了9 字节的公共文件头外,body 部分就是一个个 tag 组成的。每一个 tag 都有 15 字节的 tag 头。字段说明如下:

script格式

image.png

PreviousTagSize,是 4 字节长度,表面之前的 Tag 的长度,包含了 tag 头和 tag 数据。第一个 tag ,此值是 0。
TagType,是 1 字节长度。音频:8, 视频:9,scriptTag = 0x12
DataSize , 是 3 字节长。flv tag 的数据长度,其实如图里 audio tag 头及其数据长度。
Timestamp,是 3 字节长。时间戳,貌似 flv 播放需要。
TimestampExtended, 是对 Timestamp 长度的扩展,当时间长度 3 字节不能表示的时候,启用扩展字段。
StreamID ,3 字节长,都填 0
头文件和tag脚本写入
private byte[] encodeFlvHeaderAndMetadata() {
		ByteBuf encodeMetaData = encodeMetaData();
		ByteBuf buf = Unpooled.buffer();

		RtmpMediaMessage msg = content.get(0);
		int timestamp = msg.getTimestamp() & 0xffffff;
		int timestampExtended = ((msg.getTimestamp() & 0xff000000) >> 24);
                
                flvHeader = new byte[] { 0x46, 0x4C, 0x56, 0x01, 0x05, 00, 00, 00, 0x09 };
		buf.writeBytes(flvHeader);
		buf.writeInt(0); // previousTagSize0

		int readableBytes = encodeMetaData.readableBytes();
		buf.writeByte(0x12); // script
		buf.writeMedium(readableBytes);
		// make the first script tag timestamp same as the keyframe
		buf.writeMedium(timestamp);
		buf.writeByte(timestampExtended);
		buf.writeMedium(0);// streamid
		buf.writeBytes(encodeMetaData);
		buf.writeInt(readableBytes + 11);
		byte[] result = new byte[buf.readableBytes()];
		buf.readBytes(result);
		return result;
	}
tag写入
private byte[] encodeMediaAsFlvTagAndPrevTagSize(RtmpMediaMessage msg) {
		int tagType = msg.getMsgType();
		byte[] data = msg.raw();
		int dataSize = data.length;
		int timestamp = msg.getTimestamp() & 0xffffff;
		int timestampExtended = ((msg.getTimestamp() & 0xff000000) >> 24);
		ByteBuf buffer = Unpooled.buffer();
		buffer.writeByte(tagType);//类型
		buffer.writeMedium(dataSize);//长度
		buffer.writeMedium(timestamp);//时间戳
		buffer.writeByte(timestampExtended);// timestampExtended
		buffer.writeMedium(0);// streamid
		buffer.writeBytes(data);
		buffer.writeInt(data.length + 11); // 长度
		byte[] r = new byte[buffer.readableBytes()];
		buffer.readBytes(r);
		return r;
	}