前面介绍了建立网络连接(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()进行发送。在下一节中我们具体来分析该函数的实现。