本文主要是介绍【流媒体】RTMPDump—AMF编码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
- 1. AMF类型
- 2. AMF编码
- 2.1 AMF_Number (AMF_EncodeNumber)
- 2.2 AMF_BOOLEAN (AMF_EncodeBoolean)
- 2.3 AMF_STRING 和 AMF_LONG_STRING (AMF_EncodeString)
- 2.3.1 AMF_EncodeInt16
- 2.3.2 AMF_EncodeInt32
- 2.4 AMF_OBJECT (AMF_Encode)
- 2.4.1 AMF_EncodeInt24
- 2.5 AMF_ECMA_ARRAY (AMF_EncodeEcmaArray)
- 2.6 AMF_STRICT_ARRAY (AMF_EncodeArray)
- 3. AMF解码
- 3.1 AMF_Number (AMF_DecodeNumber)
- 3.2 AMF_BOOLEAN (AMF_DecodeBoolean)
- 3.3 AMF_STRING 和 AMF_LONG_STRING (AMF_DecodeString 和 AMF_DecodeLongString)
- 3.3.1 AMF_DecodeInt16
- 3.3.2 AMF_DecodeInt32
- 3.4 AMF_OBJECT (AMF_Decode)
- 3.4.1 AMF_DecodeInt24
- 3.4.2 AMFProp_Decode
- 3.5 AMF_STRICT_ARRAY (AMF_DecodeArray)
RTMP协议相关:
【流媒体】RTMP协议概述
【流媒体】RTMP协议的数据格式
【流媒体】RTMP协议的消息类型
【流媒体】RTMPDump—主流程简单分析
【流媒体】RTMPDump—RTMP_Connect函数(握手、网络连接)
【流媒体】RTMPDump—RTMP_ConnectStream(创建流连接)
【流媒体】RTMPDump—Download(接收流媒体信息)
【流媒体】RTMPDump—AMF编码
【流媒体】基于libRTMP的H264推流器
参考雷博的系列文章(可以从一篇链接到其他文章):
RTMPdump 源代码分析 1: main()函数
在看RTMPDump代码过程中,发现一个比较核心的地方还没有记录,即AMF编码。RTMP协议的数据很多都是以AMF格式进行编码的,也应该做重点记录。参考RTMPDump代码中的amf.c和amf.h两个文件。由于AMF编码会将数据转换成为大端存储,可以参考 数据存储:大端存储与小端存储
1. AMF类型
参考amf.h中的AMFDataType,AMF0一共有18种类型
typedef enum
{AMF_NUMBER = 0, // doubleAMF_BOOLEAN, // boolAMF_STRING, // stringAMF_OBJECT, // 对象类型,包括property和property numAMF_MOVIECLIP, /* reserved, not used */AMF_NULL,AMF_UNDEFINED,AMF_REFERENCE, // not supportedAMF_ECMA_ARRAY, // ECMAAMF_OBJECT_END,AMF_STRICT_ARRAY, // STRICTAMF_DATE, // AMF_LONG_STRING, // 32-bit stringAMF_UNSUPPORTED, AMF_RECORDSET, /* reserved, not used */AMF_XML_DOC,AMF_TYPED_OBJECT,AMF_AVMPLUS, /* switch to AMF3 */AMF_INVALID = 0xff
} AMFDataType;
AMF3一共有13种类型
typedef enum
{AMF3_UNDEFINED = 0, AMF3_NULL, AMF3_FALSE,AMF3_TRUE,AMF3_INTEGER, AMF3_DOUBLE, AMF3_STRING, AMF3_XML_DOC, AMF3_DATE,AMF3_ARRAY, AMF3_OBJECT, AMF3_XML, AMF3_BYTE_ARRAY
} AMF3DataType;
AMF自定义类型AVal,包括val内容和val长度
typedef struct AVal
{char* av_val; // val内容int av_len; // val长度
} AVal;
AMF自定义对象类型Object,
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;
2. AMF编码
在进行AMF编码时,不同的数据类型编码的方式是不同的,需要分开讨论
2.1 AMF_Number (AMF_EncodeNumber)
AMF_NUMBER类型相当于double类型,在进行编码时,会首先写入一个AMF_NUMBER字段,随后会将输入数据转换成为大端存储
char*
AMF_EncodeNumber(char* output, char* outend, double dVal)
{if (output + 1 + 8 > outend)return NULL;// 写入AMF_NUMBER数据类型*output++ = AMF_NUMBER; /* type: Number */#if __FLOAT_WORD_ORDER == __BYTE_ORDER
#if __BYTE_ORDER == __BIG_ENDIANmemcpy(output, &dVal, 8);
#elif __BYTE_ORDER == __LITTLE_ENDIAN{unsigned char* ci, * co;ci = (unsigned char*)& dVal; // double类型8字节,逐个字节翻转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];}
#endif
#else
#if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */{unsigned char* ci, * co;ci = (unsigned char*)& dVal;co = (unsigned char*)output;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*)& dVal;co = (unsigned char*)output;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
#endifreturn output + 8;
}
2.2 AMF_BOOLEAN (AMF_EncodeBoolean)
AMF_BOOLEAN类型相当于bool类型,在编码时首先写入描述字段AMF_BOOLEAN,随后根据输入bVal来判定是写入0x01或0x00
char*
AMF_EncodeBoolean(char* output, char* outend, int bVal)
{if (output + 2 > outend)return NULL;*output++ = AMF_BOOLEAN;*output++ = bVal ? 0x01 : 0x00;return output;
}
2.3 AMF_STRING 和 AMF_LONG_STRING (AMF_EncodeString)
AMF_STRING和AMF_LONG_STRING两种类型同时使用AMF_EncodeString进行编码,分两种情况:
(1)如果av_len小于65536,先写入AMF_STRING标识字段,随后写入16位的string长度
(2)如果av_len大于65536,先写入AMF_LONG_STRING标识字段,随后写入32位的string长度
最后写入string的内容
char*
AMF_EncodeString(char* output, char* outend, const AVal * bv)
{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) // 字符串长度小于65536,即16位{*output++ = AMF_STRING;// 写入字符串长度为16位output = AMF_EncodeInt16(output, outend, bv->av_len);}else{*output++ = AMF_LONG_STRING; // 写入AMF_LONG_STRING标识符字段// 写入字符串长度为32位output = AMF_EncodeInt32(output, outend, bv->av_len);}// 写入字符串内容memcpy(output, bv->av_val, bv->av_len);output += bv->av_len;return output;
}
2.3.1 AMF_EncodeInt16
原子函数,实现了AMF编码两个字节的功能,在很多地方都会用到
char*
AMF_EncodeInt16(char* output, char* outend, short nVal)
{if (output + 2 > outend) // 检查越界return NULL;output[1] = nVal & 0xff; // 取出低8位output[0] = nVal >> 8; // 取出高8位return output + 2;
}
2.3.2 AMF_EncodeInt32
原子函数,实现了AMF编码4个字节的功能
char*
AMF_EncodeInt32(char* output, char* outend, int nVal)
{if (output + 4 > outend) // 检查越界return NULL;output[3] = nVal & 0xff; // 取出第4个字节output[2] = nVal >> 8; // 取出第3个字节output[1] = nVal >> 16; // 取出第2个字节output[0] = nVal >> 24; // 取出第1个字节return output + 4;
}
2.4 AMF_OBJECT (AMF_Encode)
AMF_OBJECT描述了一个对象类型,使用AMF_Encode进行编码,这里和前面不同的是,Object是一个具有多个参数的数据类型,需要将其中的"属性" (props)也进行编码,最后还需要写入object_end的字段
char*
AMF_Encode(AMFObject * obj, char* pBuffer, char* pBufEnd)
{int i;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 */// 写入object结束字段pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);return pBuffer;
}
2.4.1 AMF_EncodeInt24
实现AMF编码3个字节功能
char*
AMF_EncodeInt24(char* output, char* outend, int nVal)
{if (output + 3 > outend)return NULL;output[2] = nVal & 0xff; // 取出第1字节output[1] = nVal >> 8; // 取出第2字节output[0] = nVal >> 16; // 取出第3字节return output + 3;
}
2.5 AMF_ECMA_ARRAY (AMF_EncodeEcmaArray)
AMF_ECMA_ARRAY类型进行编码时,与AMF_Encode看上去很相似,不同之处在于AMF_ECMA_ARRAY编码时还需要将num进行编码
char*
AMF_EncodeEcmaArray(AMFObject * obj, char* pBuffer, char* pBufEnd)
{int i;if (pBuffer + 4 >= pBufEnd)return NULL;*pBuffer++ = AMF_ECMA_ARRAY;pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num);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;
}
2.6 AMF_STRICT_ARRAY (AMF_EncodeArray)
AMF_STRICT_ARRAY类型进行编码时,也与前面两个编码函数很像。先编码字段类型AMF_STRICT_ARRAY,随后编码obj中的num字段,再写入属性值props。不过区别在于,这种类型最后不会写入AMF_OBJECT
char*
AMF_EncodeArray(AMFObject * obj, char* pBuffer, char* pBufEnd)
{int i;if (pBuffer + 4 >= pBufEnd)return NULL;*pBuffer++ = AMF_STRICT_ARRAY;pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num);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;
}
3. AMF解码
3.1 AMF_Number (AMF_DecodeNumber)
基本就是编码过程反过来,double类型反过来
double
AMF_DecodeNumber(const char* data)
{double dVal;
#if __FLOAT_WORD_ORDER == __BYTE_ORDER
#if __BYTE_ORDER == __BIG_ENDIANmemcpy(&dVal, data, 8);
#elif __BYTE_ORDER == __LITTLE_ENDIANunsigned 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
#endifreturn dVal;
}
3.2 AMF_BOOLEAN (AMF_DecodeBoolean)
查看data是否为0
int
AMF_DecodeBoolean(const char* data)
{return *data != 0;
}
3.3 AMF_STRING 和 AMF_LONG_STRING (AMF_DecodeString 和 AMF_DecodeLongString)
对于AMF_STRING类型而言,先解析16位的length,随后解析val
void
AMF_DecodeString(const char* data, AVal * bv)
{bv->av_len = AMF_DecodeInt16(data);bv->av_val = (bv->av_len > 0) ? (char*)data + 2 : NULL;
}
对于AMF_LONG_STRING类型而言,先解析32位的length,随后解析val
void
AMF_DecodeLongString(const char* data, AVal * bv)
{bv->av_len = AMF_DecodeInt32(data);bv->av_val = (bv->av_len > 0) ? (char*)data + 4 : NULL;
}
3.3.1 AMF_DecodeInt16
unsigned short
AMF_DecodeInt16(const char* data)
{unsigned char* c = (unsigned char*)data;unsigned short val;val = (c[0] << 8) | c[1]; // c[0] << 8 是高8位return val;
}
3.3.2 AMF_DecodeInt32
unsigned int
AMF_DecodeInt32(const char* data)
{unsigned char* c = (unsigned char*)data;unsigned int val;/*c[0] << 24 表示第1字节c[1] << 16 表示第2字节c[2] << 8 表示第3字节c[3] 表示第4字节*/val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];return val;
}
3.4 AMF_OBJECT (AMF_Decode)
在解析object过程中,先检查字段是否包括AMF_OBJECT_END,随后使用AMFProp_Decoe()来解析属性值 “prop”,最后使用AMF_AddProp()将属性值添加到传入进来的obj中
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;// 先解析字段是否包括 AMF_OBJECT_ENDif (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;}// 解码propnRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);if (nRes == -1){bError = TRUE;break;}else{nSize -= nRes;if (nSize < 0){bError = TRUE;break;}pBuffer += nRes;// 将解析出来的prop添加到obj中AMF_AddProp(obj, &prop);}}if (bError)return -1;return nOriginalSize - nSize;
}
3.4.1 AMF_DecodeInt24
unsigned int
AMF_DecodeInt24(const char* data)
{unsigned char* c = (unsigned char*)data;unsigned int val;/*c[0] << 16 表示第1字节c[1] << 8 表示第2字节c[2] 表示第3字节*/val = (c[0] << 16) | (c[1] << 8) | c[2];return val;
}
3.4.2 AMFProp_Decode
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 (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) // 解析prop名称{unsigned short nNameSize = AMF_DecodeInt16(pBuffer); // 解析prop名称长度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); // 获取prop的具体名称nSize -= 2 + nNameSize;pBuffer += 2 + nNameSize;}if (nSize == 0){return -1;}nSize--;// 根据不同的type来确定如何进行AMF解码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;break;}case AMF_OBJECT_END:{return -1;break;}case AMF_STRICT_ARRAY:{// 解析32位的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;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:{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;
}
3.5 AMF_STRICT_ARRAY (AMF_DecodeArray)
调用了和AMF_Decode()函数类似的步骤
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--;if (nSize <= 0){bError = TRUE;break;}nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);if (nRes == -1){bError = TRUE;break;}else{nSize -= nRes;pBuffer += nRes;AMF_AddProp(obj, &prop);}}if (bError)return -1;return nOriginalSize - nSize;
}
这篇关于【流媒体】RTMPDump—AMF编码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!