AMF是Adobe公司開發(fā)的一種高效的消息序列化和反序列化協(xié)議声畏,它包括兩種數(shù)據(jù)類型格式:AMF0和AMF3庶诡。其中AMF0是基本的消息格式拜秧,但是后來Adobe對(duì)AMF0進(jìn)行了優(yōu)化,開發(fā)了擴(kuò)展的AMF3尸疆,即AMF3并不是AMF0的完全替代臼膏,使用AMF3需要在外面套一層AMF0做為容器硼被。我們可以在librtmp的源碼中找到AMF的一個(gè)實(shí)現(xiàn)版本,但是我不清楚什么原因渗磅,居然有些函數(shù)如AMFProp_SetNumber()至今都沒有實(shí)現(xiàn)祷嘶。
”水滿則溢,月滿則虧”夺溢,librtmp雖然有些瑕疵,但是仍然不影響它是那么的優(yōu)秀烛谊,如果在朋友們的自研項(xiàng)目或者工作中有機(jī)會(huì)用到它风响,我還是非常推薦的。
結(jié)構(gòu)定義:
AMF0支持的類型定義:
typedef enum
{
AMF_NUMBER = 0, // 數(shù)字丹禀,用8字節(jié)double表示
AMF_BOOLEAN, // 布爾數(shù)状勤,用1字節(jié)整數(shù)表示
AMF_STRING, // 字符串,用LV范式表示
AMF_OBJECT, // 對(duì)象
AMF_MOVIECLIP, // 保留双泪,未使用
AMF_NULL, // 空類型
AMF_UNDEFINED, // 未定義
AMF_REFERENCE, // 引用
AMF_ECMA_ARRAY, // 數(shù)組
AMF_OBJECT_END, // 對(duì)象結(jié)束
AMF_STRICT_ARRAY, // 嚴(yán)格數(shù)組
AMF_DATE, // 日期
AMF_LONG_STRING, // 長字符串
AMF_UNSUPPORTED, // 未支持
AMF_RECORDSET, // 保留持搜,未使用
AMF_XML_DOC, // XML
AMF_TYPED_OBJECT, // 有類型的對(duì)象
AMF_AVMPLUS, // 需要擴(kuò)展到AMF3
AMF_INVALID = 0xff // 無效類型
} AMFDataType;
AMF3支持的類型定義:
typedef enum
{
AMF3_UNDEFINED = 0, // 未定義
AMF3_NULL, // 空類型
AMF3_FALSE, // FALSE
AMF3_TRUE, // TRUE
AMF3_INTEGER, // 整型
AMF3_DOUBLE, // 浮點(diǎn)型
AMF3_STRING, // 字符串
AMF3_XML_DOC, // XML文檔
AMF3_DATE, // 日期
AMF3_ARRAY, // 數(shù)組
AMF3_OBJECT, // 對(duì)象
AMF3_XML, // XML
AMF3_BYTE_ARRAY // 字節(jié)數(shù)組
} 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))
對(duì)象屬性定義:
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;
對(duì)象定義:
typedef struct AMFObject
{
int o_num;
struct AMFObjectProperty *o_props;
} AMFObject;
函數(shù)定義:
整數(shù)操作:
注意:由于整數(shù)不屬于AMF的基本類型,序列化時(shí)不會(huì)壓入類型字節(jié)焙矛。
// 16位整數(shù)序列化
char *AMF_EncodeInt16(char *output, char *outend, short nVal);
// 24位整數(shù)序列化
char *AMF_EncodeInt24(char *output, char *outend, int nVal);
// 32位整數(shù)序列化
char *AMF_EncodeInt32(char *output, char *outend, int nVal);
// 16位整數(shù)反序列化
unsigned short AMF_DecodeInt16(const char *data);
// 24位整數(shù)反序列化
unsigned int AMF_DecodeInt24(const char *data);
// 32位整數(shù)反序列化
unsigned int AMF_DecodeInt32(const char *data);
基本類型操作:
注意:基本類型序列化時(shí)會(huì)壓入類型字節(jié)葫盼,但反序列化時(shí)需跳過類型字節(jié)。
// 浮點(diǎn)數(shù)序列化
char *AMF_EncodeNumber(char *output, char *outend, double dVal);
// Bool型序列化
char *AMF_EncodeBoolean(char *output, char *outend, int bVal);
// 字符串序列化村斟,自動(dòng)支持短串和長串贫导,即當(dāng)串長度>=65536時(shí)使用長串
char *AMF_EncodeString(char *output, char *outend, const AVal * str);
// 浮點(diǎn)數(shù)反序列化
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);
命名的基本類型操作:
注意:命名的基本類型是復(fù)合操作,序列化時(shí)首先壓入不帶類型的字符串蟆盹,然后壓入帶類型的基本類型孩灯。
// 串值KV序列化,用于屬性或數(shù)組元素
char *AMF_EncodeNamedString(char *output, char *outend, const AVal * name, const AVal * value);
// 浮點(diǎn)數(shù)KV序列化逾滥,用于屬性或數(shù)組元素
char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal * name, double dVal);
// Bool型KV序列化峰档,用于屬性或數(shù)組元素
char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal * name, int bVal);
對(duì)象操作:
// 打印對(duì)象信息
void AMF_Dump(AMFObject * obj);
// 添加新屬性到對(duì)象中,若容量不夠,可能會(huì)申請(qǐng)空間
void AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop);
// 獲取對(duì)象的屬性個(gè)數(shù)
int AMF_CountProp(AMFObject * obj);
// 對(duì)象序列化
char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd);
// 對(duì)象反序列化讥巡,bDecodeName為FALSE表示不解析屬性名稱
int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize, int bDecodeName);
// 重置對(duì)象掀亩,釋放屬性空間
void AMF_Reset(AMFObject * obj);
對(duì)象屬性操作:
// 打印對(duì)象屬性
void AMFProp_Dump(AMFObjectProperty * prop);
// 對(duì)象屬性序列化
char *AMFProp_Encode(AMFObjectProperty * prop, char *pBuffer, char *pBufEnd);
// 對(duì)象屬性反序列化,bDecodeName為FALSE表示不解析屬性名稱
int AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer, int nSize, int bDecodeName);
// 重置對(duì)象屬性
void AMFProp_Reset(AMFObjectProperty * prop);
對(duì)象屬性訪問:
// 根據(jù)索引位置或Key名稱找屬性尚卫,當(dāng)nIndex>=0時(shí)归榕,表示按索引查找,否則按名稱查找
AMFObjectProperty *AMF_GetProp(AMFObject * obj, const AVal * name, int nIndex);
// 檢查是否無效屬性
int AMFProp_IsValid(AMFObjectProperty * prop);
// 設(shè)置屬性名稱
void AMFProp_SetName(AMFObjectProperty * prop, AVal * name);
// 獲取屬性名稱
void AMFProp_GetName(AMFObjectProperty * prop, AVal * name);
// 獲取屬性的值類型
AMFDataType AMFProp_GetType(AMFObjectProperty * prop);
// 獲取浮點(diǎn)數(shù)屬性值
double AMFProp_GetNumber(AMFObjectProperty * prop);
// 獲取Bool型屬性值
int AMFProp_GetBoolean(AMFObjectProperty * prop);
// 獲取字符串屬性值
void AMFProp_GetString(AMFObjectProperty * prop, AVal * str);
// 獲取對(duì)象屬性值
void AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj);
數(shù)組操作:
注意:librtmp的實(shí)現(xiàn)中沒有數(shù)組的序列化操作接口吱涉,我查閱了源代碼刹泄,發(fā)現(xiàn)AMF_DecodeArray()是可以反序列化對(duì)象的,但是首先必須先獲得數(shù)組元素的個(gè)數(shù)怎爵。
// 數(shù)組反序列化
int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize, int nArrayLen, int bDecodeName);
源碼解析:
為了加深對(duì)實(shí)現(xiàn)的理解特石,下面我摘錄一部分源碼,代碼中加了一些注冊(cè)鳖链,與諸位一起分享姆蘸。
整數(shù)的序列化與反序列化:
char *
AMF_EncodeInt32(char *output, char *outend, int nVal)
{
// 預(yù)判長度溢出
if (output+4 > outend)
return NULL;
// 小端轉(zhuǎn)大端
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;
// 大端轉(zhuǎn)小端
val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
return val;
}
浮點(diǎn)數(shù)的序列化與反序列化:
char *
AMF_EncodeNumber(char *output, char *outend, double dVal)
{
// 預(yù)判長度溢出,需要1字節(jié)類型+8字節(jié)浮點(diǎn)
if (output+1+8 > outend)
return NULL;
// 壓入類型
*output++ = AMF_NUMBER; /* type: Number */
// 小端轉(zhuǎn)大端
{
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;
// 大端轉(zhuǎn)小端
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)
{
// 預(yù)判長度溢出芙委,需要1字節(jié)類型+1字節(jié)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)
{
// 預(yù)判長度溢出逞敷,短串需要1字節(jié)類型+2字節(jié)短長度+字符串長度,或長串需要1字節(jié)類型+4字節(jié)長長度+字符串長度
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字節(jié)長度
output = AMF_EncodeInt16(output, outend, bv->av_len);
}
else
{
// 壓入類型
*output++ = AMF_LONG_STRING;
// 壓入4字節(jié)長度
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字節(jié)大小
bv->av_len = AMF_DecodeInt16(data);
// 設(shè)置bv串指向
bv->av_val = (bv->av_len > 0) ? (char *)data + 2 : NULL;
}
void
AMF_DecodeLongString(const char *data, AVal *bv)
{
// 解4字節(jié)大小
bv->av_len = AMF_DecodeInt32(data);
// 設(shè)置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)
{
// 預(yù)判長度溢出灌侣,需要2字節(jié)串長度+命名串長度
if (output+2+strName->av_len > outend)
return NULL;
// 壓入2字節(jié)長度
output = AMF_EncodeInt16(output, outend, strName->av_len);
// 拷貝串
memcpy(output, strName->av_val, strName->av_len);
output += strName->av_len;
// 繼續(xù)壓入基本串
return AMF_EncodeString(output, outend, strValue);
}
char *
AMF_EncodeNamedNumber(char *output, char *outend, const AVal *strName, double dVal)
{
// 預(yù)判長度溢出推捐,需要2字節(jié)串長度+命名串長度
if (output+2+strName->av_len > outend)
return NULL;
// 壓入2字節(jié)長度
output = AMF_EncodeInt16(output, outend, strName->av_len);
// 拷貝串
memcpy(output, strName->av_val, strName->av_len);
output += strName->av_len;
// 繼續(xù)壓入基本浮點(diǎn)數(shù)
return AMF_EncodeNumber(output, outend, dVal);
}
char *
AMF_EncodeNamedBoolean(char *output, char *outend, const AVal *strName, int bVal)
{
// 預(yù)判長度溢出,需要2字節(jié)串長度+命名串長度
if (output+2+strName->av_len > outend)
return NULL;
// 壓入2字節(jié)長度
output = AMF_EncodeInt16(output, outend, strName->av_len);
// 拷貝串
memcpy(output, strName->av_val, strName->av_len);
output += strName->av_len;
// 繼續(xù)壓入基本Bool
return AMF_EncodeBoolean(output, outend, bVal);
}
屬性的操作侧啼,這些都屬于普通的結(jié)構(gòu)體操作:
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)
{
// 索引有效時(shí)牛柒,按索引取屬性
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;
// 預(yù)判長度溢出,需要2字節(jié)串長度+屬性名稱長度+1字節(jié)類型
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;
}
// 繼續(xù)壓入實(shí)際基本類型
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;
// 壓入子對(duì)象
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;
}
// 包含屬性名稱時(shí)皮壁,最小長度為2字節(jié)屬性名稱長度指示+至少1字節(jié)屬性+至少1字節(jié)值的類型
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;
// 如果是數(shù)組,按對(duì)象方式反序列化
/* 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;
}
對(duì)象的序列化與反序列化:
char *
AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd)
{
int i;
// 預(yù)判長度溢出哪审,需要5個(gè)字節(jié)
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 */
// 壓入對(duì)象結(jié)束標(biāo)記
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;
// 判斷結(jié)束標(biāo)志
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;
}
數(shù)組的反序列化:
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范式蛾魄,但是為了進(jìn)一步節(jié)省空間,比如對(duì)象的屬性湿滓,在序列化屬性名稱的時(shí)候畏腕,即使屬性名是字符串,也并不壓入類型茉稠。我想這有兩個(gè)原因描馅,一是壓入類型會(huì)增加序列化的長度,二是開放了TLV而线,就等于支持屬性名稱可以是其它的類型铭污。這是一個(gè)駁論恋日,即然不能做到完美,那就做到安全嘹狞。當(dāng)然岂膳,類似這樣的約束加入,等于在算法中加了很多特殊條件磅网,對(duì)使用者來說也無法形成統(tǒng)一的規(guī)范思維谈截,這也是無耐之舉吧。
關(guān)于編碼格式涧偷,如果您讀懂了AMF的源碼實(shí)現(xiàn)簸喂,基本上也就明白了序列化后的數(shù)據(jù)排隊(duì)。百度上可以搜索到很多關(guān)于編碼格式的精彩說明和圖解燎潮,出于文檔的完整性喻鳄,所以我這里只簡單地描述幾個(gè)結(jié)構(gòu)。如下:
浮點(diǎn)數(shù):
0x00 + 8字節(jié)浮點(diǎn)數(shù)
Bool型:
0x01 + 1字節(jié)Bool值
短字符串:
0x02 + 2字節(jié)長度 + 字符串
長字符串
0x02 + 4字節(jié)長度 + 字符串
對(duì)象:
0x03 + 屬性1名稱長度 + 屬性1名稱 + 1字節(jié)屬性1類型 + n字節(jié)屬性1值 + 屬性2名稱長度 + 屬性2名稱 + 1字節(jié)屬性2類型 + n字節(jié)屬性2值 + 3字節(jié)結(jié)尾標(biāo)志
使用舉例:
#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);
// 整數(shù)操作:
{
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);
}
// 對(duì)象操作:
{
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;
}
運(yùn)行輸出:
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的二進(jìn)制內(nèi)容确封,我用線段分開了除呵,具體交給大家解讀吧。