RTMP学习(十五)rtmpdump源码阅读(9)AMF格式

AMF格式



    AMF就是一种数据转换的规则(或者协议),它把本地字节顺序的数据转换成网络字节顺序(大端字节顺序)。

    因为在进行网络通信的时候,我们需要对数据进行格式转换,这里的数据是指除了Byte(char字节)以外的所有数据类型,这样才方便通信双方对数据的理解。

    AMF分成两种:
    1、AMF0,基本的数据转换规则
    2、AMF3,是AMF0的扩展


AMF支持的数据类型


    AMF0支持的数据类型

	// amf(AMF0)的数据类型
	typedef enum
	{
		AMF_NUMBER = 0, // 数字(double)
		AMF_BOOLEAN, // 布尔
		AMF_STRING,  // 字符串
		AMF_OBJECT, // 对象
		AMF_MOVIECLIP,		/* reserved, not used */ // 保留
		AMF_NULL, // null
		AMF_UNDEFINED,  // 未定义
		AMF_REFERENCE,  // 引用
		AMF_ECMA_ARRAY,  // 数组
		AMF_OBJECT_END, // 对象结束
		AMF_STRICT_ARRAY,  // 严格的数组
		AMF_DATE, // 日期
		AMF_LONG_STRING,  // 长字符串
		AMF_UNSUPPORTED, // 未支持
		AMF_RECORDSET,		/* reserved, not used */ // 保留
		AMF_XML_DOC, // xml文档
		AMF_TYPED_OBJECT, // 有类型的对象
		AMF_AVMPLUS,		/* switch to AMF3 */ // 需要扩展到AMF3
		AMF_INVALID = 0xff
	} AMFDataType;

    AMF3支持的数据类型

	typedef enum
	{
		AMF3_UNDEFINED = 0, // 未定义
		AMF3_NULL,  // null
		AMF3_FALSE, // false
		AMF3_TRUE, // true
		AMF3_INTEGER, // int
		AMF3_DOUBLE, // double
		AMF3_STRING,  // string
		AMF3_XML_DOC, // xml
		AMF3_DATE, // 日期
		AMF3_ARRAY, // 数组
		AMF3_OBJECT,  // 对象
		AMF3_XML,  // xml
		AMF3_BYTE_ARRAY // 字节数组
	} AMF3DataType;



与AMF相关的数据结构




    “值”是指普通的非复合结构的值,例如:整形、布尔、浮点、字符串等等。

	/*
	** AVal表示一个值
	*/
	typedef struct AVal
	{
		char *av_val; // value的存放的地址
		int av_len; // value的长度
	} AVal;


对象


    对象表示了一个复合的数据结构。


	// AMF对象
	typedef struct AMFObject
	{
		// 属性的数量,因为对象实际就是由一系列的属性构成的
		int o_num;
		// 属性数组
		struct AMFObjectProperty *o_props;
	} AMFObject;



对象属性


    对象实际由多个对象属性构成。

    属性包含:属性名字、属性类型、属性值等


	// AMF对象的属性
	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; // UTC偏移
	} AMFObjectProperty;



数据转换规则


    下面的数据转换规则以解码为例子,解码就是把网络字节顺序的数据转换为本地字节顺序。



整形转换


// 解码int16
unsigned short
AMF_DecodeInt16(const char *data)
{
	unsigned char *c = (unsigned char *)data;
	unsigned short val;
	// int16的两个字节互换
	val = (c[0] << 8) | c[1];
	return val;
}


// 解码int24(这个整数占用三个字节)
unsigned int
AMF_DecodeInt24(const char *data)
{
	unsigned char *c = (unsigned char *)data;
	unsigned int val;
	// 调整内部字节的顺序
	val = (c[0] << 16) | (c[1] << 8) | c[2];
	return val;
}

// 解码int32
unsigned int
AMF_DecodeInt32(const char *data)
{
	unsigned char *c = (unsigned char *)data;
	unsigned int val;
	val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
	return val;
}



字符串转换

    解析的流程:

    1、先解析字符串的长度

    2、再获取字符串的数据

// 解码字符串
void
AMF_DecodeString(const char *data, AVal *bv)
{
	// 字符串的长度是int16
	bv->av_len = AMF_DecodeInt16(data);
	bv->av_val = (bv->av_len > 0) ? (char *)data + 2 : NULL;
}

// 解码长字符串
void
AMF_DecodeLongString(const char *data, AVal *bv)
{
	// 字符串的长度是int32
	bv->av_len = AMF_DecodeInt32(data);
	bv->av_val = (bv->av_len > 0) ? (char *)data + 4 : NULL;
}


数值(double)转换


// 解码数值(double)
double
AMF_DecodeNumber(const char *data)
{
	double dVal;
#if __FLOAT_WORD_ORDER == __BYTE_ORDER  // 如果float的字的存储顺序等于字节顺序

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



布尔转换


// 解码boolean
int
AMF_DecodeBoolean(const char *data)
{
	return *data != 0;
}



对象转换


    因为对象是由一个个的属性构成的,因此,它的内部实际是对对象属性进行转换。

// 解码一个AMF对象
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;

		// 得到object 结束的标签,表示对象解码完成
		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;
		}

		// 解码一个属性
		nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
		if (nRes == -1)
			bError = TRUE;
		else
		{
			// 把属性添加到对象中
			nSize -= nRes;
			pBuffer += nRes;
			AMF_AddProp(obj, &prop);
		}
	}

	if (bError)
		return -1;

	return nOriginalSize - nSize;
}



对象属性转换

    解析的过程如下:

    1、先解析属性名称(字符串)的长度

    2、解析属性名称

    3、解析属性的数据类型

    4、根据数据类型的不同调用不同的解析函数

// 对象属性解码
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 (*pBuffer == AMF_NULL)
		bDecodeName = 0;

	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)
	{
		// 解析属性名字的长度
		unsigned short nNameSize = AMF_DecodeInt16(pBuffer);
		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);
		nSize -= 2 + nNameSize;
		pBuffer += 2 + nNameSize;
	}

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

	nSize--;

	// 属性的数据类型
	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;
		prop->p_type = AMF_OBJECT;
		break;
	}
	case AMF_OBJECT_END:
	{
		return -1;
		break;
	}
	case AMF_STRICT_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;
		prop->p_type = AMF_OBJECT;
		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: // xml
	{
		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;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值