amf.h中关于 AMFObject 是这样的定义的:
typedef struct AMFObject { int o_num; struct AMFObjectProperty *o_props; } AMFObject;
有里面变量可知 o_num 代表 o_props的个数;
在rtmp.c里有这样一段使用在
static int HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) { AMFObject obj; AVal method; AVal method11; int txn; int ret = 0, nRes; if (body[0] != 0x02) /* make sure it is a string method name we start with */ { //RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet", __FUNCTION__); return 0; } nRes = AMF_Decode(&obj, body, nBodySize, FALSE); if (nRes < 0) { //RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__); return 0; } AMF_Dump(&obj); AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method); ..... ..... }
在此函数内 AMFObject obj;通过 nRes = AMF_Decode(&obj, body, nBodySize, FALSE); 会对body进行AMF解码相当于给obj进行赋值,然后通过AMF_Dump(&obj) 应该是进行一个解析?(最下面会贴出该函数的源码)
经过AMF_Dump()之后,会获通过 AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method); 获取信令的操作。当然也可以自定义他们的操作,类似于:
static const AVal av_NetStream_Pause_Notify = AVC("NetStream.Pause.Notify"); static const AVal av_NetStream_Play_UnpublishNotify = AVC("NetStream.Play.UnpublishNotify"); //................
下面代码会根据method的内容进行相应的操作。
-----------------------------------------------
扯的有点偏了,这里还是 讲AMFObject 上面的操作经过抓包可以看一下大致的内容:
===================================================
由包里的内容此次信令操作为自定义的“NoticeNotify”,而obj相当于一个容器,里面就有上面标识的3项内容,也就是根据AMFObject 的数据结构可知:
o_num 个数为3 项 o_props 内容。而 o_props 的数据结构为:
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;
其中 o_props 里有个“联合”数据结构成员,从上面抓包的内容来看其中的三项,也很好的解释了其中三项的内容。
第一项:type 为string 对应 AVal 类似于 key-value
第二项:type 为Number 对应 double。
第三项:type 也为string 对应 AVal。
这样来看如果想取obj里的内容就比较好取了。
其中obj的成员 o_props 就是一个类似数组,在操作的时候可以 obj->o_props[index];
或者再细一点就是 obj->o_props[index].p_type;
=====================================
总结的比较乱,因为现在对amf还不是很了解,不过对于amf真的可以花些时间去分析一下它的博大精深。
再此贴一下AMF_Dump()的源码:
void AMFProp_Dump(AMFObjectProperty *prop) { char strRes[256]; char str[256]; AVal name; if (prop->p_type == AMF_INVALID) { //RTMP_Log(RTMP_LOGDEBUG, "Property: INVALID"); return; } if (prop->p_type == AMF_NULL) { //RTMP_Log(RTMP_LOGDEBUG, "Property: NULL"); return; } if (prop->p_name.av_len) { name = prop->p_name; } else { name.av_val = "no-name."; name.av_len = sizeof("no-name.") - 1; } if (name.av_len > 18) name.av_len = 18; snprintf(strRes, 255, "Name: %18.*s, ", name.av_len, name.av_val); if (prop->p_type == AMF_OBJECT) { //RTMP_Log(RTMP_LOGDEBUG, "Property: <%sOBJECT>", strRes); AMF_Dump(&prop->p_vu.p_object); return; } switch (prop->p_type) { case AMF_NUMBER: snprintf(str, 255, "NUMBER:\t%.2f", prop->p_vu.p_number); break; case AMF_BOOLEAN: snprintf(str, 255, "BOOLEAN:\t%s", prop->p_vu.p_number != 0.0 ? "TRUE" : "FALSE"); break; case AMF_STRING: snprintf(str, 255, "STRING:\t%.*s", prop->p_vu.p_aval.av_len, prop->p_vu.p_aval.av_val); break; case AMF_DATE: snprintf(str, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d", prop->p_vu.p_number, prop->p_UTCoffset); break; default: snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)prop->p_type); } //RTMP_Log(RTMP_LOGDEBUG, "Property: <%s%s>", strRes, str); }