HTTP-FLV协议 - 教程

HTTP-FLV协议


在这里插入图片描述

前言

http-flv 其实网络中发送flv文件, 也是网络格式

一、FLV简介

Flash Video(简称FLV),是一种网络视频格式,也是一种流媒体格式。

FLV文件由 FLV File Header + FLV File Body组成。
在这里插入图片描述

二、FLV File Header

FLV File Header固定为9个字节,用来描述版本号,有没有音视频
结构如下:

名称比特数描述
Signature24签名,固定为“FLV”
Version8版本号,固定为0x01,表示FLV Version 1
TypeFlagsReserved5全0
TypeFlagsAudio11表示有audio tag,0表示没有audio tag
TypeFlagsReserved1全0
TypeFlagsVideo11表示有video tag,0表示没有video tag
DataOffset32FLV header的大小,单位是字节
struct FLVHeader{
//FLV
char flv[3];
//File version (for example, 0x01 for FLV version 1)
uint8_t version;
// 保留,置0   
uint8_t : 5;
// 是否有音频   
uint8_t have_audio : 1;
// 保留,置0   
uint8_t : 1;
// 是否有视频   
uint8_t have_video : 1;
固定为9
uint32_t length;
// 固定为0   
uint32_t previous_tag_size0;
};

三、FLV File Body

FLV File Body由 PreviousTagSize0+tag1+PreviousTagSize1+…+tagN+PreviousTagSizeN组成。
PreviousTagSize 是一个4字节的整数,表示前一个TAG的大小。

|PreviousTagSize0|tag1|PreviousTagSize1|PreviousTagSize1|tag2|PreviousTagSize2|…|tagN|PreviousTagSizeN|

//tag header
std::string tag_header;
tag_header.append((char *)&header, sizeof(header));
Writer((const uint8_t *)tag_header.c_str(), tag_header.size()); ///
//tag data
Writer(data, size);
//PreviousTagSize
uint32_t PreviousTag_Size = htonl((uint32_t)(size + sizeof(header)));
std::string PreviousTagSize;
PreviousTagSize.append((char *)&PreviousTag_Size, 4);
Writer((const uint8_t *)PreviousTagSize.c_str(), PreviousTagSize.size(), true);

四、Flv Tag 存储音频和视频数据的地方

FLV tag由 tag header + tag body组成。

|Tag Header|Tag Body|

tag header固定为11个字节,结构如下:

名称比特数描述
TagType8tag类型
8:audio
9:video
18:script data
其他:保留
DataSize24message 长度,从StreamID后面到tag结束
Timestamp24相对于第一个tag的时间戳(单位是 毫秒),第一个tag的Timestamp总为0
TimestampExtended8时间戳的扩展字段,当 Timestamp 3个字节不够时,会启用这个字段,代表高8位
StreamID24总是0
struct FlvTagHeader {
uint8_t type = 0;
uint8_t data_size[3] = { 0 };
uint8_t timestamp[3] = { 0 };
uint8_t timestamp_ex = 0;
uint8_t streamid[3] = { 0 }; /* Always 0. */
};

五、FLV Tag Body

根据 tag type的值,tag body可以分为AUDIODATA(tag type为8),VIDEODATA(tag type为9),SCRIPTDATAOBJECT(tag type为18)

1、AUDIODATA

AUDIODATA 承载音频数据,第一个字节描述音频的信息,第二个字节开始为音频数据。

① AUDIODATA的结构如下:
名称比特数描述
SoundFormat4音频编码格式
SoundRate2采样率
SoundSize1采样精度,0表示8-bit,1表示16-bit
SoundType1声道类型,0表示单声道,1表示立体声
SoundDataN*8音频数据
② 音频编码格式:
ID值音频编码
0Linear PCM, platform endian
1ADPCM
2MP3
3Linear PCM, little endian
4Nellymoser 16 kHz mono
5Nellymoser 8 kHz mono
6Nellymoser
7G.711 A-law logarithmic PCM
8G.711 mu-law logarithmic PCM
9reserved
10AAC
11Speex
13Opus
14MP3 8 kHz
15Device-specific sound
③ 当SoundFormat=10,即AAC音频时,SoundData的第一个字节为AACPacketType。AACPacketType为0表示这个音频包是AudioSpecificConfig;否则这个音频包为AAC帧数据。

AudioSpecificConfig的结构:

名称比特数描述
AudioObjectType5音频对象类型
SamplingFrequencyIndex4采样率索引值,比如4表示44100
ChannelConfiguration4声道配置

AudioObjectType的取值:

ObjectType
1AAC Main
2AAC LC
3AAC SSR
5AAC HE
29AAC HEV2

SamplingFrequency的取值:

sampling frequency indexfrequency
0x096000
0x188200
0x264000
0x348000
0x444100
0x532000
0x624000
0x722050
0x816000
0x912000
0xa11025
0xb8000
0xc7350
0xdreserved
0xereserved
0xfescape value

SamplingFrequencyIndex是SamplingFrequency数组的一个索引。

当SoundFormat=2,即MP3音频时,SoundData就是MP3 RAW数据

2、VideoData

VIDEODATA Tag第一个字节的高4位描述视频帧的类型,低4位描述视频编码器ID,VIDEODATA Tag的结构如下:

名称比特数描述
FrameType4帧类型
CodecID4视频编码ID
VideoDataN*8视频数据
uint8_t *ptr = buffer;
*ptr = FLV_CODECID_H264;
*ptr++ |= FLV_FRAME_KEY;

FrameType:视频帧的类型。一般keyframe是指IDR帧,而inter frame是指普通I帧。

类型值视频帧
1key frame (for AVC, a seekable frame)
2inter frame (for AVC, a non-seekable frame)
3disposable inter frame (H.263 only)
4generated key frame (reserved for server use only)
5video info/command frame

视频编码ID:

ID值视频编码
2Sorenson H.263
3Screen video
4On2 VP6
5On2 VP6 with alpha channel
6Screen video version 2
7AVC

当CodecID 为7时,即为AVC视频,第一个字节为AvcPacketType,第二三四个字节为CompositionTime。当AvcPacketType=0,第5个字节开始为AVCDecoderConfigurationRecord;否则VideoData为Avc Raw数据。
AVCDecoderConfigurationRecord的结构:

名称比特数描述
configurationVersion8版本号,总是1
AVCProfileIndication8sps[1]
profile_compatibility8sps[2]
AVCLevelIndication8sps[3]
  • configurationVersion,AVCProfileIndication,profile_compatibility,AVCLevelIndication:都是一个字节,具体的内容由解码器去理解。
  • lengthSizeMinusOne:unit_length长度所占的字节数减1,也即lengthSizeMinusOne的值+1才是unit_length所占用的字节数。
  • numOfSequenceParameterSets:sps的个数
  • sequenceParameterSetLength:sps内容的长度
  • sequenceParameterSetNALUnit:sps的内容
  • numOfPictureParameterSets:pps的个数
  • pictureParameterSetLength:pps内容的长度
  • pictureParameterSetNALUnit:pps的内容

在这里插入图片描述

avcc 格式 |size|nal|

sps

01 42 c0  1f ff e0 00 18 67 42 c0 1f 00 00 00 0...

01 版本
42 profile
c0
1f level 级别

18就是avcc格式的sps的数据的大小

std::string extra_data;
{
/*
configurationVersion	8	版本号,总是1
AVCProfileIndication	8	sps[1]
profile_compatibility	8	sps[2]
AVCLevelIndication	8	sps[3]
configurationVersion,AVCProfileIndication,profile_compatibility,AVCLevelIndication:都是一个字节,具体的内容由解码器去理解。
lengthSizeMinusOne:unit_length长度所占的字节数减1,也即lengthSizeMinusOne的值+1才是unit_length所占用的字节数。
numOfSequenceParameterSets:sps的个数
sequenceParameterSetLength:sps内容的长度
sequenceParameterSetNALUnit:sps的内容
numOfPictureParameterSets:pps的个数
pictureParameterSetLength:pps内容的长度
pictureParameterSetNALUnit:pps的内容
*/
// AVCDecoderConfigurationRecord start
extra_data.push_back(1); // version
//	*ptr++ = 1;
extra_data.push_back(sps_[1]); // profile
//*ptr++ = sps_[1];
extra_data.push_back(sps_[2]); // compat
//*ptr++ = sps_[2];
extra_data.push_back(sps_[3]); // level
//*ptr++ = sps_[3];
extra_data.push_back((char)0xff); // 6 bits reserved + 2 bits nal size length - 1 (11)
//*ptr++ = 0xff;
extra_data.push_back((char)0xe1); // 3 bits reserved + 5 bits number of sps (00001)
//*ptr++ = 0xe1;
// sps
uint16_t size = (uint16_t)sps_.size();
size = htons(size);
extra_data.append((char *)&size, 2);
extra_data.append(sps_);
// pps
extra_data.push_back(1); // version
size = (uint16_t)pps_.size();
size = htons(size);
extra_data.append((char *)&size, 2);
extra_data.append(pps_);
}
// memcpy()
//packet.append(extra_data);
memcpy(ptr, extra_data.c_str(), extra_data.size());
ptr += extra_data.size();
WriteFlvTag(libflv::kFlvMsgTypeVideo, buffer, ptr - buffer, 0);

当VideoData为AVC RAW时,AVC RAW的结构是avcc的

3、 Script OnMeta (实际解码拿sps和pps中信息解析解码的, 所以该字段没有啥作用)

Script Data Tags通常用来存放跟FLV中音视频相关的元数据信息(onMetaData),比如时长、长度、宽度等。它的定义相对复杂些,采用AMF(Action Message Format)封装了一系列数据类型,比如字符串、数值、数组等。

onMetaData中包含了音视频相关的元数据,封装在Script Data Tag中,它包含了两个AMF。

第一个AMF是固定的值:

0x02 0x000A 0x6F 0x6E 0x4D 0x65 0x74 0x61 0x44 0x61 0x74 0x61
  • 第1个字节:0x02,表示字符串类型
  • 第2-3个字节:UI16类型,值为0x000A,表示字符串的长度为10(onMetaData的长度);
  • 第4-13个字节:字符串onMetaData对应的16进制数字(0x6F 0x6E 0x4D 0x65 0x74 0x61 0x44 0x61 0x74 0x61);

第二个AMF则是键值对描述的流媒体属性,每个实现这些属性都可能不一样:

字段字段类型字段含义
durationDOUBLE文件的时长
widthDOUBLE视频宽度(px)
heightDOUBLE视频高度(px)
videodatarateDOUBLE视频比特率(kb/s)
framerateDOUBLE视频帧率(帧/s)
videocodecidDOUBLE视频编解码器ID(参考Video Tag)
audiosamplerateDOUBLE音频采样率
audiosamplesizeDOUBLE音频采样精度(参考Audio Tag)
stereoBOOL是否立体声
audiocodecidDOUBLE音频编解码器ID(参考Audio Tag)
filesizeDOUBLE文件总得大小(字节)
//prev_packet_size_ = index;
uint8_t * metadata = current_ ;
uint8_t * ptr = metadata;
uint8_t *end = ptr + (1024 * 1023);
uint8_t count = (has_auido ? 5 : 0) + (has_video ? 7 : 0) + 1;
ptr = AMFWriteString(ptr, end, "onMetaData", 10);
// value: SCRIPTDATAECMAARRAY
ptr[0] = AMF_ECMA_ARRAY;
ptr[1] = (uint8_t)((count >> 24) & 0xFF);;
ptr[2] = (uint8_t)((count >> 16) & 0xFF);;
ptr[3] = (uint8_t)((count >> 8) & 0xFF);
ptr[4] = (uint8_t)(count & 0xFF);
ptr += 5;
if (has_auido)
{
ptr = AMFWriteNamedDouble(ptr, end, "audiocodecid", 12, 10);
ptr = AMFWriteNamedDouble(ptr, end, "audiodatarate", 13, 125 /* / 1024.0*/);
ptr = AMFWriteNamedDouble(ptr, end, "audiosamplerate", 15, 44100);
ptr = AMFWriteNamedDouble(ptr, end, "audiosamplesize", 15, 16);
ptr = AMFWriteNamedBoolean(ptr, end, "stereo", 6, (uint8_t)true);
}
if (has_video)
{
ptr = AMFWriteNamedDouble(ptr, end, "duration", 8, 0 );
//ptr = AMFWriteNamedDouble(ptr, end, "interval", 8, metadata->interval);
ptr = AMFWriteNamedDouble(ptr, end, "videocodecid", 12, 7);
ptr = AMFWriteNamedDouble(ptr, end, "videodatarate", 13, 0 /* / 1024.0*/);
ptr = AMFWriteNamedDouble(ptr, end, "framerate", 9, 25);
ptr = AMFWriteNamedDouble(ptr, end, "height", 6, 2560);
ptr = AMFWriteNamedDouble(ptr, end, "width", 5, 1440);
}
ptr = AMFWriteNamedString(ptr, end, "encoder", 7, kflv_muxer, strlen(kflv_muxer));
ptr = AMFWriteObjectEnd(ptr, end);
WriteFlvTag(libflv::kFlvMsgTypeAMFMeta, metadata, ptr - metadata, 0);

demo测试效果
在这里插入图片描述

http-flv拉流解码效果图
在这里插入图片描述

总结

libflv 源码地址:https://github.com/chensongpoixs/libmedia_transfer_protocol/tree/master/libflv

posted @ 2025-11-25 16:58  gccbuaa  阅读(4)  评论(0)    收藏  举报