【流媒体】RTMPDump—AMF编码

RTMP协议相关:
【流媒体】RTMP协议概述
【流媒体】RTMP协议的数据格式
【流媒体】RTMP协议的消息类型
【流媒体】RTMPDump—主流程简单分析
【流媒体】RTMPDump—RTMP_Connect函数(握手、网络连接)
【流媒体】RTMPDump—RTMP_ConnectStream(创建流连接)
【流媒体】RTMPDump—Download(接收流媒体信息)

参考雷博的系列文章(可以从一篇链接到其他文章):
RTMPdump 源代码分析 1: main()函数

在看RTMPDump代码过程中,发现一个比较核心的地方还没有记录,即AMF编码。RTMP协议的数据很多都是以AMF格式进行编码的,也应该做重点记录。参考RTMPDump代码中的amf.c和amf.h两个文件。由于AMF编码会将数据转换成为大端存储,可以参考 数据存储:大端存储与小端存储

1. AMF类型

参考amf.h中的AMFDataType,AMF0一共有18种类型

typedef enum
{
	AMF_NUMBER = 0, 	// double
	AMF_BOOLEAN, 		// bool
	AMF_STRING, 		// string
	AMF_OBJECT,			// 对象类型,包括property和property num
	AMF_MOVIECLIP,		/* reserved, not used */
	AMF_NULL,
	AMF_UNDEFINED,
	AMF_REFERENCE, 		// not supported
	AMF_ECMA_ARRAY, 	// ECMA
	AMF_OBJECT_END,
	AMF_STRICT_ARRAY, 	// STRICT
	AMF_DATE, 			// 
	AMF_LONG_STRING, 	// 32-bit string
	AMF_UNSUPPORTED,	
	AMF_RECORDSET,		/* reserved, not used */
	AMF_XML_DOC,
	AMF_TYPED_OBJECT,
	AMF_AVMPLUS,		/* switch to AMF3 */
	AMF_INVALID = 0xff
} AMFDataType;

AMF3一共有13种类型

typedef enum
{
	AMF3_UNDEFINED = 0, 
	AMF3_NULL, 
	AMF3_FALSE,
	AMF3_TRUE,
	AMF3_INTEGER, 
	AMF3_DOUBLE, 
	AMF3_STRING, 
	AMF3_XML_DOC, 
	AMF3_DATE,
	AMF3_ARRAY, 
	AMF3_OBJECT, 
	AMF3_XML, 
	AMF3_BYTE_ARRAY
} AMF3DataType;

AMF自定义类型AVal,包括val内容和val长度

typedef struct AVal
{
	char* av_val; // val内容
	int av_len;   // val长度
} AVal;

AMF自定义对象类型Object,

typedef struct AMFObjectProperty
{
	AVal p_name;
	AMFDataType p_type;
	union
	{
		double p_number;
		AVal p_aval;
		AMFObject p_object;
	} p_vu;
	int16_t p_UTCoffset;
} AMFObjectProperty;

typedef struct AMFObject
{
	int o_num;
	struct AMFObjectProperty* o_props;
} AMFObject;

2. AMF编码

在进行AMF编码时,不同的数据类型编码的方式是不同的,需要分开讨论

2.1 AMF_Number (AMF_EncodeNumber)

AMF_NUMBER类型相当于double类型,在进行编码时,会首先写入一个AMF_NUMBER字段,随后会将输入数据转换成为大端存储

char*
AMF_EncodeNumber(char* output, char* outend, double dVal)
{
	if (output + 1 + 8 > outend)
		return NULL;
	// 写入AMF_NUMBER数据类型
	*output++ = AMF_NUMBER;	/* type: Number */

#if __FLOAT_WORD_ORDER == __BYTE_ORDER
#if __BYTE_ORDER == __BIG_ENDIAN
	memcpy(output, &dVal, 8);
#elif __BYTE_ORDER == __LITTLE_ENDIAN
	{
		unsigned char* ci, * co;
		ci = (unsigned char*)& dVal; // double类型8字节,逐个字节翻转
		co = (unsigned char*)output;
		co[0] = ci[7];
		co[1] = ci[6];
		co[2] = ci[5];
		co[3] = ci[4];
		co[4] = ci[3];
		co[5] = ci[2];
		co[6] = ci[1];
		co[7] = ci[0];
	}
#endif
#else
#if __BYTE_ORDER == __LITTLE_ENDIAN	/* __FLOAT_WORD_ORER == __BIG_ENDIAN */
	{
		unsigned char* ci, * co;
		ci = (unsigned char*)& dVal;
		co = (unsigned char*)output;
		co[0] = ci[3];
		co[1] = ci[2];
		co[2] = ci[1];
		co[3] = ci[0];
		co[4] = ci[7];
		co[5] = ci[6];
		co[6] = ci[5];
		co[7] = ci[4];
	}
#else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
	{
		unsigned char* ci, * co;
		ci = (unsigned char*)& dVal;
		co = (unsigned char*)output;
		co[0] = ci[4];
		co[1] = ci[5];
		co[2] = ci[6];
		co[3] = ci[7];
		co[4] = ci[0];
		co[5] = ci[1];
		co[6] = ci[2];
		co[7] = ci[3];
	}
#endif
#endif

	return output + 8;
}

2.2 AMF_BOOLEAN (AMF_EncodeBoolean)

AMF_BOOLEAN类型相当于bool类型,在编码时首先写入描述字段AMF_BOOLEAN,随后根据输入bVal来判定是写入0x01或0x00

char*
AMF_EncodeBoolean(char* output, char* outend, int bVal)
{
	if (output + 2 > outend)
		return NULL;

	*output++ = AMF_BOOLEAN;

	*output++ = bVal ? 0x01 : 0x00;

	return output;
}

2.3 AMF_STRING 和 AMF_LONG_STRING (AMF_EncodeString)

AMF_STRING和AMF_LONG_STRING两种类型同时使用AMF_EncodeString进行编码,分两种情况:
(1)如果av_len小于65536,先写入AMF_STRING标识字段,随后写入16位的string长度
(2)如果av_len大于65536,先写入AMF_LONG_STRING标识字段,随后写入32位的string长度
最后写入string的内容

char*
AMF_EncodeString(char* output, char* outend, const AVal * bv)
{
	if ((bv->av_len < 65536 && output + 1 + 2 + bv->av_len > outend) ||
		output + 1 + 4 + bv->av_len > outend)
		return NULL;

	if (bv->av_len < 65536) // 字符串长度小于65536,即16位
	{
		*output++ = AMF_STRING;
		// 写入字符串长度为16位
		output = AMF_EncodeInt16(output, outend, bv->av_len);
	}
	else
	{
		*output++ = AMF_LONG_STRING; // 写入AMF_LONG_STRING标识符字段
		// 写入字符串长度为32位
		output = AMF_EncodeInt32(output, outend, bv->av_len);
	}
	// 写入字符串内容
	memcpy(output, bv->av_val, bv->av_len);
	output += bv->av_len;

	return output;
}

2.3.1 AMF_EncodeInt16

原子函数,实现了AMF编码两个字节的功能,在很多地方都会用到

char*
AMF_EncodeInt16(char* output, char* outend, short nVal)
{
	if (output + 2 > outend) // 检查越界
		return NULL;
	
	output[1] = nVal & 0xff; // 取出低8位
	output[0] = nVal >> 8;	 // 取出高8位
	return output + 2;
}

2.3.2 AMF_EncodeInt32

原子函数,实现了AMF编码4个字节的功能

char*
AMF_EncodeInt32(char* output, char* outend, int nVal)
{
	if (output + 4 > outend)	// 检查越界
		return NULL;

	output[3] = nVal & 0xff;	// 取出第4个字节
	output[2] = nVal >> 8;		// 取出第3个字节
	output[1] = nVal >> 16;		// 取出第2个字节
	output[0] = nVal >> 24;		// 取出第1个字节
	return output + 4;
}

2.4 AMF_OBJECT (AMF_Encode)

AMF_OBJECT描述了一个对象类型,使用AMF_Encode进行编码,这里和前面不同的是,Object是一个具有多个参数的数据类型,需要将其中的"属性" (props)也进行编码,最后还需要写入object_end的字段

char*
AMF_Encode(AMFObject * obj, char* pBuffer, char* pBufEnd)
{
	int i;

	if (pBuffer + 4 >= pBufEnd)
		return NULL;
	// 写入类型字段
	*pBuffer++ = AMF_OBJECT;

	for (i = 0; i < obj->o_num; i++)
	{
		// 写入属性值
		char* res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
		if (res == NULL)
		{
			RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
				i);
			break;
		}
		else
		{
			pBuffer = res;
		}
	}

	if (pBuffer + 3 >= pBufEnd)
		return NULL;			/* no room for the end marker */
	// 写入object结束字段
	pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);

	return pBuffer;
}

2.4.1 AMF_EncodeInt24

实现AMF编码3个字节功能

char*
AMF_EncodeInt24(char* output, char* outend, int nVal)
{
	if (output + 3 > outend)
		return NULL;

	output[2] = nVal & 0xff;	// 取出第1字节
	output[1] = nVal >> 8;		// 取出第2字节
	output[0] = nVal >> 16;		// 取出第3字节
	return output + 3;
}

2.5 AMF_ECMA_ARRAY (AMF_EncodeEcmaArray)

AMF_ECMA_ARRAY类型进行编码时,与AMF_Encode看上去很相似,不同之处在于AMF_ECMA_ARRAY编码时还需要将num进行编码

char*
AMF_EncodeEcmaArray(AMFObject * obj, char* pBuffer, char* pBufEnd)
{
	int i;

	if (pBuffer + 4 >= pBufEnd)
		return NULL;

	*pBuffer++ = AMF_ECMA_ARRAY;

	pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num);

	for (i = 0; i < obj->o_num; i++)
	{
		char* res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
		if (res == NULL)
		{
			RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
				i);
			break;
		}
		else
		{
			pBuffer = res;
		}
	}

	if (pBuffer + 3 >= pBufEnd)
		return NULL;			/* no room for the end marker */

	pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);

	return pBuffer;
}

2.6 AMF_STRICT_ARRAY (AMF_EncodeArray)

AMF_STRICT_ARRAY类型进行编码时,也与前面两个编码函数很像。先编码字段类型AMF_STRICT_ARRAY,随后编码obj中的num字段,再写入属性值props。不过区别在于,这种类型最后不会写入AMF_OBJECT

char*
AMF_EncodeArray(AMFObject * obj, char* pBuffer, char* pBufEnd)
{
	int i;

	if (pBuffer + 4 >= pBufEnd)
		return NULL;

	*pBuffer++ = AMF_STRICT_ARRAY;

	pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num);

	for (i = 0; i < obj->o_num; i++)
	{
		char* res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
		if (res == NULL)
		{
			RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
				i);
			break;
		}
		else
		{
			pBuffer = res;
		}
	}

	//if (pBuffer + 3 >= pBufEnd)
	//  return NULL;			/* no room for the end marker */

	//pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);

	return pBuffer;
}

3. AMF解码

3.1 AMF_Number (AMF_DecodeNumber)

基本就是编码过程反过来,double类型反过来

double
AMF_DecodeNumber(const char* data)
{
	double dVal;
#if __FLOAT_WORD_ORDER == __BYTE_ORDER
#if __BYTE_ORDER == __BIG_ENDIAN
	memcpy(&dVal, data, 8);
#elif __BYTE_ORDER == __LITTLE_ENDIAN
	unsigned char* ci, * co;
	ci = (unsigned char*)data;
	co = (unsigned char*)& dVal;
	co[0] = ci[7];
	co[1] = ci[6];
	co[2] = ci[5];
	co[3] = ci[4];
	co[4] = ci[3];
	co[5] = ci[2];
	co[6] = ci[1];
	co[7] = ci[0];
#endif
#else
#if __BYTE_ORDER == __LITTLE_ENDIAN	/* __FLOAT_WORD_ORER == __BIG_ENDIAN */
	unsigned char* ci, * co;
	ci = (unsigned char*)data;
	co = (unsigned char*)& dVal;
	co[0] = ci[3];
	co[1] = ci[2];
	co[2] = ci[1];
	co[3] = ci[0];
	co[4] = ci[7];
	co[5] = ci[6];
	co[6] = ci[5];
	co[7] = ci[4];
#else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
	unsigned char* ci, * co;
	ci = (unsigned char*)data;
	co = (unsigned char*)& dVal;
	co[0] = ci[4];
	co[1] = ci[5];
	co[2] = ci[6];
	co[3] = ci[7];
	co[4] = ci[0];
	co[5] = ci[1];
	co[6] = ci[2];
	co[7] = ci[3];
#endif
#endif
	return dVal;
}

3.2 AMF_BOOLEAN (AMF_DecodeBoolean)

查看data是否为0

int
AMF_DecodeBoolean(const char* data)
{
	return *data != 0;
}

3.3 AMF_STRING 和 AMF_LONG_STRING (AMF_DecodeString 和 AMF_DecodeLongString)

对于AMF_STRING类型而言,先解析16位的length,随后解析val

void
AMF_DecodeString(const char* data, AVal * bv)
{
	bv->av_len = AMF_DecodeInt16(data);
	bv->av_val = (bv->av_len > 0) ? (char*)data + 2 : NULL;
}

对于AMF_LONG_STRING类型而言,先解析32位的length,随后解析val

void
AMF_DecodeLongString(const char* data, AVal * bv)
{
	bv->av_len = AMF_DecodeInt32(data);
	bv->av_val = (bv->av_len > 0) ? (char*)data + 4 : NULL;
}

3.3.1 AMF_DecodeInt16

unsigned short
AMF_DecodeInt16(const char* data)
{
	unsigned char* c = (unsigned char*)data;
	unsigned short val;
	val = (c[0] << 8) | c[1]; // c[0] << 8 是高8位
	return val;
}

3.3.2 AMF_DecodeInt32

unsigned int
AMF_DecodeInt32(const char* data)
{
	unsigned char* c = (unsigned char*)data;
	unsigned int val;
	/*
		c[0] << 24 表示第1字节
		c[1] << 16 表示第2字节
		c[2] << 8 表示第3字节
		c[3] 表示第4字节
	*/
	val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
	return val;
}

3.4 AMF_OBJECT (AMF_Decode)

在解析object过程中,先检查字段是否包括AMF_OBJECT_END,随后使用AMFProp_Decoe()来解析属性值 “prop”,最后使用AMF_AddProp()将属性值添加到传入进来的obj中

int
AMF_Decode(AMFObject * obj, const char* pBuffer, int nSize, int bDecodeName)
{
	int nOriginalSize = nSize;
	int bError = FALSE;		/* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */

	obj->o_num = 0;
	obj->o_props = NULL;
	while (nSize > 0)
	{
		AMFObjectProperty prop;
		int nRes;
		// 先解析字段是否包括 AMF_OBJECT_END
		if (nSize >= 3 && AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END)
		{
			nSize -= 3;
			bError = FALSE;
			break;
		}

		if (bError)
		{
			RTMP_Log(RTMP_LOGERROR,
				"DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
			nSize--;
			pBuffer++;
			continue;
		}
		// 解码prop
		nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
		if (nRes == -1)
		{
			bError = TRUE;
			break;
		}
		else
		{
			nSize -= nRes;
			if (nSize < 0)
			{
				bError = TRUE;
				break;
			}
			pBuffer += nRes;
			// 将解析出来的prop添加到obj中
			AMF_AddProp(obj, &prop);
		}
	}

	if (bError)
		return -1;

	return nOriginalSize - nSize;
}

3.4.1 AMF_DecodeInt24

unsigned int
AMF_DecodeInt24(const char* data)
{
	unsigned char* c = (unsigned char*)data;
	unsigned int val;
	/*
		c[0] << 16 表示第1字节
		c[1] << 8 表示第2字节
		c[2] 表示第3字节
	*/
	val = (c[0] << 16) | (c[1] << 8) | c[2];
	return val;
}

3.4.2 AMFProp_Decode

int
AMFProp_Decode(AMFObjectProperty * prop, const char* pBuffer, int nSize,
	int bDecodeName)
{
	int nOriginalSize = nSize;
	int nRes;

	prop->p_name.av_len = 0;
	prop->p_name.av_val = NULL;

	if (nSize == 0 || !pBuffer)
	{
		RTMP_Log(RTMP_LOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__);
		return -1;
	}

	if (bDecodeName && nSize < 4)
	{				/* at least name (length + at least 1 byte) and 1 byte of data */
		RTMP_Log(RTMP_LOGDEBUG,
			"%s: Not enough data for decoding with name, less than 4 bytes!",
			__FUNCTION__);
		return -1;
	}

	if (bDecodeName) // 解析prop名称
	{
		unsigned short nNameSize = AMF_DecodeInt16(pBuffer); // 解析prop名称长度
		if (nNameSize > nSize - 2)
		{
			RTMP_Log(RTMP_LOGDEBUG,
				"%s: Name size out of range: namesize (%d) > len (%d) - 2",
				__FUNCTION__, nNameSize, nSize);
			return -1;
		}

		AMF_DecodeString(pBuffer, &prop->p_name); // 获取prop的具体名称
		nSize -= 2 + nNameSize;
		pBuffer += 2 + nNameSize;
	}

	if (nSize == 0)
	{
		return -1;
	}

	nSize--;
	// 根据不同的type来确定如何进行AMF解码
	prop->p_type = *pBuffer++;
	switch (prop->p_type)
	{
	case AMF_NUMBER:
		if (nSize < 8)
			return -1;
		prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
		nSize -= 8;
		break;
	case AMF_BOOLEAN:
		if (nSize < 1)
			return -1;
		prop->p_vu.p_number = (double)AMF_DecodeBoolean(pBuffer);
		nSize--;
		break;
	case AMF_STRING:
	{
		unsigned short nStringSize = AMF_DecodeInt16(pBuffer);

		if (nSize < (long)nStringSize + 2)
			return -1;
		AMF_DecodeString(pBuffer, &prop->p_vu.p_aval);
		nSize -= (2 + nStringSize);
		break;
	}
	case AMF_OBJECT:
	{
		int nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
		if (nRes == -1)
			return -1;
		nSize -= nRes;
		break;
	}
	case AMF_MOVIECLIP:
	{
		RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!");
		return -1;
		break;
	}
	case AMF_NULL:
	case AMF_UNDEFINED:
	case AMF_UNSUPPORTED:
		prop->p_type = AMF_NULL;
		break;
	case AMF_REFERENCE:
	{
		RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!");
		return -1;
		break;
	}
	case AMF_ECMA_ARRAY:
	{
		nSize -= 4;

		/* next comes the rest, mixed array has a final 0x000009 mark and names, so its an object */
		nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer + 4, nSize, TRUE);
		if (nRes == -1)
			return -1;
		nSize -= nRes;
		break;
	}
	case AMF_OBJECT_END:
	{
		return -1;
		break;
	}
	case AMF_STRICT_ARRAY:
	{
		// 解析32位的array长度
		unsigned int nArrayLen = AMF_DecodeInt32(pBuffer);
		nSize -= 4;

		nRes = AMF_DecodeArray(&prop->p_vu.p_object, pBuffer + 4, nSize,
			nArrayLen, FALSE);
		if (nRes == -1)
			return -1;
		nSize -= nRes;
		break;
	}
	case AMF_DATE:
	{
		RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE");

		if (nSize < 10)
			return -1;

		prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
		prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8);

		nSize -= 10;
		break;
	}
	case AMF_LONG_STRING:
	case AMF_XML_DOC:
	{
		unsigned int nStringSize = AMF_DecodeInt32(pBuffer);
		if (nSize < (long)nStringSize + 4)
			return -1;
		AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval);
		nSize -= (4 + nStringSize);
		if (prop->p_type == AMF_LONG_STRING)
			prop->p_type = AMF_STRING;
		break;
	}
	case AMF_RECORDSET:
	{
		RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!");
		return -1;
		break;
	}
	case AMF_TYPED_OBJECT:
	{
		RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!");
		return -1;
		break;
	}
	case AMF_AVMPLUS:
	{
		int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
		if (nRes == -1)
			return -1;
		nSize -= nRes;
		prop->p_type = AMF_OBJECT;
		break;
	}
	default:
		RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__,
			prop->p_type, pBuffer - 1);
		return -1;
	}

	return nOriginalSize - nSize;
}

3.5 AMF_STRICT_ARRAY (AMF_DecodeArray)

调用了和AMF_Decode()函数类似的步骤

int
AMF_DecodeArray(AMFObject * obj, const char* pBuffer, int nSize,
	int nArrayLen, int bDecodeName)
{
	int nOriginalSize = nSize;
	int bError = FALSE;

	obj->o_num = 0;
	obj->o_props = NULL;
	while (nArrayLen > 0)
	{
		AMFObjectProperty prop;
		int nRes;
		nArrayLen--;

		if (nSize <= 0)
		{
			bError = TRUE;
			break;
		}
		nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
		if (nRes == -1)
		{
			bError = TRUE;
			break;
		}
		else
		{
			nSize -= nRes;
			pBuffer += nRes;
			AMF_AddProp(obj, &prop);
		}
	}
	if (bError)
		return -1;

	return nOriginalSize - nSize;
}
  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值