live555学习2(转)

六 建立RTP会话


首先更正一个概念:
ServerMediaSession原先说代表一个流,其实是不准确的。它代表的是server端的一个媒体的名字,而说ServerMediaSubsession代表一个Track是准确的。以后流指的是那些有数据流动的组合。


RTP的建立:
RTP的建立过程无非是这样:client告诉server自己的rtp/rtcp端口号,server建立自己的rtp/rtcp socket,然后在收到PLAY请求时向客户端发数据。看起来非常简单。
在收到SETUP请求时才建立连接,让我们看一下处理这个命令的函数:
[cpp] view plain copy print ?
  1. void RTSPServer::RTSPClientSession::handleCmd_SETUP(  
  2.         char const* cseq,  
  3.         char const* urlPreSuffix,  
  4.         char const* urlSuffix,  
  5.         char const* fullRequestStr)  
  6. {  
  7.     // Normally, "urlPreSuffix" should be the session (stream) name,  
  8.     // and "urlSuffix" should be the subsession (track) name.  
  9.     // However (being "liberal in what we accept"), we also handle  
  10.     // 'aggregate' SETUP requests (i.e., without a track name),  
  11.     // in the special case where we have only a single track.  I.e.,  
  12.     // in this case, we also handle:   
  13.     //    "urlPreSuffix" is empty and "urlSuffix" is the session (stream) name, or  
  14.     //    "urlPreSuffix" concatenated with "urlSuffix" (with "/" inbetween)  
  15.     //    is the session (stream) name.  
  16.     char const* streamName = urlPreSuffix; // in the normal case  
  17.     char const* trackId = urlSuffix; // in the normal case  
  18.     char* concatenatedStreamName = NULL; // in the normal case  
  19.   
  20.   
  21.     do {  
  22.         // First, make sure the specified stream name exists:  
  23.         fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);  
  24.         if (fOurServerMediaSession == NULL) {  
  25.             // Check for the special case (noted above), before we up:  
  26.             if (urlPreSuffix[0] == '\0') {  
  27.                 streamName = urlSuffix;  
  28.             } else {  
  29.                 concatenatedStreamName = new char[strlen(urlPreSuffix)  
  30.                         + strlen(urlSuffix) + 2]; // allow for the "/" and the trailing '\0'  
  31.                 sprintf(concatenatedStreamName, "%s/%s", urlPreSuffix,  
  32.                         urlSuffix);  
  33.                 streamName = concatenatedStreamName;  
  34.             }  
  35.             trackId = NULL;  
  36.   
  37.   
  38.             // Check again:   
  39.             fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName);  
  40.         }  
  41.         if (fOurServerMediaSession == NULL) {  
  42.             handleCmd_notFound(cseq);  
  43.             break;  
  44.         }  
  45.   
  46.   
  47.         fOurServerMediaSession->incrementReferenceCount();  
  48.   
  49.   
  50.         //为一个流中所有的track都分配一个stream state   
  51.         if (fStreamStates == NULL) {  
  52.             // This is the first "SETUP" for this session.  Set up our  
  53.             // array of states for all of this session's subsessions (tracks):  
  54.             ServerMediaSubsessionIterator iter(*fOurServerMediaSession);  
  55.             for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {  
  56.             } // begin by counting the number of subsessions (tracks)  
  57.   
  58.   
  59.             fStreamStates = new struct streamState[fNumStreamStates];  
  60.   
  61.   
  62.             iter.reset();  
  63.             ServerMediaSubsession* subsession;  
  64.             for (unsigned i = 0; i < fNumStreamStates; ++i) {  
  65.                 subsession = iter.next();  
  66.                 fStreamStates[i].subsession = subsession;  
  67.                 fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later  
  68.             }  
  69.         }  
  70.   
  71.   
  72.         //查找当前请求的track的信息   
  73.         // Look up information for the specified subsession (track):  
  74.         ServerMediaSubsession* subsession = NULL;  
  75.         unsigned streamNum;  
  76.         if (trackId != NULL && trackId[0] != '\0') { // normal case  
  77.             for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {  
  78.                 subsession = fStreamStates[streamNum].subsession;  
  79.                 if (subsession != NULL  && strcmp(trackId, subsession->trackId()) == 0)  
  80.                     break//找到啦!  
  81.             }  
  82.             if (streamNum >= fNumStreamStates) {  
  83.                 // The specified track id doesn't exist, so this request fails:  
  84.                 handleCmd_notFound(cseq);  
  85.                 break;  
  86.             }  
  87.         } else {  
  88.             // Weird case: there was no track id in the URL.  
  89.             // This works only if we have only one subsession:  
  90.             if (fNumStreamStates != 1) {  
  91.                 handleCmd_bad(cseq);  
  92.                 break;  
  93.             }  
  94.             streamNum = 0;  
  95.             subsession = fStreamStates[streamNum].subsession;  
  96.         }  
  97.         // ASSERT: subsession != NULL  
  98.   
  99.   
  100.         //分析RTSP请求字符串中的传输要求   
  101.         // Look for a "Transport:" header in the request string, to extract client parameters:  
  102.         StreamingMode streamingMode;  
  103.         char* streamingModeString = NULL; // set when RAW_UDP streaming is specified  
  104.         char* clientsDestinationAddressStr;  
  105.         u_int8_t clientsDestinationTTL;  
  106.         portNumBits clientRTPPortNum, clientRTCPPortNum;  
  107.         unsigned char rtpChannelId, rtcpChannelId;  
  108.         parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,  
  109.                 clientsDestinationAddressStr, clientsDestinationTTL,  
  110.                 clientRTPPortNum, clientRTCPPortNum, rtpChannelId,  
  111.                 rtcpChannelId);  
  112.         if (streamingMode == RTP_TCP && rtpChannelId == 0xFF  
  113.                 || streamingMode != RTP_TCP &&  
  114.                 fClientOutputSocket != fClientInputSocket) {  
  115.             // An anomolous situation, caused by a buggy client.  Either:  
  116.             //     1/ TCP streaming was requested, but with no "interleaving=" fields.  (QuickTime Player sometimes does this.), or  
  117.             //     2/ TCP streaming was not requested, but we're doing RTSP-over-HTTP tunneling (which implies TCP streaming).  
  118.             // In either case, we assume TCP streaming, and set the RTP and RTCP channel ids to proper values:  
  119.             streamingMode = RTP_TCP;  
  120.             rtpChannelId = fTCPStreamIdCount;  
  121.             rtcpChannelId = fTCPStreamIdCount + 1;  
  122.         }  
  123.         fTCPStreamIdCount += 2;  
  124.   
  125.   
  126.         Port clientRTPPort(clientRTPPortNum);  
  127.         Port clientRTCPPort(clientRTCPPortNum);  
  128.   
  129.   
  130.         // Next, check whether a "Range:" header is present in the request.  
  131.         // This isn't legal, but some clients do this to combine "SETUP" and "PLAY":  
  132.         double rangeStart = 0.0, rangeEnd = 0.0;  
  133.         fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart,  
  134.                 rangeEnd) || parsePlayNowHeader(fullRequestStr);  
  135.   
  136.   
  137.         // Then, get server parameters from the 'subsession':  
  138.         int tcpSocketNum = streamingMode == RTP_TCP ? fClientOutputSocket : -1;  
  139.         netAddressBits destinationAddress = 0;  
  140.         u_int8_t destinationTTL = 255;  
  141. #ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING  
  142.         if (clientsDestinationAddressStr != NULL) {  
  143.             // Use the client-provided "destination" address.  
  144.             // Note: This potentially allows the server to be used in denial-of-service  
  145.             // attacks, so don't enable this code unless you're sure that clients are  
  146.             // trusted.   
  147.             destinationAddress = our_inet_addr(clientsDestinationAddressStr);  
  148.         }  
  149.         // Also use the client-provided TTL.  
  150.         destinationTTL = clientsDestinationTTL;  
  151. #endif   
  152.         delete[] clientsDestinationAddressStr;  
  153.         Port serverRTPPort(0);  
  154.         Port serverRTCPPort(0);  
  155.   
  156.   
  157.         // Make sure that we transmit on the same interface that's used by  
  158.         // the client (in case we're a multi-homed server):  
  159.         struct sockaddr_in sourceAddr;  
  160.         SOCKLEN_T namelen = sizeof sourceAddr;  
  161.         getsockname(fClientInputSocket, (struct sockaddr*) &sourceAddr, &namelen);  
  162.         netAddressBits origSendingInterfaceAddr = SendingInterfaceAddr;  
  163.         netAddressBits origReceivingInterfaceAddr = ReceivingInterfaceAddr;  
  164.         // NOTE: The following might not work properly, so we ifdef it out for now:  
  165. #ifdef HACK_FOR_MULTIHOMED_SERVERS  
  166.         ReceivingInterfaceAddr = SendingInterfaceAddr = sourceAddr.sin_addr.s_addr;  
  167. #endif   
  168.   
  169.   
  170.         //获取rtp连接信息,在其中已建立起了server端的rtp和rtcp socket,返回  
  171.         //fStreamStates[streamNum].streamToken表示数据流已经建立起来了  
  172.         subsession->getStreamParameters(fOurSessionId,  
  173.                 fClientAddr.sin_addr.s_addr, clientRTPPort, clientRTCPPort,  
  174.                 tcpSocketNum, rtpChannelId, rtcpChannelId, destinationAddress,  
  175.                 destinationTTL, fIsMulticast, serverRTPPort, serverRTCPPort,  
  176.                 fStreamStates[streamNum].streamToken);  
  177.         SendingInterfaceAddr = origSendingInterfaceAddr;  
  178.         ReceivingInterfaceAddr = origReceivingInterfaceAddr;  
  179.   
  180.   
  181.         //形成RTSP回应字符串   
  182.         struct in_addr destinationAddr;  
  183.         destinationAddr.s_addr = destinationAddress;  
  184.         char* destAddrStr = strDup(our_inet_ntoa(destinationAddr));  
  185.         char* sourceAddrStr = strDup(our_inet_ntoa(sourceAddr.sin_addr));  
  186.         if (fIsMulticast) {  
  187.             switch (streamingMode) {  
  188.             case RTP_UDP:  
  189.                 snprintf(  
  190.                         (char*) fResponseBuffer,  
  191.                         sizeof fResponseBuffer,  
  192.                         "RTSP/1.0 200 OK\r\n"  
  193.                                 "CSeq: %s\r\n"  
  194.                                 "%s"  
  195.                                 "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"  
  196.                                 "Session: %08X\r\n\r\n", cseq, dateHeader(),  
  197.                         destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()),  
  198.                         ntohs(serverRTCPPort.num()), destinationTTL,  
  199.                         fOurSessionId);  
  200.                 break;  
  201.             case RTP_TCP:  
  202.                 // multicast streams can't be sent via TCP  
  203.                 handleCmd_unsupportedTransport(cseq);  
  204.                 break;  
  205.             case RAW_UDP:  
  206.                 snprintf(  
  207.                         (char*) fResponseBuffer,  
  208.                         sizeof fResponseBuffer,  
  209.                         "RTSP/1.0 200 OK\r\n"  
  210.                                 "CSeq: %s\r\n"  
  211.                                 "%s"  
  212.                                 "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"  
  213.                                 "Session: %08X\r\n\r\n", cseq, dateHeader(),  
  214.                         streamingModeString, destAddrStr, sourceAddrStr,  
  215.                         ntohs(serverRTPPort.num()), destinationTTL,  
  216.                         fOurSessionId);  
  217.                 break;  
  218.             }  
  219.         } else {  
  220.             switch (streamingMode) {  
  221.             case RTP_UDP: {  
  222.                 snprintf(  
  223.                         (char*) fResponseBuffer,  
  224.                         sizeof fResponseBuffer,  
  225.                         "RTSP/1.0 200 OK\r\n"  
  226.                                 "CSeq: %s\r\n"  
  227.                                 "%s"  
  228.                                 "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"  
  229.                                 "Session: %08X\r\n\r\n", cseq, dateHeader(),  
  230.                         destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()),  
  231.                         ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()),  
  232.                         ntohs(serverRTCPPort.num()), fOurSessionId);  
  233.                 break;  
  234.             }  
  235.             case RTP_TCP: {  
  236.                 snprintf(  
  237.                         (char*) fResponseBuffer,  
  238.                         sizeof fResponseBuffer,  
  239.                         "RTSP/1.0 200 OK\r\n"  
  240.                                 "CSeq: %s\r\n"  
  241.                                 "%s"  
  242.                                 "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"  
  243.                                 "Session: %08X\r\n\r\n", cseq, dateHeader(),  
  244.                         destAddrStr, sourceAddrStr, rtpChannelId, rtcpChannelId,  
  245.                         fOurSessionId);  
  246.                 break;  
  247.             }  
  248.             case RAW_UDP: {  
  249.                 snprintf(  
  250.                         (char*) fResponseBuffer,  
  251.                         sizeof fResponseBuffer,  
  252.                         "RTSP/1.0 200 OK\r\n"  
  253.                                 "CSeq: %s\r\n"  
  254.                                 "%s"  
  255.                                 "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"  
  256.                                 "Session: %08X\r\n\r\n", cseq, dateHeader(),  
  257.                         streamingModeString, destAddrStr, sourceAddrStr,  
  258.                         ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),  
  259.                         fOurSessionId);  
  260.                 break;  
  261.             }  
  262.             }  
  263.         }  
  264.         delete[] destAddrStr;  
  265.         delete[] sourceAddrStr;  
  266.         delete[] streamingModeString;  
  267.     } while (0);  
  268.   
  269.   
  270.     delete[] concatenatedStreamName;  
  271.     //返回后,回应字符串会被立即发送   
  272. }  


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 给对应了起来。


streamToken的建立过程存在于函数subsession->getStreamParameters()中,让我们看一下下:
[cpp] view plain copy print ?
  1. void OnDemandServerMediaSubsession::getStreamParameters(  
  2.         unsigned clientSessionId,  
  3.         netAddressBits clientAddress,  
  4.         Port const& clientRTPPort,  
  5.         Port const& clientRTCPPort,  
  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. {  
  16.     if (destinationAddress == 0)  
  17.         destinationAddress = clientAddress;  
  18.   
  19.   
  20.     struct in_addr destinationAddr;  
  21.     destinationAddr.s_addr = destinationAddress;  
  22.     isMulticast = False;  
  23.   
  24.   
  25.     //ServerMediaSubsession并没有保存所有基于自己的数据流,而是只记录了最后一次建立的数据流。  
  26.     //利用这个变量和fReuseFirstSource可以实现多client连接到一个流的形式。  
  27.     if (fLastStreamToken != NULL && fReuseFirstSource) {  
  28.         //如果已经基于这个ServerMediaSubsession创建了一个连接,并且希望使用这个连接  
  29.         //则直接返回这个连接。   
  30.         // Special case: Rather than creating a new 'StreamState',  
  31.         // we reuse the one that we've already created:  
  32.         serverRTPPort = ((StreamState*) fLastStreamToken)->serverRTPPort();  
  33.         serverRTCPPort = ((StreamState*) fLastStreamToken)->serverRTCPPort();  
  34.         ++((StreamState*) fLastStreamToken)->referenceCount();  
  35.         streamToken = fLastStreamToken;  
  36.     } else {  
  37.         // Normal case: Create a new media source:  
  38.         unsigned streamBitrate;  
  39.         FramedSource* mediaSource = createNewStreamSource(clientSessionId,streamBitrate);  
  40.   
  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.             // We're streaming raw UDP (not RTP). Create a single groupsock:  
  51.             NoReuse dummy; // ensures that we skip over ports that are already in use  
  52.             for (serverPortNum = fInitialPortNum;; ++serverPortNum) {  
  53.                 struct in_addr dummyAddr;  
  54.                 dummyAddr.s_addr = 0;  
  55.   
  56.   
  57.                 serverRTPPort = serverPortNum;  
  58.                 rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);  
  59.                 if (rtpGroupsock->socketNum() >= 0)  
  60.                     break// success  
  61.             }  
  62.   
  63.   
  64.             rtcpGroupsock = NULL;  
  65.             rtpSink = NULL;  
  66.             udpSink = BasicUDPSink::createNew(envir(), rtpGroupsock);  
  67.         } else {  
  68.             // Normal case: We're streaming RTP (over UDP or TCP).  Create a pair of  
  69.             // groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even):  
  70.             NoReuse dummy; // ensures that we skip over ports that are already in use  
  71.             for (portNumBits serverPortNum = fInitialPortNum;; serverPortNum += 2) {  
  72.                 struct in_addr dummyAddr;  
  73.                 dummyAddr.s_addr = 0;  
  74.   
  75.   
  76.                 serverRTPPort = serverPortNum;  
  77.                 rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);  
  78.                 if (rtpGroupsock->socketNum() < 0) {  
  79.                     delete rtpGroupsock;  
  80.                     continue// try again  
  81.                 }  
  82.   
  83.   
  84.                 serverRTCPPort = serverPortNum + 1;  
  85.                 rtcpGroupsock = new Groupsock(envir(), dummyAddr,serverRTCPPort, 255);  
  86.                 if (rtcpGroupsock->socketNum() < 0) {  
  87.                     delete rtpGroupsock;  
  88.                     delete rtcpGroupsock;  
  89.                     continue// try again  
  90.                 }  
  91.   
  92.   
  93.                 break// success  
  94.             }  
  95.   
  96.   
  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)  
  106.             rtpGroupsock->removeAllDestinations();  
  107.         if (rtcpGroupsock != NULL)  
  108.             rtcpGroupsock->removeAllDestinations();  
  109.   
  110.   
  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)  
  116.                 rtpBufSize = 50 * 1024;  
  117.             increaseSendBufferTo(envir(), rtpGroupsock->socketNum(),rtpBufSize);  
  118.         }  
  119.   
  120.   
  121.         // Set up the state of the stream.  The stream will get started later:  
  122.         streamToken = fLastStreamToken = new StreamState(*this, serverRTPPort,  
  123.                 serverRTCPPort, rtpSink, udpSink, streamBitrate, mediaSource,  
  124.                 rtpGroupsock, rtcpGroupsock);  
  125.     }  
  126.   
  127.   
  128.     // Record these destinations as being for this client session id:  
  129.     Destinations* destinations;  
  130.     if (tcpSocketNum < 0) { // UDP  
  131.         destinations = new Destinations(destinationAddr, clientRTPPort, clientRTCPPort);  
  132.     } else { // TCP  
  133.         destinations = new Destinations(tcpSocketNum, rtpChannelId, rtcpChannelId);  
  134.     }  
  135.       
  136.     //记录下所有clientSessionID对应的目的rtp/rtcp地址,是因为现在不能把目的rtp,rtcp地址加入到  
  137.     //server端rtp的groupSocket中。试想在ReuseFirstSource时,这样会引起client端立即收到rtp数据。  
  138.     //其次,也可以利用这个hash table找出client的rtp/rtcp端口等信息,好像其它地方还真没有可以保存的  
  139.     //RTSPClientSession中的streamstates在ReuseFirstSource时也不能准确找出client端的端口等信息。  
  140.     fDestinationsHashTable->Add((char const*) clientSessionId, destinations);  
  141. }  


流程不复杂:如果需要重用上一次建立的流,就利用之(这样就可以实现一rtp server对应多个rtp client的形式);如果不需要,则创建合适的source,然后创建rtp sink,然后利用它们创建streamSoken。


启动一个流:
当RTSPClientSession收到PLAY请求时,就开始传输RTP数据。下面看一下流启动的代码:
[cpp] view plain copy print ?
  1. void RTSPServer::RTSPClientSession::handleCmd_PLAY(  
  2.         ServerMediaSubsession* subsession,  
  3.         char const* cseq,  
  4.         char const* fullRequestStr)  
  5. {  
  6.     char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession,fClientInputSocket);  
  7.     unsigned rtspURLSize = strlen(rtspURL);  
  8.   
  9.   
  10.     // Parse the client's "Scale:" header, if any:  
  11.     float scale;  
  12.     Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);  
  13.   
  14.   
  15.     // Try to set the stream's scale factor to this value:  
  16.     if (subsession == NULL /*aggregate op*/) {  
  17.         fOurServerMediaSession->testScaleFactor(scale);  
  18.     } else {  
  19.         subsession->testScaleFactor(scale);  
  20.     }  
  21.   
  22.   
  23.     char buf[100];  
  24.     char* scaleHeader;  
  25.     if (!sawScaleHeader) {  
  26.         buf[0] = '\0'// Because we didn't see a Scale: header, don't send one back  
  27.     } else {  
  28.         sprintf(buf, "Scale: %f\r\n", scale);  
  29.     }  
  30.     scaleHeader = strDup(buf);  
  31.   
  32.   
  33.     //分析客户端对于播放范围的要求   
  34.     // Parse the client's "Range:" header, if any:  
  35.     double rangeStart = 0.0, rangeEnd = 0.0;  
  36.     Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart,rangeEnd);  
  37.   
  38.   
  39.     // Use this information, plus the stream's duration (if known), to create  
  40.     // our own "Range:" header, for the response:  
  41.     float duration = subsession == NULL /*aggregate op*/  
  42.     ? fOurServerMediaSession->duration() : subsession->duration();  
  43.     if (duration < 0.0) {  
  44.         // We're an aggregate PLAY, but the subsessions have different durations.  
  45.         // Use the largest of these durations in our header  
  46.         duration = -duration;  
  47.     }  
  48.   
  49.   
  50.     // Make sure that "rangeStart" and "rangeEnd" (from the client's "Range:" header) have sane values  
  51.     // before we send back our own "Range:" header in our response:  
  52.     if (rangeStart < 0.0)  
  53.         rangeStart = 0.0;  
  54.     else if (rangeStart > duration)  
  55.         rangeStart = duration;  
  56.     if (rangeEnd < 0.0)  
  57.         rangeEnd = 0.0;  
  58.     else if (rangeEnd > duration)  
  59.         rangeEnd = duration;  
  60.     if ((scale > 0.0 && rangeStart > rangeEnd && rangeEnd > 0.0)  
  61.             || (scale < 0.0 && rangeStart < rangeEnd)) {  
  62.         // "rangeStart" and "rangeEnd" were the wrong way around; swap them:  
  63.         double tmp = rangeStart;  
  64.         rangeStart = rangeEnd;  
  65.         rangeEnd = tmp;  
  66.     }  
  67.   
  68.   
  69.     char* rangeHeader;  
  70.     if (!sawRangeHeader) {  
  71.         buf[0] = '\0'// Because we didn't see a Range: header, don't send one back  
  72.     } else if (rangeEnd == 0.0 && scale >= 0.0) {  
  73.         sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);  
  74.     } else {  
  75.         sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);  
  76.     }  
  77.     rangeHeader = strDup(buf);  
  78.   
  79.   
  80.     // Create a "RTP-Info:" line.  It will get filled in from each subsession's state:  
  81.     char const* rtpInfoFmt = "%s" // "RTP-Info:", plus any preceding rtpInfo items  
  82.                     "%s"// comma separator, if needed  
  83.                     "url=%s/%s"  
  84.                     ";seq=%d"  
  85.                     ";rtptime=%u";  
  86.     unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);  
  87.     char* rtpInfo = strDup("RTP-Info: ");  
  88.     unsigned i, numRTPInfoItems = 0;  
  89.   
  90.   
  91.     // Do any required seeking/scaling on each subsession, before starting streaming:  
  92.     for (i = 0; i < fNumStreamStates; ++i) {  
  93.         if (subsession == NULL /* means: aggregated operation */  
  94.         || subsession == fStreamStates[i].subsession) {  
  95.             if (sawScaleHeader) {  
  96.                 fStreamStates[i].subsession->setStreamScale(fOurSessionId,  
  97.                         fStreamStates[i].streamToken, scale);  
  98.             }  
  99.             if (sawRangeHeader) {  
  100.                 double streamDuration = 0.0; // by default; means: stream until the end of the media  
  101.                 if (rangeEnd > 0.0 && (rangeEnd + 0.001) < duration) { // the 0.001 is because we limited the values to 3 decimal places  
  102.                     // We want the stream to end early.  Set the duration we want:  
  103.                     streamDuration = rangeEnd - rangeStart;  
  104.                     if (streamDuration < 0.0)  
  105.                         streamDuration = -streamDuration; // should happen only if scale < 0.0  
  106.                 }  
  107.                 u_int64_t numBytes;  
  108.                 fStreamStates[i].subsession->seekStream(fOurSessionId,  
  109.                         fStreamStates[i].streamToken, rangeStart,  
  110.                         streamDuration, numBytes);  
  111.             }  
  112.         }  
  113.     }  
  114.   
  115.   
  116.     // Now, start streaming:   
  117.     for (i = 0; i < fNumStreamStates; ++i) {  
  118.         if (subsession == NULL /* means: aggregated operation */  
  119.                 || subsession == fStreamStates[i].subsession) {  
  120.             unsigned short rtpSeqNum = 0;  
  121.             unsigned rtpTimestamp = 0;  
  122.             //启动流   
  123.             fStreamStates[i].subsession->startStream(fOurSessionId,  
  124.                     fStreamStates[i].streamToken,  
  125.                     (TaskFunc*) noteClientLiveness, this, rtpSeqNum,  
  126.                     rtpTimestamp, handleAlternativeRequestByte, this);  
  127.             const char *urlSuffix = fStreamStates[i].subsession->trackId();  
  128.             char* prevRTPInfo = rtpInfo;  
  129.             unsigned rtpInfoSize = rtpInfoFmtSize + strlen(prevRTPInfo) + 1  
  130.                     + rtspURLSize + strlen(urlSuffix) + 5 /*max unsigned short len*/  
  131.             + 10 /*max unsigned (32-bit) len*/  
  132.             + 2 /*allows for trailing \r\n at final end of string*/;  
  133.             rtpInfo = new char[rtpInfoSize];  
  134.             sprintf(rtpInfo, rtpInfoFmt, prevRTPInfo,  
  135.                     numRTPInfoItems++ == 0 ? "" : ",", rtspURL, urlSuffix,  
  136.                     rtpSeqNum, rtpTimestamp);  
  137.             delete[] prevRTPInfo;  
  138.         }  
  139.     }  
  140.     if (numRTPInfoItems == 0) {  
  141.         rtpInfo[0] = '\0';  
  142.     } else {  
  143.         unsigned rtpInfoLen = strlen(rtpInfo);  
  144.         rtpInfo[rtpInfoLen] = '\r';  
  145.         rtpInfo[rtpInfoLen + 1] = '\n';  
  146.         rtpInfo[rtpInfoLen + 2] = '\0';  
  147.     }  
  148.   
  149.   
  150.     // Fill in the response:   
  151.     snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,  
  152.             "RTSP/1.0 200 OK\r\n"  
  153.                     "CSeq: %s\r\n"  
  154.                     "%s"  
  155.                     "%s"  
  156.                     "%s"  
  157.                     "Session: %08X\r\n"  
  158.                     "%s\r\n", cseq, dateHeader(), scaleHeader, rangeHeader,  
  159.             fOurSessionId, rtpInfo);  
  160.     delete[] rtpInfo;  
  161.     delete[] rangeHeader;  
  162.     delete[] scaleHeader;  
  163.     delete[] rtspURL;  
  164. }  


有个问题,如果这个streamToken使用的是已存在的(还记得ReuseFirstSource吗),为它再次调用startStream()时,究竟会做什么呢?你猜啊!呵呵我猜吧:应该是把这个客户端的地址和rtp/rtcp端口传给rtp server的groupSocket,rtp server自然就开始向这个客户端发送数据了。是不是这样尼?看代码吧:
[cpp] view plain copy print ?
  1. void StreamState::startPlaying(  
  2.         Destinations* dests,  
  3.         TaskFunc* rtcpRRHandler,  
  4.         void* rtcpRRHandlerClientData,  
  5.         ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,  
  6.         void* serverRequestAlternativeByteHandlerClientData)   
  7. {  
  8.     //目标ip address rtp && rtcp port   
  9.     if (dests == NULL)  
  10.         return;  
  11.   
  12.   
  13.     //创建RTCPInstance对象,以计算和收发RTCP包  
  14.     if (fRTCPInstance == NULL && fRTPSink != NULL) {  
  15.         // Create (and start) a 'RTCP instance' for this RTP sink:  
  16.         fRTCPInstance = RTCPInstance::createNew(fRTPSink->envir(), fRTCPgs,  
  17.                 fTotalBW, (unsigned char*) fMaster.fCNAME, fRTPSink,  
  18.                 NULL /* we're a server */);  
  19.         // Note: This starts RTCP running automatically  
  20.     }  
  21.   
  22.   
  23.     if (dests->isTCP) {  
  24.         //如果RTP over TCP   
  25.         // Change RTP and RTCP to use the TCP socket instead of UDP:  
  26.         if (fRTPSink != NULL) {  
  27.             fRTPSink->addStreamSocket(dests->tcpSocketNum, dests->rtpChannelId);  
  28.             fRTPSink->setServerRequestAlternativeByteHandler(  
  29.                     dests->tcpSocketNum, serverRequestAlternativeByteHandler,  
  30.                     serverRequestAlternativeByteHandlerClientData);  
  31.         }  
  32.         if (fRTCPInstance != NULL) {  
  33.             fRTCPInstance->addStreamSocket(dests->tcpSocketNum,  
  34.                     dests->rtcpChannelId);  
  35.             fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum,  
  36.                     dests->rtcpChannelId, rtcpRRHandler,  
  37.                     rtcpRRHandlerClientData);  
  38.         }  
  39.     } else {  
  40.         //向RTP和RTCP的groupsocket增加这个目标   
  41.         // Tell the RTP and RTCP 'groupsocks' about this destination  
  42.         // (in case they don't already have it):  
  43.         if (fRTPgs != NULL)  
  44.             fRTPgs->addDestination(dests->addr, dests->rtpPort);  
  45.         if (fRTCPgs != NULL)  
  46.             fRTCPgs->addDestination(dests->addr, dests->rtcpPort);  
  47.         if (fRTCPInstance != NULL) {  
  48.             fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr,  
  49.                     dests->rtcpPort, rtcpRRHandler, rtcpRRHandlerClientData);  
  50.         }  
  51.     }  
  52.   
  53.   
  54.     if (!fAreCurrentlyPlaying && fMediaSource != NULL) {  
  55.         //如果还没有启动传输,现在启动之。   
  56.         if (fRTPSink != NULL) {  
  57.             fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState,  
  58.                     this);  
  59.             fAreCurrentlyPlaying = True;  
  60.         } else if (fUDPSink != NULL) {  
  61.             fUDPSink->startPlaying(*fMediaSource, afterPlayingStreamState,this);  
  62.             fAreCurrentlyPlaying = True;  
  63.         }  
  64.     }  
  65. }  



嘿嘿,还真的这样嘀!




 

七 RTP打包与发送


rtp传送开始于函数:MediaSink::startPlaying()。想想也有道理,应是sink跟source要数据,所以从sink上调用startplaying(嘿嘿,相当于directshow的拉模式)。


看一下这个函数:

[cpp] view plain copy print ?
  1. Boolean MediaSink::startPlaying(MediaSource& source,  
  2.         afterPlayingFunc* afterFunc, void* afterClientData)  
  3. {  
  4.     //参数afterFunc是在播放结束时才被调用。   
  5.     // Make sure we're not already being played:  
  6.     if (fSource != NULL) {  
  7.         envir().setResultMsg("This sink is already being played");  
  8.         return False;  
  9.     }  
  10.   
  11.   
  12.     // Make sure our source is compatible:  
  13.     if (!sourceIsCompatibleWithUs(source)) {  
  14.         envir().setResultMsg(  
  15.                 "MediaSink::startPlaying(): source is not compatible!");  
  16.         return False;  
  17.     }  
  18.     //记下一些要使用的对象   
  19.     fSource = (FramedSource*) &source;  
  20.   
  21.   
  22.     fAfterFunc = afterFunc;  
  23.     fAfterClientData = afterClientData;  
  24.     return continuePlaying();  
  25. }  

为了进一步封装(让继承类少写一些代码),搞出了一个虚函数continuePlaying()。让我们来看一下:

[cpp] view plain copy print ?
  1. Boolean MultiFramedRTPSink::continuePlaying() {  
  2.     // Send the first packet.   
  3.     // (This will also schedule any future sends.)  
  4.     buildAndSendPacket(True);  
  5.     return True;  
  6. }  

MultiFramedRTPSink是与帧有关的类,其实它要求每次必须从source获得一个帧的数据,所以才叫这个name。可以看到continuePlaying()完全被buildAndSendPacket()代替。看一下buildAndSendPacket():

[cpp] view plain copy print ?
  1. void MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket)   
  2. {  
  3.     //此函数中主要是准备rtp包的头,为一些需要跟据实际数据改变的字段留出位置。  
  4.     fIsFirstPacket = isFirstPacket;  
  5.   
  6.   
  7.     // Set up the RTP header:   
  8.     unsigned rtpHdr = 0x80000000; // RTP version 2; marker ('M') bit not set (by default; it can be set later)  
  9.     rtpHdr |= (fRTPPayloadType << 16);  
  10.     rtpHdr |= fSeqNo; // sequence number   
  11.     fOutBuf->enqueueWord(rtpHdr);//向包中加入一个字  
  12.   
  13.   
  14.     // Note where the RTP timestamp will go.  
  15.     // (We can't fill this in until we start packing payload frames.)  
  16.     fTimestampPosition = fOutBuf->curPacketSize();  
  17.     fOutBuf->skipBytes(4); // leave a hole for the timestamp 在缓冲中空出时间戳的位置  
  18.   
  19.   
  20.     fOutBuf->enqueueWord(SSRC());   
  21.   
  22.   
  23.     // Allow for a special, payload-format-specific header following the  
  24.     // RTP header:   
  25.     fSpecialHeaderPosition = fOutBuf->curPacketSize();  
  26.     fSpecialHeaderSize = specialHeaderSize();  
  27.     fOutBuf->skipBytes(fSpecialHeaderSize);  
  28.   
  29.   
  30.     // Begin packing as many (complete) frames into the packet as we can:  
  31.     fTotalFrameSpecificHeaderSizes = 0;  
  32.     fNoFramesLeft = False;  
  33.     fNumFramesUsedSoFar = 0; // 一个包中已打入的帧数。  
  34.     //头准备好了,再打包帧数据   
  35.     packFrame();  
  36. }  

继续看packFrame():

[cpp] view plain copy print ?
  1. void MultiFramedRTPSink::packFrame()  
  2. {  
  3.     // First, see if we have an overflow frame that was too big for the last pkt  
  4.     if (fOutBuf->haveOverflowData()) {  
  5.         //如果有帧数据,则使用之。OverflowData是指上次打包时剩下的帧数据,因为一个包可能容纳不了一个帧。  
  6.         // Use this frame before reading a new one from the source  
  7.         unsigned frameSize = fOutBuf->overflowDataSize();  
  8.         struct timeval presentationTime = fOutBuf->overflowPresentationTime();  
  9.         unsigned durationInMicroseconds =fOutBuf->overflowDurationInMicroseconds();  
  10.         fOutBuf->useOverflowData();  
  11.   
  12.   
  13.         afterGettingFrame1(frameSize, 0, presentationTime,durationInMicroseconds);  
  14.     } else {  
  15.         //一点帧数据都没有,跟source要吧。   
  16.         // Normal case: we need to read a new frame from the source  
  17.         if (fSource == NULL)  
  18.             return;  
  19.   
  20.   
  21.         //更新缓冲中的一些位置   
  22.         fCurFrameSpecificHeaderPosition = fOutBuf->curPacketSize();  
  23.         fCurFrameSpecificHeaderSize = frameSpecificHeaderSize();  
  24.         fOutBuf->skipBytes(fCurFrameSpecificHeaderSize);  
  25.         fTotalFrameSpecificHeaderSizes += fCurFrameSpecificHeaderSize;  
  26.   
  27.   
  28.         //从source获取下一帧   
  29.         fSource->getNextFrame(fOutBuf->curPtr(),//新数据存放开始的位置  
  30.                 fOutBuf->totalBytesAvailable(),//缓冲中空余的空间大小  
  31.                 afterGettingFrame,  //因为可能source中的读数据函数会被放在任务调度中,所以把获取帧后应调用的函数传给source  
  32.                 this,  
  33.                 ourHandleClosure, //这个是source结束时(比如文件读完了)要调用的函数。  
  34.                 this);  
  35.     }  
  36. }  

可以想像下面就是source从文件(或某个设备)中读取一帧数据,读完后返回给sink,当然不是从函数返回了,而是以调用afterGettingFrame这个回调函数的方式。所以下面看一下afterGettingFrame():

[cpp] view plain copy print ?
  1. void MultiFramedRTPSink::afterGettingFrame(void* clientData,  
  2.         unsigned numBytesRead, unsigned numTruncatedBytes,  
  3.         struct timeval presentationTime, unsigned durationInMicroseconds)  
  4. {  
  5.     MultiFramedRTPSink* sink = (MultiFramedRTPSink*) clientData;  
  6.     sink->afterGettingFrame1(numBytesRead, numTruncatedBytes, presentationTime,  
  7.             durationInMicroseconds);  
  8. }  

没什么可看的,只是过度为调用成员函数,所以afterGettingFrame1()才是重点:

[cpp] view plain copy print ?
  1. void MultiFramedRTPSink::afterGettingFrame1(  
  2.         unsigned frameSize,  
  3.         unsigned numTruncatedBytes,  
  4.         struct timeval presentationTime,  
  5.         unsigned durationInMicroseconds)  
  6. {  
  7.     if (fIsFirstPacket) {  
  8.         // Record the fact that we're starting to play now:  
  9.         gettimeofday(&fNextSendTime, NULL);  
  10.     }  
  11.   
  12.   
  13.     //如果给予一帧的缓冲不够大,就会发生截断一帧数据的现象。但也只能提示一下用户  
  14.     if (numTruncatedBytes > 0) {  
  15.   
  16.   
  17.         unsigned const bufferSize = fOutBuf->totalBytesAvailable();  
  18.         envir()  
  19.                 << "MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size ("  
  20.                 << bufferSize  
  21.                 << ").  "  
  22.                 << numTruncatedBytes  
  23.                 << " bytes of trailing data was dropped!  Correct this by increasing \"OutPacketBuffer::maxSize\" to at least "  
  24.                 << OutPacketBuffer::maxSize + numTruncatedBytes  
  25.                 << ", *before* creating this 'RTPSink'.  (Current value is "  
  26.                 << OutPacketBuffer::maxSize << ".)\n";  
  27.     }  
  28.     unsigned curFragmentationOffset = fCurFragmentationOffset;  
  29.     unsigned numFrameBytesToUse = frameSize;  
  30.     unsigned overflowBytes = 0;  
  31.   
  32.   
  33.     //如果包只已经打入帧数据了,并且不能再向这个包中加数据了,则把新获得的帧数据保存下来。  
  34.     // If we have already packed one or more frames into this packet,  
  35.     // check whether this new frame is eligible to be packed after them.  
  36.     // (This is independent of whether the packet has enough room for this  
  37.     // new frame; that check comes later.)  
  38.     if (fNumFramesUsedSoFar > 0) {  
  39.         //如果包中已有了一个帧,并且不允许再打入新的帧了,则只记录下新的帧。  
  40.         if ((fPreviousFrameEndedFragmentation && !allowOtherFramesAfterLastFragment())  
  41.                 || !frameCanAppearAfterPacketStart(fOutBuf->curPtr(), frameSize))  
  42.         {  
  43.             // Save away this frame for next time:  
  44.             numFrameBytesToUse = 0;  
  45.             fOutBuf->setOverflowData(fOutBuf->curPacketSize(), frameSize,  
  46.                     presentationTime, durationInMicroseconds);  
  47.         }  
  48.     }  
  49.       
  50.     //表示当前打入的是否是上一个帧的最后一块数据。   
  51.     fPreviousFrameEndedFragmentation = False;  
  52.   
  53.   
  54.     //下面是计算获取的帧中有多少数据可以打到当前包中,剩下的数据就作为overflow数据保存下来。  
  55.     if (numFrameBytesToUse > 0) {  
  56.         // Check whether this frame overflows the packet  
  57.         if (fOutBuf->wouldOverflow(frameSize)) {  
  58.             // Don't use this frame now; instead, save it as overflow data, and  
  59.             // send it in the next packet instead.  However, if the frame is too  
  60.             // big to fit in a packet by itself, then we need to fragment it (and  
  61.             // use some of it in this packet, if the payload format permits this.)  
  62.             if (isTooBigForAPacket(frameSize)  
  63.                     && (fNumFramesUsedSoFar == 0 || allowFragmentationAfterStart())) {  
  64.                 // We need to fragment this frame, and use some of it now:  
  65.                 overflowBytes = computeOverflowForNewFrame(frameSize);  
  66.                 numFrameBytesToUse -= overflowBytes;  
  67.                 fCurFragmentationOffset += numFrameBytesToUse;  
  68.             } else {  
  69.                 // We don't use any of this frame now:  
  70.                 overflowBytes = frameSize;  
  71.                 numFrameBytesToUse = 0;  
  72.             }  
  73.             fOutBuf->setOverflowData(fOutBuf->curPacketSize() + numFrameBytesToUse,  
  74.                     overflowBytes, presentationTime, durationInMicroseconds);  
  75.         } else if (fCurFragmentationOffset > 0) {  
  76.             // This is the last fragment of a frame that was fragmented over  
  77.             // more than one packet.  Do any special handling for this case:  
  78.             fCurFragmentationOffset = 0;  
  79.             fPreviousFrameEndedFragmentation = True;  
  80.         }  
  81.     }  
  82.   
  83.   
  84.       
  85.     if (numFrameBytesToUse == 0 && frameSize > 0) {  
  86.         //如果包中有数据并且没有新数据了,则发送之。(这种情况好像很难发生啊!)  
  87.         // Send our packet now, because we have filled it up:  
  88.         sendPacketIfNecessary();  
  89.     } else {  
  90.         //需要向包中打入数据。   
  91.           
  92.         // Use this frame in our outgoing packet:  
  93.         unsigned char* frameStart = fOutBuf->curPtr();  
  94.         fOutBuf->increment(numFrameBytesToUse);  
  95.         // do this now, in case "doSpecialFrameHandling()" calls "setFramePadding()" to append padding bytes  
  96.   
  97.   
  98.         // Here's where any payload format specific processing gets done:  
  99.         doSpecialFrameHandling(curFragmentationOffset, frameStart,  
  100.                 numFrameBytesToUse, presentationTime, overflowBytes);  
  101.   
  102.   
  103.         ++fNumFramesUsedSoFar;  
  104.   
  105.   
  106.         // Update the time at which the next packet should be sent, based  
  107.         // on the duration of the frame that we just packed into it.  
  108.         // However, if this frame has overflow data remaining, then don't  
  109.         // count its duration yet.  
  110.         if (overflowBytes == 0) {  
  111.             fNextSendTime.tv_usec += durationInMicroseconds;  
  112.             fNextSendTime.tv_sec += fNextSendTime.tv_usec / 1000000;  
  113.             fNextSendTime.tv_usec %= 1000000;  
  114.         }  
  115.   
  116.   
  117.         //如果需要,就发出包,否则继续打入数据。   
  118.         // Send our packet now if (i) it's already at our preferred size, or  
  119.         // (ii) (heuristic) another frame of the same size as the one we just  
  120.         //      read would overflow the packet, or  
  121.         // (iii) it contains the last fragment of a fragmented frame, and we  
  122.         //      don't allow anything else to follow this or  
  123.         // (iv) one frame per packet is allowed:  
  124.         if (fOutBuf->isPreferredSize()  
  125.                 || fOutBuf->wouldOverflow(numFrameBytesToUse)  
  126.                 || (fPreviousFrameEndedFragmentation  
  127.                         && !allowOtherFramesAfterLastFragment())  
  128.                 || !frameCanAppearAfterPacketStart(  
  129.                         fOutBuf->curPtr() - frameSize, frameSize)) {  
  130.             // The packet is ready to be sent now  
  131.             sendPacketIfNecessary();  
  132.         } else {  
  133.             // There's room for more frames; try getting another:  
  134.             packFrame();  
  135.         }  
  136.     }  
  137. }  


看一下发送数据的函数:

[cpp] view plain copy print ?
  1. void MultiFramedRTPSink::sendPacketIfNecessary()  
  2. {  
  3.     //发送包   
  4.     if (fNumFramesUsedSoFar > 0) {  
  5.         // Send the packet:   
  6. #ifdef TEST_LOSS   
  7.         if ((our_random()%10) != 0) // simulate 10% packet loss #####  
  8. #endif   
  9.         if (!fRTPInterface.sendPacket(fOutBuf->packet(),fOutBuf->curPacketSize())) {  
  10.             // if failure handler has been specified, call it  
  11.             if (fOnSendErrorFunc != NULL)  
  12.                 (*fOnSendErrorFunc)(fOnSendErrorData);  
  13.         }  
  14.         ++fPacketCount;  
  15.         fTotalOctetCount += fOutBuf->curPacketSize();  
  16.         fOctetCount += fOutBuf->curPacketSize() - rtpHeaderSize  
  17.                 - fSpecialHeaderSize - fTotalFrameSpecificHeaderSizes;  
  18.   
  19.   
  20.         ++fSeqNo; // for next time   
  21.     }  
  22.   
  23.   
  24.     //如果还有剩余数据,则调整缓冲区   
  25.     if (fOutBuf->haveOverflowData()  
  26.             && fOutBuf->totalBytesAvailable() > fOutBuf->totalBufferSize() / 2) {  
  27.         // Efficiency hack: Reset the packet start pointer to just in front of  
  28.         // the overflow data (allowing for the RTP header and special headers),  
  29.         // so that we probably don't have to "memmove()" the overflow data  
  30.         // into place when building the next packet:  
  31.         unsigned newPacketStart = fOutBuf->curPacketSize()-   
  32.                 (rtpHeaderSize + fSpecialHeaderSize + frameSpecificHeaderSize());  
  33.         fOutBuf->adjustPacketStart(newPacketStart);  
  34.     } else {  
  35.         // Normal case: Reset the packet start pointer back to the start:  
  36.         fOutBuf->resetPacketStart();  
  37.     }  
  38.     fOutBuf->resetOffset();  
  39.     fNumFramesUsedSoFar = 0;  
  40.   
  41.   
  42.     if (fNoFramesLeft) {  
  43.         //如果再没有数据了,则结束之   
  44.         // We're done:   
  45.         onSourceClosure(this);  
  46.     } else {  
  47.         //如果还有数据,则在下一次需要发送的时间再次打包发送。  
  48.         // We have more frames left to send.  Figure out when the next frame  
  49.         // is due to start playing, then make sure that we wait this long before  
  50.         // sending the next packet.   
  51.         struct timeval timeNow;  
  52.         gettimeofday(&timeNow, NULL);  
  53.         int secsDiff = fNextSendTime.tv_sec - timeNow.tv_sec;  
  54.         int64_t uSecondsToGo = secsDiff * 1000000  
  55.                 + (fNextSendTime.tv_usec - timeNow.tv_usec);  
  56.         if (uSecondsToGo < 0 || secsDiff < 0) { // sanity check: Make sure that the time-to-delay is non-negative:  
  57.             uSecondsToGo = 0;  
  58.         }  
  59.   
  60.   
  61.         // Delay this amount of time:  
  62.         nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo,  
  63.                 (TaskFunc*) sendNext, this);  
  64.     }  
  65. }  



可以看到为了延迟包的发送,使用了delay task来执行下次打包发送任务。

sendNext()中又调用了buildAndSendPacket()函数,呵呵,又是一个圈圈。

总结一下调用过程:

live555学习2(转)_第1张图片


最后,再说明一下包缓冲区的使用:

MultiFramedRTPSink中的帧数据和包缓冲区共用一个,只是用一些额外的变量指明缓冲区中属于包的部分以及属于帧数据的部分(包以外的数据叫做overflow data)。它有时会把overflow data以mem move的方式移到包开始的位置,有时把包的开始位置直接设置到overflow data开始的地方。那么这个缓冲的大小是怎样确定的呢?是跟据调用者指定的的一个最大的包的大小+60000算出的。这个地方把我搞胡涂了:如果一次从source获取一个帧的话,那这个缓冲应设为不小于最大的一个帧的大小才是,为何是按包的大小设置呢?可以看到,当缓冲不够时只是提示一下:

[cpp] view plain copy print ?
  1. if (numTruncatedBytes > 0) {  
  2.   
  3.   
  4.     unsigned const bufferSize = fOutBuf->totalBytesAvailable();  
  5.     envir()  
  6.             << "MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size ("  
  7.             << bufferSize  
  8.             << ").  "  
  9.             << numTruncatedBytes  
  10.             << " bytes of trailing data was dropped!  Correct this by increasing \"OutPacketBuffer::maxSize\" to at least "  
  11.             << OutPacketBuffer::maxSize + numTruncatedBytes  
  12.             << ", *before* creating this 'RTPSink'.  (Current value is "  
  13.             << OutPacketBuffer::maxSize << ".)\n";  
  14. }  

当然此时不会出错,但有可能导致时间戳计算不准,或增加时间戳计算与source端处理的复杂性(因为一次取一帧时间戳是很好计算的)。

 

 

 

 

八 RTSPClient分析

有RTSPServer,当然就要有RTSPClient。
如果按照Server端的架构,想一下Client端各部分的组成可能是这样:
因为要连接RTSP server,所以RTSPClient要有TCP socket。当获取到server端的DESCRIBE后,应建立一个对应于ServerMediaSession的ClientMediaSession。对应每个Track,ClientMediaSession中应建立ClientMediaSubsession。当建立RTP Session时,应分别为所拥有的Track发送SETUP请求连接,在获取回应后,分别为所有的track建立RTP socket,然后请求PLAY,然后开始传输数据。事实是这样吗?只能分析代码了。


testProgs中的OpenRTSP是典型的RTSPClient示例,所以分析它吧。
main()函数在playCommon.cpp文件中。main()的流程比较简单,跟服务端差别不大:建立任务计划对象--建立环境对象--处理用户输入的参数(RTSP地址)--创建RTSPClient实例--发出第一个RTSP请求(可能是OPTIONS也可能是DESCRIBE)--进入Loop。


RTSP的tcp连接是在发送第一个RTSP请求时才建立的,在RTSPClient的那几个发请求的函数sendXXXXXXCommand()中最终都调用sendRequest(),sendRequest()中会跟据情况建立起TCP连接。在建立连接时马上向任务计划中加入处理从这个TCP接收数据的socket handler:RTSPClient::incomingDataHandler()。
下面就是发送RTSP请求,OPTIONS就不必看了,从请求DESCRIBE开始:

[cpp] view plain copy print ?
  1. void getSDPDescription(RTSPClient::responseHandler* afterFunc)  
  2. {  
  3.     ourRTSPClient->sendDescribeCommand(afterFunc, ourAuthenticator);  
  4. }  
  5. unsigned RTSPClient::sendDescribeCommand(responseHandler* responseHandler,  
  6.         Authenticator* authenticator)  
  7. {  
  8.     if (authenticator != NULL)  
  9.         fCurrentAuthenticator = *authenticator;  
  10.     return sendRequest(new RequestRecord(++fCSeq, "DESCRIBE", responseHandler));  
  11. }  
参数responseHandler是调用者提供的回调函数,用于在处理完请求的回应后再调用之。并且在这个回调函数中会发出下一个请求--所有的请求都是这样依次发出的。使用回调函数的原因主要是因为socket的发送与接收不是同步进行的。类RequestRecord就代表一个请求,它不但保存了RTSP请求相关的信息,而且保存了请求完成后的回调函数--就是responseHandler。有些请求发出时还没建立tcp连接,不能立即发送,则加入fRequestsAwaitingConnection队列;有些发出后要等待Server端的回应,就加入fRequestsAwaitingResponse队列,当收到回应后再从队列中把它取出。
由于RTSPClient::sendRequest()太复杂,就不列其代码了,其无非是建立起RTSP请求字符串然后用TCP socket发送之。


现在看一下收到DESCRIBE的回应后如何处理它。理论上是跟据媒体信息建立起MediaSession了,看看是不是这样:

[cpp] view plain copy print ?
  1. void continueAfterDESCRIBE(RTSPClient*, int resultCode, char* resultString)  
  2. {  
  3.     char* sdpDescription = resultString;  
  4.     //跟据SDP创建MediaSession。   
  5.     // Create a media session object from this SDP description:  
  6.     session = MediaSession::createNew(*env, sdpDescription);  
  7.     delete[] sdpDescription;  
  8.   
  9.     // Then, setup the "RTPSource"s for the session:  
  10.     MediaSubsessionIterator iter(*session);  
  11.     MediaSubsession *subsession;  
  12.     Boolean madeProgress = False;  
  13.     char const* singleMediumToTest = singleMedium;  
  14.     //循环所有的MediaSubsession,为每个设置其RTPSource的参数  
  15.     while ((subsession = iter.next()) != NULL) {  
  16.         //初始化subsession,在其中会建立RTP/RTCP socket以及RTPSource。  
  17.         if (subsession->initiate(simpleRTPoffsetArg)) {  
  18.             madeProgress = True;  
  19.             if (subsession->rtpSource() != NULL) {  
  20.                 // Because we're saving the incoming data, rather than playing  
  21.                 // it in real time, allow an especially large time threshold  
  22.                 // (1 second) for reordering misordered incoming packets:  
  23.                 unsigned const thresh = 1000000; // 1 second  
  24.                 subsession->rtpSource()->setPacketReorderingThresholdTime(thresh);  
  25.   
  26.                 // Set the RTP source's OS socket buffer size as appropriate - either if we were explicitly asked (using -B),  
  27.                 // or if the desired FileSink buffer size happens to be larger than the current OS socket buffer size.  
  28.                 // (The latter case is a heuristic, on the assumption that if the user asked for a large FileSink buffer size,  
  29.                 // then the input data rate may be large enough to justify increasing the OS socket buffer size also.)  
  30.                 int socketNum = subsession->rtpSource()->RTPgs()->socketNum();  
  31.                 unsigned curBufferSize = getReceiveBufferSize(*env,socketNum);  
  32.                 if (socketInputBufferSize > 0 || fileSinkBufferSize > curBufferSize) {  
  33.                     unsigned newBufferSize = socketInputBufferSize > 0 ?   
  34.                         socketInputBufferSize : fileSinkBufferSize;  
  35.                     newBufferSize = setReceiveBufferTo(*env, socketNum, newBufferSize);  
  36.                     if (socketInputBufferSize > 0) { // The user explicitly asked for the new socket buffer size; announce it:  
  37.                         *env  
  38.                                 << "Changed socket receive buffer size for the \""  
  39.                                 << subsession->mediumName() << "/"  
  40.                                 << subsession->codecName()  
  41.                                 << "\" subsession from " << curBufferSize  
  42.                                 << " to " << newBufferSize << " bytes\n";  
  43.                     }  
  44.                 }  
  45.             }  
  46.         }  
  47.     }  
  48.     if (!madeProgress)  
  49.         shutdown();  
  50.   
  51.     // Perform additional 'setup' on each subsession, before playing them:  
  52.     //下一步就是发送SETUP请求了。需要为每个Track分别发送一次。   
  53.     setupStreams();  
  54. }  
此函数被删掉很多枝叶,所以发现与原版不同请不要惊掉大牙。
的确在DESCRIBE回应后建立起了MediaSession,而且我们发现Client端的MediaSession不叫ClientMediaSesson,SubSession亦不是。我现在很想看看MediaSession与MediaSubsession的建立过程:

[cpp] view plain copy print ?
  1. MediaSession* MediaSession::createNew(UsageEnvironment& env,char const* sdpDescription)  
  2. {  
  3.     MediaSession* newSession = new MediaSession(env);  
  4.     if (newSession != NULL) {  
  5.         if (!newSession->initializeWithSDP(sdpDescription)) {  
  6.             delete newSession;  
  7.             return NULL;  
  8.         }  
  9.     }  
  10.   
  11.     return newSession;  
  12. }  

我可以告诉你,MediaSession的构造函数没什么可看的,那么就来看initializeWithSDP():
内容太多,不必看了,我大体说说吧:就是处理SDP,跟据每一行来初始化一些变量。当遇到"m="行时,就建立一个MediaSubsession,然后再处理这一行之下,下一个"m="行之上的行们,用这些参数初始化MediaSubsession的变量。循环往复,直到尽头。然而这其中并没有建立RTP socket。我们发现在continueAfterDESCRIBE()中,创建MediaSession之后又调用了subsession->initiate(simpleRTPoffsetArg),那么socket是不是在它里面创建的呢?look:

[cpp] view plain copy print ?
  1. Boolean MediaSubsession::initiate(int useSpecialRTPoffset)  
  2. {  
  3.     if (fReadSource != NULL)  
  4.         return True; // has already been initiated  
  5.   
  6.     do {  
  7.         if (fCodecName == NULL) {  
  8.             env().setResultMsg("Codec is unspecified");  
  9.             break;  
  10.         }  
  11.   
  12.         //创建RTP/RTCP sockets   
  13.         // Create RTP and RTCP 'Groupsocks' on which to receive incoming data.  
  14.         // (Groupsocks will work even for unicast addresses)  
  15.         struct in_addr tempAddr;  
  16.         tempAddr.s_addr = connectionEndpointAddress();  
  17.         // This could get changed later, as a result of a RTSP "SETUP"  
  18.   
  19.         if (fClientPortNum != 0) {  
  20.             //当server端指定了建议的client端口   
  21.             // The sockets' port numbers were specified for us.  Use these:  
  22.             fClientPortNum = fClientPortNum & ~1; // even  
  23.             if (isSSM()) {  
  24.                 fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr,  
  25.                         fClientPortNum);  
  26.             } else {  
  27.                 fRTPSocket = new Groupsock(env(), tempAddr, fClientPortNum,  
  28.                         255);  
  29.             }  
  30.             if (fRTPSocket == NULL) {  
  31.                 env().setResultMsg("Failed to create RTP socket");  
  32.                 break;  
  33.             }  
  34.   
  35.             // Set our RTCP port to be the RTP port +1  
  36.             portNumBits const rtcpPortNum = fClientPortNum | 1;  
  37.             if (isSSM()) {  
  38.                 fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr,  
  39.                         rtcpPortNum);  
  40.             } else {  
  41.                 fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);  
  42.             }  
  43.             if (fRTCPSocket == NULL) {  
  44.                 char tmpBuf[100];  
  45.                 sprintf(tmpBuf, "Failed to create RTCP socket (port %d)",  
  46.                         rtcpPortNum);  
  47.                 env().setResultMsg(tmpBuf);  
  48.                 break;  
  49.             }  
  50.         } else {  
  51.             //Server端没有指定client端口,我们自己找一个。之所以做的这样复杂,是为了能找到连续的两个端口  
  52.             //RTP/RTCP的端口号不是要连续吗?还记得不?   
  53.             // Port numbers were not specified in advance, so we use ephemeral port numbers.  
  54.             // Create sockets until we get a port-number pair (even: RTP; even+1: RTCP).  
  55.             // We need to make sure that we don't keep trying to use the same bad port numbers over and over again.  
  56.             // so we store bad sockets in a table, and delete them all when we're done.  
  57.             HashTable* socketHashTable = HashTable::create(ONE_WORD_HASH_KEYS);  
  58.             if (socketHashTable == NULL)  
  59.                 break;  
  60.             Boolean success = False;  
  61.             NoReuse dummy; // ensures that our new ephemeral port number won't be one that's already in use  
  62.   
  63.             while (1) {  
  64.                 // Create a new socket:   
  65.                 if (isSSM()) {  
  66.                     fRTPSocket = new Groupsock(env(), tempAddr,  
  67.                             fSourceFilterAddr, 0);  
  68.                 } else {  
  69.                     fRTPSocket = new Groupsock(env(), tempAddr, 0, 255);  
  70.                 }  
  71.                 if (fRTPSocket == NULL) {  
  72.                     env().setResultMsg(  
  73.                             "MediaSession::initiate(): unable to create RTP and RTCP sockets");  
  74.                     break;  
  75.                 }  
  76.   
  77.                 // Get the client port number, and check whether it's even (for RTP):  
  78.                 Port clientPort(0);  
  79.                 if (!getSourcePort(env(), fRTPSocket->socketNum(),  
  80.                         clientPort)) {  
  81.                     break;  
  82.                 }  
  83.                 fClientPortNum = ntohs(clientPort.num());  
  84.                 if ((fClientPortNum & 1) != 0) { // it's odd  
  85.                     // Record this socket in our table, and keep trying:  
  86.                     unsigned key = (unsigned) fClientPortNum;  
  87.                     Groupsock* existing = (Groupsock*) socketHashTable->Add(  
  88.                             (char const*) key, fRTPSocket);  
  89.                     delete existing; // in case it wasn't NULL  
  90.                     continue;  
  91.                 }  
  92.   
  93.                 // Make sure we can use the next (i.e., odd) port number, for RTCP:  
  94.                 portNumBits rtcpPortNum = fClientPortNum | 1;  
  95.                 if (isSSM()) {  
  96.                     fRTCPSocket = new Groupsock(env(), tempAddr,  
  97.                             fSourceFilterAddr, rtcpPortNum);  
  98.                 } else {  
  99.                     fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum,  
  100.                             255);  
  101.                 }  
  102.                 if (fRTCPSocket != NULL && fRTCPSocket->socketNum() >= 0) {  
  103.                     // Success! Use these two sockets.  
  104.                     success = True;  
  105.                     break;  
  106.                 } else {  
  107.                     // We couldn't create the RTCP socket (perhaps that port number's already in use elsewhere?).  
  108.                     delete fRTCPSocket;  
  109.   
  110.                     // Record the first socket in our table, and keep trying:  
  111.                     unsigned key = (unsigned) fClientPortNum;  
  112.                     Groupsock* existing = (Groupsock*) socketHashTable->Add(  
  113.                             (char const*) key, fRTPSocket);  
  114.                     delete existing; // in case it wasn't NULL  
  115.                     continue;  
  116.                 }  
  117.             }  
  118.   
  119.             // Clean up the socket hash table (and contents):  
  120.             Groupsock* oldGS;  
  121.             while ((oldGS = (Groupsock*) socketHashTable->RemoveNext()) != NULL) {  
  122.                 delete oldGS;  
  123.             }  
  124.             delete socketHashTable;  
  125.   
  126.             if (!success)  
  127.                 break// a fatal error occurred trying to create the RTP and RTCP sockets; we can't continue  
  128.         }  
  129.   
  130.         // Try to use a big receive buffer for RTP - at least 0.1 second of  
  131.         // specified bandwidth and at least 50 KB  
  132.         unsigned rtpBufSize = fBandwidth * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes  
  133.         if (rtpBufSize < 50 * 1024)  
  134.             rtpBufSize = 50 * 1024;  
  135.         increaseReceiveBufferTo(env(), fRTPSocket->socketNum(), rtpBufSize);  
  136.   
  137.         // ASSERT: fRTPSocket != NULL && fRTCPSocket != NULL  
  138.         if (isSSM()) {  
  139.             // Special case for RTCP SSM: Send RTCP packets back to the source via unicast:  
  140.             fRTCPSocket->changeDestinationParameters(fSourceFilterAddr, 0, ~0);  
  141.         }  
  142.   
  143.         //创建RTPSource的地方   
  144.         // Create "fRTPSource" and "fReadSource":  
  145.         if (!createSourceObjects(useSpecialRTPoffset))  
  146.             break;  
  147.   
  148.         if (fReadSource == NULL) {  
  149.             env().setResultMsg("Failed to create read source");  
  150.             break;  
  151.         }  
  152.   
  153.         // Finally, create our RTCP instance. (It starts running automatically)  
  154.         if (fRTPSource != NULL) {  
  155.             // If bandwidth is specified, use it and add 5% for RTCP overhead.  
  156.             // Otherwise make a guess at 500 kbps.  
  157.             unsigned totSessionBandwidth =  
  158.                     fBandwidth ? fBandwidth + fBandwidth / 20 : 500;  
  159.             fRTCPInstance = RTCPInstance::createNew(env(), fRTCPSocket,  
  160.                     totSessionBandwidth, (unsigned char const*) fParent.CNAME(),  
  161.                     NULL /* we're a client */, fRTPSource);  
  162.             if (fRTCPInstance == NULL) {  
  163.                 env().setResultMsg("Failed to create RTCP instance");  
  164.                 break;  
  165.             }  
  166.         }  
  167.   
  168.         return True;  
  169.     } while (0);  
  170.   
  171.     //失败时执行到这里   
  172.     delete fRTPSocket;  
  173.     fRTPSocket = NULL;  
  174.     delete fRTCPSocket;  
  175.     fRTCPSocket = NULL;  
  176.     Medium::close(fRTCPInstance);  
  177.     fRTCPInstance = NULL;  
  178.     Medium::close(fReadSource);  
  179.     fReadSource = fRTPSource = NULL;  
  180.     fClientPortNum = 0;  
  181.     return False;  
  182. }  
是的,在其中创建了RTP/RTCP socket并创建了RTPSource,创建RTPSource在函数createSourceObjects()中,看一下:

[cpp] view plain copy print ?
  1. Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset)  
  2. {  
  3.     do {  
  4.         // First, check "fProtocolName"   
  5.         if (strcmp(fProtocolName, "UDP") == 0) {  
  6.             // A UDP-packetized stream (*not* a RTP stream)  
  7.             fReadSource = BasicUDPSource::createNew(env(), fRTPSocket);  
  8.             fRTPSource = NULL; // Note!   
  9.   
  10.             if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream  
  11.                 fReadSource = MPEG2TransportStreamFramer::createNew(env(),  
  12.                         fReadSource);  
  13.                 // this sets "durationInMicroseconds" correctly, based on the PCR values  
  14.             }  
  15.         } else {  
  16.             // Check "fCodecName" against the set of codecs that we support,  
  17.             // and create our RTP source accordingly  
  18.             // (Later make this code more efficient, as this set grows #####)  
  19.             // (Also, add more fmts that can be implemented by SimpleRTPSource#####)  
  20.             Boolean createSimpleRTPSource = False; // by default; can be changed below  
  21.             Boolean doNormalMBitRule = False; // default behavior if "createSimpleRTPSource" is True  
  22.             if (strcmp(fCodecName, "QCELP") == 0) { // QCELP audio  
  23.                 fReadSource = QCELPAudioRTPSource::createNew(env(), fRTPSocket,  
  24.                         fRTPSource, fRTPPayloadFormat, fRTPTimestampFrequency);  
  25.                 // Note that fReadSource will differ from fRTPSource in this case  
  26.             } else if (strcmp(fCodecName, "AMR") == 0) { // AMR audio (narrowband)  
  27.                 fReadSource = AMRAudioRTPSource::createNew(env(), fRTPSocket,  
  28.                         fRTPSource, fRTPPayloadFormat, 0 /*isWideband*/,  
  29.                         fNumChannels, fOctetalign, fInterleaving,  
  30.                         fRobustsorting, fCRC);  
  31.                 // Note that fReadSource will differ from fRTPSource in this case  
  32.             } else if (strcmp(fCodecName, "AMR-WB") == 0) { // AMR audio (wideband)  
  33.                 fReadSource = AMRAudioRTPSource::createNew(env(), fRTPSocket,  
  34.                         fRTPSource, fRTPPayloadFormat, 1 /*isWideband*/,  
  35.                         fNumChannels, fOctetalign, fInterleaving,  
  36.                         fRobustsorting, fCRC);  
  37.                 // Note that fReadSource will differ from fRTPSource in this case  
  38.             } else if (strcmp(fCodecName, "MPA") == 0) { // MPEG-1 or 2 audio  
  39.                 fReadSource = fRTPSource = MPEG1or2AudioRTPSource::createNew(  
  40.                         env(), fRTPSocket, fRTPPayloadFormat,  
  41.                         fRTPTimestampFrequency);  
  42.             } else if (strcmp(fCodecName, "MPA-ROBUST") == 0) { // robust MP3 audio  
  43.                 fRTPSource = MP3ADURTPSource::createNew(env(), fRTPSocket,  
  44.                         fRTPPayloadFormat, fRTPTimestampFrequency);  
  45.                 if (fRTPSource == NULL)  
  46.                     break;  
  47.   
  48.                 // Add a filter that deinterleaves the ADUs after depacketizing them:  
  49.                 MP3ADUdeinterleaver* deinterleaver = MP3ADUdeinterleaver::createNew(  
  50.                         env(), fRTPSource);  
  51.                 if (deinterleaver == NULL)  
  52.                     break;  
  53.   
  54.                 // Add another filter that converts these ADUs to MP3 frames:  
  55.                 fReadSource = MP3FromADUSource::createNew(env(), deinterleaver);  
  56.             } else if (strcmp(fCodecName, "X-MP3-DRAFT-00") == 0) {  
  57.                 // a non-standard variant of "MPA-ROBUST" used by RealNetworks  
  58.                 // (one 'ADU'ized MP3 frame per packet; no headers)  
  59.                 fRTPSource = SimpleRTPSource::createNew(env(), fRTPSocket,  
  60.                         fRTPPayloadFormat, fRTPTimestampFrequency,  
  61.                         "audio/MPA-ROBUST" /*hack*/);  
  62.                 if (fRTPSource == NULL)  
  63.                     break;  
  64.   
  65.                 // Add a filter that converts these ADUs to MP3 frames:  
  66.                 fReadSource = MP3FromADUSource::createNew(env(), fRTPSource,  
  67.                         False /*no ADU header*/);  
  68.             } else if (strcmp(fCodecName, "MP4A-LATM") == 0) { // MPEG-4 LATM audio  
  69.                 fReadSource = fRTPSource = MPEG4LATMAudioRTPSource::createNew(  
  70.                         env(), fRTPSocket, fRTPPayloadFormat,  
  71.                         fRTPTimestampFrequency);  
  72.             } else if (strcmp(fCodecName, "AC3") == 0  
  73.                     || strcmp(fCodecName, "EAC3") == 0) { // AC3 audio  
  74.                 fReadSource = fRTPSource = AC3AudioRTPSource::createNew(env(),  
  75.                         fRTPSocket, fRTPPayloadFormat, fRTPTimestampFrequency);  
  76.             } else if (strcmp(fCodecName, "MP4V-ES") == 0) { // MPEG-4 Elem Str vid  
  77.                 fReadSource = fRTPSource = MPEG4ESVideoRTPSource::createNew(  
  78.                         env(), fRTPSocket, fRTPPayloadFormat,  
  79.                         fRTPTimestampFrequency);  
  80.             } else if (strcmp(fCodecName, "MPEG4-GENERIC") == 0) {  
  81.                 fReadSource = fRTPSource = MPEG4GenericRTPSource::createNew(  
  82.                         env(), fRTPSocket, fRTPPayloadFormat,  
  83.                         fRTPTimestampFrequency, fMediumName, fMode, fSizelength,  
  84.                         fIndexlength, fIndexdeltalength);  
  85.             } else if (strcmp(fCodecName, "MPV") == 0) { // MPEG-1 or 2 video  
  86.                 fReadSource = fRTPSource = MPEG1or2VideoRTPSource::createNew(  
  87.                         env(), fRTPSocket, fRTPPayloadFormat,  
  88.                         fRTPTimestampFrequency);  
  89.             } else if (strcmp(fCodecName, "MP2T") == 0) { // MPEG-2 Transport Stream  
  90.                 fRTPSource = SimpleRTPSource::createNew(env(), fRTPSocket,  
  91.                         fRTPPayloadFormat, fRTPTimestampFrequency, "video/MP2T",  
  92.                         0, False);  
  93.                 fReadSource = MPEG2TransportStreamFramer::createNew(env(),  
  94.                         fRTPSource);  
  95.                 // this sets "durationInMicroseconds" correctly, based on the PCR values  
  96.             } else if (strcmp(fCodecName, "H261") == 0) { // H.261  
  97.                 fReadSource = fRTPSource = H261VideoRTPSource::createNew(env(),  
  98.                         fRTPSocket, fRTPPayloadFormat, fRTPTimestampFrequency);  
  99.             } else if (strcmp(fCodecName, "H263-1998") == 0  
  100.                     || strcmp(fCodecName, "H263-2000") == 0) { // H.263+  
  101.                 fReadSource = fRTPSource = H263plusVideoRTPSource::createNew(  
  102.                         env(), fRTPSocket, fRTPPayloadFormat,  
  103.                         fRTPTimestampFrequency);  
  104.             } else if (strcmp(fCodecName, "H264") == 0) {  
  105.                 fReadSource = fRTPSource = H264VideoRTPSource::createNew(env(),  
  106.                         fRTPSocket, fRTPPayloadFormat, fRTPTimestampFrequency);  
  107.             } else if (strcmp(fCodecName, "DV") == 0) {  
  108.                 fReadSource = fRTPSource = DVVideoRTPSource::createNew(env(),  
  109.                         fRTPSocket, fRTPPayloadFormat, fRTPTimestampFrequency);  
  110.             } else if (strcmp(fCodecName, "JPEG") == 0) { // motion JPEG  
  111.                 fReadSource = fRTPSource = JPEGVideoRTPSource::createNew(env(),  
  112.                         fRTPSocket, fRTPPayloadFormat, fRTPTimestampFrequency,  
  113.                         videoWidth(), videoHeight());  
  114.             } else if (strcmp(fCodecName, "X-QT") == 0  
  115.                     || strcmp(fCodecName, "X-QUICKTIME") == 0) {  
  116.                 // Generic QuickTime streams, as defined in  
  117.                 // <http://developer.apple.com/quicktime/icefloe/dispatch026.html>  
  118.                 char* mimeType = new char[strlen(mediumName())  
  119.                         + strlen(codecName()) + 2];  
  120.                 sprintf(mimeType, "%s/%s", mediumName(), codecName());  
  121.                 fReadSource = fRTPSource = QuickTimeGenericRTPSource::createNew(  
  122.                         env(), fRTPSocket, fRTPPayloadFormat,  
  123.                         fRTPTimestampFrequency, mimeType);  
  124.                 delete[] mimeType;  
  125.             } else if (strcmp(fCodecName, "PCMU") == 0 // PCM u-law audio  
  126.             || strcmp(fCodecName, "GSM") == 0 // GSM audio  
  127.             || strcmp(fCodecName, "DVI4") == 0 // DVI4 (IMA ADPCM) audio  
  128.             || strcmp(fCodecName, "PCMA") == 0 // PCM a-law audio  
  129.             || strcmp(fCodecName, "MP1S") == 0 // MPEG-1 System Stream  
  130.             || strcmp(fCodecName, "MP2P") == 0 // MPEG-2 Program Stream  
  131.             || strcmp(fCodecName, "L8") == 0 // 8-bit linear audio  
  132.             || strcmp(fCodecName, "L16") == 0 // 16-bit linear audio  
  133.             || strcmp(fCodecName, "L20") == 0 // 20-bit linear audio (RFC 3190)  
  134.             || strcmp(fCodecName, "L24") == 0 // 24-bit linear audio (RFC 3190)  
  135.             || strcmp(fCodecName, "G726-16") == 0 // G.726, 16 kbps  
  136.             || strcmp(fCodecName, "G726-24") == 0 // G.726, 24 kbps  
  137.             || strcmp(fCodecName, "G726-32") == 0 // G.726, 32 kbps  
  138.             || strcmp(fCodecName, "G726-40") == 0 // G.726, 40 kbps  
  139.             || strcmp(fCodecName, "SPEEX") == 0 // SPEEX audio  
  140.             || strcmp(fCodecName, "T140") == 0 // T.140 text (RFC 4103)  
  141.             || strcmp(fCodecName, "DAT12") == 0 // 12-bit nonlinear audio (RFC 3190)  
  142.                     ) {  
  143.                 createSimpleRTPSource = True;  
  144.                 useSpecialRTPoffset = 0;  
  145.             } else if (useSpecialRTPoffset >= 0) {  
  146.                 // We don't know this RTP payload format, but try to receive  
  147.                 // it using a 'SimpleRTPSource' with the specified header offset:  
  148.                 createSimpleRTPSource = True;  
  149.             } else {  
  150.                 env().setResultMsg(  
  151.                         "RTP payload format unknown or not supported");  
  152.                 break;  
  153.             }  
  154.   
  155.             if (createSimpleRTPSource) {  
  156.                 char* mimeType = new char[strlen(mediumName())  
  157.                         + strlen(codecName()) + 2];  
  158.                 sprintf(mimeType, "%s/%s", mediumName(), codecName());  
  159.                 fReadSource = fRTPSource = SimpleRTPSource::createNew(env(),  
  160.                         fRTPSocket, fRTPPayloadFormat, fRTPTimestampFrequency,  
  161.                         mimeType, (unsigned) useSpecialRTPoffset,  
  162.                         doNormalMBitRule);  
  163.                 delete[] mimeType;  
  164.             }  
  165.         }  
  166.   
  167.         return True;  
  168.     } while (0);  
  169.   
  170.     return False; // an error occurred  
  171. }  
可以看到,这个函数里主要是跟据前面分析出的媒体和传输信息建立合适的Source。

socket建立了,Source也创建了,下一步应该是连接Sink,形成一个流。到此为止还未看到Sink的影子,应该是在下一步SETUP中建立,我们看到在continueAfterDESCRIBE()的最后调用了setupStreams(),那么就来探索一下setupStreams():

[cpp] view plain copy print ?
  1. void setupStreams()  
  2. {  
  3.     static MediaSubsessionIterator* setupIter = NULL;  
  4.     if (setupIter == NULL)  
  5.         setupIter = new MediaSubsessionIterator(*session);  
  6.   
  7.     //每次调用此函数只为一个Subsession发出SETUP请求。  
  8.     while ((subsession = setupIter->next()) != NULL) {  
  9.         // We have another subsession left to set up:  
  10.         if (subsession->clientPortNum() == 0)  
  11.             continue// port # was not set  
  12.   
  13.         //为一个Subsession发送SETUP请求。请求处理完成时调用continueAfterSETUP(),  
  14.         //continueAfterSETUP()又调用了setupStreams(),在此函数中为下一个SubSession发送SETUP请求。  
[cpp] view plain copy print ?
  1. <SPAN style="WHITE-SPACE: pre">     </SPAN>//直到处理完所有的SubSession  
  2.         setupSubsession(subsession, streamUsingTCP, continueAfterSETUP);  
  3.         return;  
  4.     }  
  5.   
  6.     //执行到这里时,已循环完所有的SubSession了   
  7.     // We're done setting up subsessions.  
  8.     delete setupIter;  
  9.     if (!madeProgress)  
  10.         shutdown();  
  11.   
  12.     //创建输出文件,看来是在这里创建Sink了。创建sink后,就开始播放它。这个播放应该只是把socket的handler加入到  
  13.     //计划任务中,而没有数据的接收或发送。只有等到发出PLAY请求后才有数据的收发。  
  14.     // Create output files:   
  15.     if (createReceivers) {  
  16.         if (outputQuickTimeFile) {  
  17.             // Create a "QuickTimeFileSink", to write to 'stdout':  
  18.             qtOut = QuickTimeFileSink::createNew(*env, *session, "stdout",  
  19.                     fileSinkBufferSize, movieWidth, movieHeight, movieFPS,  
  20.                     packetLossCompensate, syncStreams, generateHintTracks,  
  21.                     generateMP4Format);  
  22.             if (qtOut == NULL) {  
  23.                 *env << "Failed to create QuickTime file sink for stdout: "  
  24.                         << env->getResultMsg();  
  25.                 shutdown();  
  26.             }  
  27.   
  28.             qtOut->startPlaying(sessionAfterPlaying, NULL);  
  29.         } else if (outputAVIFile) {  
  30.             // Create an "AVIFileSink", to write to 'stdout':  
  31.             aviOut = AVIFileSink::createNew(*env, *session, "stdout",  
  32.                     fileSinkBufferSize, movieWidth, movieHeight, movieFPS,  
  33.                     packetLossCompensate);  
  34.             if (aviOut == NULL) {  
  35.                 *env << "Failed to create AVI file sink for stdout: "  
  36.                         << env->getResultMsg();  
  37.                 shutdown();  
  38.             }  
  39.   
  40.             aviOut->startPlaying(sessionAfterPlaying, NULL);  
  41.         } else {  
  42.             // Create and start "FileSink"s for each subsession:  
  43.             madeProgress = False;  
  44.             MediaSubsessionIterator iter(*session);  
  45.             while ((subsession = iter.next()) != NULL) {  
  46.                 if (subsession->readSource() == NULL)  
  47.                     continue// was not initiated  
  48.   
  49.                 // Create an output file for each desired stream:  
  50.                 char outFileName[1000];  
  51.                 if (singleMedium == NULL) {  
  52.                     // Output file name is  
  53.                     //     "<filename-prefix><medium_name>-<codec_name>-<counter>"  
  54.                     static unsigned streamCounter = 0;  
  55.                     snprintf(outFileName, sizeof outFileName, "%s%s-%s-%d",  
  56.                             fileNamePrefix, subsession->mediumName(),  
  57.                             subsession->codecName(), ++streamCounter);  
  58.                 } else {  
  59.                     sprintf(outFileName, "stdout");  
  60.                 }  
  61.                 FileSink* fileSink;  
  62.                 if (strcmp(subsession->mediumName(), "audio") == 0  
  63.                         && (strcmp(subsession->codecName(), "AMR") == 0  
  64.                                 || strcmp(subsession->codecName(), "AMR-WB")  
  65.                                         == 0)) {  
  66.                     // For AMR audio streams, we use a special sink that inserts AMR frame hdrs:  
  67.                     fileSink = AMRAudioFileSink::createNew(*env, outFileName,  
  68.                             fileSinkBufferSize, oneFilePerFrame);  
  69.                 } else if (strcmp(subsession->mediumName(), "video") == 0  
  70.                         && (strcmp(subsession->codecName(), "H264") == 0)) {  
  71.                     // For H.264 video stream, we use a special sink that insert start_codes:  
  72.                     fileSink = H264VideoFileSink::createNew(*env, outFileName,  
  73.                             subsession->fmtp_spropparametersets(),  
  74.                             fileSinkBufferSize, oneFilePerFrame);  
  75.                 } else {  
  76.                     // Normal case:   
  77.                     fileSink = FileSink::createNew(*env, outFileName,  
  78.                             fileSinkBufferSize, oneFilePerFrame);  
  79.                 }  
  80.                 subsession->sink = fileSink;  
  81.                 if (subsession->sink == NULL) {  
  82.                     *env << "Failed to create FileSink for \"" << outFileName  
  83.                             << "\": " << env->getResultMsg() << "\n";  
  84.                 } else {  
  85.                     if (singleMedium == NULL) {  
  86.                         *env << "Created output file: \"" << outFileName  
  87.                                 << "\"\n";  
  88.                     } else {  
  89.                         *env << "Outputting data from the \""  
  90.                                 << subsession->mediumName() << "/"  
  91.                                 << subsession->codecName()  
  92.                                 << "\" subsession to 'stdout'\n";  
  93.                     }  
  94.   
  95.                     if (strcmp(subsession->mediumName(), "video") == 0  
  96.                             && strcmp(subsession->codecName(), "MP4V-ES") == 0 &&  
  97.                             subsession->fmtp_config() != NULL) {  
  98.                         // For MPEG-4 video RTP streams, the 'config' information  
  99.                         // from the SDP description contains useful VOL etc. headers.  
  100.                         // Insert this data at the front of the output file:  
  101.                         unsigned                    configLen;  
  102.                         unsigned char* configData  
  103.                         = parseGeneralConfigStr(subsession->fmtp_config(), configLen);  
  104.                         struct timeval timeNow;  
  105.                         gettimeofday(&timeNow, NULL);  
  106.                         fileSink->addData(configData, configLen, timeNow);  
  107.                         delete[] configData;  
  108.                     }  
  109.   
  110.                     //开始传输   
  111.                     subsession->sink->startPlaying(*(subsession->readSource()),  
  112.                             subsessionAfterPlaying, subsession);  
  113.   
  114.                     // Also set a handler to be called if a RTCP "BYE" arrives  
  115.                     // for this subsession:  
  116.                     if (subsession->rtcpInstance() != NULL) {  
  117.                         subsession->rtcpInstance()->setByeHandler(  
  118.                                 subsessionByeHandler, subsession);  
  119.                     }  
  120.   
  121.                     madeProgress = True;  
  122.                 }  
  123.             }  
  124.             if (!madeProgress)  
  125.                 shutdown();  
  126.         }  
  127.     }  
  128.   
  129.     // Finally, start playing each subsession, to start the data flow:  
  130.     if (duration == 0) {  
  131.         if (scale > 0)  
  132.             duration = session->playEndTime() - initialSeekTime; // use SDP end time  
  133.         else if (scale < 0)  
  134.             duration = initialSeekTime;  
  135.     }  
  136.     if (duration < 0)  
  137.         duration = 0.0;  
  138.   
  139.     endTime = initialSeekTime;  
  140.     if (scale > 0) {  
  141.         if (duration <= 0)  
  142.             endTime = -1.0f;  
  143.         else  
  144.             endTime = initialSeekTime + duration;  
  145.     } else {  
  146.         endTime = initialSeekTime - duration;  
  147.         if (endTime < 0)  
  148.             endTime = 0.0f;  
  149.     }  
  150.   
  151.     //发送PLAY请求,之后才能从Server端接收数据  
  152.     startPlayingSession(session, initialSeekTime, endTime, scale,  
  153.             continueAfterPLAY);  
  154. }  
仔细看看注释,应很容易了解此函数。

你可能感兴趣的:(live555学习2(转))