【流媒体协议】图解 FLV 协议 快速入门
文章目录前言FLV 格式FLV headerFLV BodyFLV tagVideo TagAudio TagScript Tagobs 打包 FLV打包 FLV header 和 Script Tag打包 Video Tag打包 Audio Tag总结推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,Mo
文章目录
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习
前言
FLV 是FLASH VIDEO的简称,FLV流媒体格式是随着Flash MX的推出发展而来的视频格式。由于它形成的文件极小、加载速度极快,使得网络观看视频文件成为可能,它的出现有效地解决了视频文件导入Flash后,使导出的SWF文件体积庞大,不能在网络上很好的使用等问题。
rtmp 和 http-flv内部使用 flv协议封装 h264和 AAC音视频包。 FLV协议非常简单,接下来通过几张图快速掌握 flv封装协议。
FLV 格式
FLV 由 FLV header 跟 FLV file body 两部分组成,而 FLV file body 又由多个 FLV tag组成,FLV tag由 tag header + tag body组成。
FLV = FLV header + FLV body
FLV body = PreviousTagSize0 + Tag1 + PreviousTagSize1 + Tag2 + … + PreviousTagSizeN-1 + TagN
Tag1 = Tag1 header + Tag1 body
FLV header
字段 | 字段类型 | 字段含义 |
---|---|---|
Signature | UI8 | 签名,固定为’F’ (0x46) |
Signature | UI8 | 签名,固定为’L’ (0x4c) |
Signature | UI8 | 签名,固定为’V’ (0x56) |
Version | UI8 | 0x01 表示 FLV 版本 1 |
TypeFlagsReserved | UB[5] | 全为0 |
TypeFlagsAudio | UB[1] | 1表示有audio tag,0表示没有 |
TypeFlagsReserved | UB[1] | 0 |
TypeFlagsVideo | UB[1] | 1表示有video tag,0表示没有 |
DataOffset | UI32 | FLV header的大小9,单位是字节 |
FLV Body
FLV file body很有规律,由一系列的TagSize和Tag组成。
- PreviousTagSize0 总是为0;
- tag 由tag header、tag body组成;
- 对FLV版本1,tag header 固定为11个字节,因此,PreviousTagSize(除第1个)的值为 11 + 前一个tag 的 tag body的大小;
FLV tag
FLV tag 分为三类
- Video Tag 0x08:audio data
- Audio Tag 0x09:video data
- Script Tag 0x12:script data
Tag head占11个字节
Video Tag
Video类型表明Data中存储的是视频数据,由 Video Tag Header(5个字节)和 Video Data组成。视频的编码类型可以是H264、H265等等。
详细定义请参照 flv_v10_1_adobe.pdf文档中的定义,结构如下图
Audio Tag
Audio类型表明Data中存储的是音频数据,由 Audio Tag Header(2个字节)和 Audio Data组成。音频的编码类型可以是aac、mp3等。
详细定义请参照 flv_v10_1_adobe.pdf文档中的定义,结构如下图:
Script Tag
Script Data Tags通常用来存放跟FLV中音视频相关的元数据信息(onMetaData),比如时长、长度、宽度等。它的定义相对复杂些,采用AMF(Action Message Format)封装了一系列数据类型,比如字符串、数值、数组等。
FLV格式整体图
obs 打包 FLV
参考上面的协议介绍,对照 obs打包 FLV tag源码,可以更好的掌握 FLV 文件协议。
源码文件 obs-studio\plugins\obs-outputs\flv-mux.c
打包 FLV header 和 Script Tag
打包 FLV header 和 Script Tag
void flv_meta_data(obs_output_t *context, uint8_t **output, size_t *size,
bool write_header)
{
struct array_output_data data;
struct serializer s;
uint8_t *meta_data = NULL;
size_t meta_data_size;
uint32_t start_pos;
array_output_serializer_init(&s, &data);
build_flv_meta_data(context, &meta_data, &meta_data_size);
//======= flv header 9 Byte ======
if (write_header) {
s_write(&s, "FLV", 3); // 3字节 FLV
s_w8(&s, 1); // 1字节 Version :1
s_w8(&s, 5); // 1字节 0000 0101 包含音频和视频
s_wb32(&s, 9); // 4字节 DataOffset 文件头部的大小:9
s_wb32(&s, 0); // 第一个 previous tag size:0
}
start_pos = serializer_get_pos(&s);
//=== tag header 11 Byte ==================
s_w8(&s, RTMP_PACKET_TYPE_INFO); //1字节 tag type 0x12: script tag
s_wb24(&s, (uint32_t)meta_data_size);// 3字节 数据区大小
s_wb32(&s, 0); // 4字节 时间戳
s_wb24(&s, 0); //3字节 流ID 总为0
//=========================================
s_write(&s, meta_data, meta_data_size); // write script tag
s_wb32(&s, (uint32_t)serializer_get_pos(&s) - start_pos - 1); //write tag size
*output = data.bytes.array;
*size = data.bytes.num;
bfree(meta_data);
}
打包 Video Tag
打包 Video Tag
// 打包视频tag
static void flv_video(struct serializer *s, int32_t dts_offset,
struct encoder_packet *packet, bool is_header)
{
int64_t offset = packet->pts - packet->dts;
int32_t time_ms = get_ms_time(packet, packet->dts) - dts_offset;
if (!packet->data || !packet->size)
return;
//=== tag header 11 Byte ==================
s_w8(s, RTMP_PACKET_TYPE_VIDEO); // 1字节 tag type 0x09:video tag
s_wb24(s, (uint32_t)packet->size + 5); // 3字节 数据区大小
s_wb24(s, time_ms); // 3字节 时间戳
s_w8(s, (time_ms >> 24) & 0x7F); // 1字节 扩展时间戳
s_wb24(s, 0); // 3字节 流ID 总为0
//=======================================
//========vidoe tag header 5 Byte ==================
/* these are the 5 extra bytes mentioned above */
s_w8(s, packet->keyframe ? 0x17 : 0x27); //1字节 frame type and codec ID (0x17:关键帧 AVC)
s_w8(s, is_header ? 0 : 1); //1字节 packet type 0:AVC sequence header (sps pps) 1:avc nalu
s_wb24(s, get_ms_time(packet, offset)); //3字节 composition time
//===================================================
//========video data ===========================
s_write(s, packet->data, packet->size); // nalu data
/* write tag size (starting byte doesn't count) */
s_wb32(s, (uint32_t)serializer_get_pos(s) - 1); // previous tag size
}
打包 Audio Tag
打包 Audio Tag
// 打包音频 tag
static void flv_audio(struct serializer *s, int32_t dts_offset,
struct encoder_packet *packet, bool is_header)
{
int32_t time_ms = get_ms_time(packet, packet->dts) - dts_offset;
if (!packet->data || !packet->size)
return;
//=== tag header 11 Byte ==================
s_w8(s, RTMP_PACKET_TYPE_AUDIO); // 1字节 tag type // 0x08:audio tag
s_wb24(s, (uint32_t)packet->size + 2); // 3字节 数据区大小
s_wb24(s, time_ms); // 3字节 时间戳
s_w8(s, (time_ms >> 24) & 0x7F); // 1字节 扩展时间戳
s_wb24(s, 0); // 3字节 流ID 总为0
//=========================================
/* these are the two extra bytes mentioned above */
s_w8(s, 0xaf); //1个字节 1010 (4个bit 10: AAC 2: mp3) 11(2个bit 3: 44 kHz) 1(1个bit 0:8bit 1: 16bit) 1(1个bit 1:stereo 0: mono)
s_w8(s, is_header ? 0 : 1); //1个字节 0:AAC Sequence Header 1:raw AAC raw data
s_write(s, packet->data, packet->size); // aac data
/* write tag size (starting byte doesn't count) */
s_wb32(s, (uint32_t)serializer_get_pos(s) - 1); // previous tag size
}
总结
FLV协议本身不算复杂,理解上的困难,更多时候来自音视频编解码相关的知识,比如H.264、AAC相关知识,建议不懂的时候自行查下。此外,FLV的字节序为大端序,在做协议解析的时候一定要注意。
- 本文部分技术点出处:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发
- FLV协议5分钟入门浅析
- FLV视频文件格式图示
- video_file_format_spec_v10.pdf https://www.adobe.com/content/dam/acom/en/devnet/flv/video_file_format_spec_v10.pdf
更多推荐
所有评论(0)