LibRTMP源代码分析6

前面介绍了建立网络连接(NetConnection)和建立网络流(NetStream),这些命令数据是怎么发送出去的呢?在底层是怎么实现的呢?接下来就要详细分析各种消息的发送过程。 首先大致列举一下消息命令:

发送connect命令          : SendConnectPacket()
发送createStream命令  : RTMP_SendCreateStream()
发送releaseStream命令 : SendReleaseStream()
发送FCSubscribe命令   : SendFCSubscribe()
发送FCPublish命令       : SendFCPublish()
发送FCUnpublish命令   : SendFCUnpublish()
发送Publish命令           : SendPublish()
发送deleteStream命令  : SendDeleteStream()
发送pause命令             : RTMP_SendPause()
发送seek命令               :  RTMP_SendSeek()
发送checkbw命令         :SendCheckBW()
发送result命令              : SendCheckBWResult()
发送pong命令               : SendPong()
发送play命令                : SendPlay()
发送playlist命令            : SendPlaylist()
发送用户控制消息         : RTMP_SendCtrl()
发送ServerBW命令(致谢窗口大小) :  RTMP_SendServerBW()
发送ClientBW命令(对等端带宽)     : RTMP_SendClientBW()
发送致谢命令(接收到的字节数)      : SendBytesReceived()
发送secureTokenResponse命令        : SendSecureTokenResponse()
发送NetStream_Authenticate_UsherToken命令 : SendUsherToken()

以上函数命名有两种规律:RTMP_Send****()或者Send****(),其中****号代表命令的名称。 以上发送命令代码比较相似,其中SendConnectPacket()函数的代码分析已经在前面第5篇文章中已经给出,其他函数也比较相似,就不一一分析,只给出部分函数的代码分析。

/**
 * @brief 发送createStream命令.
 *  命令消息由命令名,传输ID,和命令对象组成.
 *  命令对象由一系列的相关参数组成.
 *  可参考rtmp协议:rtmp命令消息--4.1.3节
 */
int RTMP_SendCreateStream(RTMP *r);

/**
 * @brief 发送releaseStream命令
 *  命令消息由命令名,传输ID,和命令对象组成.
 *  命令对象由一系列的相关参数组成.
 */
static int SendReleaseStream(RTMP *r);

/**
 * @brief 发送FCPublish命令
 *  命令消息由命令名,传输ID,和命令对象组成.
 *  命令对象由一系列的相关参数组成.
 */
static int SendFCPublish(RTMP *r);

/**
 * @brief 发送FCUnpublish命令
 *  命令消息由命令名,传输ID,和命令对象组成.
 *  命令对象由一系列的相关参数组成.
 */
static int SendFCUnpublish(RTMP *r);

/**
 * @brief 发送Publish命令
 *  命令消息由命令名,传输ID,和命令对象组成.
 *  命令对象由一系列的相关参数组成.
 *  可参考rtmp协议:rtmp命令消息--4.2.6节
 */
static int SendPublish(RTMP *r)
{
RTMPPacket packet;
char pbuf[1024], *pend = pbuf + sizeof(pbuf);
char *enc;

// 块流ID为4
packet.m_nChannel = 0x04; /* source channel (invoke) */
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; // 命令消息,类型为20
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = r->m_stream_id; // msg stream id
packet.m_hasAbsTimestamp = 0;
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; // 指向Chunk的负载

enc = packet.m_body;
enc = AMF_EncodeString(enc, pend, &av_publish); // 对“publish”字符串进行AMF编码 
enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); // 传输ID
*enc++ = AMF_NULL;
enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
if (!enc)
return FALSE;

/* FIXME: should we choose live based on Link.lFlags & RTMP_LF_LIVE? */
enc = AMF_EncodeString(enc, pend, &av_live); // 编码发布类型,live表示发布直播数据而不录制到文件
if (!enc)
return FALSE;

packet.m_nBodySize = enc - packet.m_body;
return RTMP_SendPacket(r, &packet, TRUE);
}

/**
 * @brief 发送deleteStream命令,其中dStreamId表示要删除的流id.
 *  命令消息由命令名,传输ID,和命令对象组成.
 *  命令对象由一系列的相关参数组成.
 *  可参考rtmp协议:rtmp命令消息--4.2.3节
 */
static int SendDeleteStream(RTMP *r, double dStreamId);

/**
 * @brief 发送pause命令
 *    可参考rtmp协议:rtmp命令消息--4.2.8节
 *
 * @param DoPause : 是否暂停,boolean值
 * @param iTime   : 流暂停或恢复播放的毫秒数
 */
int RTMP_SendPause(RTMP *r, int DoPause, int iTime);

/**
 * @brief 发送seek命令
 *    可参考rtmp协议:rtmp命令消息--4.2.7节
 */
int RTMP_SendSeek(RTMP *r, int iTime);

/**
 * @brief 发送ServerBW命令(致谢窗口大小)
 *    可参考rtmp协议:rtmp消息格式 -- 5.5节
 */
int RTMP_SendServerBW(RTMP *r)
{
RTMPPacket packet;
char pbuf[256], *pend = pbuf + sizeof(pbuf);

// 协议控制消息必须有消息流ID 0和块流ID 2,并且有最高的发送优先级
packet.m_nChannel = 0x02; /* control channel (invoke),块流ID */
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
packet.m_packetType = RTMP_PACKET_TYPE_SERVER_BW; // 协议控制消息类型5
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = 0;
packet.m_hasAbsTimestamp = 0;
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
packet.m_nBodySize = 4; // 致谢窗口大小(4字节)

AMF_EncodeInt32(packet.m_body, pend, r->m_nServerBW);
return RTMP_SendPacket(r, &packet, FALSE);
}

/**
 * @brief 发送ClientBW命令(对等端带宽)
 *    可参考rtmp协议:rtmp消息格式 -- 5.6节
 */
int RTMP_SendClientBW(RTMP *r)
{
RTMPPacket packet;
char pbuf[256], *pend = pbuf + sizeof(pbuf);

// 协议控制消息必须有消息流ID 0和块流ID 2,并且有最高的发送优先级
packet.m_nChannel = 0x02; /* control channel (invoke),块流ID */
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
packet.m_packetType = RTMP_PACKET_TYPE_CLIENT_BW; // 协议控制消息类型6
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = 0;
packet.m_hasAbsTimestamp = 0;
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
packet.m_nBodySize = 5; // 致谢窗口大小(4字节) + 限制类型(1字节)

AMF_EncodeInt32(packet.m_body, pend, r->m_nClientBW);
packet.m_body[4] = r->m_nClientBW2; // 限制类型:硬(0)、软(1)、或者动态(2)
return RTMP_SendPacket(r, &packet, FALSE);
}

/**
 * @brief 发送致谢命令(接收到的字节数)
 *   可参考rtmp协议:rtmp消息格式 -- 5.3节
 */
static int SendBytesReceived(RTMP *r)
{
RTMPPacket packet;
char pbuf[256], *pend = pbuf + sizeof(pbuf);

// 协议控制消息必须有消息流ID 0和块流ID 2,并且有最高的发送优先级
packet.m_nChannel = 0x02; /* control channel (invoke),块流ID */
packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
packet.m_packetType = RTMP_PACKET_TYPE_BYTES_READ_REPORT; // 协议控制消息类型3
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = 0;
packet.m_hasAbsTimestamp = 0;
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
packet.m_nBodySize = 4; // 序列号(4字节),是到当前时间为止已经接收到的字节数

AMF_EncodeInt32(packet.m_body, pend, r->m_nBytesIn); /* hard coded for now */
r->m_nBytesInSent = r->m_nBytesIn;
return RTMP_SendPacket(r, &packet, FALSE);
}

/**
 * @brief 发送checkbw命令
 *  命令消息由命令名,传输ID,和命令对象组成.
 *  命令对象由一系列的相关参数组成.
 */
static int SendCheckBW(RTMP *r);

/**
 * @brief 发送result命令
 */
static int SendCheckBWResult(RTMP *r, double txn);

/**
 * @brief 发送pong命令
 */
static int SendPong(RTMP *r, double txn);

/**
 * @brief 发送play命令
 *  可参考rtmp协议:rtmp命令消息--4.2.1节
 */
static int SendPlay(RTMP *r);

/**
 * @brief 发送playlist命令
 */
static int SendPlaylist(RTMP *r);

/**
 * @brief 发送secureTokenResponse命令
 */
static int SendSecureTokenResponse(RTMP *r, AVal *resp);

/**
 * @brief 发送用户控制消息命令
 */
int RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime);

以上函数的总体思路是声明一个RTMPPacket类型的结构体,然后设置各种属性值,最后交给RTMP_SendPacket()进行发送。在下一节中我们具体来分析该函数的实现。

猜你喜欢

转载自blog.csdn.net/wongainia158158/article/details/48626907