麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

RTMP學習(九)rtmpdump源碼閱讀(4)建立連接

2019-11-06 08:50:56
字體:
來源:轉載
供稿:網友

建立連接

    建立連接的步驟如下:

    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內部的網絡連接就算完成了!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 亚洲成人精品久久 | 香蕉在线播放 | 男男羞羞视频网站国产 | 日本一区二区在线 | 99成人在线 | 91精品国啪老师啪 | 另类亚洲孕妇分娩网址 | 国产精品久久久久久久不卡 | 成码无人av片在线观看网站 | 一本色道久久综合狠狠躁篇适合什么人看 | 日本xxxx视频| 成人在线视频播放 | 欧美精品久久久久久久多人混战 | 欧美成人精品一区 | 久久逼网 | 美女久久久久 | 99re热精品视频 | 福利在线小视频 | 国产在线地址 | 日本成人一区 | 成人做爰s片免费看网站 | 91精品国产乱码久久久久久久久 | 国产成人精品一区二区视频免费 | 99亚洲伊人久久精品影院红桃 | 男女羞羞视频在线免费观看 | 性感美女一级毛片 | 日日噜噜噜噜久久久精品毛片 | 久久人人爽人人爽人人片av高请 | 最近高清无吗免费看 | 丰满年轻岳中文字幕一区二区 | 99国产精品自拍 | 欧美一级做性受免费大片免费 | 黄视频在线网站 | 娇妻被各种姿势c到高潮小说 | 久久亚洲国产精品 | 午色影院| 国产一精品一av一免费爽爽 | a黄在线观看 | 午夜爱爱福利 | 免费人成在线观看网站 | 国产成人精品视频在线 |