RTMP学习(九)rtmpdump源码阅读(4)建立连接

建立连接



    建立连接的步骤如下:

    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内部的握手操作和连接操作


    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内部的握手操作


    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内部的网络连接操作



发送“connect命令”


    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内部网络连接的其他步骤的函数不是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),暂时不清楚该函数调用的目的

    对比网络连接建立的时序图和理论上的操作流程,可以看到基本上是差不多的


RTMP学习(九)rtmpdump源码阅读(4)建立连接_第1张图片


处理服务器发来的“确认窗口大小”

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);
}

处理服务器发来的“stream begin”

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”

    处理“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内部的网络连接就算完成了!



你可能感兴趣的:(流媒体协议RTMP)