建立连接的步骤如下:
1、设置套接字的地址信息 2、调用RTMP_Connect0,和服务器建立套接字连接,套接字的连接是所有今后通信的基础,这里就是tcp的三次握手 3、调用RTMP_Connect1,进行rtmp内部的握手操作和网络连接操作。
建立连接的入口函数:
/*** 1、设置套接字的地址信息** 2、调用RTMP_Connect0,和服务器建立套接字连接,套接字的连接是所有今后通信的基础,这里就是tcp的三次握手** 3、调用RTMP_Connect1,进行rtmp内部的握手操作*/intRTMP_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算法,禁用之后可以达到更好的实时性。
/*** 建立套接字的链接** 套接字的连接是所有通信的基础*/intRTMP_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 intSocksNegotiate(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内部的握手操作和连接操作
RTMP_Connect1函数执行rtmp协议内部的握手和连接操作!
/*** RTMP_Connect1间接调用HandShake,进行rtmp内部的握手操作*/intRTMP_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内部的握手操作
RTMP_Connect1间接调用HandShake,进行rtmp内部的握手操作
客户端的握手操作如下: 1、发送C0和C1 2、接收S0 3、接收S1 4、发送C2 5、接收S2
/*** 客户端的握手操作*/static intHandShake(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内部的网络连接操作
发送“connect命令”
rtmp内部的网络连接操作是通过SendConnectPacket函数执行的!但是请注意,这个函数里只发送了“connect”命令,剩余的连接步骤在RTMP_ConnectStream中进行。先分析SendConnectPacket:
// 注意rtmp内部建立网络连接的时候,参数cp是nullstatic intSendConnectPacket(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内部网络连接的其他步骤的函数不是RTMP_Connect,而是RTMP_ConnectStream。
RTMP_ConnectStream函数的功能是在播放开始之前不断的读取数据包,然后分析数据包的内容,如果有必要的的话就进行解析和处理。
其中RTMP_ConnectStream通过调用RTMP_ClientPacket函数来分析处理服务器发送来的数据包。所以先分析一下这个比较核心的处理函数:
/*** 解析服务器发送来的packet*/intRTMP_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 voidHandleServerBW(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 voidHandleClientBW(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);}处理服务器发来的“stream begin”
static voidHandleCtrl(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”
处理“result”的时候,判断这是不是“connect”命令的结果,如果是,那么:
1、调用RTMP_SendServerBW函数,给服务器发送“确认窗口大小”
2、调用RTMP_SendCtrl(r, 3, 0, 300),暂时不清楚该函数调用的目的
/*** 处理命令消息** 这些命令消息是使用AMF0格式进行编码的*/static intHandleInvoke(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内部的网络连接就算完成了!
新闻热点
疑难解答