live555源码分析---- PLAY命令的处理

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

PLAY命令概述


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

PLAY命令要求在SETUP命令之后进行,此命令处理过程中就开始发送数据了,在处理PLAY命令过程中还创建了RTCPInstance实例。

客户端可以通过PLAY命令的Scale头部域,指定播放速率,不过这个功能要看服务器对特定媒体的具体实现,当sacale=1时正常播放,sacale>1时快进,sacale<0时快退。

客户端可以通过PLAY命令的Range头部域,指定播放的时间范围,同样此功能也依赖于服务器中特定媒体的具体实现。

对于PLAY命令请求中的URL有以下几种情况(与PAUSE、TEARDOWN、GET_PARAMETER、SET_PARAMETER处理是一样的):
1)非聚合,如rtsp://192.168.1.1/urlPreSuffix/urlSuffix,urlPreSuffix作为stream name, urlSuffix作为subsession的trackId
2)非聚合的情况下,才能根据trackId找到subsession
3)聚合,如
    rtsp://192.168.1.1/urlPreSuffix/urlSuffix, 将urlSuffix作为stream name,而urlPreSuffix忽略
    rtsp://192.168.1.1/urlPreSuffix, 只存在urlPreSuffix,并将其作为stream name, 这应该是最常见的情况
4)聚合,如rtsp://192.168.1.1/urlPreSuffix/urlSuffix, 将urlPreSuffix/urlSuffix整个作为stream name 
我们可以对session中的subsession进行单独控制(这需要提供subsession的trackId), 也可以对整个session进行控制(这种情况应该是最常见的吧)。


贴一个SETUP消息实例:

[cpp] view plaincopyprint?
  1. PLAY rtsp://192.168.9.80/123.mpg/ RTSP/1.0  
  2. CSeq: 5  
  3. Session: 263BD44B  
  4. Range: npt=0.000-  
  5. User-Agent: LibVLC/1.1.0 (LIVE555 Streaming Media v2010.03.16)  
  6.   
  7. response: RTSP/1.0 200 OK  
  8. CSeq: 5  
  9. Date: Wed, Nov 30 2011 06:55:07 GMT  
  10. Range: npt=0.000-  
  11. Session: 263BD44B  
  12. RTP-Info: url=rtsp://192.168.9.80/123.mpg/track1;seq=38851;rtptime=1434098600,ur  
  13. l=rtsp://192.168.9.80/123.mpg/track2;seq=27752;rtptime=3595585826  


代码分析的过程比较烦琐,就先把总结性的东西放到最前面了

1.关于SETUP命令请求包中的ULR处理

[cpp] view plaincopyprint?
  1. void RTSPServer::RTSPClientSession  
  2. ::handleCmd_withinSession(char const* cmdName,  
  3.               char const* urlPreSuffix, char const* urlSuffix,  
  4.               char const* cseq, char const* fullRequestStr) {  
  5.   
  6.     //非聚合,如rtsp://192.168.1.1/urlPreSuffix/urlSuffix,urlPreSuffix作为stream name, urlSuffix作为subsession的trackId  
  7.     //非聚合的情况下,才能根据trackId找到subsession  
  8.     //聚合,如  
  9.     //1)rtsp://192.168.1.1/urlPreSuffix/urlSuffix, 将urlSuffix作为stream name,而urlPreSuffix忽略  
  10.     //2)rtsp://192.168.1.1/urlPreSuffix, 只存在urlPreSuffix,并将其作为stream name, 这应该是最常见的情况  
  11.     //聚合,如rtsp://192.168.1.1/urlPreSuffix/urlSuffix, 将urlPreSuffix/urlSuffix整个作为stream name   
  12.   
  13.   // This will either be:  
  14.   // - an operation on the entire server, if "urlPreSuffix" is "", and "urlSuffix" is "*" (i.e., the special "*" URL), or  
  15.   // - a non-aggregated operation, if "urlPreSuffix" is the session (stream)  
  16.   //   name and "urlSuffix" is the subsession (track) name, or  
  17.   // - an aggregated operation, if "urlSuffix" is the session (stream) name,  
  18.   //   or "urlPreSuffix" is the session (stream) name, and "urlSuffix" is empty,  
  19.   //   or "urlPreSuffix" and "urlSuffix" are both nonempty, but when concatenated, (with "/") form the session (stream) name.  
  20.   // Begin by figuring out which of these it is:  
  21.   ServerMediaSubsession* subsession;  
  22.   if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0') {  
  23.     // An operation on the entire server.  This works only for GET_PARAMETER and SET_PARAMETER:  
  24.     if (strcmp(cmdName, "GET_PARAMETER") == 0) {  
  25.       handleCmd_GET_PARAMETER(NULL, cseq, fullRequestStr);  
  26.     } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {  
  27.       handleCmd_SET_PARAMETER(NULL, cseq, fullRequestStr);  
  28.     } else {  
  29.       handleCmd_notSupported(cseq);  
  30.     }  
  31.     return;  
  32.   } else if (fOurServerMediaSession == NULL) { // There wasn't a previous SETUP!  
  33.     handleCmd_notSupported(cseq);  
  34.     return;  
  35.   } else if (urlSuffix[0] != '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {  
  36.   
  37.     //非聚合,如rtsp://192.168.1.1/urlPreSuffix/urlSuffix,urlPreSuffix作为stream name, urlSuffix作为subsession的trackId  
  38.     //非聚合的情况下,才能根据trackId找到subsession  
  39.   
  40.     // Non-aggregated operation.  
  41.     // Look up the media subsession whose track id is "urlSuffix":  
  42.     ServerMediaSubsessionIterator iter(*fOurServerMediaSession);  
  43.     while ((subsession = iter.next()) != NULL) {  
  44.       if (strcmp(subsession->trackId(), urlSuffix) == 0) break// success  
  45.     }  
  46.     if (subsession == NULL) { // no such track!  
  47.       handleCmd_notFound(cseq);  
  48.       return;  
  49.     }  
  50.   } else if (strcmp(fOurServerMediaSession->streamName(), urlSuffix) == 0 ||  
  51.          (urlSuffix[0] == '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0)) {  
  52.       
  53.     //聚合,如  
  54.     //1)rtsp://192.168.1.1/urlPreSuffix/urlSuffix, 将urlSuffix作为stream name,而urlPreSuffix忽略  
  55.     //2)rtsp://192.168.1.1/urlPreSuffix, 只存在urlPreSuffix,并将其作为stream name  
  56.   
  57.   
  58.     // Aggregated operation  
  59.     subsession = NULL;  
  60.   } else if (urlPreSuffix[0] != '\0' && urlSuffix[0] != '\0') {  
  61.   
  62.      //聚合,如rtsp://192.168.1.1/urlPreSuffix/urlSuffix, 将urlPreSuffix/urlSuffix整个作为stream name   
  63.   
  64.     //Aggregated operation,  
  65.     // Aggregated operation, if <urlPreSuffix>/<urlSuffix> is the session (stream) name:  
  66.     unsigned const urlPreSuffixLen = strlen(urlPreSuffix);  
  67.     if (strncmp(fOurServerMediaSession->streamName(), urlPreSuffix, urlPreSuffixLen) == 0 &&  
  68.     fOurServerMediaSession->streamName()[urlPreSuffixLen] == '/' &&  
  69.     strcmp(&(fOurServerMediaSession->streamName())[urlPreSuffixLen+1], urlSuffix) == 0) {  
  70.       subsession = NULL;  
  71.     } else {  
  72.       handleCmd_notFound(cseq);  
  73.       return;  
  74.     }  
  75.   } else { // the request doesn't match a known stream and/or track at all!  
  76.     handleCmd_notFound(cseq);  
  77.     return;  
  78.   }  
  79.   
  80.   if (strcmp(cmdName, "TEARDOWN") == 0) {  
  81.     handleCmd_TEARDOWN(subsession, cseq);  
  82.   } else if (strcmp(cmdName, "PLAY") == 0) {  
  83.     handleCmd_PLAY(subsession, cseq, fullRequestStr);  
  84.   } else if (strcmp(cmdName, "PAUSE") == 0) {  
  85.     handleCmd_PAUSE(subsession, cseq);  
  86.   } else if (strcmp(cmdName, "GET_PARAMETER") == 0) {  
  87.     handleCmd_GET_PARAMETER(subsession, cseq, fullRequestStr);  
  88.   } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {  
  89.     handleCmd_SET_PARAMETER(subsession, cseq, fullRequestStr);  
  90.   }  
  91. }  

2.PLAY命令处理函数handleCmd_PLAY(1.1)

[cpp] view plaincopyprint?
  1. <span style="font-family:Arial, Verdana, sans-serif;"><span style="white-space: normal;"><span style="font-family:monospace;"><span style="white-space: pre;"></span></span></span></span><pre name="code" class="cpp">void RTSPServer::RTSPClientSession  
  2.   ::handleCmd_PLAY(ServerMediaSubsession* subsession, char const* cseq,  
  3.   char const* fullRequestStr) {  
  4.   char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession, fClientInputSocket);  
  5.   unsigned rtspURLSize = strlen(rtspURL);  
  6.   
  7.   //分析"Scale:"头部  
  8.   //Scale头,指示了播放的速率,scale = 1为正常播放,大于1快进,小于0则表示快退  
  9.   
  10.   
  11.   // Parse the client's "Scale:" header, if any:  
  12.   float scale;  
  13.   Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);   
  14.   
  15.   
  16.   //测试scale的值是否能满足,这期间可能会改变scale的值  
  17.   // Try to set the stream's scale factor to this value:  
  18.   if (subsession == NULL /*aggregate op*/) {    //聚合的情况下,subsession还不确定  
  19.     fOurServerMediaSession->testScaleFactor(scale); //测试scale的值(见2.1)  
  20.   } else {  
  21.     subsession->testScaleFactor(scale);  
  22.   }  
  23.   
  24.   char buf[100];  
  25.   char* scaleHeader;  
  26.   if (!sawScaleHeader) {  
  27.     buf[0] = '\0'// Because we didn't see a Scale: header, don't send one back  
  28.   } else {  
  29.     sprintf(buf, "Scale: %f\r\n", scale);  
  30.   }  
  31.   scaleHeader = strDup(buf);  
  32.   
  33.    //分析"Range:"头部  
  34.    //"Range:"头部,表示要播放的时间范围。如Range: npt=0.000-,从0时刻开始播放看到结束   
  35.     //不含Range 首部域的PLAY 请求也是合法的。它从媒体流开头开始播放,直到媒体流被暂停  
  36.   
  37.   // Parse the client's "Range:" header, if any:      
  38.   double rangeStart = 0.0, rangeEnd = 0.0;  
  39.   Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd);  
  40.   
  41.   //关于"Range:"头部的其它操作  
  42.    ...  
  43.   
  44.   //下面创建响应中的"RTP-Info:"行  
  45.   
  46.   
  47.   // Create a "RTP-Info:" line.  It will get filled in from each subsession's state:  
  48.   char const* rtpInfoFmt =  
  49.     "%s" // "RTP-Info:", plus any preceding rtpInfo items  
  50.     "%s" // comma separator, if needed  
  51.     "url=%s/%s"  
  52.     ";seq=%d"  
  53.     ";rtptime=%u"  
  54.     ;  
  55.   unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);  
  56.   char* rtpInfo = strDup("RTP-Info: ");  
  57.   unsigned i, numRTPInfoItems = 0;  
  58.   
  59.  //根据要求,在每个subsession上进行seeking/scaling操作  
  60.   // Do any required seeking/scaling on each subsession, before starting streaming:  
  61.   for (i = 0; i < fNumStreamStates; ++i) {  
  62.     if (subsession == NULL /* means: aggregated operation */  
  63. || subsession == fStreamStates[i].subsession) {  
  64.       if (sawScaleHeader) {  
  65. fStreamStates[i].subsession->setStreamScale(fOurSessionId,      //设置subsession的scale值(见2.1)   
  66.    fStreamStates[i].streamToken,  
  67.    scale);  
  68.       }  
  69.       if (sawRangeHeader) {  
  70.       
  71.     //计算流的播放时间streamDuration      
  72. double streamDuration = 0.0; // by default; means: stream until the end of the media  
  73. if (rangeEnd > 0.0 && (rangeEnd+0.001) < duration) { // the 0.001 is because we limited the values to 3 decimal places  
  74.  // We want the stream to end early.  Set the duration we want:  
  75.  streamDuration = rangeEnd - rangeStart;  
  76.  if (streamDuration < 0.0) streamDuration = -streamDuration; // should happen only if scale < 0.0  这里情况下进行快退操作  
  77. }  
  78. u_int64_t numBytes;  
  79. fStreamStates[i].subsession->seekStream(fOurSessionId,      //设置每个subsession上的播放时间范围(见2.2)  
  80. fStreamStates[i].streamToken,  
  81. rangeStart, streamDuration, numBytes);    
  82.       }  
  83.     }  
  84.   }  
  85.   
  86.   
  87.   // Create the "Range:" header that we'll send back in our response.  
  88.   // (Note that we do this after seeking, in case the seeking operation changed the range start time.)  
  89.   char* rangeHeader;  
  90.   if (!sawRangeHeader) {  
  91.     buf[0] = '\0'// Because we didn't see a Range: header, don't send one back  
  92.   } else if (rangeEnd == 0.0 && scale >= 0.0) {  
  93.     sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);  
  94.   } else {  
  95.     sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);  
  96.   }  
  97.   rangeHeader = strDup(buf);  
  98.   
  99.   //现在终于开始媒体数据传输了  
  100.   
  101.   // Now, start streaming:  
  102.   for (i = 0; i < fNumStreamStates; ++i) {  
  103.     if (subsession == NULL /* means: aggregated operation */  
  104. || subsession == fStreamStates[i].subsession) {  
  105.       unsigned short rtpSeqNum = 0;  
  106.       unsigned rtpTimestamp = 0;  
  107.   
  108.   
  109.       //开始各个subsession上的数据传输, 即开始播放了(见2.3)  
  110.   
  111.   
  112.       fStreamStates[i].subsession->startStream(fOurSessionId,  
  113.       fStreamStates[i].streamToken,  
  114.       (TaskFunc*)noteClientLiveness, this,  
  115.       rtpSeqNum, rtpTimestamp,  
  116.       handleAlternativeRequestByte, this);  
  117.       const char *urlSuffix = fStreamStates[i].subsession->trackId();  
  118.       char* prevRTPInfo = rtpInfo;  
  119.       unsigned rtpInfoSize = rtpInfoFmtSize  
  120. + strlen(prevRTPInfo)  
  121. + 1  
  122. + rtspURLSize + strlen(urlSuffix)  
  123. + 5 /*max unsigned short len*/  
  124. + 10 /*max unsigned (32-bit) len*/  
  125. + 2 /*allows for trailing \r\n at final end of string*/;  
  126.       rtpInfo = new char[rtpInfoSize];  
  127.   
  128.       //subsession中的信息添加到"RTP-Info:"行中  
  129.   
  130.       sprintf(rtpInfo, rtpInfoFmt,  
  131.      prevRTPInfo,  
  132.      numRTPInfoItems++ == 0 ? "" : ",",  
  133.      rtspURL, urlSuffix,  
  134.      rtpSeqNum,  
  135.      rtpTimestamp  
  136.      );  
  137.       delete[] prevRTPInfo;  
  138.     }  
  139.   }  
  140.     
  141.   //下面是组装响应包的操作  
  142. ...  
  143. }</pre><br>  
  144. <br>  
  145. <pre></pre>  
  146. <h3><a name="t3"></a></h3>  
  147. <h3><a name="t4"></a>3.关于播放速度参数scale(2.1)</h3>  
  148. scale参数指定了播放的速率,scale = 1为正常播放,大于1快进,小于0则表示快退。是否能满足scale的要求,要看服务器是否支持。看ServerMediaSession中的测试函数<pre name="code" class="cpp">void ServerMediaSession::testScaleFactor(float& scale) {  
  149.   // First, try setting all subsessions to the desired scale.  
  150.   // If the subsessions' actual scales differ from each other, choose the  
  151.   // value that's closest to 1, and then try re-setting all subsessions to that  
  152.   // value.  If the subsessions' actual scales still differ, re-set them all to 1.  
  153.   float minSSScale = 1.0;  
  154.   float maxSSScale = 1.0;  
  155.   float bestSSScale = 1.0;  
  156.   float bestDistanceTo1 = 0.0;  
  157.   ServerMediaSubsession* subsession;  
  158.   for (subsession = fSubsessionsHead; subsession != NULL;  
  159.        subsession = subsession->fNext) {  
  160.     float ssscale = scale;  
  161.     subsession->testScaleFactor(ssscale);  
  162.     if (subsession == fSubsessionsHead) { // this is the first subsession  
  163.       minSSScale = maxSSScale = bestSSScale = ssscale;  
  164.       bestDistanceTo1 = (float)fabs(ssscale - 1.0f);  
  165.     } else {  
  166.       if (ssscale < minSSScale) {  
  167. minSSScale = ssscale;  
  168.       } else if (ssscale > maxSSScale) {  
  169. maxSSScale = ssscale;  
  170.       }  
  171.   
  172.       float distanceTo1 = (float)fabs(ssscale - 1.0f);  
  173.       if (distanceTo1 < bestDistanceTo1) {  
  174. bestSSScale = ssscale;  
  175. bestDistanceTo1 = distanceTo1;  
  176.       }  
  177.     }  
  178.   }  
  179.   if (minSSScale == maxSSScale) {  
  180.     // All subsessions are at the same scale: minSSScale == bestSSScale == maxSSScale  
  181.     scale = minSSScale;  
  182.     return;  
  183.   }  
  184.   
  185.   
  186.   // The scales for each subsession differ.  Try to set each one to the value  
  187.   // that's closest to 1:  
  188.   for (subsession = fSubsessionsHead; subsession != NULL;  
  189.        subsession = subsession->fNext) {  
  190.     float ssscale = bestSSScale;  
  191.     subsession->testScaleFactor(ssscale);  
  192.     if (ssscale != bestSSScale) break// no luck  
  193.   }  
  194.   if (subsession == NULL) {  
  195.     // All subsessions are at the same scale: bestSSScale  
  196.     scale = bestSSScale;  
  197.     return;  
  198.   }  
  199.   
  200.   // Still no luck.  Set each subsession's scale to 1:  
  201.   for (subsession = fSubsessionsHead; subsession != NULL;  
  202.        subsession = subsession->fNext) {  
  203.     float ssscale = 1;  
  204.     subsession->testScaleFactor(ssscale);  
  205.   }  
  206.   scale = 1;  
  207. }  
  208. </pre>上面的函数处理过程有些繁锁,主要是处理各subsession支持不同的scale的情况, 这种情况下将选取scale值最靠近1的值(1为正常速率)。上面的函数中实际上调用了subsession上的testScaleFactor函数。其在ServerMediaSubsession类中有默认实现,如下:<br>  
  209. <pre name="code" class="cpp">void ServerMediaSubsession::testScaleFactor(float& scale) {  
  210.   // default implementation: Support scale = 1 only  
  211.   scale = 1;  
  212. }</pre>默认只能正常播放,再来看subsession上的setStreamScale函数<br>  
  213. <pre name="code" class="cpp">void ServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,  
  214.   void/*streamToken*/float /*scale*/) {  
  215.   // default implementation: do nothing  
  216. }</pre>do nothing!不过这是一个虚函数,在nDemandServerMediaSubsession中有重新实现<br>  
  217. <pre name="code" class="cpp">void OnDemandServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,  
  218.   void* streamToken, float scale) {  
  219.   
  220.   //当多个客户端从同一个source接收数据时,sacale值是不能改变的  
  221.   
  222.   // Changing the scale factor isn't allowed if multiple clients are receiving data  
  223.   // from the same source:  
  224.   if (fReuseFirstSource) return;  
  225.   
  226.   StreamState* streamState = (StreamState*)streamToken;  
  227.   if (streamState != NULL && streamState->mediaSource() != NULL) {  
  228.     setStreamSourceScale(streamState->mediaSource(), scale);  
  229.   }  
  230. }</pre>继续看setStreamSourceScale函数<br>  
  231. <pre name="code" class="cpp">void OnDemandServerMediaSubsession  
  232. ::setStreamSourceScale(FramedSource* /*inputSource*/float /*scale*/) {  
  233.   // Default implementation: Do nothing  
  234. }</pre>什么都做,这样如果要实现快进/快退操作, 只需要在自己的subsession中重新实现这个函数即可。<br>  
  235. <br>  
  236. <h3><a name="t5"></a>4.设置播放的时间范围(2.2)</h3>  
  237. seekStream是ServerMediaSubsession类的一个虚函数,默认实现do nothing,直接看OnDemandServerMediaSubsession类中的实现<br>  
  238. <pre name="code" class="cpp">void OnDemandServerMediaSubsession::seekStream(unsigned /*clientSessionId*/,  
  239.       void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes) {  
  240.   numBytes = 0; // by default: unknown  
  241.   
  242.   //同样的多个客户端对应同一个source时,不充许此操作  
  243.   // Seeking isn't allowed if multiple clients are receiving data from  
  244.   // the same source:  
  245.   if (fReuseFirstSource) return;  
  246.   
  247.   StreamState* streamState = (StreamState*)streamToken;  
  248.   if (streamState != NULL && streamState->mediaSource() != NULL) {  
  249.     seekStreamSource(streamState->mediaSource(), seekNPT, streamDuration, numBytes);  
  250.   }  
  251. }</pre>seekStreamSource也是定义在OnDemandServerMediaSubsession上的虚函数,默认实现也是do nothing, 需要在自己的子类中重新实现。看了下H264VideoFileServerMediaSubsession类,并没有去实现seekStreamSource,所以*.264的文件每次打开只能从头看起了。<br>  
  252. <br>  
  253. <h3><a name="t6"></a>5.开始播放(2.3)</h3>  
  254. 进行了如此多的工作,终于要开始播放了,期待。。。<br>  
  255. startStream函数是定义在ServerMediaSubsession类中的纯虚函数,首先来看其子类OnDemandServerMediaSubsession中的实现<br>  
  256. <pre name="code" class="cpp">void OnDemandServerMediaSubsession::startStream(unsigned clientSessionId,  
  257. void* streamToken,  
  258. TaskFunc* rtcpRRHandler,  
  259. void* rtcpRRHandlerClientData,  
  260. unsigned short& rtpSeqNum,  
  261. unsigned& rtpTimestamp,  
  262. ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,  
  263. void* serverRequestAlternativeByteHandlerClientData) {  
  264.   StreamState* streamState = (StreamState*)streamToken;  
  265.   Destinations* destinations  
  266.     = (Destinations*)(fDestinationsHashTable->Lookup((char const*)clientSessionId));  
  267.   if (streamState != NULL) {  
  268.     streamState->startPlaying(destinations,  
  269.      rtcpRRHandler, rtcpRRHandlerClientData,  
  270.      serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);  
  271.     if (streamState->rtpSink() != NULL) {  
  272.       rtpSeqNum = streamState->rtpSink()->currentSeqNo();       //这个rtpSeqNum有什么用呢?  
  273.       rtpTimestamp = streamState->rtpSink()->presetNextTimestamp();  
  274.     }  
  275.   }  
  276. }  
  277. </pre>这里主要调用函数StreamState::startPlaying<br>  
  278. <pre name="code" class="cpp">void StreamState::startPlaying(Destinations* dests,  
  279.       TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData,  
  280.       ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,  
  281.       void* serverRequestAlternativeByteHandlerClientData) {  
  282.   if (dests == NULL) return;  
  283.   
  284.   //创建RTCPInstance实例  
  285.   
  286.   if (fRTCPInstance == NULL && fRTPSink != NULL) {  
  287.     // Create (and start) a 'RTCP instance' for this RTP sink:  
  288.     fRTCPInstance  
  289.       = RTCPInstance::createNew(fRTPSink->envir(), fRTCPgs,  
  290. fTotalBW, (unsigned char*)fMaster.fCNAME,  
  291. fRTPSink, NULL /* we're a server */);  
  292.         // Note: This starts RTCP running automatically  
  293.   }  
  294.   
  295.   if (dests->isTCP) {  
  296.     //使用TCP传输RTP 和 RTCP  
  297.   
  298.     // Change RTP and RTCP to use the TCP socket instead of UDP:  
  299.     if (fRTPSink != NULL) {  
  300.       fRTPSink->addStreamSocket(dests->tcpSocketNum, dests->rtpChannelId);  
  301.       fRTPSink->setServerRequestAlternativeByteHandler(dests->tcpSocketNum, serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);  
  302.     }  
  303.     if (fRTCPInstance != NULL) {  
  304.       fRTCPInstance->addStreamSocket(dests->tcpSocketNum, dests->rtcpChannelId);  
  305.       fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum, dests->rtcpChannelId,  
  306.  rtcpRRHandler, rtcpRRHandlerClientData);  
  307.     }  
  308.   } else {  
  309.     //使用UDP传输RTP、RTCP  
  310.   
  311.     // Tell the RTP and RTCP 'groupsocks' about this destination  
  312.     // (in case they don't already have it):  
  313.     if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort);  
  314.     if (fRTCPgs != NULL) fRTCPgs->addDestination(dests->addr, dests->rtcpPort);  
  315.     if (fRTCPInstance != NULL) {  
  316.       fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort,  
  317.  rtcpRRHandler, rtcpRRHandlerClientData);  
  318.     }  
  319.   }  
  320.   
  321.   //下面调用sink上的sdtartPlaying函数开始传输数据  
  322.   if (!fAreCurrentlyPlaying && fMediaSource != NULL) {  
  323.     if (fRTPSink != NULL) {     //通过RTP协议传输  
  324.       fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);  
  325.       fAreCurrentlyPlaying = True;  
  326.     } else if (fUDPSink != NULL) {  //裸的UDP数据包,不使用RTP协议   
  327.       fUDPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);  
  328.       fAreCurrentlyPlaying = True;  
  329.     }  
  330.   }  
  331. }  
  332. </pre>上面的函数最终调用了MediaSink::startPlaying函数,开始传输数据,这个过程的函数调用比较复杂,将在下一篇文章中单独分析。大致过程是,先取一个帧发送出去,然后此函数就返回了,再处理response包的发送等剩余操作。<br>  
  333. <br>  
  334. <pre></pre>  
  335. <pre></pre>  
  336. <pre></pre>  
  337. <pre></pre>  
  338. <pre></pre>  
  339. <pre></pre>