librtmp源码分析

阅读了librtmp的源码,简单记录下。

首先补充下AMF格式基本知识

1 AMF格式

        AMF是Action Message Format(动作消息格式)的简写,它是一种二进制的数据格式。它的设计是为了把actionscript里面的数据(包括Object, Array, Boolean, Number等)序列化成二进制数据,然后把这段数据随意发送给其他接收方程序,比如发给远程的服务器,在远程服务器那边,可以把这段数据给还原出来,以此达到一个数据传输的作用。

1.1 AMFObject

 AMF分成两种: 1. AMF0,基本的数据转换规则; 2. AMF3,是AMF0的扩展

AMF0数据类型:

// AMF0数据类型
typedef enum
{
    AMF_NUMBER = 0,         // 数字(double)
    AMF_BOOLEAN,            // 布尔
    AMF_STRING,             // 字符串
    AMF_OBJECT,             // 对象
    AMF_MOVIECLIP,          // 保留,未使用
    AMF_NULL,               // 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数据类型:

// AMF3数据类型
typedef enum
{
    AMF3_UNDEFINED = 0,     // 未定义
    AMF3_NULL,              // null
    AMF3_FALSE,             // false
    AMF3_TRUE,              // true
    AMF3_INTEGER,           // 数字int
    AMF3_DOUBLE,            // double
    AMF3_STRING,            // 字符串
    AMF3_XML_DOC,           // xml文档
    AMF3_DATE,              // 日期
    AMF3_ARRAY,             // 数组
    AMF3_OBJECT,            // 对象
    AMF3_XML,               // xml
    AMF3_BYTE_ARRAY         // 字节数组
} AMF3DataType;

AMF定义了自己的字符串类型:

  // AMF自定义的字符串
  typedef struct AVal
  {
    char *av_val;
    int av_len;
  } AVal;

// AVal的快速初始化
#define AVC(str)	{str,sizeof(str)-1}
// 比较AVal字符串
#define AVMATCH(a1,a2)	((a1)->av_len == (a2)->av_len && !memcmp((a1)->av_val,(a2)->av_val,(a1)->av_len))

AMFObject表示AMF对象,o_num 代表 o_props的个数, 一个对象内部可以包含N个对象属性

// AMF对象, 就是由一系列的属性构成的
typedef struct AMFObject
{
    int o_num;                          // 属性数目;
    struct AMFObjectProperty *o_props;  // 属性数组;
} AMFObject;

AMFObjectProperty表示AMF对象属性,即key-value键值对。p_name表示key;p_type表示value的类型;p_vu表示value的数值。

// AMF对象的属性;
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;    // UTC偏移;
} AMFObjectProperty;

p_vu设置为联合体的目的:

当p_type为number时, m_vu取值double类型 p_number;

当p_type为string时,    m_vu取值AVal类型 p_aval;

当p_type为object时,   m_vu取值AMFObject类型 p_object。

1.2 编码格式

浮点数:
0x00 + 8字节浮点数

Bool型:
0x01 + 1字节Bool值

短字符串:
0x02 + 2字节长度 + 字符串
长字符串
0x02 + 4字节长度 + 字符串

对象:
0x03 + 属性1名称长度 + 属性1名称 + 1字节属性1类型 + n字节属性1值 + 属性2名称长度 + 属性2名称 + 1字节属性2类型 + n字节属性2值 + 3字节结尾标志

2 librtmp源码分析

2.1 RTMP_ParseURL

解析URL,得到协议名称(protocol),主机名称(host),应用程序名称(app)

2.2 HandShake(握手)

handshake.h文件里面的HandShake有些代码是处理rtmp加密版协议,考虑普通的rtmp协议,分析rtmp.c文件里的HandShake

static int
HandShake(RTMP *r, int FP9HandShake)
{
  int i;
  uint32_t uptime, suptime;
  int bMatch;
  char type;
  char clientbuf[RTMP_SIG_SIZE + 1], *clientsig = clientbuf + 1;
  char serversig[RTMP_SIG_SIZE];

  clientbuf[0] = 0x03;		/* not encrypted */

  uptime = htonl(RTMP_GetTime());
  memcpy(clientsig, &uptime, 4);

  memset(&clientsig[4], 0, 4);

#ifdef _DEBUG
  for (i = 8; i < RTMP_SIG_SIZE; i++)
    clientsig[i] = 0xff;
#else
  for (i = 8; i < RTMP_SIG_SIZE; i++)
    clientsig[i] = (char)(rand() % 256);
#endif

  if (!WriteN(r, clientbuf, RTMP_SIG_SIZE + 1))
    return FALSE;

  if (ReadN(r, &type, 1) != 1)	/* 0x03 or 0x06 */
    return FALSE;

  RTMP_Log(RTMP_LOGDEBUG, "%s: Type Answer   : %02X", __FUNCTION__, type);

  if (type != clientbuf[0])
    RTMP_Log(RTMP_LOGWARNING, "%s: Type mismatch: client sent %d, server answered %d",
	__FUNCTION__, clientbuf[0], type);

  if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
    return FALSE;

  /* decode server response */

  memcpy(&suptime, serversig, 4);
  suptime = ntohl(suptime);

  RTMP_Log(RTMP_LOGDEBUG, "%s: Server Uptime : %d", __FUNCTION__, suptime);
  RTMP_Log(RTMP_LOGDEBUG, "%s: FMS Version   : %d.%d.%d.%d", __FUNCTION__,
      serversig[4], serversig[5], serversig[6], serversig[7]);

  /* 2nd part of handshake */
  if (!WriteN(r, serversig, RTMP_SIG_SIZE))
    return FALSE;

  if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
    return FALSE;

  bMatch = (memcmp(serversig, clientsig, RTMP_SIG_SIZE) == 0);
  if (!bMatch)
    {
      RTMP_Log(RTMP_LOGWARNING, "%s, client signature does not match!", __FUNCTION__);
    }
  return TRUE;
}

1)填充C0=0x3;C1填充时间戳和随机数共1536byte

2)发送C0 C1给服务器

3)从服务器读取S0比对是否为0x3,从服务器读取S1

4)把S1作为C2发送给服务器

5)从服务器读取S2,比对C1和S2,相同则握手成功

2.3 RTMP_Connect

建立NetConnection

主要调用了两个函数,RTMP_Connect0和RTMP_Connect1

RTMP_Connect0

建立Socket连接

RTMP_Connect1

建立RTMP连接,HandShake完成握手,SendConnectPacket发送"connect"命令建立RTMP连接

SendConnectPacket

填充packet头

m_nChannel --> chunk Stream ID

m_headerType --> chunk header中的basic header中的fmt

m_packetType --> Message Type ID,填充的0x14,表示命令消息

m_nTimeStamp --> 时间戳

m_nInfoField2 --> chunk fmt为0时,header的最后四个字节,即Message Stream ID

m_hasAbsTimestamp --> 时间戳是绝对的还是相对的,即chunk type为0时为绝对时间戳,其他类型时为时间戳增量

  packet.m_nChannel = 0x03;	/* control channel (invoke) */
  packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
  packet.m_nTimeStamp = 0;
  packet.m_nInfoField2 = 0;
  packet.m_hasAbsTimestamp = 0;
  packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;

将av_x串化为"x",如:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值