• RTMPdump(libRTMP) 源代码分析 7: 建立一个流媒体连接 (NetStream部分 2)


    注:此前写了一些列的分析RTMPdump(libRTMP)源代码的文章,在此列一个列表:
    RTMPdump 源代码分析 1: main()函数
    RTMPDump(libRTMP)源代码分析 2:解析RTMP地址——RTMP_ParseURL()
    RTMPdump(libRTMP) 源代码分析 3: AMF编码
    RTMPdump(libRTMP)源代码分析 4: 连接第一步——握手(Hand Shake)
    RTMPdump(libRTMP) 源代码分析 5: 建立一个流媒体连接 (NetConnection部分)
    RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)
    RTMPdump(libRTMP) 源代码分析 7: 建立一个流媒体连接 (NetStream部分 2)
    RTMPdump(libRTMP) 源代码分析 8: 发送消息(Message)
    RTMPdump(libRTMP) 源代码分析 9: 接收消息(Message)(接收视音频数据)
    RTMPdump(libRTMP) 源代码分析 10: 处理各种消息(Message)

    ===============================


    书接上回:RTMPdump 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)

    上回说到,有两个函数尤为重要:

    RTMP_ReadPacket()

    RTMP_ClientPacket()

    而且分析了第一个函数。现在我们再来看看第二个函数吧。第二个函数的主要作用是:处理消息(Message),并做出响应。

    先把带注释的代码贴上:

    //处理接收到的Chunk
    int
    RTMP_ClientPacket(RTMP *r, RTMPPacket *packet)
    {
      int bHasMediaPacket = 0;
      switch (packet->m_packetType)
        {
    	//RTMP消息类型ID=1,设置块大小
        case 0x01:
          /* chunk size */
    		//----------------
    		r->dlg->AppendCInfo("处理收到的数据。消息 Set Chunk Size (typeID=1)。");
    		//-----------------------------
    		RTMP_LogPrintf("处理消息 Set Chunk Size (typeID=1)
    ");
          HandleChangeChunkSize(r, packet);
          break;
    	//RTMP消息类型ID=3,致谢
        case 0x03:
          /* bytes read report */
          RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__);
          break;
    	//RTMP消息类型ID=4,用户控制
        case 0x04:
          /* ctrl */
    		//----------------
    		r->dlg->AppendCInfo("处理收到的数据。消息 User Control (typeID=4)。");
    		//-----------------------------
    		RTMP_LogPrintf("处理消息 User Control (typeID=4)
    ");
          HandleCtrl(r, packet);
          break;
    	//RTMP消息类型ID=5
        case 0x05:
          /* server bw */
    		//----------------
    		r->dlg->AppendCInfo("处理收到的数据。消息 Window Acknowledgement Size (typeID=5)。");
    		//-----------------------------
    		RTMP_LogPrintf("处理消息 Window Acknowledgement Size (typeID=5)
    ");
          HandleServerBW(r, packet);
          break;
    	//RTMP消息类型ID=6
        case 0x06:
          /* client bw */
    		//----------------
    		r->dlg->AppendCInfo("处理收到的数据。消息 Set Peer Bandwidth (typeID=6)。");
    		//-----------------------------
    		RTMP_LogPrintf("处理消息 Set Peer Bandwidth (typeID=6)
    ");
          HandleClientBW(r, packet);
          break;
    	//RTMP消息类型ID=8,音频数据
        case 0x08:
          /* 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;
    	//RTMP消息类型ID=9,视频数据
        case 0x09:
          /* 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;
    	//RTMP消息类型ID=15,AMF3编码,忽略
        case 0x0F:			/* flex stream send */
          RTMP_Log(RTMP_LOGDEBUG,
    	  "%s, flex stream send, size %lu bytes, not supported, ignoring",
    	  __FUNCTION__, packet->m_nBodySize);
          break;
    	//RTMP消息类型ID=16,AMF3编码,忽略
        case 0x10:			/* flex shared object */
          RTMP_Log(RTMP_LOGDEBUG,
    	  "%s, flex shared object, size %lu bytes, not supported, ignoring",
    	  __FUNCTION__, packet->m_nBodySize);
          break;
    	//RTMP消息类型ID=17,AMF3编码,忽略
        case 0x11:			/* flex message */
          {
    	RTMP_Log(RTMP_LOGDEBUG,
    	    "%s, flex message, size %lu 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;
          }
    	//RTMP消息类型ID=18,AMF0编码,数据消息
        case 0x12:
          /* metadata (notify) */
    
          RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %lu bytes", __FUNCTION__,
    	  packet->m_nBodySize);
    	  //处理元数据,暂时注释
    	  /*
          if (HandleMetadata(r, packet->m_body, packet->m_nBodySize))
    	bHasMediaPacket = 1;
          break;
    	  */
    	//RTMP消息类型ID=19,AMF0编码,忽略
        case 0x13:
          RTMP_Log(RTMP_LOGDEBUG, "%s, shared object, not supported, ignoring",
    	  __FUNCTION__);
          break;
    	//RTMP消息类型ID=20,AMF0编码,命令消息
    	//处理命令消息!
        case 0x14:
    		//----------------
    		r->dlg->AppendCInfo("处理收到的数据。消息 命令 (AMF0编码) (typeID=20)。");
    		//-----------------------------
          /* invoke */
          RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__,
    	  packet->m_nBodySize);
    	  RTMP_LogPrintf("处理命令消息 (typeID=20,AMF0编码)
    ");
          /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
    
          if (HandleInvoke(r, packet->m_body, packet->m_nBodySize) == 1)
    	bHasMediaPacket = 2;
          break;
    	//RTMP消息类型ID=22
        case 0x16:
          {
    	/* 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, (const uint8_t *)packet->m_body, packet->m_nBodySize);
    #endif
        }
    
      return bHasMediaPacket;
    }

    里面注释的比较多,可以看出,大体的思路是,根据接收到的消息(Message)类型的不同,做出不同的响应。例如收到的消息类型为0x01,那么就是设置块(Chunk)大小的协议,那么就调用相应的函数进行处理。

    因此,本函数可以说是程序的灵魂,收到的各种命令消息都要经过本函数的判断决定调用哪个函数进行相应的处理。

    在这里注意一下消息类型为0x14的消息,即消息类型ID为20的消息,是AMF0编码的命令消息。这在RTMP连接中是非常常见的,比如说各种控制命令:播放,暂停,停止等等。我们来仔细看看它的调用。

    可以发现它调用了HandleInvoke()函数来处理服务器发来的AMF0编码的命令,来看看细节:

    /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
    static int
    HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
    {
      AMFObject obj;
      AVal method;
      int txn;
      int ret = 0, nRes;
      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 = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
      RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val);
    
      if (AVMATCH(&method, &av__result))
        {
          AVal methodInvoked = {0};
          int i;
    
          for (i=0; i<r->m_numCalls; i++) {
      	if (r->m_methodCalls[i].num == 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 %d without matching request",
    	  __FUNCTION__, txn);
    	goto leave;
          }
    	  //----------------
    	  char temp_str[100];
    	  sprintf(temp_str,"接收数据。消息 %s 的 Result",methodInvoked.av_val);
    	  r->dlg->AppendCInfo(temp_str);
    	  //-----------------------------
          RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__,
    	  methodInvoked.av_val);
    
          if (AVMATCH(&methodInvoked, &av_connect))
    	{
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","Result (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
    	    {
    			//----------------
    			r->dlg->AppendCInfo("发送数据。消息 Window Acknowledgement Size (typeID=5)。");
    			//-----------------------------
    			RTMP_LogPrintf("发送消息Window Acknowledgement Size(typeID=5)
    ");
    	      RTMP_SendServerBW(r);
    	      RTMP_SendCtrl(r, 3, 0, 300);
    	    }
    	  //----------------
    	  r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (CreateStream)。");
    	  //-----------------------------
    	  RTMP_LogPrintf("发送命令消息“CreateStream” (typeID=20)
    ");
    	  RTMP_SendCreateStream(r);
    
    	  if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
    	    {
    	      /* 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)
    	        SendFCSubscribe(r, &r->Link.playpath);
    	    }
    	}
          else if (AVMATCH(&methodInvoked, &av_createStream))
    	{
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","Result (CreateStream)");
    		//-----------------------------
    	  r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
    
    	  if (r->Link.protocol & RTMP_FEATURE_WRITE)
    	    {
    	      SendPublish(r);
    	    }
    	  else
    	    {
    	      if (r->Link.lFlags & RTMP_LF_PLST)
    	        SendPlaylist(r);
    		  //----------------
    		  r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (Play)。");
    		  //-----------------------------
    		  RTMP_LogPrintf("发送命令消息“play” (typeID=20)
    ");
    	      SendPlay(r);
    	      RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS);
    	    }
    	}
          else if (AVMATCH(&methodInvoked, &av_play) ||
          	AVMATCH(&methodInvoked, &av_publish))
    	{
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","Result (Play or Publish)");
    		//-----------------------------
    	  r->m_bPlaying = TRUE;
    	}
          free(methodInvoked.av_val);
        }
      else if (AVMATCH(&method, &av_onBWDone))
        {
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","onBWDone");
    		//-----------------------------
    	  if (!r->m_nBWCheckCounter)
            SendCheckBW(r);
        }
      else if (AVMATCH(&method, &av_onFCSubscribe))
        {
          /* SendOnFCSubscribe(); */
        }
      else if (AVMATCH(&method, &av_onFCUnsubscribe))
        {
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","onFCUnsubscribe");
    		//-----------------------------
          RTMP_Close(r);
          ret = 1;
        }
      else if (AVMATCH(&method, &av_ping))
        {
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","Ping");
    		//-----------------------------
          SendPong(r, txn);
        }
      else if (AVMATCH(&method, &av__onbwcheck))
        {
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","onBWcheck");
    		//-----------------------------
          SendCheckBWResult(r, txn);
        }
      else if (AVMATCH(&method, &av__onbwdone))
        {
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","onBWdone");
    		//-----------------------------
          int i;
          for (i = 0; i < r->m_numCalls; i++)
    	if (AVMATCH(&r->m_methodCalls[i].name, &av__checkbw))
    	  {
    	    AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);
    	    break;
    	  }
        }
      else if (AVMATCH(&method, &av__error))
        {
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","error");
    		//-----------------------------
          RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
        }
      else if (AVMATCH(&method, &av_close))
        {
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","close");
    		//-----------------------------
          RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
          RTMP_Close(r);
        }
      else if (AVMATCH(&method, &av_onStatus))
        {
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","onStatus");
    		//-----------------------------
          AMFObject obj2;
          AVal code, level;
          AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
          AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code);
          AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level);
    
          RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val);
          if (AVMATCH(&code, &av_NetStream_Failed)
    	  || AVMATCH(&code, &av_NetStream_Play_Failed)
    	  || AVMATCH(&code, &av_NetStream_Play_StreamNotFound)
    	  || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp))
    	{
    	  r->m_stream_id = -1;
    	  RTMP_Close(r);
    	  RTMP_Log(RTMP_LOGERROR, "Closing connection: %s", code.av_val);
    	}
    
          else if (AVMATCH(&code, &av_NetStream_Play_Start))
    	{
    	  int i;
    	  r->m_bPlaying = TRUE;
    	  for (i = 0; i < r->m_numCalls; i++)
    	    {
    	      if (AVMATCH(&r->m_methodCalls[i].name, &av_play))
    		{
    		  AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);
    		  break;
    		}
    	    }
    	}
    
          else if (AVMATCH(&code, &av_NetStream_Publish_Start))
    	{
    	  int i;
    	  r->m_bPlaying = TRUE;
    	  for (i = 0; i < r->m_numCalls; i++)
    	    {
    	      if (AVMATCH(&r->m_methodCalls[i].name, &av_publish))
    		{
    		  AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);
    		  break;
    		}
    	    }
    	}
    
          /* Return 1 if this is a Play.Complete or Play.Stop */
          else if (AVMATCH(&code, &av_NetStream_Play_Complete)
    	  || AVMATCH(&code, &av_NetStream_Play_Stop)
    	  || AVMATCH(&code, &av_NetStream_Play_UnpublishNotify))
    	{
    	  RTMP_Close(r);
    	  ret = 1;
    	}
    
          else if (AVMATCH(&code, &av_NetStream_Seek_Notify))
            {
    	  r->m_read.flags &= ~RTMP_READ_SEEKING;
    	}
    
          else if (AVMATCH(&code, &av_NetStream_Pause_Notify))
            {
    	  if (r->m_pausing == 1 || r->m_pausing == 2)
    	  {
    	    RTMP_SendPause(r, FALSE, r->m_pauseStamp);
    	    r->m_pausing = 3;
    	  }
    	}
        }
      else if (AVMATCH(&method, &av_playlist_ready))
        {
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","playlist_ready");
    		//-----------------------------
          int i;
          for (i = 0; i < r->m_numCalls; i++)
            {
              if (AVMATCH(&r->m_methodCalls[i].name, &av_set_playlist))
    	    {
    	      AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);
    	      break;
    	    }
            }
        }
      else
        {
    
        }
    leave:
      AMF_Reset(&obj);
      return ret;
    }
    
    int
    RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name,
    			       AMFObjectProperty * p)
    {
      int n;
      /* this is a small object search to locate the "duration" property */
      for (n = 0; n < obj->o_num; n++)
        {
          AMFObjectProperty *prop = AMF_GetProp(obj, NULL, n);
    
          if (AVMATCH(&prop->p_name, name))
    	{
    	  *p = *prop;
    	  return TRUE;
    	}
    
          if (prop->p_type == AMF_OBJECT)
    	{
    	  if (RTMP_FindFirstMatchingProperty(&prop->p_vu.p_object, name, p))
    	    return TRUE;
    	}
        }
      return FALSE;
    }

    该函数主要做了以下几步:

    1.调用AMF_Decode()解码AMF命令数据

    2.调用AMFProp_GetString()获取具体命令的字符串

    3.调用AVMATCH()比较字符串,不同的命令做不同的处理,例如以下几个:

    AVMATCH(&methodInvoked, &av_connect)
    AVMATCH(&methodInvoked, &av_createStream)
    AVMATCH(&methodInvoked, &av_play)
    AVMATCH(&methodInvoked, &av_publish)
    AVMATCH(&method, &av_onBWDone)

    等等,不一一例举了

    具体的处理过程如下所示。在这里说一个“建立网络流”(createStream)的例子,通常发生在建立网络连接(NetConnection)之后,播放(Play)之前。

    else if (AVMATCH(&methodInvoked, &av_createStream))
    	{
    		//----------------
    		r->dlg->AppendMLInfo(20,0,"命令消息","Result (CreateStream)");
    		//-----------------------------
    	  r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
    
    	  if (r->Link.protocol & RTMP_FEATURE_WRITE)
    	    {
    	      SendPublish(r);
    	    }
    	  else
    	    {
    	      if (r->Link.lFlags & RTMP_LF_PLST)
    	        SendPlaylist(r);
    		  //----------------
    		  r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (Play)。");
    		  //-----------------------------
    		  RTMP_LogPrintf("发送命令消息“play” (typeID=20)
    ");
    	      SendPlay(r);
    	      RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS);
    	    }
    	}

    由代码可见,程序先获取了stream_id,然后发送了两个消息(Message),分别是SendPlaylist()和SendPlay(),用于获取播放列表,以及开始播放流媒体数据。

    rtmpdump源代码(Linux):http://download.csdn.net/detail/leixiaohua1020/6376561

    rtmpdump源代码(VC 2005 工程):http://download.csdn.net/detail/leixiaohua1020/6563163


  • 相关阅读:
    LOJ 10160
    LOJ 10155
    2018-11-1 NOIP 模拟赛解题报告
    联考前停课集训随笔
    一个博客园代码高亮的方案
    详解使用 Tarjan 求 LCA 问题(图解)
    NOIP2018普及初赛解析
    关于CCR测评器的自定义校验器(Special Judge)
    日常,异常处理
    Androidstudio 编译慢 这样的体验肯定很多人都有!!!
  • 原文地址:https://www.cnblogs.com/leixiaohua1020/p/3902025.html
Copyright © 2020-2023  润新知