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

函数调用结构图

RTMPDump (libRTMP)的整体的函数调用结构图如下图所示。


单击查看大图



详细分析

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

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

RTMP_ReadPacket()

RTMP_ClientPacket()

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

先把带注释的代码贴上:

  1. //处理接收到的Chunk  
  2. int  
  3. RTMP_ClientPacket(RTMP *r, RTMPPacket *packet)  
  4. {  
  5.   int bHasMediaPacket = 0;  
  6.   switch (packet->m_packetType)  
  7.     {  
  8.     //RTMP消息类型ID=1,设置块大小  
  9.     case 0x01:  
  10.       /* chunk size */  
  11.         //----------------  
  12.         r->dlg->AppendCInfo("处理收到的数据。消息 Set Chunk Size (typeID=1)。");  
  13.         //-----------------------------  
  14.         RTMP_LogPrintf("处理消息 Set Chunk Size (typeID=1)\n");  
  15.       HandleChangeChunkSize(r, packet);  
  16.       break;  
  17.     //RTMP消息类型ID=3,致谢  
  18.     case 0x03:  
  19.       /* bytes read report */  
  20.       RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__);  
  21.       break;  
  22.     //RTMP消息类型ID=4,用户控制  
  23.     case 0x04:  
  24.       /* ctrl */  
  25.         //----------------  
  26.         r->dlg->AppendCInfo("处理收到的数据。消息 User Control (typeID=4)。");  
  27.         //-----------------------------  
  28.         RTMP_LogPrintf("处理消息 User Control (typeID=4)\n");  
  29.       HandleCtrl(r, packet);  
  30.       break;  
  31.     //RTMP消息类型ID=5  
  32.     case 0x05:  
  33.       /* server bw */  
  34.         //----------------  
  35.         r->dlg->AppendCInfo("处理收到的数据。消息 Window Acknowledgement Size (typeID=5)。");  
  36.         //-----------------------------  
  37.         RTMP_LogPrintf("处理消息 Window Acknowledgement Size (typeID=5)\n");  
  38.       HandleServerBW(r, packet);  
  39.       break;  
  40.     //RTMP消息类型ID=6  
  41.     case 0x06:  
  42.       /* client bw */  
  43.         //----------------  
  44.         r->dlg->AppendCInfo("处理收到的数据。消息 Set Peer Bandwidth (typeID=6)。");  
  45.         //-----------------------------  
  46.         RTMP_LogPrintf("处理消息 Set Peer Bandwidth (typeID=6)\n");  
  47.       HandleClientBW(r, packet);  
  48.       break;  
  49.     //RTMP消息类型ID=8,音频数据  
  50.     case 0x08:  
  51.       /* audio data */  
  52.       /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */  
  53.       HandleAudio(r, packet);  
  54.       bHasMediaPacket = 1;  
  55.       if (!r->m_mediaChannel)  
  56.     r->m_mediaChannel = packet->m_nChannel;  
  57.       if (!r->m_pausing)  
  58.     r->m_mediaStamp = packet->m_nTimeStamp;  
  59.       break;  
  60.     //RTMP消息类型ID=9,视频数据  
  61.     case 0x09:  
  62.       /* video data */  
  63.       /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */  
  64.       HandleVideo(r, packet);  
  65.       bHasMediaPacket = 1;  
  66.       if (!r->m_mediaChannel)  
  67.     r->m_mediaChannel = packet->m_nChannel;  
  68.       if (!r->m_pausing)  
  69.     r->m_mediaStamp = packet->m_nTimeStamp;  
  70.       break;  
  71.     //RTMP消息类型ID=15,AMF3编码,忽略  
  72.     case 0x0F:          /* flex stream send */  
  73.       RTMP_Log(RTMP_LOGDEBUG,  
  74.       "%s, flex stream send, size %lu bytes, not supported, ignoring",  
  75.       __FUNCTION__, packet->m_nBodySize);  
  76.       break;  
  77.     //RTMP消息类型ID=16,AMF3编码,忽略  
  78.     case 0x10:          /* flex shared object */  
  79.       RTMP_Log(RTMP_LOGDEBUG,  
  80.       "%s, flex shared object, size %lu bytes, not supported, ignoring",  
  81.       __FUNCTION__, packet->m_nBodySize);  
  82.       break;  
  83.     //RTMP消息类型ID=17,AMF3编码,忽略  
  84.     case 0x11:          /* flex message */  
  85.       {  
  86.     RTMP_Log(RTMP_LOGDEBUG,  
  87.         "%s, flex message, size %lu bytes, not fully supported",  
  88.         __FUNCTION__, packet->m_nBodySize);  
  89.     /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */  
  90.   
  91.     /* some DEBUG code */  
  92. #if 0  
  93.        RTMP_LIB_AMFObject obj;  
  94.        int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);  
  95.        if(nRes < 0) {  
  96.        RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);  
  97.        /*return; */  
  98.        }  
  99.   
  100.        obj.Dump();  
  101. #endif  
  102.   
  103.     if (HandleInvoke(r, packet->m_body + 1, packet->m_nBodySize - 1) == 1)  
  104.       bHasMediaPacket = 2;  
  105.     break;  
  106.       }  
  107.     //RTMP消息类型ID=18,AMF0编码,数据消息  
  108.     case 0x12:  
  109.       /* metadata (notify) */  
  110.   
  111.       RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %lu bytes", __FUNCTION__,  
  112.       packet->m_nBodySize);  
  113.       //处理元数据,暂时注释  
  114.       /* 
  115.       if (HandleMetadata(r, packet->m_body, packet->m_nBodySize)) 
  116.     bHasMediaPacket = 1; 
  117.       break; 
  118.       */  
  119.     //RTMP消息类型ID=19,AMF0编码,忽略  
  120.     case 0x13:  
  121.       RTMP_Log(RTMP_LOGDEBUG, "%s, shared object, not supported, ignoring",  
  122.       __FUNCTION__);  
  123.       break;  
  124.     //RTMP消息类型ID=20,AMF0编码,命令消息  
  125.     //处理命令消息!  
  126.     case 0x14:  
  127.         //----------------  
  128.         r->dlg->AppendCInfo("处理收到的数据。消息 命令 (AMF0编码) (typeID=20)。");  
  129.         //-----------------------------  
  130.       /* invoke */  
  131.       RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__,  
  132.       packet->m_nBodySize);  
  133.       RTMP_LogPrintf("处理命令消息 (typeID=20,AMF0编码)\n");  
  134.       /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */  
  135.   
  136.       if (HandleInvoke(r, packet->m_body, packet->m_nBodySize) == 1)  
  137.     bHasMediaPacket = 2;  
  138.       break;  
  139.     //RTMP消息类型ID=22  
  140.     case 0x16:  
  141.       {  
  142.     /* go through FLV packets and handle metadata packets */  
  143.     unsigned int pos = 0;  
  144.     uint32_t nTimeStamp = packet->m_nTimeStamp;  
  145.   
  146.     while (pos + 11 < packet->m_nBodySize)  
  147.       {  
  148.         uint32_t dataSize = AMF_DecodeInt24(packet->m_body + pos + 1);   /* size without header (11) and prevTagSize (4) */  
  149.   
  150.         if (pos + 11 + dataSize + 4 > packet->m_nBodySize)  
  151.           {  
  152.         RTMP_Log(RTMP_LOGWARNING, "Stream corrupt?!");  
  153.         break;  
  154.           }  
  155.         if (packet->m_body[pos] == 0x12)  
  156.           {  
  157.         HandleMetadata(r, packet->m_body + pos + 11, dataSize);  
  158.           }  
  159.         else if (packet->m_body[pos] == 8 || packet->m_body[pos] == 9)  
  160.           {  
  161.         nTimeStamp = AMF_DecodeInt24(packet->m_body + pos + 4);  
  162.         nTimeStamp |= (packet->m_body[pos + 7] << 24);  
  163.           }  
  164.         pos += (11 + dataSize + 4);  
  165.       }  
  166.     if (!r->m_pausing)  
  167.       r->m_mediaStamp = nTimeStamp;  
  168.   
  169.     /* FLV tag(s) */  
  170.     /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize); */  
  171.     bHasMediaPacket = 1;  
  172.     break;  
  173.       }  
  174.     default:  
  175.       RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,  
  176.       packet->m_packetType);  
  177. #ifdef _DEBUG  
  178.       RTMP_LogHex(RTMP_LOGDEBUG, (const uint8_t *)packet->m_body, packet->m_nBodySize);  
  179. #endif  
  180.     }  
  181.   
  182.   return bHasMediaPacket;  
  183. }  

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

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

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

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

  1. /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */  
  2. static int  
  3. HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)  
  4. {  
  5.   AMFObject obj;  
  6.   AVal method;  
  7.   int txn;  
  8.   int ret = 0, nRes;  
  9.   if (body[0] != 0x02)      /* make sure it is a string method name we start with */  
  10.     {  
  11.       RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",  
  12.       __FUNCTION__);  
  13.       return 0;  
  14.     }  
  15.   
  16.   nRes = AMF_Decode(&obj, body, nBodySize, FALSE);  
  17.   if (nRes < 0)  
  18.     {  
  19.       RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);  
  20.       return 0;  
  21.     }  
  22.   
  23.   AMF_Dump(&obj);  
  24.   AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);  
  25.   txn = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));  
  26.   RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val);  
  27.   
  28.   if (AVMATCH(&method, &av__result))  
  29.     {  
  30.       AVal methodInvoked = {0};  
  31.       int i;  
  32.   
  33.       for (i=0; i<r->m_numCalls; i++) {  
  34.     if (r->m_methodCalls[i].num == txn) {  
  35.       methodInvoked = r->m_methodCalls[i].name;  
  36.       AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE);  
  37.       break;  
  38.     }  
  39.       }  
  40.       if (!methodInvoked.av_val) {  
  41.         RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %d without matching request",  
  42.       __FUNCTION__, txn);  
  43.     goto leave;  
  44.       }  
  45.       //----------------  
  46.       char temp_str[100];  
  47.       sprintf(temp_str,"接收数据。消息 %s 的 Result",methodInvoked.av_val);  
  48.       r->dlg->AppendCInfo(temp_str);  
  49.       //-----------------------------  
  50.       RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__,  
  51.       methodInvoked.av_val);  
  52.   
  53.       if (AVMATCH(&methodInvoked, &av_connect))  
  54.     {  
  55.         //----------------  
  56.         r->dlg->AppendMLInfo(20,0,"命令消息","Result (Connect)");  
  57.         //-----------------------------  
  58.       if (r->Link.token.av_len)  
  59.         {  
  60.           AMFObjectProperty p;  
  61.           if (RTMP_FindFirstMatchingProperty(&obj, &av_secureToken, &p))  
  62.         {  
  63.           DecodeTEA(&r->Link.token, &p.p_vu.p_aval);  
  64.           SendSecureTokenResponse(r, &p.p_vu.p_aval);  
  65.         }  
  66.         }  
  67.       if (r->Link.protocol & RTMP_FEATURE_WRITE)  
  68.         {  
  69.           SendReleaseStream(r);  
  70.           SendFCPublish(r);  
  71.         }  
  72.       else  
  73.         {  
  74.             //----------------  
  75.             r->dlg->AppendCInfo("发送数据。消息 Window Acknowledgement Size (typeID=5)。");  
  76.             //-----------------------------  
  77.             RTMP_LogPrintf("发送消息Window Acknowledgement Size(typeID=5)\n");  
  78.           RTMP_SendServerBW(r);  
  79.           RTMP_SendCtrl(r, 3, 0, 300);  
  80.         }  
  81.       //----------------  
  82.       r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (CreateStream)。");  
  83.       //-----------------------------  
  84.       RTMP_LogPrintf("发送命令消息“CreateStream” (typeID=20)\n");  
  85.       RTMP_SendCreateStream(r);  
  86.   
  87.       if (!(r->Link.protocol & RTMP_FEATURE_WRITE))  
  88.         {  
  89.           /* Send the FCSubscribe if live stream or if subscribepath is set */  
  90.           if (r->Link.subscribepath.av_len)  
  91.             SendFCSubscribe(r, &r->Link.subscribepath);  
  92.           else if (r->Link.lFlags & RTMP_LF_LIVE)  
  93.             SendFCSubscribe(r, &r->Link.playpath);  
  94.         }  
  95.     }  
  96.       else if (AVMATCH(&methodInvoked, &av_createStream))  
  97.     {  
  98.         //----------------  
  99.         r->dlg->AppendMLInfo(20,0,"命令消息","Result (CreateStream)");  
  100.         //-----------------------------  
  101.       r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));  
  102.   
  103.       if (r->Link.protocol & RTMP_FEATURE_WRITE)  
  104.         {  
  105.           SendPublish(r);  
  106.         }  
  107.       else  
  108.         {  
  109.           if (r->Link.lFlags & RTMP_LF_PLST)  
  110.             SendPlaylist(r);  
  111.           //----------------  
  112.           r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (Play)。");  
  113.           //-----------------------------  
  114.           RTMP_LogPrintf("发送命令消息“play” (typeID=20)\n");  
  115.           SendPlay(r);  
  116.           RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS);  
  117.         }  
  118.     }  
  119.       else if (AVMATCH(&methodInvoked, &av_play) ||  
  120.         AVMATCH(&methodInvoked, &av_publish))  
  121.     {  
  122.         //----------------  
  123.         r->dlg->AppendMLInfo(20,0,"命令消息","Result (Play or Publish)");  
  124.         //-----------------------------  
  125.       r->m_bPlaying = TRUE;  
  126.     }  
  127.       free(methodInvoked.av_val);  
  128.     }  
  129.   else if (AVMATCH(&method, &av_onBWDone))  
  130.     {  
  131.         //----------------  
  132.         r->dlg->AppendMLInfo(20,0,"命令消息","onBWDone");  
  133.         //-----------------------------  
  134.       if (!r->m_nBWCheckCounter)  
  135.         SendCheckBW(r);  
  136.     }  
  137.   else if (AVMATCH(&method, &av_onFCSubscribe))  
  138.     {  
  139.       /* SendOnFCSubscribe(); */  
  140.     }  
  141.   else if (AVMATCH(&method, &av_onFCUnsubscribe))  
  142.     {  
  143.         //----------------  
  144.         r->dlg->AppendMLInfo(20,0,"命令消息","onFCUnsubscribe");  
  145.         //-----------------------------  
  146.       RTMP_Close(r);  
  147.       ret = 1;  
  148.     }  
  149.   else if (AVMATCH(&method, &av_ping))  
  150.     {  
  151.         //----------------  
  152.         r->dlg->AppendMLInfo(20,0,"命令消息","Ping");  
  153.         //-----------------------------  
  154.       SendPong(r, txn);  
  155.     }  
  156.   else if (AVMATCH(&method, &av__onbwcheck))  
  157.     {  
  158.         //----------------  
  159.         r->dlg->AppendMLInfo(20,0,"命令消息","onBWcheck");  
  160.         //-----------------------------  
  161.       SendCheckBWResult(r, txn);  
  162.     }  
  163.   else if (AVMATCH(&method, &av__onbwdone))  
  164.     {  
  165.         //----------------  
  166.         r->dlg->AppendMLInfo(20,0,"命令消息","onBWdone");  
  167.         //-----------------------------  
  168.       int i;  
  169.       for (i = 0; i < r->m_numCalls; i++)  
  170.     if (AVMATCH(&r->m_methodCalls[i].name, &av__checkbw))  
  171.       {  
  172.         AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);  
  173.         break;  
  174.       }  
  175.     }  
  176.   else if (AVMATCH(&method, &av__error))  
  177.     {  
  178.         //----------------  
  179.         r->dlg->AppendMLInfo(20,0,"命令消息","error");  
  180.         //-----------------------------  
  181.       RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");  
  182.     }  
  183.   else if (AVMATCH(&method, &av_close))  
  184.     {  
  185.         //----------------  
  186.         r->dlg->AppendMLInfo(20,0,"命令消息","close");  
  187.         //-----------------------------  
  188.       RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");  
  189.       RTMP_Close(r);  
  190.     }  
  191.   else if (AVMATCH(&method, &av_onStatus))  
  192.     {  
  193.         //----------------  
  194.         r->dlg->AppendMLInfo(20,0,"命令消息","onStatus");  
  195.         //-----------------------------  
  196.       AMFObject obj2;  
  197.       AVal code, level;  
  198.       AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);  
  199.       AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code);  
  200.       AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level);  
  201.   
  202.       RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val);  
  203.       if (AVMATCH(&code, &av_NetStream_Failed)  
  204.       || AVMATCH(&code, &av_NetStream_Play_Failed)  
  205.       || AVMATCH(&code, &av_NetStream_Play_StreamNotFound)  
  206.       || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp))  
  207.     {  
  208.       r->m_stream_id = -1;  
  209.       RTMP_Close(r);  
  210.       RTMP_Log(RTMP_LOGERROR, "Closing connection: %s", code.av_val);  
  211.     }  
  212.   
  213.       else if (AVMATCH(&code, &av_NetStream_Play_Start))  
  214.     {  
  215.       int i;  
  216.       r->m_bPlaying = TRUE;  
  217.       for (i = 0; i < r->m_numCalls; i++)  
  218.         {  
  219.           if (AVMATCH(&r->m_methodCalls[i].name, &av_play))  
  220.         {  
  221.           AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);  
  222.           break;  
  223.         }  
  224.         }  
  225.     }  
  226.   
  227.       else if (AVMATCH(&code, &av_NetStream_Publish_Start))  
  228.     {  
  229.       int i;  
  230.       r->m_bPlaying = TRUE;  
  231.       for (i = 0; i < r->m_numCalls; i++)  
  232.         {  
  233.           if (AVMATCH(&r->m_methodCalls[i].name, &av_publish))  
  234.         {  
  235.           AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);  
  236.           break;  
  237.         }  
  238.         }  
  239.     }  
  240.   
  241.       /* Return 1 if this is a Play.Complete or Play.Stop */  
  242.       else if (AVMATCH(&code, &av_NetStream_Play_Complete)  
  243.       || AVMATCH(&code, &av_NetStream_Play_Stop)  
  244.       || AVMATCH(&code, &av_NetStream_Play_UnpublishNotify))  
  245.     {  
  246.       RTMP_Close(r);  
  247.       ret = 1;  
  248.     }  
  249.   
  250.       else if (AVMATCH(&code, &av_NetStream_Seek_Notify))  
  251.         {  
  252.       r->m_read.flags &= ~RTMP_READ_SEEKING;  
  253.     }  
  254.   
  255.       else if (AVMATCH(&code, &av_NetStream_Pause_Notify))  
  256.         {  
  257.       if (r->m_pausing == 1 || r->m_pausing == 2)  
  258.       {  
  259.         RTMP_SendPause(r, FALSE, r->m_pauseStamp);  
  260.         r->m_pausing = 3;  
  261.       }  
  262.     }  
  263.     }  
  264.   else if (AVMATCH(&method, &av_playlist_ready))  
  265.     {  
  266.         //----------------  
  267.         r->dlg->AppendMLInfo(20,0,"命令消息","playlist_ready");  
  268.         //-----------------------------  
  269.       int i;  
  270.       for (i = 0; i < r->m_numCalls; i++)  
  271.         {  
  272.           if (AVMATCH(&r->m_methodCalls[i].name, &av_set_playlist))  
  273.         {  
  274.           AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);  
  275.           break;  
  276.         }  
  277.         }  
  278.     }  
  279.   else  
  280.     {  
  281.   
  282.     }  
  283. leave:  
  284.   AMF_Reset(&obj);  
  285.   return ret;  
  286. }  
  287.   
  288. int  
  289. RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name,  
  290.                    AMFObjectProperty * p)  
  291. {  
  292.   int n;  
  293.   /* this is a small object search to locate the "duration" property */  
  294.   for (n = 0; n < obj->o_num; n++)  
  295.     {  
  296.       AMFObjectProperty *prop = AMF_GetProp(obj, NULL, n);  
  297.   
  298.       if (AVMATCH(&prop->p_name, name))  
  299.     {  
  300.       *p = *prop;  
  301.       return TRUE;  
  302.     }  
  303.   
  304.       if (prop->p_type == AMF_OBJECT)  
  305.     {  
  306.       if (RTMP_FindFirstMatchingProperty(&prop->p_vu.p_object, name, p))  
  307.         return TRUE;  
  308.     }  
  309.     }  
  310.   return FALSE;  
  311. }  

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

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

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

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

  1. AVMATCH(&methodInvoked, &av_connect)  
  2. AVMATCH(&methodInvoked, &av_createStream)  
  3. AVMATCH(&methodInvoked, &av_play)  
  4. AVMATCH(&methodInvoked, &av_publish)  
  5. AVMATCH(&method, &av_onBWDone)  

等等,不一一例举了

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

  1. else if (AVMATCH(&methodInvoked, &av_createStream))  
  2.     {  
  3.         //----------------  
  4.         r->dlg->AppendMLInfo(20,0,"命令消息","Result (CreateStream)");  
  5.         //-----------------------------  
  6.       r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));  
  7.   
  8.       if (r->Link.protocol & RTMP_FEATURE_WRITE)  
  9.         {  
  10.           SendPublish(r);  
  11.         }  
  12.       else  
  13.         {  
  14.           if (r->Link.lFlags & RTMP_LF_PLST)  
  15.             SendPlaylist(r);  
  16.           //----------------  
  17.           r->dlg->AppendCInfo("发送数据。消息 命令 (typeID=20) (Play)。");  
  18.           //-----------------------------  
  19.           RTMP_LogPrintf("发送命令消息“play” (typeID=20)\n");  
  20.           SendPlay(r);  
  21.           RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS);  
  22.         }  
  23.     }  

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值