FLV封装格式解析

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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eiter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值