建立连接的步骤如下:
1、设置套接字的地址信息
2、调用RTMP_Connect0,和服务器建立套接字连接,套接字的连接是所有今后通信的基础,这里就是tcp的三次握手
3、调用RTMP_Connect1,进行rtmp内部的握手操作和网络连接操作。
建立连接的入口函数:
/*
** 1、设置套接字的地址信息
** 2、调用RTMP_Connect0,和服务器建立套接字连接,套接字的连接是所有今后通信的基础,这里就是tcp的三次握手
** 3、调用RTMP_Connect1,进行rtmp内部的握手操作
*/
int
RTMP_Connect(RTMP *r, RTMPPacket *cp)
{
struct sockaddr_in service;
if (!r->Link.hostname.av_len)
return FALSE;
memset(&service, 0, sizeof(struct sockaddr_in));
service.sin_family = AF_INET;
// 设置套接字地址
if (r->Link.socksport)
{
/* Connect via SOCKS */
if (!add_addr_info(&service, &r->Link.sockshost, r->Link.socksport))
return FALSE;
}
else
{
/* Connect directly */
if (!add_addr_info(&service, &r->Link.hostname, r->Link.port))
return FALSE;
}
// 建立套接字连接
if (!RTMP_Connect0(r, (struct sockaddr *)&service))
return FALSE;
r->m_bSendCounter = TRUE;
// 进行rtmp内部的握手过程
return RTMP_Connect1(r, cp);
}
建立套接字连接的步骤:
1、创建tcp类型的套接字
2、调用connect连接到服务器
3、如果有必要就调用SocksNegotiate,它用来判断客户端与服务器之间能否收发数据
4、设置tcp的TCP_NODELAY选项,即禁用Nagle算法,禁用之后可以达到更好的实时性。
/*
** 建立套接字的链接
** 套接字的连接是所有通信的基础
*/
int
RTMP_Connect0(RTMP *r, struct sockaddr * service)
{
int on = 1;
r->m_sb.sb_timedout = FALSE;
r->m_pausing = 0;
r->m_fDuration = 0.0;
r->m_sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (r->m_sb.sb_socket != -1)
{
if (connect(r->m_sb.sb_socket, service, sizeof(struct sockaddr)) < 0)
{
int err = GetSockError();
RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket. %d (%s)",
__FUNCTION__, err, strerror(err));
RTMP_Close(r);
return FALSE;
}
if (r->Link.socksport)
{
RTMP_Log(RTMP_LOGDEBUG, "%s ... SOCKS negotiation", __FUNCTION__);
if (!SocksNegotiate(r))
{
RTMP_Log(RTMP_LOGERROR, "%s, SOCKS negotiation failed.", __FUNCTION__);
RTMP_Close(r);
return FALSE;
}
}
}
else
{
RTMP_Log(RTMP_LOGERROR, "%s, failed to create socket. Error: %d", __FUNCTION__,
GetSockError());
return FALSE;
}
/* set timeout */
{
SET_RCVTIMEO(tv, r->Link.timeout);
if (setsockopt
(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)))
{
RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!",
__FUNCTION__, r->Link.timeout);
}
}
setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on));
return TRUE;
}
static int
SocksNegotiate(RTMP *r)
{
unsigned long addr;
struct sockaddr_in service;
memset(&service, 0, sizeof(struct sockaddr_in));
add_addr_info(&service, &r->Link.hostname, r->Link.port);
addr = htonl(service.sin_addr.s_addr);
{
// 构造数据包
char packet[] = {
4, 1, /* SOCKS 4, connect */
(r->Link.port >> 8) & 0xFF,
(r->Link.port) & 0xFF,
(char)(addr >> 24) & 0xFF, (char)(addr >> 16) & 0xFF,
(char)(addr >> 8) & 0xFF, (char)addr & 0xFF,
0
}; /* NULL terminate */
// 发送数据
WriteN(r, packet, sizeof packet);
// 接收回应
if (ReadN(r, packet, 8) != 8)
return FALSE;
if (packet[0] == 0 && packet[1] == 90)
{
return TRUE;
}
else
{
RTMP_Log(RTMP_LOGERROR, "%s, SOCKS returned error code %d", __FUNCTION__, packet[1]);
return FALSE;
}
}
}
RTMP_Connect1函数执行rtmp协议内部的握手和连接操作!
/*
** RTMP_Connect1间接调用HandShake,进行rtmp内部的握手操作
*/
int
RTMP_Connect1(RTMP *r, RTMPPacket *cp)
{
if (r->Link.protocol & RTMP_FEATURE_SSL)
{
#if defined(CRYPTO) && !defined(NO_SSL)
TLS_client(RTMP_TLS_ctx, r->m_sb.sb_ssl);
TLS_setfd(r->m_sb.sb_ssl, r->m_sb.sb_socket);
if (TLS_connect(r->m_sb.sb_ssl) < 0)
{
RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
RTMP_Close(r);
return FALSE;
}
#else
RTMP_Log(RTMP_LOGERROR, "%s, no SSL/TLS support", __FUNCTION__);
RTMP_Close(r);
return FALSE;
#endif
}
if (r->Link.protocol & RTMP_FEATURE_HTTP)
{
r->m_msgCounter = 1;
r->m_clientID.av_val = NULL;
r->m_clientID.av_len = 0;
HTTP_Post(r, RTMPT_OPEN, "", 1);
if (HTTP_read(r, 1) != 0)
{
r->m_msgCounter = 0;
RTMP_Log(RTMP_LOGDEBUG, "%s, Could not connect for handshake", __FUNCTION__);
RTMP_Close(r);
return 0;
}
r->m_msgCounter = 0;
}
RTMP_Log(RTMP_LOGDEBUG, "%s, ... connected, handshaking", __FUNCTION__);
if (!HandShake(r, TRUE))
{
RTMP_Log(RTMP_LOGERROR, "%s, handshake failed.", __FUNCTION__);
RTMP_Close(r);
return FALSE;
}
RTMP_Log(RTMP_LOGDEBUG, "%s, handshaked", __FUNCTION__);
// 建立网络连接操作,注意cp是null!
if (!SendConnectPacket(r, cp))
{
RTMP_Log(RTMP_LOGERROR, "%s, RTMP connect failed.", __FUNCTION__);
RTMP_Close(r);
return FALSE;
}
return TRUE;
}
RTMP_Connect1间接调用HandShake,进行rtmp内部的握手操作
客户端的握手操作如下:
1、发送C0和C1
2、接收S0
3、接收S1
4、发送C2
5、接收S2
/*
** 客户端的握手操作
*/
static int
HandShake(RTMP *r, int FP9HandShake)
{
int i;
uint32_t uptime, suptime;
int bMatch;
char type;
// 将要发送给服务器的数据块,简称C
char clientbuf[RTMP_SIG_SIZE + 1], *clientsig = clientbuf + 1;
// 从服务器接收到的数据块,简称S
char serversig[RTMP_SIG_SIZE];
// 第一个字节填充0x03,表示开始标识
clientbuf[0] = 0x03; /* not encrypted */
// 取得当前时间
uptime = htonl(RTMP_GetTime());
// 把时间填入数据块C中
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
// 把数据块C发送给服务器,发送C0和C1
if (!WriteN(r, clientbuf, RTMP_SIG_SIZE + 1))
return FALSE;
// 接收S0
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);
// 接收S1
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 */
// 发送C2
if (!WriteN(r, serversig, RTMP_SIG_SIZE))
return FALSE;
// 接收S2
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;
}
rtmp内部的网络连接操作是通过SendConnectPacket函数执行的!但是请注意,这个函数里只发送了“connect”命令,剩余的连接步骤在RTMP_ConnectStream中进行。先分析SendConnectPacket:
// 注意rtmp内部建立网络连接的时候,参数cp是null
static int
SendConnectPacket(RTMP *r, RTMPPacket *cp)
{
RTMPPacket packet;
char pbuf[4096], *pend = pbuf + sizeof(pbuf);
char *enc;
if (r->Link.CombineConnectPacket)
r->Link.ConnectPacket = TRUE;
if (cp)
return RTMP_SendPacket(r, cp, TRUE);
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;
// 构建连接命令
enc = packet.m_body;
enc = AMF_EncodeString(enc, pend, &av_connect); // connect命令
enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
*enc++ = AMF_OBJECT;
enc = AMF_EncodeNamedString(enc, pend, &av_app, &r->Link.app);
if (!enc)
return FALSE;
if (r->Link.protocol & RTMP_FEATURE_WRITE)
{
enc = AMF_EncodeNamedString(enc, pend, &av_type, &av_nonprivate);
if (!enc)
return FALSE;
}
if (r->Link.flashVer.av_len)
{
enc = AMF_EncodeNamedString(enc, pend, &av_flashVer, &r->Link.flashVer);
if (!enc)
return FALSE;
}
if (r->Link.swfUrl.av_len)
{
enc = AMF_EncodeNamedString(enc, pend, &av_swfUrl, &r->Link.swfUrl);
if (!enc)
return FALSE;
}
if (r->Link.tcUrl.av_len)
{
enc = AMF_EncodeNamedString(enc, pend, &av_tcUrl, &r->Link.tcUrl);
if (!enc)
return FALSE;
}
if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
{
enc = AMF_EncodeNamedBoolean(enc, pend, &av_fpad, FALSE);
if (!enc)
return FALSE;
enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 15.0);
if (!enc)
return FALSE;
enc = AMF_EncodeNamedNumber(enc, pend, &av_audioCodecs, r->m_fAudioCodecs);
if (!enc)
return FALSE;
enc = AMF_EncodeNamedNumber(enc, pend, &av_videoCodecs, r->m_fVideoCodecs);
if (!enc)
return FALSE;
enc = AMF_EncodeNamedNumber(enc, pend, &av_videoFunction, 1.0);
if (!enc)
return FALSE;
if (r->Link.pageUrl.av_len)
{
enc = AMF_EncodeNamedString(enc, pend, &av_pageUrl, &r->Link.pageUrl);
if (!enc)
return FALSE;
}
}
if (r->m_fEncoding != 0.0 || r->m_bSendEncoding)
{ /* AMF0, AMF3 not fully supported yet */
enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding);
if (!enc)
return FALSE;
}
if (enc + 3 >= pend)
return FALSE;
*enc++ = 0;
*enc++ = 0; /* end of object - 0x00 0x00 0x09 */
*enc++ = AMF_OBJECT_END;
/* add auth string */
if (r->Link.auth.av_len)
{
enc = AMF_EncodeBoolean(enc, pend, r->Link.lFlags & RTMP_LF_AUTH);
if (!enc)
return FALSE;
enc = AMF_EncodeString(enc, pend, &r->Link.auth);
if (!enc)
return FALSE;
}
if (r->Link.extras.o_num)
{
int i;
for (i = 0; i < r->Link.extras.o_num; i++)
{
enc = AMFProp_Encode(&r->Link.extras.o_props[i], enc, pend);
if (!enc)
return FALSE;
}
}
packet.m_nBodySize = enc - packet.m_body;
// 发送数据包
return RTMP_SendPacket(r, &packet, TRUE);
}
首先要知道,处理rtmp内部网络连接的其他步骤的函数不是RTMP_Connect,而是RTMP_ConnectStream。
RTMP_ConnectStream函数的功能是在播放开始之前不断的读取数据包,然后分析数据包的内容,如果有必要的的话就进行解析和处理。
其中RTMP_ConnectStream通过调用RTMP_ClientPacket函数来分析处理服务器发送来的数据包。所以先分析一下这个比较核心的处理函数:
/*
** 解析服务器发送来的packet
*/
int
RTMP_ClientPacket(RTMP *r, RTMPPacket *packet)
{
int bHasMediaPacket = 0;
switch (packet->m_packetType)
{
// 设置块大小
case RTMP_PACKET_TYPE_CHUNK_SIZE:
/* chunk size */
HandleChangeChunkSize(r, packet);
break;
// 报告
case RTMP_PACKET_TYPE_BYTES_READ_REPORT:
/* bytes read report */
RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__);
break;
// 用户的控制命令
case RTMP_PACKET_TYPE_CONTROL:
/* ctrl */
HandleCtrl(r, packet);
break;
// 确认窗口大小 Window Acknowledgement Size
case RTMP_PACKET_TYPE_SERVER_BW:
/* server bw */
HandleServerBW(r, packet);
break;
// 设置带宽
case RTMP_PACKET_TYPE_CLIENT_BW:
/* client bw */
HandleClientBW(r, packet);
break;
// 音频数据
case RTMP_PACKET_TYPE_AUDIO:
/* audio data */
/*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */
HandleAudio(r, packet);
bHasMediaPacket = 1;
if (!r->m_mediaChannel)
r->m_mediaChannel = packet->m_nChannel;
if (!r->m_pausing)
r->m_mediaStamp = packet->m_nTimeStamp;
break;
// 视频数据
case RTMP_PACKET_TYPE_VIDEO:
/* video data */
/*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */
HandleVideo(r, packet);
bHasMediaPacket = 1;
if (!r->m_mediaChannel)
r->m_mediaChannel = packet->m_nChannel;
if (!r->m_pausing)
r->m_mediaStamp = packet->m_nTimeStamp;
break;
// flex 流发送(AMF3编码)
case RTMP_PACKET_TYPE_FLEX_STREAM_SEND:
/* flex stream send */
RTMP_Log(RTMP_LOGDEBUG,
"%s, flex stream send, size %u bytes, not supported, ignoring",
__FUNCTION__, packet->m_nBodySize);
break;
// flex共享对象(AMF3编码)
case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT:
/* flex shared object */
RTMP_Log(RTMP_LOGDEBUG,
"%s, flex shared object, size %u bytes, not supported, ignoring",
__FUNCTION__, packet->m_nBodySize);
break;
// flex消息(AMF3编码)
case RTMP_PACKET_TYPE_FLEX_MESSAGE:
/* flex message */
{
RTMP_Log(RTMP_LOGDEBUG,
"%s, flex message, size %u bytes, not fully supported",
__FUNCTION__, packet->m_nBodySize);
/*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
/* some DEBUG code */
#if 0
RTMP_LIB_AMFObject obj;
int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
if(nRes < 0) {
RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
/*return; */
}
obj.Dump();
#endif
if (HandleInvoke(r, packet->m_body + 1, packet->m_nBodySize - 1) == 1)
bHasMediaPacket = 2;
break;
}
// info数据(AMF0编码)
case RTMP_PACKET_TYPE_INFO:
/* metadata (notify) */
RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %u bytes", __FUNCTION__,
packet->m_nBodySize);
// 处理元数据
if (HandleMetadata(r, packet->m_body, packet->m_nBodySize))
bHasMediaPacket = 1;
break;
// 共享对象
case RTMP_PACKET_TYPE_SHARED_OBJECT:
RTMP_Log(RTMP_LOGDEBUG, "%s, shared object, not supported, ignoring",
__FUNCTION__);
break;
// 命令消息
case RTMP_PACKET_TYPE_INVOKE:
/* invoke */
RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,
packet->m_nBodySize);
/*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
// 处理命令
if (HandleInvoke(r, packet->m_body, packet->m_nBodySize) == 1)
bHasMediaPacket = 2;
break;
// Flash视频
case RTMP_PACKET_TYPE_FLASH_VIDEO:
{
/* go through FLV packets and handle metadata packets */
unsigned int pos = 0;
uint32_t nTimeStamp = packet->m_nTimeStamp;
while (pos + 11 < packet->m_nBodySize)
{
uint32_t dataSize = AMF_DecodeInt24(packet->m_body + pos + 1); /* size without header (11) and prevTagSize (4) */
if (pos + 11 + dataSize + 4 > packet->m_nBodySize)
{
RTMP_Log(RTMP_LOGWARNING, "Stream corrupt?!");
break;
}
if (packet->m_body[pos] == 0x12)
{
HandleMetadata(r, packet->m_body + pos + 11, dataSize);
}
else if (packet->m_body[pos] == 8 || packet->m_body[pos] == 9)
{
nTimeStamp = AMF_DecodeInt24(packet->m_body + pos + 4);
nTimeStamp |= (packet->m_body[pos + 7] << 24);
}
pos += (11 + dataSize + 4);
}
if (!r->m_pausing)
r->m_mediaStamp = nTimeStamp;
/* FLV tag(s) */
/*RTMP_Log(RTMP_LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize); */
bHasMediaPacket = 1;
break;
}
default:
RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,
packet->m_packetType);
#ifdef _DEBUG
RTMP_LogHex(RTMP_LOGDEBUG, packet->m_body, packet->m_nBodySize);
#endif
}
return bHasMediaPacket;
}
为了弄懂这些操作流程,最好手动调试,经过调试之后可以发现libRtmp处理网络连接的步骤(除去发送“connect”命令)如下:
1、HandleServerBW函数被调用,处理服务器发送过来的“确认窗口大小”2、HandleClientBW函数被调用,处理服务器发送过来的“设置带宽”
3、HandleCtrl函数被调用,处理服务器发来的“stream begin”
4、HandleInvoke函数被调用,处理服务器发来的“result结果”
5、调用RTMP_SendServerBW函数,发送“确认窗口大小”
6、调用RTMP_SendCtrl(r, 3, 0, 300),暂时不清楚该函数调用的目的
对比网络连接建立的时序图和理论上的操作流程,可以看到基本上是差不多的
static void
HandleServerBW(RTMP *r, const RTMPPacket *packet)
{
r->m_nServerBW = AMF_DecodeInt32(packet->m_body);
RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, r->m_nServerBW);
}
static void
HandleClientBW(RTMP *r, const RTMPPacket *packet)
{
r->m_nClientBW = AMF_DecodeInt32(packet->m_body);
if (packet->m_nBodySize > 4)
r->m_nClientBW2 = packet->m_body[4];
else
r->m_nClientBW2 = -1;
RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, r->m_nClientBW,
r->m_nClientBW2);
}
static void
HandleCtrl(RTMP *r, const RTMPPacket *packet)
{
short nType = -1;
unsigned int tmp;
if (packet->m_body && packet->m_nBodySize >= 2)
nType = AMF_DecodeInt16(packet->m_body);
RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl, type: %d, len: %d", __FUNCTION__, nType,
packet->m_nBodySize);
/*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
if (packet->m_nBodySize >= 6)
{
switch (nType)
{
case 0:
// 处理”stream begin“
tmp = AMF_DecodeInt32(packet->m_body + 2);
RTMP_Log(RTMP_LOGDEBUG, "%s, Stream Begin %d", __FUNCTION__, tmp);
break;
// 其他代码省略***
}
}
// 其他代码省略***
}
处理“result”的时候,判断这是不是“connect”命令的结果,如果是,那么:
1、调用RTMP_SendServerBW函数,给服务器发送“确认窗口大小”
2、调用RTMP_SendCtrl(r, 3, 0, 300),暂时不清楚该函数调用的目的
/*
** 处理命令消息
** 这些命令消息是使用AMF0格式进行编码的
*/
static int
HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
{
AMFObject obj;
AVal method;
double txn;
int ret = 0, nRes;
char pbuf[256], *pend = pbuf + sizeof(pbuf), *enc, **params = NULL;
char *host = r->Link.hostname.av_len ? r->Link.hostname.av_val : "";
char *pageUrl = r->Link.pageUrl.av_len ? r->Link.pageUrl.av_val : "";
int param_count;
AVal av_Command, av_Response;
if (body[0] != 0x02) /* make sure it is a string method name we start with */
{
RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
__FUNCTION__);
return 0;
}
nRes = AMF_Decode(&obj, body, nBodySize, FALSE);
if (nRes < 0)
{
RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);
return 0;
}
AMF_Dump(&obj);
AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);
txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val);
// 处理”result“
if (AVMATCH(&method, &av__result))
{
AVal methodInvoked = { 0 };
int i;
for (i = 0; i < r->m_numCalls; i++) {
if (r->m_methodCalls[i].num == (int)txn) {
methodInvoked = r->m_methodCalls[i].name;
AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE);
break;
}
}
if (!methodInvoked.av_val) {
RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request",
__FUNCTION__, txn);
goto leave;
}
RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__,
methodInvoked.av_val);
// ”connect“命令完成
if (AVMATCH(&methodInvoked, &av_connect))
{
if (r->Link.token.av_len)
{
AMFObjectProperty p;
if (RTMP_FindFirstMatchingProperty(&obj, &av_secureToken, &p))
{
DecodeTEA(&r->Link.token, &p.p_vu.p_aval);
SendSecureTokenResponse(r, &p.p_vu.p_aval);
}
}
if (r->Link.protocol & RTMP_FEATURE_WRITE)
{
SendReleaseStream(r);
SendFCPublish(r);
}
else
{
// 向服务器发送”确认窗口大小“
RTMP_SendServerBW(r);
RTMP_SendCtrl(r, 3, 0, 300);
}
if (strstr(host, "tv-stream.to") || strstr(pageUrl, "tv-stream.to"))
{
AVal av_requestAccess = AVC("requestAccess");
AVal av_auth = AVC("h§4jhH43d");
enc = pbuf;
enc = AMF_EncodeString(enc, pend, &av_requestAccess);
enc = AMF_EncodeNumber(enc, pend, 0);
*enc++ = AMF_NULL;
enc = AMF_EncodeString(enc, pend, &av_auth);
av_Command.av_val = pbuf;
av_Command.av_len = enc - pbuf;
SendCustomCommand(r, &av_Command, FALSE);
AVal av_getConnectionCount = AVC("getConnectionCount");
enc = pbuf;
enc = AMF_EncodeString(enc, pend, &av_getConnectionCount);
enc = AMF_EncodeNumber(enc, pend, 0);
*enc++ = AMF_NULL;
av_Command.av_val = pbuf;
av_Command.av_len = enc - pbuf;
SendCustomCommand(r, &av_Command, FALSE);
SendGetStreamLength(r);
}
else if (strstr(host, "jampo.com.ua") || strstr(pageUrl, "jampo.com.ua"))
{
SendGetStreamLength(r);
}
else if (strstr(host, "streamscene.cc") || strstr(pageUrl, "streamscene.cc")
|| strstr(host, "tsboard.tv") || strstr(pageUrl, "teamstream.in"))
{
AVal av_r = AVC("r");
enc = pbuf;
enc = AMF_EncodeString(enc, pend, &av_r);
enc = AMF_EncodeNumber(enc, pend, 0);
*enc++ = AMF_NULL;
av_Command.av_val = pbuf;
av_Command.av_len = enc - pbuf;
SendCustomCommand(r, &av_Command, FALSE);
SendGetStreamLength(r);
}
else if (strstr(host, "chaturbate.com") || strstr(pageUrl, "chaturbate.com"))
{
AVal av_ModelName;
AVal av_CheckPublicStatus = AVC("CheckPublicStatus");
if (strlen(pageUrl) > 7)
{
strsplit(pageUrl + 7, FALSE, '/', ¶ms);
av_ModelName.av_val = params[1];
av_ModelName.av_len = strlen(params[1]);
enc = pbuf;
enc = AMF_EncodeString(enc, pend, &av_CheckPublicStatus);
enc = AMF_EncodeNumber(enc, pend, 0);
*enc++ = AMF_NULL;
enc = AMF_EncodeString(enc, pend, &av_ModelName);
av_Command.av_val = pbuf;
av_Command.av_len = enc - pbuf;
SendCustomCommand(r, &av_Command, FALSE);
}
}
/* Weeb.tv specific authentication */
else if (r->Link.WeebToken.av_len)
{
AVal av_Token, av_Username, av_Password;
AVal av_determineAccess = AVC("determineAccess");
param_count = strsplit(r->Link.WeebToken.av_val, FALSE, ';', ¶ms);
if (param_count >= 1)
{
av_Token.av_val = params[0];
av_Token.av_len = strlen(params[0]);
}
if (param_count >= 2)
{
av_Username.av_val = params[1];
av_Username.av_len = strlen(params[1]);
}
if (param_count >= 3)
{
av_Password.av_val = params[2];
av_Password.av_len = strlen(params[2]);
}
enc = pbuf;
enc = AMF_EncodeString(enc, pend, &av_determineAccess);
enc = AMF_EncodeNumber(enc, pend, 0);
*enc++ = AMF_NULL;
enc = AMF_EncodeString(enc, pend, &av_Token);
enc = AMF_EncodeString(enc, pend, &av_Username);
enc = AMF_EncodeString(enc, pend, &av_Password);
av_Command.av_val = pbuf;
av_Command.av_len = enc - pbuf;
RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", r->Link.WeebToken.av_val);
SendCustomCommand(r, &av_Command, FALSE);
}
else
RTMP_SendCreateStream(r);
}
else if (AVMATCH(&methodInvoked, &av_getStreamLength))
{
RTMP_SendCreateStream(r);
}
else if (AVMATCH(&methodInvoked, &av_createStream))
{
r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
{
/* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
if (r->Link.usherToken.av_len)
SendUsherToken(r, &r->Link.usherToken);
/* Send the FCSubscribe if live stream or if subscribepath is set */
if (r->Link.subscribepath.av_len)
SendFCSubscribe(r, &r->Link.subscribepath);
else if ((r->Link.lFlags & RTMP_LF_LIVE) && (!r->Link.WeebToken.av_len))
SendFCSubscribe(r, &r->Link.playpath);
}
if (r->Link.protocol & RTMP_FEATURE_WRITE)
{
SendPublish(r);
}
else
{
if (r->Link.lFlags & RTMP_LF_PLST)
SendPlaylist(r);
SendPlay(r);
RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS);
}
}
else if (AVMATCH(&methodInvoked, &av_play) ||
AVMATCH(&methodInvoked, &av_publish))
{
r->m_bPlaying = TRUE;
}
free(methodInvoked.av_val);
}
else if (AVMATCH(&method, &av_onBWDone))
{
if (!r->m_nBWCheckCounter)
SendCheckBW(r);
}
// 删除其他代码***
// ***
// 删除其他代码***
leave:
AMF_Reset(&obj);
return ret;
}
经过上述的一系列步骤,rtmp内部的网络连接就算完成了!