文档章节

live555学习笔记-RTSPClient分析

雷霄骅
 雷霄骅
发布于 2014/08/16 13:47
字数 3625
阅读 282
收藏 0

八 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  

  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  

  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  

  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  

  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  

  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  

  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  

  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. }  

仔细看看注释,应很容易了解此函数。


原文地址:http://blog.csdn.net/niu_gao/article/details/6927461

live555源代码(VC6):http://download.csdn.net/detail/leixiaohua1020/6374387

本文转载自:http://blog.csdn.net/leixiaohua1020/article/details/11851311

雷霄骅
粉丝 205
博文 419
码字总数 2129
作品 4
朝阳
程序员
私信 提问
RTSP学习笔记(2)live555

RTSPClient RTSPClient 是RTSP协议的客户端实现,用于发送RTSP请求命令 类接口: static RTSPClient createNew() 用于外部创建RTSPClient实例的方法,将构造方法进行了隐藏(protect); un...

Sean-x
2016/02/23
60
0
EasyDarwin在做拉模式转发海康RTSP摄像机视频流的过程中出现花屏问题的解决方案

问题描述 在3年前我当时基于EasyDarwin为用户开发了一款RTSP拉模式转发的程序,也发布了一篇博客《用Darwin开发RTSP级联服务器(拉模式转发)》,当时考虑的很简单,只要将RTSP源的sdp和RTP流拉...

xiejiashu
2017/11/19
0
0
RTSP学习笔记(5)openRTSP流程分析

openRTSP是对RTSPClient的实现,与服务器对应的客户端功能 1、初始化 BasicTaskschedular::createNew() BasicUsageEnvironment::createNew() 解析输入streamURL ourClient= createClient(str......

Sean-x
2016/02/24
50
0
关于EasyRTSPClient、EasyPlayer RTSP流重连问题的解释

EasyPlayer、EasyRTSPClient是如何设计重连的 首先大概解释一下EasyRTSPClient与EasyPlayer间的关系:EasyRTSPClient是一个专门用于与RTSP流媒体服务器交互的RTSPClient框架,类似于live555...

xiejiashu
2017/12/09
0
0
Live555源代码解读(7)

八 、RTSPClient分析 有RTSPServer,当然就要有RTSPClient。 如果按照Server端的架构,想一下Client端各部分的组成可能是这样: 因为要连接RTSP server,所以RTSPClient要有TCP socket。当获...

Sean-x
2016/02/25
49
0

没有更多内容

加载失败,请刷新页面

加载更多

聊聊中国的通信行业:从“七国八制”到“中华”脊梁

本期文章和大家一起来聊一聊我曾经从事过的通信行业吧。最近各方面信息的泛滥,包括和华为的同学聊天,自己确实也感慨颇多。想想我自己本科主修通信工程,研究生再修信息与通信工程,从本科开...

CodeSheep
49分钟前
3
0
MDK:ARM M451M:exceed the range of code meory, continue to erase or not?

问题: 代码空间超限 几天前就遇到:exceed the range of code meory, continue to erase or not? 如下所示: 解决过程 开始以为中MDK软件的128KB限制,如是就不能生成HEX文件,应该链接时有提...

SamXIAO
56分钟前
1
1
OSChina 周六乱弹 —— 因违反《中华人民共和国治安管理处罚法》第四十四条之规定

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @xiaoshiyue :#今日歌曲推荐# 惊艳分享谷微的单曲《安守本份》(@网易云音乐) 《安守本份》- 谷微 手机党少年们想听歌,请使劲儿戳(这里) ...

小小编辑
今天
266
7
Angular 英雄编辑器

应用程序现在有了基本的标题。 接下来你要创建一个新的组件来显示英雄信息并且把这个组件放到应用程序的外壳里去。 创建英雄组件 使用 Angular CLI 创建一个名为 heroes 的新组件。 ng gener...

honeymoose
今天
8
0
Kernel DMA

为什么会有DMA(直接内存访问)?我们知道通常情况下,内存数据跟外设之间的通信是通过cpu来传递的。cpu运行io指令将数据从内存拷贝到外设的io端口,或者从外设的io端口拷贝到内存。由于外设...

yepanl
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部