SETUP命令處理

live555學習 --SETUP命令處理  

SETUP命令概述

       首先更正一個概念:
       ServerMediaSession原先說代表一個流,其實是不準確的。它代表的是server端的一個媒體的名字,而說ServerMediaSubsession代表一個Track是準確的。以後流指的是那些有數據流動的組合。
      

         SETUP命令,主要用於協商客戶端與服務器的通信細節,如通信協議、地址等等,SETUP請求中最重要的是"Transport"頭部。

        客戶端需要對文件中的每一個流發送一個SETUP命令。客戶端還可以通過其中的"destination"屬性來重定向RTP數據的接收地址,不過這是需要服務器支持的,在live555中需要定義宏RTSP_ALLOW_CLIENT_DESTINATION_SETTING。

        SETUP的響應中,包含一個"Session"頭部,這是服務器產生的一個隨機數,用於標識特定的客戶端。來看一個具體的SETUP消息實例:
  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  
 11: 
1.命令處理函數handleCmd_SETUP
        RTP的建立過程無非是這樣:client告訴server自己的rtp/rtcp端口號,server建立自己的rtp/rtcp socket,然後在收到PLAY請求時向客戶端發數據。看起來非常簡單。在收到SETUP請求時才建立連接,下面是處理這個命令的函數:
  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 {                      // allow for the "/" and the trailing '\0' 
 25:     concatenatedStreamName = new char[strlen(urlPreSuffix) + strlen(urlSuffix) + 2]; 
 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);

// begin by counting the number of subsessions (tracks)

 46:       for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {} 
 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: //獲取rtp連接信息,在其中已建立起了server端的rtp和rtcp socket,返回

//fStreamStates[streamNum].streamToken表示數據流已經建立起來了

152: //fOurSessionId, 標識了一個客戶端的session,是在RTSPServer::incomingConnectionHandler函數

//中生成的隨機數

153:     subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr,    
154:                     clientRTPPort, clientRTCPPort,    
155:                     tcpSocketNum, rtpChannelId, rtcpChannelId,    
156:                     destinationAddress, destinationTTL, fIsMulticast,    
157:                     serverRTPPort, serverRTCPPort,    
158:                     fStreamStates[streamNum].streamToken);    
159:     SendingInterfaceAddr = origSendingInterfaceAddr;    
160:     ReceivingInterfaceAddr = origReceivingInterfaceAddr;    
161:      //形成RTSP迴應字符串     
162:         struct in_addr destinationAddr;    
163:         destinationAddr.s_addr = destinationAddress;    
164:         char* destAddrStr = strDup(our_inet_ntoa(destinationAddr));    
165:         char* sourceAddrStr = strDup(our_inet_ntoa(sourceAddr.sin_addr));    
166:         if (fIsMulticast) {    
167:             switch (streamingMode) {    
168:             case RTP_UDP:    
169:                 snprintf(    
170:                         (char*) fResponseBuffer,    
171:                         sizeof fResponseBuffer,    
172:                         "RTSP/1.0 200 OK\r\n"    
173:                                 "CSeq: %s\r\n"    
174:                                 "%s"    
175:                       "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"    
176:                                 "Session: %08X\r\n\r\n", cseq, dateHeader(),    
177:                         destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()),    
178:                         ntohs(serverRTCPPort.num()), destinationTTL,    
179:                         fOurSessionId);    
180:                 break;    
181:             case RTP_TCP:    
182:                 // multicast streams can't be sent via TCP     
183:                 handleCmd_unsupportedTransport(cseq);    
184:                 break;    
185:             case RAW_UDP:    
186:                 snprintf(    
187:                         (char*) fResponseBuffer,    
188:                         sizeof fResponseBuffer,    
189:                         "RTSP/1.0 200 OK\r\n"    
190:                                 "CSeq: %s\r\n"    
191:                                 "%s"    
192:                         "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"    
193:                                 "Session: %08X\r\n\r\n", cseq, dateHeader(),    
194:                         streamingModeString, destAddrStr, sourceAddrStr,    
195:                         ntohs(serverRTPPort.num()), destinationTTL,    
196:                         fOurSessionId);    
197:                 break;    
198:             }    
199:         } else {    
200:             switch (streamingMode) {    
201:             case RTP_UDP: {    
202:                 snprintf(    
203:                         (char*) fResponseBuffer,    
204:                         sizeof fResponseBuffer,    
205:                         "RTSP/1.0 200 OK\r\n"    
206:                                 "CSeq: %s\r\n"    
207:                                 "%s"    
208:       "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"    
209:                                 "Session: %08X\r\n\r\n", cseq, dateHeader(),    
210:                         destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()),    
211:                         ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()),    
212:                         ntohs(serverRTCPPort.num()), fOurSessionId);    
213:                 break;    
214:             }    
215:             case RTP_TCP: {    
216:                 snprintf(    
217:                         (char*) fResponseBuffer,    
218:                         sizeof fResponseBuffer,    
219:                         "RTSP/1.0 200 OK\r\n"    
220:                                 "CSeq: %s\r\n"    
221:                                 "%s"    
222:                    "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"    
223:                                 "Session: %08X\r\n\r\n", cseq, dateHeader(),    
224:                         destAddrStr, sourceAddrStr, rtpChannelId, rtcpChannelId,    
225:                         fOurSessionId);    
226:                 break;    
227:             }    
228:             case RAW_UDP: {    
229:                 snprintf(    
230:                         (char*) fResponseBuffer,    
231:                         sizeof fResponseBuffer,    
232:                         "RTSP/1.0 200 OK\r\n"    
233:                          "CSeq: %s\r\n"    
234:                          "%s"    
235:                  "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"    
236:                  "Session: %08X\r\n\r\n", cseq, dateHeader(),    
237:                         streamingModeString, destAddrStr, sourceAddrStr,    
238:                         ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),    
239:                         fOurSessionId);    
240:                 break;    
241:             }    
242:             }    
243:         }    
244:         delete[] destAddrStr;    
245:         delete[] sourceAddrStr;    
246:         delete[] streamingModeString;    
247:     } while (0);    
248:     
249:     
250:     delete[] concatenatedStreamName;    
251:     //返回後,迴應字符串會被立即發送     
252: }   
253: 

        live555 中有兩個 streamstate,一個是類 StreamState ,一個是此處的結構 struct streamState。類 SteamState 就是 streamToken,而 struct streamState 中保存了 MediaSubsession (即track) 和類 StreamState 的對應。類 StreamState 代表一個真正流動起來的數據流。這個數據流是從源流到 Sink 。客戶端與服務端的一個 rtp 會話中,有兩個數據流,服務端是從 XXXFileSouce 流到 RTPSink,而客戶端則是從 RTPSource 流到 XXXFileSink 。建立數據流的過程就是把 Source 與 Sink 連接起來。
        爲何不把 StreamToken 保存在 MediaSubsession 中呢?看起來在 struct streamState 中是一個MediaSubsession 對應一個 streamToken 呀?因爲 MediaSubsession 代表一個track 的靜態數據,它是可以被其它 rtp 會話重用的。比如不同的用戶可能會連接到同一個媒體的同一個track 。所以streamToken與MediaSubsession獨立存在,只是被RTSPClientSession給對應了起來。

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的分析函數:

  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協議

//這種方式沒見過,看名字應該是使用某種協議的UDP傳輸,但也被當成了裸的UDP數據

 21:            strcmp(field, "MP2T/H2221/UDP") == 0) {      
 22:       streamingMode = RAW_UDP;    

23: streamingModeString = strDup(field); /

/destination屬性, 客戶端可以通過這個屬性重新設置RTP的發送地址,注意,服務器端可能拒絕該屬性

 24:     } else if (_strncasecmp(field, "destination=", 12) == 0) {  
 25:       delete[] destinationAddressStr;    
 26:       destinationAddressStr = strDup(field+12);    
 27:     } else if (sscanf(field, "ttl%u", &ttl) == 1) {    

28: destinationTTL = (u_int8_t)ttl;

//client_port屬性,客戶端接收RTP、RTCP的端口號

 29:     } else if (sscanf(field, "client_port=%hu-%hu", &p1, &p2) == 2) {   
 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;             //interleaved屬性,僅在使用RTP OVER TCP方式傳輸時有用,兩個數字分別標識了RTP和RTCP的TCP數據包 
 35:     } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) {
 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: }   
 46: 

  

3.從subsession中獲取參數(1.2)

        getStreamParameters是定義在ServerMediaSubsession類中的純虛函數,其實現在子類OnDemandServerMediaSubsession中。這個函數中將完成source,RTPSink的創建工作,並將其與客戶端的映射關係保存下來。

  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:   //ServerMediaSubsession並沒有保存所有基於自己的數據流,而是隻記錄了最後一次建立的數據流。     
 20:   //利用這個變量和fReuseFirstSource可以實現多client連接到一個流的形式。  
 21:   if (fLastStreamToken != NULL && fReuseFirstSource) {    

22: //當fReuseFirstSource參數爲True時,不需要再創建source,sink, groupsock等實例,

//只需要記錄客戶端的地址即可

 23:   //如果已經基於這個ServerMediaSubsession創建了一個連接,並且希望使用這個連接,則直接返回這個連接。    
 24:     
 25:     // Special case: Rather than creating a new 'StreamState',     
 26:     // we reuse the one that we've already created:     
 27:     serverRTPPort = ((StreamState*)fLastStreamToken)->serverRTPPort();    
 28:     serverRTCPPort = ((StreamState*)fLastStreamToken)->serverRTCPPort();    
 29:     ++((StreamState*)fLastStreamToken)->referenceCount();   //增加引用記數     
 30:     streamToken = fLastStreamToken;    
 31:   } else {    
 32:     //正常情況下,創建一個新的media source     
 33:     // Normal case: Create a new media source:     
 34:     unsigned streamBitrate;    
 35:     
 36:     //創建source,還記得在處理DESCRIBE命令時,也創建過嗎? 是的,那是在     
 37:     //OnDemandServerMediaSubsession::sdpLines()函數中, 但參數clientSessionId爲0。     
 38:     //createNewStreamSource函數的具體實現參見前前的文章中關於DESCRIBE命令的處理流程     
 39:     FramedSource* mediaSource    
 40:       = createNewStreamSource(clientSessionId, streamBitrate);      
 41:     
 42:     // Create 'groupsock' and 'sink' objects for the destination,     
 43:     // using previously unused server port numbers:     
 44:     RTPSink* rtpSink;    
 45:     BasicUDPSink* udpSink;    
 46:     Groupsock* rtpGroupsock;    
 47:     Groupsock* rtcpGroupsock;    
 48:     portNumBits serverPortNum;    
 49:     if (clientRTCPPort.num() == 0) {    
 50:         
 51:     //使用RAW UDP傳輸,當然就不用使用RTCP了     
 52:     
 53:       // We're streaming raw UDP (not RTP). Create a single groupsock:     
 54:       NoReuse dummy; // ensures that we skip over ports that are already in use     
 55:       for (serverPortNum = fInitialPortNum; ; ++serverPortNum) {    
 56:     struct in_addr dummyAddr; dummyAddr.s_addr = 0;    
 57:     
 58:     serverRTPPort = serverPortNum;    
 59:     rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);    
 60:     if (rtpGroupsock->socketNum() >= 0) break; // success     
 61:       }    
 62:     
 63:       rtcpGroupsock = NULL;    
 64:       rtpSink = NULL;    
 65:       udpSink = BasicUDPSink::createNew(envir(), rtpGroupsock);    
 66:     } else {    
 67:     
 68:     //創建一對groupsocks實例,分別用於傳輸RTP、RTCP     
 69:     
 70:     //RTP、RTCP的端口號是相鄰的,並且RTP端口號爲偶數。初始端口fInitialPortNum = 6970,     
 71:     //這是OnDemandServerMediaSubsession構造函數的缺省參數     
 72:     
 73:       // Normal case: We're streaming RTP (over UDP or TCP).  Create a pair of     
 74:       // groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even):     
 75:       NoReuse dummy; // ensures that we skip over ports that are already in use     
 76:       for (portNumBits serverPortNum = fInitialPortNum; ; serverPortNum += 2) {    
 77:     struct in_addr dummyAddr; dummyAddr.s_addr = 0;    
 78:     
 79:     serverRTPPort = serverPortNum;    
 80:     rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);    
 81:     if (rtpGroupsock->socketNum() < 0) {    
 82:       delete rtpGroupsock;    
 83:       continue; // try again     
 84:     }    
 85:     
 86:     serverRTCPPort = serverPortNum+1;    
 87:     rtcpGroupsock = new Groupsock(envir(), dummyAddr, serverRTCPPort, 255);    
 88:     if (rtcpGroupsock->socketNum() < 0) {    
 89:       delete rtpGroupsock;    
 90:       delete rtcpGroupsock;    
 91:       continue; // try again     
 92:     }    
 93:     
 94:     break; // success     
 95:       }    
 96:         
 97:     //創建RTPSink,與source類似,在處理DESCRIBE命令進行過,具體過程參見DESCRIBE命令的處理流程     
 98:       unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic     
 99:       rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource);    
100:       udpSink = NULL;    
101:     }    
102:     
103:     
104:     // Turn off the destinations for each groupsock.  They'll get set later     
105:     // (unless TCP is used instead):     
106:     if (rtpGroupsock != NULL) rtpGroupsock->removeAllDestinations();    
107:     if (rtcpGroupsock != NULL) rtcpGroupsock->removeAllDestinations();    
108:     
109:     
110:     //重新配置發送RTP 的socket緩衝區大小     
111:     if (rtpGroupsock != NULL) {    
112:       // Try to use a big send buffer for RTP -  at least 0.1 second of     
113:       // specified bandwidth and at least 50 KB     
114:       unsigned rtpBufSize = streamBitrate * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes     

115: if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 1024;

//這個函數在groupsock中定義

116:       increaseSendBufferTo(envir(), rtpGroupsock->socketNum(), rtpBufSize); 
117:     }    
118:     
119:     
120:     //建立流的狀態對像(stream token),其它包括sink、source、groupsock等的對應關係     
121:     //注意,live555中定義了兩個StreamState結構,這裏的StreamState定義爲一個類。在RTSPServer中,     
122:     //定義了一個內部結構體StreamState,其streamToken成員指向此處的StreamState實例     
123:     
124:     
125:     // Set up the state of the stream.  The stream will get started later:     
126:     streamToken = fLastStreamToken    
127:       = new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink,    
128:             streamBitrate, mediaSource,    
129:             rtpGroupsock, rtcpGroupsock);    
130:   }    
131:     
132:     
133:     //這裏定義了類Destinations來保存目的地址、RTP端口、RTCP端口,並將其與對應的clientSessionId保存到哈希表     
134:     //fDestinationsHashTable中,這個哈希表是定義在OnDemandServerMediaSubsession類中     
135:     

136: //記錄下所有clientSessionID對應的目的rtp/rtcp地址,是因爲現在不能把目的rtp,rtcp地址加入到server端rtp的

//groupSocket中。試想在ReuseFirstSource時,這樣會引起client端立即收到rtp數據。 其次,也可以利用這個

//hash table找出client的rtp/rtcp端口等信息,好像其它地方還真沒有可以保存的RTSPClientSession中的

//streamstates在ReuseFirstSource時也不能準確找出client端的端口等信息。

137:   // Record these destinations as being for this client session id:     
138:   Destinations* destinations;    
139:   if (tcpSocketNum < 0) { // UDP     
140:     destinations = new Destinations(destinationAddr, clientRTPPort, clientRTCPPort);    
141:   } else { // TCP     
142:     destinations = new Destinations(tcpSocketNum, rtpChannelId, rtcpChannelId);    
143:   }    
144:   fDestinationsHashTable->Add((char const*)clientSessionId, destinations);    
145: }   
146: 

流程不復雜:如果需要重用上一次建立的流,就利用之(這樣就可以實現一rtp server對應多個rtp client的形式);如果不需要,則創建合適的source,然後創建rtp sink,然後利用它們創建streamSoken。

以上內容出自:http://blog.csdn.net/gavinr/article/details/7029830

                      http://blog.csdn.net/nkmnkm/article/details/6914637

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章