1、FLV封装格式概览
2.代码解析
#include <fstream>
#include <iostream>
#pragma warning(disable:4996)
using namespace std;
enum AMF
{
//8byte Double
AMF_DATA_TYPE_NUMBER = 0x00,
//bool
AMF_DATA_TYPE_BOOL = 0x01,
//后面2个字节为长度
AMF_DATA_TYPE_STRING = 0x02,
AMF_DATA_TYPE_OBJECT = 0x03,
AMF_DATA_TYPE_NULL = 0x05,
AMF_DATA_TYPE_UNDEFINED = 0x06,
AMF_DATA_TYPE_REFERENCE = 0x07,
//数组 类似map
AMF_DATA_TYPE_MIXEDARRAY = 0x08,
AMF_DATA_TYPE_OBJECT_END = 0x09,
AMF_DATA_TYPE_ARRAY = 0x0A,
AMF_DATA_TYPE_DATE = 0x0B,
//后面4个字节为长度
AMF_DATA_TYPE_LONG_STRING = 0x0C,
AMF_DATA_TYPE_UNSUPPORTED = 0x0D
};
struct FlvHeader
{
unsigned char signature[3];
unsigned char version;
unsigned char flags;
unsigned char dataOffset[4];
};
struct TagHeader
{
unsigned char tagType;
unsigned char dataSize[3];
unsigned char timeStamp[3];
unsigned char timeStampExtend;
unsigned char streamId[3];
};
struct FlvTag
{
TagHeader tagHeader;
unsigned char* tagData;
};
struct FlvBody
{
unsigned char previousTagSize[4];
FlvTag flvTag;
};
unsigned int char2Int16(unsigned char ch[])
{
unsigned int result = 0;
result = ch[0];
result <<= 8;
result |= ch[1];
return result;
}
unsigned int char2Int24(unsigned char ch[])
{
unsigned int result = 0;
result = ch[0];
result <<= 8;
result |= ch[1];
result <<= 8;
result |= ch[2];
return result;
}
unsigned int char2Int32(unsigned char ch[])
{
unsigned int result = 0;
result = ch[0];
result <<= 8;
result |= ch[1];
result <<= 8;
result |= ch[2];
result <<= 8;
result |= ch[3];
return result;
}
unsigned long char2Int64(unsigned char ch[])
{
unsigned long result = 0;
result = ch[0];
result <<= 8;
result |= ch[1];
result <<= 8;
result |= ch[2];
result <<= 8;
result |= ch[3];
result <<= 8;
result |= ch[4];
result <<= 8;
result |= ch[5];
result <<= 8;
result |= ch[6];
result <<= 8;
result |= ch[7];
return result;
}
void reverseCharArray8(unsigned char ch[])
{
for (int i = 0; i < 4; i++)
{
char temp = ch[i];
ch[i] = ch[7 - i];
ch[7 - i] = temp;
}
}
void readScriptData(ifstream& iStream)
{
char type = 0;
unsigned char strLength[2] = {0};
iStream.read(&type, 1);
iStream.read((char*)strLength, 2);
if (type == 0x02)
{
printf("First AFM TYPE: String\n");
}
const int stringLength = char2Int16(strLength);
printf("First AFM String Length: %d\n", stringLength);
auto stringData = new char[stringLength];
iStream.read(stringData, stringLength);
printf("First AFM String Data: %s\n", stringData);
delete []stringData;
char type2 = 0;
iStream.read(&type2, 1);
if (type2 == 0x08)
{
printf("Second AFM TYPE: Array\n");
}
unsigned char arrayNums[4] = {0};
iStream.read((char*)arrayNums, 4);
int nums = char2Int32(arrayNums);
printf("Second AFM Array Nums: %d\n", nums);
for (int i = 0; i < nums; i++)
{
unsigned char length[2] = {0};
iStream.read((char*)length, 2);
int llength = char2Int16(length);
auto str = new char[llength];
memset(str, 0, llength);
iStream.read(str, llength);
char metaType = 0;
iStream.read(&metaType, 1);
switch (metaType)
{
case AMF_DATA_TYPE_NUMBER:
{
unsigned char num[8] = {0};
iStream.read((char*)num, 8);
reverseCharArray8(num);
printf("Second AFM Data Key: %s Value: %lf\n", str, *(double*)num);
break;
}
case AMF_DATA_TYPE_BOOL:
{
char data = 0;
iStream.read(&data, 1);
printf("Second AFM Data Key: %s Value: %d\n", str, data);
break;
}
case AMF_DATA_TYPE_STRING:
{
unsigned char len[2] = {0};
iStream.read((char*)len, 2);
int m_len = char2Int16(len);
char* data = new char[m_len];
iStream.read(data, m_len);
printf("Second AFM Data Key: %s Value: %s\n", str, data);
break;
}
case AMF_DATA_TYPE_LONG_STRING:
{
unsigned char len[4] = {0};
iStream.read((char*)len, 4);
int m_len = char2Int32(len);
char* data = new char[m_len];
iStream.read(data, m_len);
printf("Second AFM Data Key: %s Value: %s\n", str, data);
break;
}
}
delete []str;
}
unsigned char end[3] = {0};
iStream.read((char*)end, 3);
int num = char2Int24(end);
printf("Second AFM END: %d\n", num);
}
void readAudioData(ifstream& iStream, int dataSize)
{
char data = 0;
iStream.read(&data, 1);
int soundFormat = data >> 4;
switch (soundFormat)
{
case 0:
printf("Audio SoundFormat: Linear PCM, platform endian\n");
break;
case 1:
printf("Audio SoundFormat: ADPCM\n");
break;
case 2:
printf("Audio SoundFormat: MP3\n");
break;
case 3:
printf("Audio SoundFormat: Linear PCM, little endian\n");
break;
case 4:
printf("Audio SoundFormat: Nellymoser 16-kHz mono\n");
break;
case 5:
printf("Audio SoundFormat: Nellymoser 8-kHz mono\n");
break;
case 6:
printf("Audio SoundFormat: Nellymoser\n");
break;
case 7:
printf("Audio SoundFormat: G.711 A-law logarithmic PCM\n");
break;
case 8:
printf("Audio SoundFormat: G.711 mu-law logarithmic PCM 9 = reserved\n");
break;
case 10:
printf("Audio SoundFormat: AAC\n");
break;
case 11:
printf("Audio SoundFormat: Speex\n");
break;
case 14:
printf("Audio SoundFormat: MP3 8-Khz\n");
break;
case 15:
printf("Audio SoundFormat: Device-specific sound\n");
break;
}
auto soundRate = (data & 0xC) >> 2;
switch (soundRate)
{
case 0x00:
printf("Audio SoundRate: 5.5 kHz\n");
break;
case 0x01:
printf("Audio SoundRate: 11 kHz\n");
break;
case 0x02:
printf("Audio SoundRate: 22 kHz\n");
break;
case 0x03:
printf("Audio SoundRate: 44 kHz\n");
break;
}
auto soundSize = (data & 2) >> 1;
if (soundSize == 0x01)
{
printf("Audio SoundSize: 16 bit\n");
}
else
{
printf("Audio SoundSize: 8 bit\n");
}
if (data & 0x01)
{
printf("Audio SoundType: Mono Channel\n");
}
else
{
printf("Audio SoundType: Single Channel\n");
}
if (soundFormat == 10)
{
char data = 0;
iStream.read(&data, 1);
if (data == 0)
{
printf("Audio AACPacketType: AAC sequence header\n");
}
else if (data == 1)
{
printf("Audio AACPacketType: AAC raw\n");
}
}
int remainSize = dataSize - (soundFormat == 10 ? 2 : 1);
char* remainData = new char[remainSize];
iStream.read(remainData, remainSize);
delete []remainData;
}
void readVideoData(ifstream& iStream, int dataSize)
{
char data = 0;
iStream.read(&data, 1);
char frameType = data >> 4;
switch (frameType)
{
case 1:
printf("Video FrameType keyframe (for AVC, a seekable frame)\n");
break;
case 2:
printf("Video FrameType inter frame (for AVC, a non-seekable frame)\n");
break;
case 3:
printf("Video FrameType disposable inter frame (H.263 only)\n");
break;
case 4:
printf("Video FrameType generated keyframe (reserved for server use only)\n");
break;
case 5:
printf("Video FrameType video info/command frame\n");
break;
}
char codecId = data & 0x0f;
switch (codecId)
{
case 1:
printf("Video CodecId JPEG (currently unused)\n");
break;
case 2:
printf("Video CodecId Sorenson H.263\n");
break;
case 3:
printf("Video CodecId Screen video\n");
break;
case 4:
printf("Video CodecId On2 VP6\n");
break;
case 5:
printf("Video CodecId On2 VP6 with alpha channel\n");
break;
case 6:
printf("Video CodecId Screen video version 2\n");
break;
case 7:
printf("Video CodecId AVC\n");
break;
}
if (codecId == 7)
{
char packetType = 0;
iStream.read(&packetType, 1);
switch (packetType)
{
case 0:
printf("Video AVCPacketType AVC sequence header\n");
break;
case 1:
printf("Video AVCPacketType AVC NALU\n");
break;
case 2:
printf("Video AVCPacketType AVC end of sequence (lower level NALU \
sequence ender is not required or supported)\n");
break;
}
unsigned char compositionTime[3] = {0};
iStream.read((char*)compositionTime, 3);
int time = char2Int24(compositionTime);
printf("Video CompositionTime %d", time);
}
int remainSize = dataSize - (codecId == 7 ? 5 : 1);
char* remainData = new char[remainSize];
iStream.read(remainData, remainSize);
delete []remainData;
}
void readBody(ifstream& iStream)
{
/*读取Previous Tag Header*/
FlvBody flvBody = {};
iStream.read((char*)&flvBody.previousTagSize, 4);
iStream.read((char*)&flvBody.flvTag.tagHeader.tagType, 1);
iStream.read((char*)&flvBody.flvTag.tagHeader.dataSize, 3);
iStream.read((char*)&flvBody.flvTag.tagHeader.timeStamp, 3);
iStream.read((char*)&flvBody.flvTag.tagHeader.timeStampExtend, 1);
iStream.read((char*)&flvBody.flvTag.tagHeader.streamId, 3);
printf("PreviousTagSize: %d\n", char2Int32(flvBody.previousTagSize));
printf("TagHeaderType: %d\n", flvBody.flvTag.tagHeader.tagType);
printf("DataSize: %d\n", char2Int24(flvBody.flvTag.tagHeader.dataSize));
printf("TimeStamp: %d\n", char2Int24(flvBody.flvTag.tagHeader.timeStamp));
printf("TimeStampExtend: %d\n", flvBody.flvTag.tagHeader.timeStampExtend);
printf("StreamId: %d\n\n", char2Int24(flvBody.flvTag.tagHeader.streamId));
auto curr = iStream.tellg();
int dataSize = char2Int24(flvBody.flvTag.tagHeader.dataSize);
if (flvBody.flvTag.tagHeader.tagType == 0x12)
{
readScriptData(iStream);
}
else if (flvBody.flvTag.tagHeader.tagType == 0x08)
{
readAudioData(iStream, dataSize);
}
else if (flvBody.flvTag.tagHeader.tagType == 0x09)
{
readVideoData(iStream, dataSize);
}
}
int main(int argNums, char* args[])
{
ifstream iStream("aa.flv", ios_base::in | ios_base::binary);
if (!iStream.is_open())
{
return -1;
}
/*读取Flv Header*/
FlvHeader flvHeader = {};
iStream.read((char*)&flvHeader.signature, 3);
iStream.read((char*)&flvHeader.version, 1);
iStream.read((char*)&flvHeader.flags, 1);
iStream.read((char*)&flvHeader.dataOffset, 4);
printf("Signature: %c%c%c\n", flvHeader.signature[0], flvHeader.signature[1], flvHeader.signature[2]);
printf("Version: %d\n", flvHeader.version);
printf("Flags: %d\n", flvHeader.flags);
printf("DataOffset: %d\n\n", char2Int32(flvHeader.dataOffset));
/*读取Flv Header*/
while (iStream.peek() != EOF)
{
readBody(iStream);
}
iStream.close();
return 0;
}