live555学习(九) --PLAY命令处理

来源:互联网 发布:苹果udid定制软件 编辑:程序博客网 时间:2024/05/01 18:52

转载至:http://xingyunbaijunwei.blog.163.com/blog/static/76538067201221621636396/


PLAY命令概述

        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消息实例:

  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   
 14: 

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

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

  1: void RTSPServer::RTSPClientSession  ::handleCmd_withinSession(char const* cmdName,    
  2:               char const* urlPreSuffix, char const* urlSuffix,    
  3:               char const* cseq, char const* fullRequestStr) {    
  4:     
  5:     //非聚合,如rtsp://192.168.1.1/urlPreSuffix/urlSuffix,urlPreSuffix作为stream name, urlSuffix作为subsession的trackId     
  6:     //非聚合的情况下,才能根据trackId找到subsession     
  7:     //聚合,如     
  8:     //1)rtsp://192.168.1.1/urlPreSuffix/urlSuffix, 将urlSuffix作为stream name,而urlPreSuffix忽略     
  9:     //2)rtsp://192.168.1.1/urlPreSuffix, 只存在urlPreSuffix,并将其作为stream name, 这应该是最常见的情况     
 10:     //聚合,如rtsp://192.168.1.1/urlPreSuffix/urlSuffix, 将urlPreSuffix/urlSuffix整个作为stream name      
 11:     
 12:   // This will either be:     
 13:   // - an operation on the entire server, if "urlPreSuffix" is "", and "urlSuffix" is "*" (i.e., the special "*" URL), or     
 14:   // - a non-aggregated operation, if "urlPreSuffix" is the session (stream)     
 15:   //   name and "urlSuffix" is the subsession (track) name, or     
 16:   // - an aggregated operation, if "urlSuffix" is the session (stream) name,     
 17:   //   or "urlPreSuffix" is the session (stream) name, and "urlSuffix" is empty,     
 18:   //   or "urlPreSuffix" and "urlSuffix" are both nonempty, but when concatenated, (with "/") form the session (stream) name.     
 19:   // Begin by figuring out which of these it is:     
 20:   ServerMediaSubsession* subsession;    
 21:   if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0') {    
 22:     // An operation on the entire server.  This works only for GET_PARAMETER and SET_PARAMETER:     
 23:     if (strcmp(cmdName, "GET_PARAMETER") == 0) {    
 24:       handleCmd_GET_PARAMETER(NULL, cseq, fullRequestStr);    
 25:     } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {    
 26:       handleCmd_SET_PARAMETER(NULL, cseq, fullRequestStr);    
 27:     } else {    
 28:       handleCmd_notSupported(cseq);    
 29:     }    
 30:     return;    
 31:   } else if (fOurServerMediaSession == NULL) { // There wasn't a previous SETUP!     
 32:     handleCmd_notSupported(cseq);    
 33:     return;    
 34:   }else if (urlSuffix[0] != '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {    
 35:     

36: //非聚合,如rtsp://192.168.1.1/urlPreSuffix/urlSuffix,urlPreSuffix作为stream name,

urlSuffix作为subsession的trackId

 37:     //非聚合的情况下,才能根据trackId找到subsession     
 38:     
 39:     // Non-aggregated operation.     
 40:     // Look up the media subsession whose track id is "urlSuffix":     
 41:     ServerMediaSubsessionIterator iter(*fOurServerMediaSession);    
 42:     while ((subsession = iter.next()) != NULL) {    
 43:       if (strcmp(subsession->trackId(), urlSuffix) == 0) break; // success     
 44:     }    
 45:     if (subsession == NULL) { // no such track!     
 46:       handleCmd_notFound(cseq);    
 47:       return;    
 48:     }    
 49:   } else if (strcmp(fOurServerMediaSession->streamName(), urlSuffix) == 0 ||    
 50:          (urlSuffix[0] == '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0)) {    
 51:         
 52:     //聚合,如     
 53:     //1)rtsp://192.168.1.1/urlPreSuffix/urlSuffix, 将urlSuffix作为stream name,而urlPreSuffix忽略     
 54:     //2)rtsp://192.168.1.1/urlPreSuffix, 只存在urlPreSuffix,并将其作为stream name     
 55:     
 56:     
 57:     // Aggregated operation     
 58:     subsession = NULL;    
 59:   } else if (urlPreSuffix[0] != '\0' && urlSuffix[0] != '\0') {    
 60:     
 61:      //聚合,如rtsp://192.168.1.1/urlPreSuffix/urlSuffix, 将urlPreSuffix/urlSuffix整个作为stream name      
 62:     
 63:     //Aggregated operation,     
 64:     // Aggregated operation, if <urlPreSuffix>/<urlSuffix> is the session (stream) name:     
 65:     unsigned const urlPreSuffixLen = strlen(urlPreSuffix);    
 66:     if (strncmp(fOurServerMediaSession->streamName(), urlPreSuffix, urlPreSuffixLen) == 0 &&    
 67:     fOurServerMediaSession->streamName()[urlPreSuffixLen] == '/' &&    
 68:     strcmp(&(fOurServerMediaSession->streamName())[urlPreSuffixLen+1], urlSuffix) == 0) {    
 69:       subsession = NULL;    
 70:     } else {    
 71:       handleCmd_notFound(cseq);    
 72:       return;    
 73:     }    
 74:   } else { // the request doesn't match a known stream and/or track at all!     
 75:     handleCmd_notFound(cseq);    
 76:     return;    
 77:   }    
 78:     
 79:   if (strcmp(cmdName, "TEARDOWN") == 0) {    
 80:     handleCmd_TEARDOWN(subsession, cseq);    
 81:   } else if (strcmp(cmdName, "PLAY") == 0) {    
 82:     handleCmd_PLAY(subsession, cseq, fullRequestStr);    
 83:   } else if (strcmp(cmdName, "PAUSE") == 0) {    
 84:     handleCmd_PAUSE(subsession, cseq);    
 85:   } else if (strcmp(cmdName, "GET_PARAMETER") == 0) {    
 86:     handleCmd_GET_PARAMETER(subsession, cseq, fullRequestStr);    
 87:   } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {    
 88:     handleCmd_SET_PARAMETER(subsession, cseq, fullRequestStr);    
 89:   }    
 90: }   
 91: 

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

       当RTSPClientSession收到PLAY请求时,就开始传输RTP数据。下面看一下流启动的代码:

  1: 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

// the 0.001 is because we limited the values to 3 decimal places

 73: if (rangeEnd > 0.0 && (rangeEnd+0.001) < duration) { 
 74:  // We want the stream to end early.  Set the duration we want:     

75: streamDuration = rangeEnd - rangeStart;

// should happen only if scale < 0.0 这里情况下进行快退操作

 76:  if (streamDuration < 0.0) streamDuration = -streamDuration; 
 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: if (numRTPInfoItems == 0) {    
143:         rtpInfo[0] = '\0';    
144:     } else {    
145:         unsigned rtpInfoLen = strlen(rtpInfo);    
146:         rtpInfo[rtpInfoLen] = '\r';    
147:         rtpInfo[rtpInfoLen + 1] = '\n';    
148:         rtpInfo[rtpInfoLen + 2] = '\0';    
149:     }    
150:     
151:     
152:     // Fill in the response:     
153:     snprintf((char*) fResponseBuffer, sizeof fResponseBuffer,    
154:             "RTSP/1.0 200 OK\r\n"    
155:                     "CSeq: %s\r\n"    
156:                     "%s"    
157:                     "%s"    
158:                     "%s"    
159:                     "Session: %08X\r\n"    
160:                     "%s\r\n", cseq, dateHeader(), scaleHeader, rangeHeader,    
161:             fOurSessionId, rtpInfo);    
162:     delete[] rtpInfo;    
163:     delete[] rangeHeader;    
164:     delete[] scaleHeader;    
165:     delete[] rtspURL;    
166: }   
167: 

        有个问题,如果这个streamToken使用的是已存在的(还记得ReuseFirstSource吗),为它再次调用startStream()时,究竟会做什么呢?应该是把这个客户端的地址和rtp/rtcp端口传给rtp server的groupSocket,rtp server自然就开始向这个客户端发送数据了。具体可以看后面void StreamState::startPlaying(…)的代码。

3.关于播放速度参数scale(2.1)

        scale参数指定了播放的速率,scale = 1为正常播放,大于1快进,小于0则表示快退。是否能满足scale的要求,要看服务器是否支持。看ServerMediaSession中的测试函数。

  1: void ServerMediaSession::testScaleFactor(float& scale) {    
  2:   // First, try setting all subsessions to the desired scale.     
  3:   // If the subsessions' actual scales differ from each other, choose the     
  4:   // value that's closest to 1, and then try re-setting all subsessions to that     
  5:   // value.  If the subsessions' actual scales still differ, re-set them all to 1.     
  6:   float minSSScale = 1.0;    
  7:   float maxSSScale = 1.0;    
  8:   float bestSSScale = 1.0;    
  9:   float bestDistanceTo1 = 0.0;    
 10:   ServerMediaSubsession* subsession;    
 11:   for (subsession = fSubsessionsHead; subsession != NULL;    
 12:        subsession = subsession->fNext) {    
 13:     float ssscale = scale;    
 14:     subsession->testScaleFactor(ssscale);    
 15:     if (subsession == fSubsessionsHead) { // this is the first subsession     
 16:       minSSScale = maxSSScale = bestSSScale = ssscale;    
 17:       bestDistanceTo1 = (float)fabs(ssscale - 1.0f);    
 18:     } else {    
 19:       if (ssscale < minSSScale) {    
 20: minSSScale = ssscale;    
 21:       } else if (ssscale > maxSSScale) {    
 22: maxSSScale = ssscale;    
 23:       }    
 24:     
 25:       float distanceTo1 = (float)fabs(ssscale - 1.0f);    
 26:       if (distanceTo1 < bestDistanceTo1) {    
 27: bestSSScale = ssscale;    
 28: bestDistanceTo1 = distanceTo1;    
 29:       }    
 30:     }    
 31:   }    
 32:   if (minSSScale == maxSSScale) {    
 33:     // All subsessions are at the same scale: minSSScale == bestSSScale == maxSSScale     
 34:     scale = minSSScale;    
 35:     return;    
 36:   }    
 37:     
 38:     
 39:   // The scales for each subsession differ.  Try to set each one to the value     
 40:   // that's closest to 1:     
 41:   for (subsession = fSubsessionsHead; subsession != NULL;    
 42:        subsession = subsession->fNext) {    
 43:     float ssscale = bestSSScale;    
 44:     subsession->testScaleFactor(ssscale);    
 45:     if (ssscale != bestSSScale) break; // no luck     
 46:   }    
 47:   if (subsession == NULL) {    
 48:     // All subsessions are at the same scale: bestSSScale     
 49:     scale = bestSSScale;    
 50:     return;    
 51:   }    
 52:     
 53:   // Still no luck.  Set each subsession's scale to 1:     
 54:   for (subsession = fSubsessionsHead; subsession != NULL;    
 55:        subsession = subsession->fNext) {    
 56:     float ssscale = 1;    
 57:     subsession->testScaleFactor(ssscale);    
 58:   }    
 59:   scale = 1;    
 60: }   
 61: 

       上面的函数处理过程有些繁锁,主要是处理各subsession支持不同的scale的情况, 这种情况下将选取scale值最靠近1的值(1为正常速率)。上面的函数中实际上调用了subsession上的testScaleFactor函数。其在ServerMediaSubsession类中有默认实现,如下:

  1: void ServerMediaSubsession::testScaleFactor(float& scale) {    
  2:   // default implementation: Support scale = 1 only     
  3:   scale = 1;    
  4: }   
  5: 

    默认只能正常播放,再来看subsession上的setStreamScale函数

  1: void ServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,    
  2:   void* /*streamToken*/, float /*scale*/) {    
  3:   // default implementation: do nothing     
  4: }   
  5: 

    do nothing!不过这是一个虚函数,在nDemandServerMediaSubsession中有重新实现

  1: void OnDemandServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,    
  2:   void* streamToken, float scale) {    
  3:     
  4:   //当多个客户端从同一个source接收数据时,sacale值是不能改变的     
  5:     
  6:   // Changing the scale factor isn't allowed if multiple clients are receiving data     
  7:   // from the same source:     
  8:   if (fReuseFirstSource) return;    
  9:     
 10:   StreamState* streamState = (StreamState*)streamToken;    
 11:   if (streamState != NULL && streamState->mediaSource() != NULL) {    
 12:     setStreamSourceScale(streamState->mediaSource(), scale);    
 13:   }    
 14: }   
 15: 

   继续看setStreamSourceScale函数

  1: void OnDemandServerMediaSubsession    
  2: ::setStreamSourceScale(FramedSource* /*inputSource*/, float /*scale*/) {    
  3:   // Default implementation: Do nothing     
  4: }   
  5: 

   什么都做,这样如果要实现快进/快退操作, 只需要在自己的subsession中重新实现这个函数即可。

4.设置播放的时间范围(2.2)

       seekStream是ServerMediaSubsession类的一个虚函数,默认实现do nothing,直接看OnDemandServerMediaSubsession类中的实现。 
  1: void OnDemandServerMediaSubsession::seekStream(unsigned /*clientSessionId*/,    
  2:       void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes) {    
  3:   numBytes = 0; // by default: unknown     
  4:     
  5:   //同样的多个客户端对应同一个source时,不充许此操作     
  6:   // Seeking isn't allowed if multiple clients are receiving data from     
  7:   // the same source:     
  8:   if (fReuseFirstSource) return;    
  9:     
 10:   StreamState* streamState = (StreamState*)streamToken;    
 11:   if (streamState != NULL && streamState->mediaSource() != NULL) {    
 12:     seekStreamSource(streamState->mediaSource(), seekNPT, streamDuration, numBytes);    
 13:   }    
 14: }  
 15: 

       seekStreamSource也是定义在OnDemandServerMediaSubsession上的虚函数,默认实现也是do nothing, 需要在自己的子类中重新实现。看了下H264VideoFileServerMediaSubsession类,并没有去实现seekStreamSource,所以*.264的文件每次打开只能从头看起了。

5.开始播放(2.3)

       进行了如此多的工作,终于要开始播放了,期待。。。 
       startStream函数是定义在ServerMediaSubsession类中的纯虚函数,首先来看其子类OnDemandServerMediaSubsession中的实现

  1: void OnDemandServerMediaSubsession::startStream(unsigned clientSessionId,    
  2: void* streamToken,    
  3: TaskFunc* rtcpRRHandler,    
  4: void* rtcpRRHandlerClientData,    
  5: unsigned short& rtpSeqNum,    
  6: unsigned& rtpTimestamp,    
  7: ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,    
  8: void* serverRequestAlternativeByteHandlerClientData) {    
  9:   StreamState* streamState = (StreamState*)streamToken;    
 10:   Destinations* destinations    
 11:     = (Destinations*)(fDestinationsHashTable->Lookup((char const*)clientSessionId));    
 12:   if (streamState != NULL) {    
 13:     streamState->startPlaying(destinations,    
 14:      rtcpRRHandler, rtcpRRHandlerClientData,    
 15:      serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);    
 16:     if (streamState->rtpSink() != NULL) {    
 17:       rtpSeqNum = streamState->rtpSink()->currentSeqNo();       //这个rtpSeqNum有什么用呢?     
 18:       rtpTimestamp = streamState->rtpSink()->presetNextTimestamp();    
 19:     }    
 20:   }    
 21: }   
 22: 

    这里主要调用函数StreamState::startPlaying

  1: void StreamState::startPlaying(Destinations* dests,    
  2:       TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData,    
  3:       ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,    
  4:       void* serverRequestAlternativeByteHandlerClientData) {    
  5:   if (dests == NULL) return;    
  6:     
  7:   //创建RTCPInstance实例     
  8:     
  9:   if (fRTCPInstance == NULL && fRTPSink != NULL) {    
 10:     // Create (and start) a 'RTCP instance' for this RTP sink:     
 11:     fRTCPInstance    
 12:       = RTCPInstance::createNew(fRTPSink->envir(), fRTCPgs,    
 13: fTotalBW, (unsigned char*)fMaster.fCNAME,    
 14: fRTPSink, NULL /* we're a server */);    
 15:         // Note: This starts RTCP running automatically     
 16:   }    
 17:     
 18:   if (dests->isTCP) {    
 19:     //使用TCP传输RTP 和 RTCP     
 20:     
 21:     // Change RTP and RTCP to use the TCP socket instead of UDP:     
 22:     if (fRTPSink != NULL) {    
 23:       fRTPSink->addStreamSocket(dests->tcpSocketNum, dests->rtpChannelId);    
 24:       fRTPSink->setServerRequestAlternativeByteHandler(dests->tcpSocketNum, serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);    
 25:     }    
 26:     if (fRTCPInstance != NULL) {    
 27:       fRTCPInstance->addStreamSocket(dests->tcpSocketNum, dests->rtcpChannelId);    
 28:       fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum, dests->rtcpChannelId,    
 29:  rtcpRRHandler, rtcpRRHandlerClientData);    
 30:     }    
 31:   } else {    
 32:     //使用UDP传输RTP、RTCP     
 33:     
 34:     // Tell the RTP and RTCP 'groupsocks' about this destination     
 35:     // (in case they don't already have it):     
 36:     if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort);    
 37:     if (fRTCPgs != NULL) fRTCPgs->addDestination(dests->addr, dests->rtcpPort);    
 38:     if (fRTCPInstance != NULL) {    
 39:       fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort,    
 40:  rtcpRRHandler, rtcpRRHandlerClientData);    
 41:     }    
 42:   }    
 43:     
 44:   //下面调用sink上的sdtartPlaying函数开始传输数据     
 45:   if (!fAreCurrentlyPlaying && fMediaSource != NULL) {    
 46:     if (fRTPSink != NULL) {     //通过RTP协议传输     
 47:       fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);    
 48:       fAreCurrentlyPlaying = True;    
 49:     } else if (fUDPSink != NULL) {  //裸的UDP数据包,不使用RTP协议      
 50:       fUDPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);    
 51:       fAreCurrentlyPlaying = True;    
 52:     }    
 53:   }    
 54: }   
 55: 

       上面的函数最终调用了MediaSink::startPlaying函数,开始传输数据,这个过程的函数调用比较复杂,将在下一篇文章中单独分析。大致过程是,先取一个帧发送出去,然后此函数就返回了,再处理response包的发送等剩余操作。 


0 0