live555源码分析----SETUP命令处理流程

来源:互联网 发布:什么编程语言好学 编辑:程序博客网 时间:2024/05/18 00:29

SETUP命令概述

http://blog.csdn.net/gavinr/article/details/7029830

SETUP命令,主要用于协商客户端与服务器的通信细节,如通信协议、地址等等,SETUP请求中最重要的是"Transport"头部。

客户端需要对,文件中的每一个流发送一个SETUP命令。

客户端还可以通过其中的"destination"属性来重定向RTP数据的接收地址,不过这是需要服务器支持的,在live555中需要定义宏RTSP_ALLOW_CLIENT_DESTINATION_SETTING。

SETUP的响应中,包含一个"Session"头部,这是服务器产生的一个随机数,用于标识特定的客户端。

来看一个具体的SETUP消息实例
[cpp] view plaincopyprint?
  1. SETUP rtsp://192.168.9.80/123.264/track1 RTSP/1.0  
  2. CSeq: 31  
  3. Transport: RTP/AVP/TCP;unicast;interleaved=0-1  
  4. User-Agent: LibVLC/1.1.0 (LIVE555 Streaming Media v2010.03.16)  
  5.   
  6. response: RTSP/1.0 200 OK  
  7. CSeq: 31  
  8. Date: Wed, Nov 30 2011 06:40:49 GMT  
  9. Transport: RTP/AVP/TCP;unicast;destination=192.168.9.80;source=192.168.9.80;interleaved=0-1  
  10. Session: A00F79DE  


下面来看看SETUP命令的具体实现
1.命令处理函数handleCmd_SETUP
[cpp] view plaincopyprint?
  1. void RTSPServer::RTSPClientSession  
  2. ::handleCmd_SETUP(char const* cseq,  
  3.           char const* urlPreSuffix, char const* urlSuffix,  
  4.           char const* fullRequestStr) {  
  5.   // Normally, "urlPreSuffix" should be the session (stream) name, and "urlSuffix" should be the subsession (track) name.  
  6.   // However (being "liberal in what we accept"), we also handle 'aggregate' SETUP requests (i.e., without a track name),  
  7.   // in the special case where we have only a single track.  I.e., in this case, we also handle:  
  8.   //    "urlPreSuffix" is empty and "urlSuffix" is the session (stream) name, or  
  9.   //    "urlPreSuffix" concatenated with "urlSuffix" (with "/" inbetween) is the session (stream) name.  
  10.   char const* streamName = urlPreSuffix; // in the normal case  
  11.   char const* trackId = urlSuffix; // in the normal case  
  12.   char* concatenatedStreamName = NULL; // in the normal case  
  13.   
  14.   do {  
  15.      //根据媒体流名称(文件名)查找相应的session, session是在DSCRIBE命令处理过程中创建的  
  16.     // First, make sure the specified stream name exists:  
  17.     fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);    
  18.       
  19.     //下面处理URL中不带 track id 的情况,当文件中只有一个流时,充许这种情况的出现,这里流名称保存在urlSuffix变量中  
  20.     if (fOurServerMediaSession == NULL) {  
  21.       // Check for the special case (noted above), before we up:  
  22.       if (urlPreSuffix[0] == '\0') {  
  23.     streamName = urlSuffix;  
  24.       } else {  
  25.     concatenatedStreamName = new char[strlen(urlPreSuffix) + strlen(urlSuffix) + 2]; // allow for the "/" and the trailing '\0'  
  26.     sprintf(concatenatedStreamName, "%s/%s", urlPreSuffix, urlSuffix);  
  27.     streamName = concatenatedStreamName;  
  28.       }  
  29.       trackId = NULL;  
  30.   
  31.   
  32.       // Check again:  
  33.       fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);     //重新查找session  
  34.     }  
  35.     if (fOurServerMediaSession == NULL) {  
  36.       handleCmd_notFound(cseq);  
  37.       break;  
  38.     }  
  39.   
  40.     fOurServerMediaSession->incrementReferenceCount();  //增加session的引用计数  
  41.   
  42.     //若这是这个session所处理的第一个"SETUP"命令,需要构建一个streamState型的数组,并初化  
  43.     if (fStreamStates == NULL) {  
  44.       // This is the first "SETUP" for this session.  Set up our array of states for all of this session's subsessions (tracks):  
  45.       ServerMediaSubsessionIterator iter(*fOurServerMediaSession);  
  46.       for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {} // begin by counting the number of subsessions (tracks)  
  47.   
  48.       fStreamStates = new struct streamState[fNumStreamStates];       
  49.   
  50.       iter.reset();  
  51.       ServerMediaSubsession* subsession;  
  52.       for (unsigned i = 0; i < fNumStreamStates; ++i) {  
  53.     subsession = iter.next();  
  54.     fStreamStates[i].subsession = subsession;  
  55.     fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later  
  56.       }  
  57.     }  
  58.   
  59.     //查找track id 对应的subsession是否存在,不存在则进行错误处理  
  60.     // Look up information for the specified subsession (track):  
  61.     ServerMediaSubsession* subsession = NULL;  
  62.     unsigned streamNum;  
  63.     if (trackId != NULL && trackId[0] != '\0') { // normal case  
  64.       for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {  
  65.     subsession = fStreamStates[streamNum].subsession;  
  66.     if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;  
  67.       }  
  68.       if (streamNum >= fNumStreamStates) {  
  69.     // The specified track id doesn't exist, so this request fails:  
  70.     handleCmd_notFound(cseq);  
  71.     break;  
  72.       }  
  73.     } else {  
  74.     //例外情况:URL中不存在 track id,仅当只有一个subsession的情况下才充许出现  
  75.       // Weird case: there was no track id in the URL.  
  76.       // This works only if we have only one subsession:  
  77.       if (fNumStreamStates != 1) {  
  78.     handleCmd_bad(cseq);  
  79.     break;  
  80.       }  
  81.       streamNum = 0;  
  82.       subsession = fStreamStates[streamNum].subsession;  
  83.     }  
  84.     // ASSERT: subsession != NULL  
  85.   
  86.     //处理Transport头部,获取传输相关信息(1.1)  
  87.     // Look for a "Transport:" header in the request string, to extract client parameters:  
  88.     StreamingMode streamingMode;  
  89.     char* streamingModeString = NULL; // set when RAW_UDP streaming is specified  
  90.     char* clientsDestinationAddressStr;  
  91.     u_int8_t clientsDestinationTTL;  
  92.     portNumBits clientRTPPortNum, clientRTCPPortNum;  
  93.     unsigned char rtpChannelId, rtcpChannelId;  
  94.     parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,  
  95.              clientsDestinationAddressStr, clientsDestinationTTL,  
  96.              clientRTPPortNum, clientRTCPPortNum,  
  97.              rtpChannelId, rtcpChannelId);  
  98.     if (streamingMode == RTP_TCP && rtpChannelId == 0xFF ||  
  99.     streamingMode != RTP_TCP && fClientOutputSocket != fClientInputSocket) {  
  100.       // An anomolous situation, caused by a buggy client.  Either:  
  101.       //     1/ TCP streaming was requested, but with no "interleaving=" fields.  (QuickTime Player sometimes does this.), or  
  102.       //     2/ TCP streaming was not requested, but we're doing RTSP-over-HTTP tunneling (which implies TCP streaming).  
  103.       // In either case, we assume TCP streaming, and set the RTP and RTCP channel ids to proper values:  
  104.       streamingMode = RTP_TCP;  
  105.       rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1;  
  106.     }  
  107.     fTCPStreamIdCount += 2;  
  108.   
  109.     Port clientRTPPort(clientRTPPortNum);  
  110.     Port clientRTCPPort(clientRTCPPortNum);  
  111.   
  112.     //处理Range头部(可选)  
  113.     // Next, check whether a "Range:" header is present in the request.  
  114.     // This isn't legal, but some clients do this to combine "SETUP" and "PLAY":  
  115.     double rangeStart = 0.0, rangeEnd = 0.0;  
  116.     fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd) || parsePlayNowHeader(fullRequestStr);  
  117.   
  118.     // Then, get server parameters from the 'subsession':  
  119.     int tcpSocketNum = streamingMode == RTP_TCP ? fClientOutputSocket : -1;  
  120.     netAddressBits destinationAddress = 0;  
  121.     u_int8_t destinationTTL = 255;  
  122. #ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING  
  123.     if (clientsDestinationAddressStr != NULL) {  
  124.   
  125.       //RTP数据发送到destination指定的地址,而不是正在通信的客户端。为了安全考虑,一般应禁止该功能(将上面的宏定义去掉)  
  126.           
  127.       // Use the client-provided "destination" address.  
  128.       // Note: This potentially allows the server to be used in denial-of-service  
  129.       // attacks, so don't enable this code unless you're sure that clients are  
  130.       // trusted.  
  131.       destinationAddress = our_inet_addr(clientsDestinationAddressStr);  
  132.     }  
  133.     // Also use the client-provided TTL.  
  134.     destinationTTL = clientsDestinationTTL;  
  135. #endif  
  136.     delete[] clientsDestinationAddressStr;  
  137.     Port serverRTPPort(0);  
  138.     Port serverRTCPPort(0);  
  139.   
  140.     // Make sure that we transmit on the same interface that's used by the client (in case we're a multi-homed server):  
  141.     struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr;  
  142.     getsockname(fClientInputSocket, (struct sockaddr*)&sourceAddr, &namelen);  
  143.     netAddressBits origSendingInterfaceAddr = SendingInterfaceAddr;  
  144.     netAddressBits origReceivingInterfaceAddr = ReceivingInterfaceAddr;  
  145.     // NOTE: The following might not work properly, so we ifdef it out for now:  
  146. #ifdef HACK_FOR_MULTIHOMED_SERVERS  
  147.     ReceivingInterfaceAddr = SendingInterfaceAddr = sourceAddr.sin_addr.s_addr;  
  148. #endif  
  149.   
  150.     //从subsession中获取参数(1.2)  
  151.     //fOurSessionId, 标识了一个客户端的session,是在RTSPServer::incomingConnectionHandler函数中生成的随机数  
  152.     subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr,  
  153.                     clientRTPPort, clientRTCPPort,  
  154.                     tcpSocketNum, rtpChannelId, rtcpChannelId,  
  155.                     destinationAddress, destinationTTL, fIsMulticast,  
  156.                     serverRTPPort, serverRTCPPort,  
  157.                     fStreamStates[streamNum].streamToken);  
  158.     SendingInterfaceAddr = origSendingInterfaceAddr;  
  159.     ReceivingInterfaceAddr = origReceivingInterfaceAddr;  
  160.       
  161.     //下面是组装响应包  
  162.     ...  
  163. }  

2.Transport头部(1.1)

Transport头部包含了用于传输的重要信息。看一个SETUP请求的Transport头部
Transport: RTP/AVP/TCP;unicast;interleaved=0-1

使用了 RTP OVER TCP 方式进行传输,使用单播方式,interleaved属性中的0和1将分别用于标识TCP包中的RTP与RTCP数据

下面看看Transport的分析函数

[cpp] view plaincopyprint?
  1. static void parseTransportHeader(char const* buf,  
  2.                  StreamingMode& streamingMode,  
  3.                  char*& streamingModeString,  
  4.                  char*& destinationAddressStr,  
  5.                  u_int8_t& destinationTTL,  
  6.                  portNumBits& clientRTPPortNum, // if UDP  
  7.                  portNumBits& clientRTCPPortNum, // if UDP  
  8.                  unsigned char& rtpChannelId, // if TCP  
  9.                  unsigned char& rtcpChannelId // if TCP  
  10.                  ) {  
  11.   // Initialize the result parameters to default values:  
  12.   streamingMode = RTP_UDP;          //默认使用UDP方式传输RTP  
  13. ...  
  14.   // Then, run through each of the fields, looking for ones we handle:  
  15.   char const* fields = buf + 11;  
  16.   char* field = strDupSize(fields);  
  17.   while (sscanf(fields, "%[^;]", field) == 1) {  
  18.     if (strcmp(field, "RTP/AVP/TCP") == 0) {            //使用了RTP OVER TCP 方式传输  
  19.       streamingMode = RTP_TCP;  
  20.     } else if (strcmp(field, "RAW/RAW/UDP") == 0 ||     //裸的UDP数据,不使用RTP协议      
  21.            strcmp(field, "MP2T/H2221/UDP") == 0) {      //这种方式没见过,看名字应该是使用某种协议的UDP传输,但也被当成了裸的UDP数据  
  22.       streamingMode = RAW_UDP;  
  23.       streamingModeString = strDup(field);  
  24.     } else if (_strncasecmp(field, "destination=", 12) == 0) {  //destination属性, 客户端可以通过这个属性重新设置RTP的发送地址,注意,服务器端可能拒绝该属性  
  25.       delete[] destinationAddressStr;  
  26.       destinationAddressStr = strDup(field+12);  
  27.     } else if (sscanf(field, "ttl%u", &ttl) == 1) {  
  28.       destinationTTL = (u_int8_t)ttl;  
  29.     } else if (sscanf(field, "client_port=%hu-%hu", &p1, &p2) == 2) {   //client_port属性,客户端接收RTP、RTCP的端口号  
  30.     clientRTPPortNum = p1;  
  31.     clientRTCPPortNum = p2;  
  32.     } else if (sscanf(field, "client_port=%hu", &p1) == 1) {    //客户端只提供了RTP的端口号的情况  
  33.     clientRTPPortNum = p1;  
  34.     clientRTCPPortNum = streamingMode == RAW_UDP ? 0 : p1 + 1;  
  35.     } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) {//interleaved属性,仅在使用RTP OVER TCP方式传输时有用,两个数字分别标识了RTP和RTCP的TCP数据包  
  36.       rtpChannelId = (unsigned char)rtpCid;         //RTP标识  
  37.       rtcpChannelId = (unsigned char)rtcpCid;       //RTCP标识  
  38.     }  
  39.   
  40.     fields += strlen(field);  
  41.     while (*fields == ';') ++fields; // skip over separating ';' chars  
  42.     if (*fields == '\0' || *fields == '\r' || *fields == '\n'break;  
  43.   }  
  44.   delete[] field;  
  45. }  

3.从subsession中获取参数(1.2)

getStreamParameters是定义在ServerMediaSubsession类中的纯虚函数,其实现在子类OnDemandServerMediaSubsession中。这个函数中将完成source,RTPSink的创建工作,并将其与客户端的映射关系保存下来。
[cpp] view plaincopyprint?
  1. void OnDemandServerMediaSubsession  
  2. ::getStreamParameters(unsigned clientSessionId,  
  3.               netAddressBits clientAddress,  
  4.               Port const& clientRTPPort,  
  5.               Port const& clientRTCPPort,<LeftMouse>  
  6.               int tcpSocketNum,  
  7.               unsigned char rtpChannelId,  
  8.               unsigned char rtcpChannelId,  
  9.               netAddressBits& destinationAddress,  
  10.               u_int8_t& /*destinationTTL*/,  
  11.               Boolean& isMulticast,  
  12.               Port& serverRTPPort,  
  13.               Port& serverRTCPPort,  
  14.               void*& streamToken) {  
  15.   if (destinationAddress == 0) destinationAddress = clientAddress;  
  16.   struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress;  
  17.   isMulticast = False;  
  18.   
  19.   
  20.   if (fLastStreamToken != NULL && fReuseFirstSource) {  
  21.     //当fReuseFirstSource参数为True时,不需要再创建source,sink, groupsock等实例,只需要记录客户端的地址即可  
  22.   
  23.   
  24.     // Special case: Rather than creating a new 'StreamState',  
  25.     // we reuse the one that we've already created:  
  26.     serverRTPPort = ((StreamState*)fLastStreamToken)->serverRTPPort();  
  27.     serverRTCPPort = ((StreamState*)fLastStreamToken)->serverRTCPPort();  
  28.     ++((StreamState*)fLastStreamToken)->referenceCount();   //增加引用记数  
  29.     streamToken = fLastStreamToken;  
  30.   } else {  
  31.     //正常情况下,创建一个新的media source  
  32.     // Normal case: Create a new media source:  
  33.     unsigned streamBitrate;  
  34.   
  35.     //创建source,还记得在处理DESCRIBE命令时,也创建过吗? 是的,那是在  
  36.     //OnDemandServerMediaSubsession::sdpLines()函数中, 但参数clientSessionId为0。  
  37.     //createNewStreamSource函数的具体实现参见前前的文章中关于DESCRIBE命令的处理流程  
  38.     FramedSource* mediaSource  
  39.       = createNewStreamSource(clientSessionId, streamBitrate);    
  40.   
  41.     // Create 'groupsock' and 'sink' objects for the destination,  
  42.     // using previously unused server port numbers:  
  43.     RTPSink* rtpSink;  
  44.     BasicUDPSink* udpSink;  
  45.     Groupsock* rtpGroupsock;  
  46.     Groupsock* rtcpGroupsock;  
  47.     portNumBits serverPortNum;  
  48.     if (clientRTCPPort.num() == 0) {  
  49.       
  50.     //使用RAW UDP传输,当然就不用使用RTCP了  
  51.   
  52.       // We're streaming raw UDP (not RTP). Create a single groupsock:  
  53.       NoReuse dummy; // ensures that we skip over ports that are already in use  
  54.       for (serverPortNum = fInitialPortNum; ; ++serverPortNum) {  
  55.     struct in_addr dummyAddr; dummyAddr.s_addr = 0;  
  56.   
  57.     serverRTPPort = serverPortNum;  
  58.     rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);  
  59.     if (rtpGroupsock->socketNum() >= 0) break// success  
  60.       }  
  61.   
  62.       rtcpGroupsock = NULL;  
  63.       rtpSink = NULL;  
  64.       udpSink = BasicUDPSink::createNew(envir(), rtpGroupsock);  
  65.     } else {  
  66.   
  67.     //创建一对groupsocks实例,分别用于传输RTP、RTCP  
  68.   
  69.     //RTP、RTCP的端口号是相邻的,并且RTP端口号为偶数。初始端口fInitialPortNum = 6970,  
  70.     //这是OnDemandServerMediaSubsession构造函数的缺省参数  
  71.   
  72.       // Normal case: We're streaming RTP (over UDP or TCP).  Create a pair of  
  73.       // groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even):  
  74.       NoReuse dummy; // ensures that we skip over ports that are already in use  
  75.       for (portNumBits serverPortNum = fInitialPortNum; ; serverPortNum += 2) {  
  76.     struct in_addr dummyAddr; dummyAddr.s_addr = 0;  
  77.   
  78.     serverRTPPort = serverPortNum;  
  79.     rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);  
  80.     if (rtpGroupsock->socketNum() < 0) {  
  81.       delete rtpGroupsock;  
  82.       continue// try again  
  83.     }  
  84.   
  85.     serverRTCPPort = serverPortNum+1;  
  86.     rtcpGroupsock = new Groupsock(envir(), dummyAddr, serverRTCPPort, 255);  
  87.     if (rtcpGroupsock->socketNum() < 0) {  
  88.       delete rtpGroupsock;  
  89.       delete rtcpGroupsock;  
  90.       continue// try again  
  91.     }  
  92.   
  93.     break// success  
  94.       }  
  95.       
  96.     //创建RTPSink,与source类似,在处理DESCRIBE命令进行过,具体过程参见DESCRIBE命令的处理流程  
  97.       unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic  
  98.       rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource);  
  99.       udpSink = NULL;  
  100.     }  
  101.   
  102.   
  103.     // Turn off the destinations for each groupsock.  They'll get set later  
  104.     // (unless TCP is used instead):  
  105.     if (rtpGroupsock != NULL) rtpGroupsock->removeAllDestinations();  
  106.     if (rtcpGroupsock != NULL) rtcpGroupsock->removeAllDestinations();  
  107.   
  108.   
  109.     //重新配置发送RTP 的socket缓冲区大小  
  110.     if (rtpGroupsock != NULL) {  
  111.       // Try to use a big send buffer for RTP -  at least 0.1 second of  
  112.       // specified bandwidth and at least 50 KB  
  113.       unsigned rtpBufSize = streamBitrate * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes  
  114.       if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 1024;  
  115.       increaseSendBufferTo(envir(), rtpGroupsock->socketNum(), rtpBufSize); //这个函数在groupsock中定义  
  116.     }  
  117.   
  118.   
  119.     //建立流的状态对像(stream token),其它包括sink、source、groupsock等的对应关系  
  120.     //注意,live555中定义了两个StreamState结构,这里的StreamState定义为一个类。在RTSPServer中,  
  121.     //定义了一个内部结构体StreamState,其streamToken成员指向此处的StreamState实例  
  122.   
  123.   
  124.     // Set up the state of the stream.  The stream will get started later:  
  125.     streamToken = fLastStreamToken  
  126.       = new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink,  
  127.             streamBitrate, mediaSource,  
  128.             rtpGroupsock, rtcpGroupsock);  
  129.   }  
  130.   
  131.   
  132.     //这里定义了类Destinations来保存目的地址、RTP端口、RTCP端口,并将其与对应的clientSessionId保存到哈希表  
  133.     //fDestinationsHashTable中,这个哈希表是定义在OnDemandServerMediaSubsession类中  
  134.   
  135.   
  136.   // Record these destinations as being for this client session id:  
  137.   Destinations* destinations;  
  138.   if (tcpSocketNum < 0) { // UDP  
  139.     destinations = new Destinations(destinationAddr, clientRTPPort, clientRTCPPort);  
  140.   } else { // TCP  
  141.     destinations = new Destinations(tcpSocketNum, rtpChannelId, rtcpChannelId);  
  142.   }  
  143.   fDestinationsHashTable->Add((char const*)clientSessionId, destinations);  
  144. }