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