AMF是Adobe公司开发的一种高效的消息序列化和反序列化协议,它包括两种数据类型格式:AMF0和AMF3。其中AMF0是基本的消息格式,但是后来Adobe对AMF0进行了优化,开发了扩展的AMF3,即AMF3并不是AMF0的完全替代,使用AMF3需要在外面套一层AMF0做为容器。我们可以在librtmp的源码中找到AMF的一个实现版本,但是我不清楚什么原因,居然有些函数如AMFProp_SetNumber()至今都没有实现。
”水满则溢,月满则亏”,librtmp虽然有些瑕疵,但是仍然不影响它是那么的优秀,如果在朋友们的自研项目或者工作中有机会用到它,我还是非常推荐的。
结构定义:
AMF0支持的类型定义:
typedef enum
{
AMF_NUMBER = 0, // 数字,用8字节double表示
AMF_BOOLEAN, // 布尔数,用1字节整数表示
AMF_STRING, // 字符串,用LV范式表示
AMF_OBJECT, // 对象
AMF_MOVIECLIP, // 保留,未使用
AMF_NULL, // 空类型
AMF_UNDEFINED, // 未定义
AMF_REFERENCE, // 引用
AMF_ECMA_ARRAY, // 数组
AMF_OBJECT_END, // 对象结束
AMF_STRICT_ARRAY, // 严格数组
AMF_DATE, // 日期
AMF_LONG_STRING, // 长字符串
AMF_UNSUPPORTED, // 未支持
AMF_RECORDSET, // 保留,未使用
AMF_XML_DOC, // XML
AMF_TYPED_OBJECT, // 有类型的对象
AMF_AVMPLUS, // 需要扩展到AMF3
AMF_INVALID = 0xff // 无效类型
} AMFDataType;
AMF3支持的类型定义:
typedef enum
{
AMF3_UNDEFINED = 0, // 未定义
AMF3_NULL, // 空类型
AMF3_FALSE, // FALSE
AMF3_TRUE, // TRUE
AMF3_INTEGER, // 整型
AMF3_DOUBLE, // 浮点型
AMF3_STRING, // 字符串
AMF3_XML_DOC, // XML文档
AMF3_DATE, // 日期
AMF3_ARRAY, // 数组
AMF3_OBJECT, // 对象
AMF3_XML, // XML
AMF3_BYTE_ARRAY // 字节数组
} AMF3DataType;
字符串定义:
typedef struct AVal
{
char *av_val;
int av_len;
} AVal;
// 初使化串
#define AVC(str) {str,sizeof(str)-1}
// 比较串
#define AVMATCH(a1,a2) ((a1)->av_len == (a2)->av_len && !memcmp((a1)->av_val,(a2)->av_val,(a1)->av_len))
对象属性定义:
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;
函数定义:
整数操作:
注意:由于整数不属于AMF的基本类型,序列化时不会压入类型字节。
// 16位整数序列化
char *AMF_EncodeInt16(char *output, char *outend, short nVal);
// 24位整数序列化
char *AMF_EncodeInt24(char *output, char *outend, int nVal);
// 32位整数序列化
char *AMF_EncodeInt32(char *output, char *outend, int nVal);
// 16位整数反序列化
unsigned short AMF_DecodeInt16(const char *data);
// 24位整数反序列化
unsigned int AMF_DecodeInt24(const char *data);
// 32位整数反序列化
unsigned int AMF_DecodeInt32(const char *data);
基本类型操作:
注意:基本类型序列化时会压入类型字节,但反序列化时需跳过类型字节。
// 浮点数序列化
char *AMF_EncodeNumber(char *output, char *outend, double dVal);
// Bool型序列化
char *AMF_EncodeBoolean(char *output, char *outend, int bVal);
// 字符串序列化,自动支持短串和长串,即当串长度>=65536时使用长串
char *AMF_EncodeString(char *output, char *outend, const AVal * str);
// 浮点数反序列化
double AMF_DecodeNumber(const char *data);
// Bool型反序列化
int AMF_DecodeBoolean(const char *data);
// 短串反序列化
void AMF_DecodeString(const char *data, AVal * str);
// 长串反序列化
void AMF_DecodeLongString(const char *data, AVal * str);
命名的基本类型操作:
注意:命名的基本类型是复合操作,序列化时首先压入不带类型的字符串,然后压入带类型的基本类型。
// 串值KV序列化,用于属性或数组元素
char *AMF_EncodeNamedString(char *output, char *outend, const AVal * name, const AVal * value);
// 浮点数KV序列化,用于属性或数组元素
char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal * name, double dVal);
// Bool型KV序列化,用于属性或数组元素
char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal * name, int bVal);
对象操作:
// 打印对象信息
void AMF_Dump(AMFObject * obj);
// 添加新属性到对象中,若容量不够,可能会申请空间
void AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop);
// 获取对象的属性个数
int AMF_CountProp(AMFObject * obj);
// 对象序列化
char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd);
// 对象反序列化,bDecodeName为FALSE表示不解析属性名称
int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize, int bDecodeName);
// 重置对象,释放属性空间
void AMF_Reset(AMFObject * obj);
对象属性操作:
// 打印对象属性
void AMFProp_Dump(AMFObjectProperty * prop);
// 对象属性序列化
char *AMFProp_Encode(AMFObjectProperty * prop, char *pBuffer, char *pBufEnd);
// 对象属性反序列化,bDecodeName为FALSE表示不解析属性名称
int AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer, int nSize, int bDecodeName);
// 重置对象属性
void AMFProp_Reset(AMFObjectProperty * prop);
对象属性访问:
// 根据索引位置或Key名称找属性,当nIndex>=0时,表示按索引查找,否则按名称查找
AMFObjectProperty *AMF_GetProp(AMFObject * obj, const AVal * name, int nIndex);
// 检查是否无效属性
int AMFProp_IsValid(AMFObjectProperty * prop);
// 设置属性名称
void AMFProp_SetName(AMFObjectProperty * prop, AVal * name);
// 获取属性名称
void AMFProp_GetName(AMFObjectProperty * prop, AVal * name);
// 获取属性的值类型
AMFDataType AMFProp_GetType(AMFObjectProperty * prop);
// 获取浮点数属性值
double AMFProp_GetNumber(AMFObjectProperty * prop);
// 获取Bool型属性值
int AMFProp_GetBoolean(AMFObjectProperty * prop);
// 获取字符串属性值
void AMFProp_GetString(AMFObjectProperty * prop, AVal * str);
// 获取对象属性值
void AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj);
数组操作:
注意:librtmp的实现中没有数组的序列化操作接口,我查阅了源代码,发现AMF_DecodeArray()是可以反序列化对象的,但是首先必须先获得数组元素的个数。
// 数组反序列化
int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize, int nArrayLen, int bDecodeName);
源码解析:
为了加深对实现的理解,下面我摘录一部分源码,代码中加了一些注册,与诸位一起分享。
整数的序列化与反序列化:
char *
AMF_EncodeInt32(char *output, char *outend, int nVal)
{
// 预判长度溢出
if (output+4 > outend)
return NULL;
// 小端转大端
output[3] = nVal & 0xff;
output[2] = nVal >> 8;
output[1] = nVal >> 16;
output[0] = nVal >> 24;
return output+4;
}
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;
}
浮点数的序列化与反序列化:
char *
AMF_EncodeNumber(char *output, char *outend, double dVal)
{
// 预判长度溢出,需要1字节类型+8字节浮点
if (output+1+8 > outend)
return NULL;
// 压入类型
*output++ = AMF_NUMBER; /* type: Number */
// 小端转大端
{
unsigned char *ci, *co;
ci = (unsigned char *)&dVal;
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];
}
return output+8;
}
double
AMF_DecodeNumber(const char *data)
{
double dVal;
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];
return dVal;
}
Bool型的序列化与反序列化:
char *
AMF_EncodeBoolean(char *output, char *outend, int bVal)
{
// 预判长度溢出,需要1字节类型+1字节Bool
if (output+2 > outend)
return NULL;
// 压入类型
*output++ = AMF_BOOLEAN;
*output++ = bVal ? 0x01 : 0x00;
return output;
}
int
AMF_DecodeBoolean(const char *data)
{
return *data != 0;
}
字符串的序列化与反序列化:
char *
AMF_EncodeString(char *output, char *outend, const AVal *bv)
{
// 预判长度溢出,短串需要1字节类型+2字节短长度+字符串长度,或长串需要1字节类型+4字节长长度+字符串长度
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)
{
// 压入类型
*output++ = AMF_STRING;
// 压入2字节长度
output = AMF_EncodeInt16(output, outend, bv->av_len);
}
else
{
// 压入类型
*output++ = AMF_LONG_STRING;
// 压入4字节长度
output = AMF_EncodeInt32(output, outend, bv->av_len);
}
// 拷贝bv指向的字符中到序列化缓冲
memcpy(output, bv->av_val, bv->av_len);
output += bv->av_len;
return output;
}
void
AMF_DecodeString(const char *data, AVal *bv)
{
// 解2字节大小
bv->av_len = AMF_DecodeInt16(data);
// 设置bv串指向
bv->av_val = (bv->av_len > 0) ? (char *)data + 2 : NULL;
}
void
AMF_DecodeLongString(const char *data, AVal *bv)
{
// 解4字节大小
bv->av_len = AMF_DecodeInt32(data);
// 设置bv串指向
bv->av_val = (bv->av_len > 0) ? (char *)data + 4 : NULL;
}
命名的基本类型的序列化与反序列化:
char *
AMF_EncodeNamedString(char *output, char *outend, const AVal *strName, const AVal *strValue)
{
// 预判长度溢出,需要2字节串长度+命名串长度
if (output+2+strName->av_len > outend)
return NULL;
// 压入2字节长度
output = AMF_EncodeInt16(output, outend, strName->av_len);
// 拷贝串
memcpy(output, strName->av_val, strName->av_len);
output += strName->av_len;
// 继续压入基本串
return AMF_EncodeString(output, outend, strValue);
}
char *
AMF_EncodeNamedNumber(char *output, char *outend, const AVal *strName, double dVal)
{
// 预判长度溢出,需要2字节串长度+命名串长度
if (output+2+strName->av_len > outend)
return NULL;
// 压入2字节长度
output = AMF_EncodeInt16(output, outend, strName->av_len);
// 拷贝串
memcpy(output, strName->av_val, strName->av_len);
output += strName->av_len;
// 继续压入基本浮点数
return AMF_EncodeNumber(output, outend, dVal);
}
char *
AMF_EncodeNamedBoolean(char *output, char *outend, const AVal *strName, int bVal)
{
// 预判长度溢出,需要2字节串长度+命名串长度
if (output+2+strName->av_len > outend)
return NULL;
// 压入2字节长度
output = AMF_EncodeInt16(output, outend, strName->av_len);
// 拷贝串
memcpy(output, strName->av_val, strName->av_len);
output += strName->av_len;
// 继续压入基本Bool
return AMF_EncodeBoolean(output, outend, bVal);
}
属性的操作,这些都属于普通的结构体操作:
void
AMFProp_GetName(AMFObjectProperty *prop, AVal *name)
{
*name = prop->p_name;
}
void
AMFProp_SetName(AMFObjectProperty *prop, AVal *name)
{
prop->p_name = *name;
}
AMFDataType
AMFProp_GetType(AMFObjectProperty *prop)
{
return prop->p_type;
}
double
AMFProp_GetNumber(AMFObjectProperty *prop)
{
return prop->p_vu.p_number;
}
int
AMFProp_GetBoolean(AMFObjectProperty *prop)
{
return prop->p_vu.p_number != 0;
}
void
AMFProp_GetString(AMFObjectProperty *prop, AVal *str)
{
*str = prop->p_vu.p_aval;
}
void
AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj)
{
*obj = prop->p_vu.p_object;
}
int
AMFProp_IsValid(AMFObjectProperty *prop)
{
return prop->p_type != AMF_INVALID;
}
void
AMF_AddProp(AMFObject *obj, const AMFObjectProperty *prop)
{
if (!(obj->o_num & 0x0f))
obj->o_props =
realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty));
obj->o_props[obj->o_num++] = *prop;
}
int
AMF_CountProp(AMFObject *obj)
{
return obj->o_num;
}
AMFObjectProperty *
AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex)
{
// 索引有效时,按索引取属性
if (nIndex >= 0)
{
if (nIndex <= obj->o_num)
return &obj->o_props[nIndex];
}
// 否则按名称取属性
else
{
int n;
for (n = 0; n < obj->o_num; n++)
{
if (AVMATCH(&obj->o_props[n].p_name, name))
return &obj->o_props[n];
}
}
return (AMFObjectProperty *)&AMFProp_Invalid;
}
属性的序列化与反序列化:
char *
AMFProp_Encode(AMFObjectProperty *prop, char *pBuffer, char *pBufEnd)
{
// 无效属性不处理
if (prop->p_type == AMF_INVALID)
return NULL;
// 预判长度溢出,需要2字节串长度+属性名称长度+1字节类型
if (prop->p_type != AMF_NULL && pBuffer + prop->p_name.av_len + 2 + 1 >= pBufEnd)
return NULL;
// 属性名称非空,压入属性名称
if (prop->p_type != AMF_NULL && prop->p_name.av_len)
{
*pBuffer++ = prop->p_name.av_len >> 8;
*pBuffer++ = prop->p_name.av_len & 0xff;
memcpy(pBuffer, prop->p_name.av_val, prop->p_name.av_len);
pBuffer += prop->p_name.av_len;
}
// 继续压入实际基本类型
switch (prop->p_type)
{
case AMF_NUMBER:
pBuffer = AMF_EncodeNumber(pBuffer, pBufEnd, prop->p_vu.p_number);
break;
case AMF_BOOLEAN:
pBuffer = AMF_EncodeBoolean(pBuffer, pBufEnd, prop->p_vu.p_number != 0);
break;
case AMF_STRING:
pBuffer = AMF_EncodeString(pBuffer, pBufEnd, &prop->p_vu.p_aval);
break;
// 压入NULL
case AMF_NULL:
if (pBuffer+1 >= pBufEnd)
return NULL;
*pBuffer++ = AMF_NULL;
break;
// 压入子对象
case AMF_OBJECT:
pBuffer = AMF_Encode(&prop->p_vu.p_object, pBuffer, pBufEnd);
break;
default:
RTMP_Log(RTMP_LOGERROR, "%s, invalid type. %d", __FUNCTION__, prop->p_type);
pBuffer = NULL;
};
return pBuffer;
}
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;
}
// 包含属性名称时,最小长度为2字节属性名称长度指示+至少1字节属性+至少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)
{
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:
{
unsigned int nStringSize = AMF_DecodeInt32(pBuffer);
if (nSize < (long)nStringSize + 4)
return -1;
AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval);
nSize -= (4 + nStringSize);
prop->p_type = AMF_STRING;
break;
}
case AMF_RECORDSET:
{
RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!");
return -1;
break;
}
case AMF_XML_DOC:
{
RTMP_Log(RTMP_LOGERROR, "AMF_XML_DOC not supported!");
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, @0x%08X", __FUNCTION__,
prop->p_type, pBuffer - 1);
return -1;
}
return nOriginalSize - nSize;
}
对象的序列化与反序列化:
char *
AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd)
{
int i;
// 预判长度溢出,需要5个字节
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 */
// 压入对象结束标记
pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
return pBuffer;
}
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;
// 判断结束标志
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;
}
数组的反序列化:
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--;
// 解属性
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;
}
编码格式:
AMF的编码基本遵循TLV范式,但是为了进一步节省空间,比如对象的属性,在序列化属性名称的时候,即使属性名是字符串,也并不压入类型。我想这有两个原因,一是压入类型会增加序列化的长度,二是开放了TLV,就等于支持属性名称可以是其它的类型。这是一个驳论,即然不能做到完美,那就做到安全。当然,类似这样的约束加入,等于在算法中加了很多特殊条件,对使用者来说也无法形成统一的规范思维,这也是无耐之举吧。
关于编码格式,如果您读懂了AMF的源码实现,基本上也就明白了序列化后的数据排队。百度上可以搜索到很多关于编码格式的精彩说明和图解,出于文档的完整性,所以我这里只简单地描述几个结构。如下:
浮点数:
0x00 + 8字节浮点数
Bool型:
0x01 + 1字节Bool值
短字符串:
0x02 + 2字节长度 + 字符串
长字符串
0x02 + 4字节长度 + 字符串
对象:
0x03 + 属性1名称长度 + 属性1名称 + 1字节属性1类型 + n字节属性1值 + 属性2名称长度 + 属性2名称 + 1字节属性2类型 + n字节属性2值 + 3字节结尾标志
使用举例:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <librtmp/amf.h>
#include <librtmp/log.h>
int main(int argc, char* argv[])
{
char sBuf[128] = {0};
char* pStart = sBuf;
char* pEnd = sBuf + sizeof(sBuf);
// 整数操作:
{
char* pPos = AMF_EncodeInt16(pStart, pEnd, 1);
pPos = AMF_EncodeInt24(pPos, pEnd, 2);
pPos = AMF_EncodeInt32(pPos, pEnd, 3);
int n1 = AMF_DecodeInt16(pStart);
int n2 = AMF_DecodeInt24(pStart + 2);
int n3 = AMF_DecodeInt32(pStart + 2 + 3);
printf("n1=%d, n2=%d, n3=%d \n", n1, n2, n3);
}
// 基本类型操作:
{
char* pPos = AMF_EncodeNumber(pStart, pEnd, 1);
pPos = AMF_EncodeBoolean(pPos, pEnd, TRUE);
AVal str = AVC("hello");
pPos = AMF_EncodeString(pPos, pEnd, &str);
double f = AMF_DecodeNumber(pStart + 1);
int b = AMF_DecodeBoolean(pStart + 1 + 8);
AVal str2 = {NULL, 0};
AMF_DecodeString(pStart + 1 + 8 + 1 + 1 + 1, &str2);
printf("f:%.02f b:%d str:%s \n", f, b, str2.av_val);
}
// 对象操作:
{
AMFObject obj = {0, NULL};
AMFObjectProperty prop1, prop2;
AVal key1 = AVC("key1");
AMFProp_SetName(&prop1, &key1);
prop1.p_type = AMF_NUMBER;
prop1.p_vu.p_number = 1;
AVal key2 = AVC("key2");
AMFProp_SetName(&prop2, &key2);
prop2.p_type = AMF_STRING;
prop2.p_vu.p_aval = AVC("test");
AMF_AddProp(&obj, &prop1);
AMF_AddProp(&obj, &prop2);
char* pPos = AMF_Encode(&obj, pStart, pEnd);
AMFObject obj2;
AMF_Decode(&obj2, pStart + 1, pPos - pStart - 1, TRUE);
RTMP_LogSetLevel(RTMP_LOGDEBUG);
AMF_Dump(&obj2);
AMF_Reset(&obj);
AMF_Reset(&obj2);
}
return 0;
}
运行输出:
n1=1, n2=2, n3=3
f:1.00 b:1 str:hello
DEBUG: (object begin)
DEBUG: Property: <Name: key1, NUMBER: 1.00>
DEBUG: Property: <Name: key2, STRING: test>
DEBUG: (object end)
最后展示下代码中obj的二进制内容,我用线段分开了,具体交给大家解读吧。