[[FLV格式]]
这里可以看到 flv 的格式,本内容只介绍 flv 解析部分,其他就不介绍了。
Flv 数据包 Parser
FlvHeader
这里可以看到 flv header
是9个字节,分别是:文件标识,版本标识、类型和头大小(就是9)。
parser 后头的创建和销毁:
FlvParser::FlvHeader* FlvParser::CreateHeader(uint8_t* pBuf) {
FlvHeader* pHeader = new FlvHeader;
pHeader->nVersion = pBuf[3]; // 直接略过文件标识提提取版本
pHeader->bHaveAudio = (pBuf[4] >> 2) & 0x01; // 提取是否有音频bit
pHeader->bHaveVideo = (pBuf[4] >> 0) & 0x01; // 提取是否有视频bit
pHeader->nHeaderSize = SolveU32(pBuf + 5);
pHeader->pFlvHeader = new uint8_t[pHeader->nHeaderSize];
memset(pHeader->pFlvHeader, 0, pHeader->nHeaderSize);
memcpy(pHeader->pFlvHeader, pBuf, pHeader->nHeaderSize); // 存储头
return pHeader;
}
void FlvParser::DestroyHeader(FlvParser::FlvHeader* pHeader) {
if (pHeader) {
if (pHeader->pFlvHeader) {
delete pHeader->pFlvHeader;
}
}
}
Tag
tag 分为很多种,有:音频 Tag、视频 Tag、Script Tag。这里我放一张之前文章的整理的 tag header
截图。
tag header
中的数据是所有 tag 都有的,所以抽象出一个 tag 基类+三个集体的 tag 子类:
// 这里抽象出了Tag Header存储上面表表格中的数据
struct TagHeader {
int nTagType;
int nDataSize;
int nTimeSamp;
int nTimeSampExt;
int nStreamID;
uint32_t nTotalTS;
TagHeader()
: nTagType(0),
nDataSize(0),
nTimeSamp(0),
nTimeSampExt(0),
nStreamID(0) {};
~TagHeader() {};
};
class Tag {
public:
Tag() : _pTagData(nullptr), _pTagHeader(nullptr), _pMedia(nullptr) {}
void Init(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen);
TagHeader _header;
uint8_t *_pTagHeader; // Tag 头
uint8_t *_pTagData; // Tag body
uint8_t *_pMedia; // 媒体数据流
int _nMediaLen; // 媒体数据流长度
};
// 视频Tag
class VideoTag : public Tag {
public:
VideoTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen,
FlvParser *pParser);
int _nFrameType;
int _nCodecType;
int ParseH264Tag(FlvParser *pParser);
int ParseH264Configuration(FlvParser *pParser, uint8_t *pTagData);
int ParseNalu(FlvParser *pParser, uint8_t *pTagData);
};
// 音频Tag
class AudioTag : public Tag {
public:
AudioTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen,
FlvParser *pParser);
int _nSoundType;
int _nSoundRate;
int _nSoundSize;
int _nSoundFormat;
static int _aacProfile;
static int _sampleRateIndex;
static int _channelConfig;
int ParseAACTag(FlvParser *pParser);
int ParseAudioSpecificConfig(FlvParser *pParser, uint8_t *pTagData);
int ParseAACRaw(FlvParser *pParser, uint8_t *pTagData);
};
// 这里的元数据Tag就是Script Tag
class MetaTag : public Tag {
public:
MetaTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen,
FlvParser *pParser);
double hexStr2Double(const unsigned char *hex,
const unsigned int length);
int parseMeta(FlvParser *pParser);
void printMeta();
uint8_t m_amf1_type;
uint32_t m_amf1_size;
uint8_t m_amf2_type;
unsigned char *m_meta;
unsigned int m_length;
double m_duration; // 视频时长
double m_width; // 视频宽度
double m_height; // 视频高度
double m_videodatarate; // 视频码率(kbps)
double m_framerate; // 视频帧率
double m_videocodecid; // 视频编码格式编号
double m_audiodatarate; // 音频码率
double m_audiosamplerate; // 音频采样率
double m_audiosamplesize; // 音频采样位数(通常是16bit)
bool m_stereo; // 是否立体声(多声道)
double m_audiocodecid; // 音频编码格式
//
std::string m_major_brand;
std::string m_minor_version;
std::string m_compatible_brand;
std::string m_encoder;
double m_filesize;
};
解析 Audio Tag
FlvParser::AudioTag::AudioTag(TagHeader* pHeader, uint8_t* pBuf, int nLeftLen,
FlvParser* pParser) {
Init(pHeader, pBuf, nLeftLen);
uint8_t* pd = _pTagData;
_nSoundFormat = (pd[0] & 0xf0) >> 4;
_nSoundRate = (pd[0] & 0x0c) >> 2;
_nSoundSize = (pd[0] & 0x02) >> 1;
_nSoundType = (pd[0] & 0x01) >> 0;
if (_nSoundFormat == 10) { // 如果编码格式为AAC,那就解析(这里只解析AAC)
ParseAACTag(pParser);
}
}
解析 AAC:
int FlvParser::AudioTag::ParseAACTag(FlvParser* pParser) {
uint8_t* pd = _pTagData;
// 第一个字节是音频格式信息,直接跳过,解析第二个
// 第二个字节代表包类型
int nAACPacketType = pd[1];
if (nAACPacketType == 0) { // 如果为0就是音频配置信息
ParseAudioSpecificConfig(pParser, pd);
} else if (nAACPacketType == 1) { // 如果是1就是AAC音频流
ParseAACRaw(pParser, pd);
} else {
}
return 0;
}
解析 AAC 音频配置信息:
int FlvParser::AudioTag::ParseAudioSpecificConfig(FlvParser* pParser,
uint8_t* pTagData) {
uint8_t* pd = pTagData;
// pd[2] & 0b11111000
_aacProfile = (pd[2] & 0xf8) >> 3;
// 采样频率索引
_sampleRateIndex = ((pd[2] & 0x07) << 1 | (pd[3] >> 7));
// 声道数量
_channelConfig = (pd[3] >> 3) & 0x0f;
// 打印出来
printf("-----AAC-----\n");
printf("aacProfile:%d\n", _aacProfile);
printf("sampleRateIndex:%d\n", _sampleRateIndex);
printf("channelConfig:%d\n", _channelConfig);
// 如果是配置Tag那就没有媒体字节
_pMedia = nullptr;
_nMediaLen = 0;
return 0;
}
解析 AAC 音频流:
int FlvParser::AudioTag::ParseAACRaw(FlvParser* pParser, uint8_t* pTagData) {
uint64_t bits = 0;
int datasize = _header.nDataSize - 2;
WriteU64(bits, 12, 0xFFF);
WriteU64(bits, 1, 0);
WriteU64(bits, 2, 0);
WriteU64(bits, 1, 1);
// 这里虽然是使用的MPEG-4标准,但是这里依然需要-1,因为如何根据标准解复用是解复用器做的事情
// 在数据包中都是从0开始计算
WriteU64(bits, 2, _aacProfile - 1);
WriteU64(bits, 4, _sampleRateIndex);
WriteU64(bits, 1, 0);
WriteU64(bits, 3, _channelConfig);
WriteU64(bits, 1, 0);
WriteU64(bits, 1, 0);
WriteU64(bits, 1, 0);
WriteU64(bits, 1, 0);
WriteU64(bits, 13, datasize + 7);
WriteU64(bits, 11, 0x7FF);
WriteU64(bits, 2, 0);
_nMediaLen = datasize + 7;
_pMedia = new uint8_t[_nMediaLen];
uint8_t p64[8] = {0};
for (size_t i = 0; i < 8; i++) {
p64[i] = bits >> (64 - 8 * (i + 1));
}
memcpy(_pMedia, p64 + 1, 7);
memcpy(_pMedia + 7, pTagData + 2, datasize);
}
解析 Video Tag
FlvParser::VideoTag::VideoTag(TagHeader* pHeader, uint8_t* pBuf, int nLeftLen,
FlvParser* pParser) {
Init(pHeader, pBuf, nLeftLen);
uint8_t* pd = _pTagData;
// 视频帧类型
_nFrameType = (pd[0] & 0xf0) >> 4;
// 视频帧编码
_nCodecType = (pd[0] & 0x0f) >> 0;
if (_header.nTagType == 0x09 && _nCodecType == 7) {
ParseH264Tag(pParser); // 解析H264编码
}
}
int FlvParser::VideoTag::ParseH264Tag(FlvParser* pParser) {
uint8_t* pd = _pTagData;
// 跳过第一个字节,理由和音频Tag一样
int nAVCPacketType = pd[1];
int nCompositionTime = SolveU24(pd + 2);
if (nAVCPacketType == 0) {
ParseH264Configuration(pParser, pd);
} else if (nAVCPacketType == 1) {
ParseNalu(pParser, pd);
}
return 0;
}
解析 H264的配置 Tag:
int FlvParser::VideoTag::ParseH264Configuration(FlvParser* pParser,
uint8_t* pTagData) {
uint8_t* pd = pTagData;
// 在第九个字节的最后2bit,加上1是因为长度就=x+1
pParser->_nNaluUnitLength = (pd[9] & 0x03) + 1;
int pps_size, sps_size;
sps_size = SolveU16(pd + 11); // 序列参数数据长度
pps_size = SolveU16(pd + 11 + 2 + (1 + sps_size) + 1); //图像参数数据长度
// 这里的4是StartCode,每一个Nalu都有StartCode
_nMediaLen = 4 + sps_size + 4 + pps_size;
_pMedia = new uint8_t[_nMediaLen];
memset(_pMedia, 0, _nMediaLen);
memcpy(_pMedia, &nH264StartCode, 4);
memcpy(_pMedia + 4, pd + 11 + 2, sps_size);
memcpy(_pMedia + 4 + sps_size, &nH264StartCode, 4);
memcpy(_pMedia + 8 + sps_size, pd + 11 + 2 + sps_size + 1 + 2, pps_size);
return 0;
}
解析 H264Nalu:
int FlvParser::VideoTag::ParseNalu(FlvParser* pParser, uint8_t* pTagData) {
uint8_t* pd = pTagData;
int nOffset = 0;
_nMediaLen = 0;
// 这里加上10是因为还需要放入StartCode
_pMedia = new uint8_t[_header.nDataSize + 10];
nOffset = 5;
while (1) {
if (nOffset >= _header.nDataSize) break;
int nNaluLen = 0;
switch (pParser->_nNaluUnitLength) {
// 匹配NaluUnitLength,一般来说都是4
case 4:
nNaluLen = SolveU32(pd + nOffset);
break;
case 3:
nNaluLen = SolveU24(pd + nOffset);
break;
case 2:
nNaluLen = SolveU16(pd + nOffset);
break;
default:
nNaluLen = SolveU8(pd + nOffset);
break;
}
memcpy(_pMedia + _nMediaLen, &nH264StartCode, 4);
memcpy(_pMedia + _nMediaLen + 4,
pd + nOffset + pParser->_nNaluUnitLength, nNaluLen);
nOffset += (nNaluLen + pParser->_nNaluUnitLength);
_nMediaLen += (4 + nNaluLen);
}
return 0;
}
评论