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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值