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, // double
AMF_BOOLEAN, // bool
AMF_STRING, // string
AMF_OBJECT, // 对象类型,包括property和property num
AMF_MOVIECLIP, /* reserved, not used */
AMF_NULL,
AMF_UNDEFINED,
AMF_REFERENCE, // not supported
AMF_ECMA_ARRAY, // ECMA
AMF_OBJECT_END,
AMF_STRICT_ARRAY, // STRICT
AMF_DATE, //
AMF_LONG_STRING, // 32-bit string
AMF_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_ENDIAN
memcpy(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
#endif
return 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_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;
}
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_END
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;
}
// 解码prop
nRes = 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;
}