庖丁解牛-----Live555源码彻底解密(根据testRTSPClient讲解)

RTSPClient建立流程(testProgs中的testRTSPClient示例)

 参考文档:

http://www.live555.com/liveMedia/doxygen/html/testRTSPClient_8cpp.html#db610df7edad8ceaf6e28e6de0367a13

testRtspClient流程图请参考下面链接:

http://blog.csdn.net/smilestone_322/article/details/17297817

1)      Sinksource

Source是接收数据,Sink是消费数据;

 

int main(intargc,char** argv) {

     // Begin by setting up our usage environment:

     TaskScheduler* scheduler = BasicTaskScheduler::createNew();

     UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

 

     // We need at least one "rtsp://" URL argument:

     if (argc < 2) {

         usage(*env,argv[0]);

         return 1;

     }

 

     // There are argc-1 URLs: argv[1] through argv[argc-1]. Open and start streaming each one:

     for (inti = 1;i <=argc-1; ++i) {

         openURL(*env,argv[0],argv[i]);

     }

 

     // All subsequent activity takes place within the event loop:

     env->taskScheduler().doEventLoop(&eventLoopWatchVariable);

     // This function call does not return, unless, at some point in time, "eventLoopWatchVariable" gets set to something non-zero.

 

     return 0;

 

}

 

 

TestRtspClient的堆栈调用流程如下:

1)  首先OpenURL,函数如下:

void openURL(UsageEnvironment& env, char const*progName,charconst* rtspURL) {

     // Begin by creating a "RTSPClient" object. Note that there is a separate "RTSPClient" object for each stream that we wish

     // to receive (even if more than stream uses the same "rtsp://" URL).

     RTSPClient* rtspClient = ourRTSPClient::createNew(env,rtspURL,RTSP_CLIENT_VERBOSITY_LEVEL,progName);

     if (rtspClient ==NULL) {

         env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": " << env.getResultMsg() << "\n";

         return;

     }

 

     ++rtspClientCount;

 

     // Next, send a RTSP "DESCRIBE" command, to get a SDP description for the stream.

     // Note that this command - like all RTSP commands - is sent asynchronously; we do not block, waiting for a response.

     // Instead, the following function call returns immediately, and we handle the RTSP response later, from within the event loop:

     rtspClient->sendDescribeCommand(continueAfterDESCRIBE);

2)  }

 

首先通过ourRTSPClient::createNew函数最终会调用ourRTSPClient的构造函数,基类RTSPClient的指针指向派生类ourRTSPClient对象,并且最终会调用RTSPClient的构造函数;

 

sendDescribeCommand函数往服务器端发送Describe请求;continueAfterDESCRIBE为回调函数;在DoEventLoop中的SingleStep中调用;RTP over tcp 还是udp 由宏#defineREQUEST_STREAMING_OVER_TCPFalse进行控制;

 

 

unsigned RTSPClient::sendDescribeCommand(responseHandler*responseHandler,Authenticator*authenticator) {

  if (authenticator !=NULL)fCurrentAuthenticator = *authenticator;

  return sendRequest(new RequestRecord(++fCSeq, "DESCRIBE", responseHandler));

}

 

continueAfterDESCRIBE函数传递到responseHandler,相当于continueAfterDESCRIBE为一个回调函数;注意RequestRecord这个类的作用;在SendRequest中调用RequestRecord的构造函数

 

RTSPClient::RequestRecord::RequestRecord(unsignedcseq,char const* commandName, responseHandler* handler,MediaSession*session,MediaSubsession*subsession,u_int32_tbooleanFlags,doublestart,double end, float scale,charconst* contentStr)

  : fNext(NULL),fCSeq(cseq),fCommandName(commandName),fSession(session),fSubsession(subsession),fBooleanFlags(booleanFlags),

    fStart(start),fEnd(end),fAbsStartTime(NULL),fAbsEndTime(NULL),fScale(scale),fContentStr(strDup(contentStr)),fHandler(handler) {

}

 

 

将回调函数保存在RequestRecord类的fHandler上; RequestRecord类定义如下:

 

  // The state of a request-in-progress:

  class RequestRecord {

  public:

    RequestRecord(unsignedcseq,char const* commandName, responseHandler* handler,

           MediaSession* session = NULL, MediaSubsession* subsession = NULL, u_int32_t booleanFlags = 0,

           double start = 0.0f, double end = -1.0f, float scale = 1.0f, char const* contentStr = NULL);

    RequestRecord(unsignedcseq,responseHandler*handler,

           char const* absStartTime, char const* absEndTime =NULL,float scale = 1.0f,

           MediaSession* session = NULL, MediaSubsession* subsession = NULL);

        // alternative constructor for creating "PLAY" requests that include 'absolute' time values

    virtual ~RequestRecord();

 

    RequestRecord*& next() { return fNext; }

    unsigned& cseq() { return fCSeq; }

    char const* commandName() const { return fCommandName; }

    MediaSession* session() const { return fSession; }

    MediaSubsession* subsession() const { return fSubsession; }

    u_int32_t booleanFlags() const { return fBooleanFlags; }

    double start() const { returnfStart; }

    double end() const { returnfEnd; }

    char const* absStartTime() const { return fAbsStartTime; }

    char const* absEndTime() const { return fAbsEndTime; }

    float scale() const { returnfScale; }

    char* contentStr() const { return fContentStr; }

    responseHandler*& handler() { return fHandler; }

 

  private:

    RequestRecord* fNext;

    unsigned fCSeq;

    char const* fCommandName;

    MediaSession* fSession;

    MediaSubsession* fSubsession;

    u_int32_t fBooleanFlags;

    double fStart, fEnd;

    char *fAbsStartTime, *fAbsEndTime;// used for optional 'absolute' (i.e., "time=") range specifications

    float fScale;

    char* fContentStr;

    responseHandler* fHandler;

  };

 

在其他地方就通过RequestRecord类的fHandler 调用到回调函数continueAfterDESCRIBE;注意跟踪在哪个地方调用了RequestRecord类的fHandler,我猜是在DoEvent();中调用的。在DoEvent的函数incomingDataHandler1中的handleResponseBytes中调用     (*foundRequest->handler())(this,resultCode,resultString);

 

  typedef void (responseHandler)(RTSPClient*rtspClient,

                    int resultCode, char* resultString);

      // A function that is called in response to a RTSP command. The parameters are as follows:

      //     "rtspClient": The "RTSPClient" object on which the original command was issued.

      //     "resultCode": If zero, then the command completed successfully.  If non-zero, then the command did not complete

      //         successfully, and "resultCode" indicates the error, as follows:

      //             A positive "resultCode" is a RTSP error code (for example, 404 means "not found")

      //             A negative "resultCode" indicates a socket/network error; 0-"resultCode" is the standard "errno" code.

      //     "resultString": A ('\0'-terminated) string returned along with the response, or else NULL.

      //         In particular:

      //             "resultString" for a successful "DESCRIBE" command will be the media session's SDP description.

      //             "resultString" for a successful "OPTIONS" command will be a list of allowed commands.

      //         Note that this string can be present (i.e., not NULL) even if "resultCode" is non-zero - i.e., an error message.

      //         Also, "resultString" can be NULL, even if "resultCode" is zero (e.g., if the RTSP command succeeded, but without

      //             including an appropriate result header).

      //         Note also that this string is dynamically allocated, and must be freed by the handler (or the caller)

      //             - using "delete[]".

 

 

sendRequest函数如下:

unsigned RTSPClient::sendRequest(RequestRecord*request) {

  char* cmd = NULL;

  do {

    Boolean connectionIsPending = False;

    if (!fRequestsAwaitingConnection.isEmpty()) {

      // A connection is currently pending (with at least one enqueued request). Enqueue this request also:

      connectionIsPending = True;

    } else if (fInputSocketNum < 0) { // we need to open a connection

      int connectResult = openConnection();

      if (connectResult < 0)break;// an error occurred

      else if (connectResult == 0) {

     // A connection is pending

        connectionIsPending = True;

      } // else the connection succeeded. Continue sending the command.

    }

if (connectionIsPending) {

//将request入队列,估计在其它的地方,遍历队列,访问request 的  responseHandler* fHandler;

 

      fRequestsAwaitingConnection.enqueue(request);

      return request->cseq();

    }

 

    // If requested (and we're not already doing it, or have done it), set up the special protocol for tunneling RTSP-over-HTTP:

    if (fTunnelOverHTTPPortNum != 0 &&strcmp(request->commandName(),"GET") != 0 && fOutputSocketNum ==fInputSocketNum) {

      if (!setupHTTPTunneling1())break;

      fRequestsAwaitingHTTPTunneling.enqueue(request);

      return request->cseq();

    }

 

    // Construct and send the command:

 

    // First, construct command-specific headers that we need:

 

    char* cmdURL = fBaseURL; // by default

    Boolean cmdURLWasAllocated = False;

 

    char const* protocolStr = "RTSP/1.0"; // by default

 

    char* extraHeaders = (char*)"";// by default

    Boolean extraHeadersWereAllocated = False;

 

    char* contentLengthHeader = (char*)"";// by default

    Boolean contentLengthHeaderWasAllocated = False;

 

    char const* contentStr = request->contentStr(); // by default

    if (contentStr ==NULL)contentStr ="";

    unsigned contentStrLen = strlen(contentStr);

    if (contentStrLen > 0) {

      char const* contentLengthHeaderFmt =

     "Content-Length: %d\r\n";

      unsigned contentLengthHeaderSize = strlen(contentLengthHeaderFmt)

     + 20 /* max int len */;

      contentLengthHeader = new char[contentLengthHeaderSize];

      sprintf(contentLengthHeader,contentLengthHeaderFmt,contentStrLen);

      contentLengthHeaderWasAllocated =True;

    }

 

    if (strcmp(request->commandName(),"DESCRIBE") == 0) {

      extraHeaders = (char*)"Accept: application/sdp\r\n";

    } else if (strcmp(request->commandName(),"OPTIONS") == 0) {

    } else if (strcmp(request->commandName(),"ANNOUNCE") == 0) {

      extraHeaders = (char*)"Content-Type: application/sdp\r\n";

    } else if (strcmp(request->commandName(),"SETUP") == 0) {

      MediaSubsession& subsession = *request->subsession();

      Boolean streamUsingTCP = (request->booleanFlags()&0x1) != 0;

      Boolean streamOutgoing = (request->booleanFlags()&0x2) != 0;

      Boolean forceMulticastOnUnspecified = (request->booleanFlags()&0x4) != 0;

 

      char const *prefix, *separator, *suffix;

      constructSubsessionURL(subsession,prefix,separator,suffix);

 

      char const* transportFmt;

      if (strcmp(subsession.protocolName(),"UDP") == 0) {

     suffix = "";

     transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n";

      } else {

     transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n";

      }

 

      cmdURL = new char[strlen(prefix) +strlen(separator) +strlen(suffix) + 1];

      cmdURLWasAllocated = True;

      sprintf(cmdURL,"%s%s%s",prefix,separator,suffix);

 

      // Construct a "Transport:" header.

      char const* transportTypeStr;

      char const* modeStr = streamOutgoing ? ";mode=receive" : "";

          // Note: I think the above is nonstandard, but DSS wants it this way

      char const* portTypeStr;

      portNumBits rtpNumber, rtcpNumber;

      if (streamUsingTCP) {// streaming over the RTSP connection

     transportTypeStr = "/TCP;unicast";

     portTypeStr = ";interleaved";

     rtpNumber = fTCPStreamIdCount++;

     rtcpNumber = fTCPStreamIdCount++;

      } else { // normal RTP streaming

     unsigned connectionAddress = subsession.connectionEndpointAddress();

        Boolean requestMulticastStreaming

       = IsMulticastAddress(connectionAddress) || (connectionAddress == 0 &&forceMulticastOnUnspecified);

     transportTypeStr = requestMulticastStreaming ? ";multicast" :";unicast";

     portTypeStr = ";client_port";

     rtpNumber = subsession.clientPortNum();

     if (rtpNumber == 0) {

       envir().setResultMsg("Client port number unknown\n");

       delete[] cmdURL;

       break;

     }

     rtcpNumber = rtpNumber + 1;

      }

      unsigned transportSize = strlen(transportFmt)

     + strlen(transportTypeStr) +strlen(modeStr) +strlen(portTypeStr) + 2*5/* max port len */;

      char* transportStr = new char[transportSize];

      sprintf(transportStr,transportFmt,

           transportTypeStr, modeStr, portTypeStr, rtpNumber, rtcpNumber);

 

      // When sending more than one "SETUP" request, include a "Session:" header in the 2nd and later commands:

      char* sessionStr = createSessionString(fLastSessionId);

 

      // The "Transport:" and "Session:" (if present) headers make up the 'extra headers':

      extraHeaders = new char[transportSize +strlen(sessionStr)];

      extraHeadersWereAllocated =True;

      sprintf(extraHeaders,"%s%s",transportStr,sessionStr);

      delete[] transportStr; delete[] sessionStr;

    } else if (strcmp(request->commandName(),"GET") == 0 ||strcmp(request->commandName(),"POST") == 0) {

      // We will be sending a HTTP (not a RTSP) request.

      // Begin by re-parsing our RTSP URL, just to get the stream name, which we'll use as our 'cmdURL' in the subsequent request:

      char* username;

      char* password;

      NetAddress destAddress;

      portNumBits urlPortNum;

      if (!parseRTSPURL(envir(),fBaseURL,username,password,destAddress,urlPortNum, (charconst**)&cmdURL))break;

      if (cmdURL[0] =='\0')cmdURL = (char*)"/";

      delete[] username;

      delete[] password;

 

      protocolStr = "HTTP/1.0";

 

      if (strcmp(request->commandName(),"GET") == 0) {

     // Create a 'session cookie' string, using MD5:

     struct {

       struct timeval timestamp;

       unsigned counter;

     } seedData;

     gettimeofday(&seedData.timestamp,NULL);

     seedData.counter = ++fSessionCookieCounter;

     our_MD5Data((unsignedchar*)(&seedData),sizeofseedData,fSessionCookie);

     // DSS seems to require that the 'session cookie' string be 22 bytes long:

     fSessionCookie[23] = '\0';

    

     char const* const extraHeadersFmt =

       "x-sessioncookie: %s\r\n"

       "Accept: application/x-rtsp-tunnelled\r\n"

       "Pragma: no-cache\r\n"

       "Cache-Control: no-cache\r\n";

     unsigned extraHeadersSize = strlen(extraHeadersFmt)

       + strlen(fSessionCookie);

     extraHeaders = new char[extraHeadersSize];

     extraHeadersWereAllocated =True;

     sprintf(extraHeaders,extraHeadersFmt,

     fSessionCookie);

      } else { // "POST"

     char const* const extraHeadersFmt =

       "x-sessioncookie: %s\r\n"

       "Content-Type: application/x-rtsp-tunnelled\r\n"

       "Pragma: no-cache\r\n"

       "Cache-Control: no-cache\r\n"

       "Content-Length: 32767\r\n"

       "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n";

     unsigned extraHeadersSize = strlen(extraHeadersFmt)

       + strlen(fSessionCookie);

     extraHeaders = new char[extraHeadersSize];

     extraHeadersWereAllocated =True;

     sprintf(extraHeaders,extraHeadersFmt,

         fSessionCookie);

      }

    } else { // "PLAY", "PAUSE", "TEARDOWN", "RECORD", "SET_PARAMETER", "GET_PARAMETER"

      // First, make sure that we have a RTSP session in progress

      if (fLastSessionId ==NULL) {

     envir().setResultMsg("No RTSP session is currently in progress\n");

     break;

      }

 

      char const* sessionId;

      float originalScale;

      if (request->session() !=NULL) {

     // Session-level operation

     cmdURL = (char*)sessionURL(*request->session());

 

     sessionId = fLastSessionId;

     originalScale = request->session()->scale();

      } else {

     // Media-level operation

     char const *prefix, *separator, *suffix;

     constructSubsessionURL(*request->subsession(),prefix,separator,suffix);

     cmdURL = new char[strlen(prefix) +strlen(separator) +strlen(suffix) + 1];

     cmdURLWasAllocated = True;

     sprintf(cmdURL,"%s%s%s",prefix,separator,suffix);

    

     sessionId = request->subsession()->sessionId();

     originalScale = request->subsession()->scale();

      }

 

      if (strcmp(request->commandName(),"PLAY") == 0) {

     // Create "Session:", "Scale:", and "Range:" headers; these make up the 'extra headers':

     char* sessionStr = createSessionString(sessionId);

     char* scaleStr = createScaleString(request->scale(),originalScale);

     char* rangeStr = createRangeString(request->start(),request->end(),request->absStartTime(),request->absEndTime());

     extraHeaders = new char[strlen(sessionStr) +strlen(scaleStr) +strlen(rangeStr) + 1];

     extraHeadersWereAllocated =True;

     sprintf(extraHeaders,"%s%s%s",sessionStr,scaleStr,rangeStr);

     delete[] sessionStr; delete[] scaleStr; delete[] rangeStr;

      } else {

     // Create a "Session:" header; this makes up our 'extra headers':

     extraHeaders = createSessionString(sessionId);

     extraHeadersWereAllocated =True;

      }

    }

 

    char* authenticatorStr = createAuthenticatorString(request->commandName(),fBaseURL);

 

    char const* const cmdFmt =

      "%s %s %s\r\n"

      "CSeq: %d\r\n"

      "%s"

      "%s"

      "%s"

      "%s"

      "\r\n"

      "%s";

    unsigned cmdSize = strlen(cmdFmt)

      + strlen(request->commandName()) +strlen(cmdURL) +strlen(protocolStr)

      + 20 /* max int len */

      + strlen(authenticatorStr)

      + fUserAgentHeaderStrLen

      + strlen(extraHeaders)

      + strlen(contentLengthHeader)

      + contentStrLen;

    cmd = new char[cmdSize];

    sprintf(cmd,cmdFmt,

         request->commandName(),cmdURL,protocolStr,

         request->cseq(),

         authenticatorStr,

         fUserAgentHeaderStr,

            extraHeaders,

         contentLengthHeader,

         contentStr);

    delete[] authenticatorStr;

    if (cmdURLWasAllocated)delete[]cmdURL;

    if (extraHeadersWereAllocated)delete[]extraHeaders;

    if (contentLengthHeaderWasAllocated)delete[]contentLengthHeader;

 

    if (fVerbosityLevel >= 1)envir() <<"Sending request: " <<cmd <<"\n";

 

    if (fTunnelOverHTTPPortNum != 0 &&strcmp(request->commandName(),"GET") != 0 && strcmp(request->commandName(),"POST") != 0) {

      // When we're tunneling RTSP-over-HTTP, we Base-64-encode the request before we send it.

      // (However, we don't do this for the HTTP "GET" and "POST" commands that we use to set up the tunnel.)

      char* origCmd = cmd;

      cmd = base64Encode(origCmd, strlen(cmd));

      if (fVerbosityLevel >= 1)envir() <<"\tThe request was base-64 encoded to: " <<cmd <<"\n\n";

      delete[] origCmd;

    }

 

    if (send(fOutputSocketNum,cmd,strlen(cmd), 0) < 0) {

      char const* errFmt = "%s send() failed: ";

      unsigned const errLength = strlen(errFmt) + strlen(request->commandName());

      char* err = new char[errLength];

      sprintf(err,errFmt,request->commandName());

      envir().setResultErrMsg(err);

      delete[] err;

      break;

    }

 

    // The command send succeeded, so enqueue the request record, so that its response (when it comes) can be handled.

    // However, note that we do not expect a response to a POST command with RTSP-over-HTTP, so don't enqueue that.

    int cseq = request->cseq();

 

    if (fTunnelOverHTTPPortNum == 0 ||strcmp(request->commandName(),"POST") != 0) {

      fRequestsAwaitingResponse.enqueue(request);

    } else {

      delete request;

    }

 

    delete[] cmd;

    return cseq;

  } while (0);

 

  // An error occurred, so call the response handler immediately (indicating the error):

  delete[] cmd;

  handleRequestError(request);

  delete request;

  return 0;

}

 

openConnection连接函数如下:

 

int RTSPClient::openConnection() {

  do {

    // Set up a connection to the server. Begin by parsing the URL:

 

    char* username;

    char* password;

    NetAddress destAddress;

    portNumBits urlPortNum;

    char const* urlSuffix;

if (!parseRTSPURL(envir(),fBaseURL,username,password,destAddress,urlPortNum, &urlSuffix))break;

    // 如果fTunnelOverHTTPPortNum!=0,那么就使用Http的端口;

 

portNumBits destPortNum = fTunnelOverHTTPPortNum == 0 ?urlPortNum :fTunnelOverHTTPPortNum;

 

    if (username !=NULL ||password !=NULL) {

      fCurrentAuthenticator.setUsernameAndPassword(username,password);

      delete[] username;

      delete[] password;

    }

 

// We don't yet have a TCP socket (or we used to have one, but it got closed). Set it up now.

    //建立一个socket;

    fInputSocketNum = fOutputSocketNum = setupStreamSocket(envir(), 0);

    if (fInputSocketNum < 0)break;

    ignoreSigPipeOnSocket(fInputSocketNum);// so that servers on the same host that get killed don't also kill us

     

    // Connect to the remote endpoint:

    fServerAddress = *(netAddressBits*)(destAddress.data());

    int connectResult = connectToServer(fInputSocketNum,destPortNum);

    if (connectResult < 0)break;

    else if (connectResult > 0) {

      // The connection succeeded.  Arrange to handle responses to requests sent on it:

      envir().taskScheduler().setBackgroundHandling(fInputSocketNum,SOCKET_READABLE|SOCKET_EXCEPTION,

                                (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler,this);

    }

    return connectResult;

  } while (0);

 

  resetTCPSockets();

  return -1;

}

 

setBackgroundHandling函数定义如下:其中 HandlerSet*fHandlers;连上服务器后,调用incomingDataHandler回调函数;

 

void BasicTaskScheduler

  ::setBackgroundHandling(intsocketNum,intconditionSet,BackgroundHandlerProc*handlerProc,void*clientData) {

  if (socketNum < 0)return;

  FD_CLR((unsigned)socketNum, &fReadSet);

  FD_CLR((unsigned)socketNum, &fWriteSet);

  FD_CLR((unsigned)socketNum, &fExceptionSet);

  if (conditionSet == 0) {

    fHandlers->clearHandler(socketNum);

    if (socketNum+1 ==fMaxNumSockets) {

      --fMaxNumSockets;

    }

  } else {

    fHandlers->assignHandler(socketNum,conditionSet,handlerProc,clientData);

    if (socketNum+1 >fMaxNumSockets) {

      fMaxNumSockets = socketNum+1;

    }

    if (conditionSet&SOCKET_READABLE)FD_SET((unsigned)socketNum, &fReadSet);

    if (conditionSet&SOCKET_WRITABLE)FD_SET((unsigned)socketNum, &fWriteSet);

    if (conditionSet&SOCKET_EXCEPTION)FD_SET((unsigned)socketNum, &fExceptionSet);

  }

}

assignHandler 定义如下:

void HandlerSet

::assignHandler(intsocketNum,intconditionSet,TaskScheduler::BackgroundHandlerProc*handlerProc,void*clientData) {

  // First, see if there's already a handler for this socket:

  HandlerDescriptor* handler = lookupHandler(socketNum);

  if (handler ==NULL) {// No existing handler, so create a new descr:

    handler = new HandlerDescriptor(fHandlers.fNextHandler);

    handler->socketNum =socketNum;

  }

 

  handler->conditionSet =conditionSet;

  handler->handlerProc = handlerProc;

  handler->clientData =clientData;

}

 

其中lookupHandler定义如下:

HandlerDescriptor* HandlerSet::lookupHandler(intsocketNum) {

  HandlerDescriptor* handler;

  HandlerIterator iter(*this);

  while ((handler =iter.next()) !=NULL) {

    if (handler->socketNum ==socketNum)break;

  }

  return handler;

}

 

openConnection函数用来连接到服务器;在该函数中首先调用parseRTSPURL解析客户端的RtspURL;然后建立socket(),然后connectToServer连接到服务器; incomingDataHandler为回调函数;incomingDataHandler函数,最终赋值给了 handler->handlerProc = handlerProc;

 

 

Boolean RTSPClient::parseRTSPURL(UsageEnvironment&env,char const* url,

                    char*& username, char*& password,

                    NetAddress&address,

                    portNumBits&portNum,

                    char const** urlSuffix) {

  do {

    // Parse the URL as "rtsp://[<username>[:<password>]@]<server-address-or-name>[:<port>][/<stream-name>]"

    char const* prefix = "rtsp://";

    unsigned const prefixLength = 7;

    if (_strncasecmp(url,prefix,prefixLength) != 0) {

      env.setResultMsg("URL is not of the form \"",prefix,"\"");

      break;

    }

 

    unsigned const parseBufferSize = 100;

    char parseBuffer[parseBufferSize];

    char const* from = &url[prefixLength];

 

    // Check whether "<username>[:<password>]@" occurs next.

    // We do this by checking whether '@' appears before the end of the URL, or before the first '/'.

    username = password = NULL; // default return values

    char const* colonPasswordStart = NULL;

    char const* p;

    for (p =from; *p !='\0' && *p !='/'; ++p) {

      if (*p ==':' &&colonPasswordStart ==NULL) {

     colonPasswordStart = p;

      } else if (*p == '@') {

     // We found <username> (and perhaps <password>). Copy them into newly-allocated result strings:

     if (colonPasswordStart ==NULL)colonPasswordStart =p;

 

     char const* usernameStart = from;

     unsigned usernameLen = colonPasswordStart - usernameStart;

     username = new char[usernameLen + 1] ;// allow for the trailing '\0'

     for (unsignedi = 0;i < usernameLen; ++i)username[i] =usernameStart[i];

     username[usernameLen] ='\0';

 

     char const* passwordStart = colonPasswordStart;

     if (passwordStart <p) ++passwordStart;// skip over the ':'

     unsigned passwordLen = p - passwordStart;

     password = new char[passwordLen + 1];// allow for the trailing '\0'

     for (unsignedj = 0;j < passwordLen; ++j)password[j] =passwordStart[j];

     password[passwordLen] ='\0';

 

     from = p + 1; // skip over the '@'

     break;

      }

    }

 

    // Next, parse <server-address-or-name>

    char* to = &parseBuffer[0];

    unsigned i;

    for (i = 0;i <parseBufferSize; ++i) {

      if (*from =='\0' || *from ==':' || *from =='/') {

     // We've completed parsing the address

     *to = '\0';

     break;

      }

      *to++ = *from++;

    }

    if (i ==parseBufferSize) {

      env.setResultMsg("URL is too long");

      break;

    }

 

    NetAddressList addresses(parseBuffer);

    if (addresses.numAddresses() == 0) {

      env.setResultMsg("Failed to find network address for \"",

                parseBuffer, "\"");

      break;

    }

    address = *(addresses.firstAddress());

 

    portNum = 554; // default value

    char nextChar = *from;

    if (nextChar ==':') {

      int portNumInt;

      if (sscanf(++from,"%d", &portNumInt) != 1) {

     env.setResultMsg("No port number follows ':'");

     break;

      }

      if (portNumInt < 1 ||portNumInt > 65535) {

     env.setResultMsg("Bad port number");

     break;

      }

      portNum = (portNumBits)portNumInt;

      while (*from >='0' && *from <='9') ++from;// skip over port number

    }

 

    // The remainder of the URL is the suffix:

    if (urlSuffix !=NULL) *urlSuffix =from;

 

    return True;

  } while (0);

 

  return False;

}

 

 

void RTSPClient::incomingDataHandler(void*instance,int /*mask*/) {

  RTSPClient* client = (RTSPClient*)instance;

  client->incomingDataHandler1();

}

 

void RTSPClient::incomingDataHandler1() {

  struct sockaddr_in dummy; // 'from' address - not used

 

  int bytesRead = readSocket(envir(),fInputSocketNum, (unsignedchar*)&fResponseBuffer[fResponseBytesAlreadySeen],fResponseBufferBytesLeft,dummy);

  handleResponseBytes(bytesRead);

}

 

建立socket的函数为setupStreamSocket,该函数建立的是一个tcp的socket;

setupStreamSocket函数首先创建socket,然后设置SO_REUSEADDR socket属性;并且调用bind函数绑定socket;最后将socket设置成非阻塞形式;

 

int setupStreamSocket(UsageEnvironment&env,

                      Port port, Boolean makeNonBlocking) {

  if (!initializeWinsockIfNecessary()) {

    socketErr(env,"Failed to initialize 'winsock': ");

    return -1;

  }

 

  int newSocket = createSocket(SOCK_STREAM);

  if (newSocket < 0) {

    socketErr(env,"unable to create stream socket: ");

    return newSocket;

  }

 

  int reuseFlag = groupsockPriv(env)->reuseFlag;

  reclaimGroupsockPriv(env);

  if (setsockopt(newSocket,SOL_SOCKET,SO_REUSEADDR,

          (const char*)&reuseFlag, sizeof reuseFlag) < 0) {

    socketErr(env,"setsockopt(SO_REUSEADDR) error: ");

    closeSocket(newSocket);

    return -1;

  }

 

  // SO_REUSEPORT doesn't really make sense for TCP sockets, so we

  // normally don't set them.  However, if you really want to do this

  // #define REUSE_FOR_TCP

#ifdef REUSE_FOR_TCP

#if defined(__WIN32__) || defined(_WIN32)

  // Windoze doesn't properly handle SO_REUSEPORT

#else

#ifdef SO_REUSEPORT

  if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT,

          (const char*)&reuseFlag, sizeof reuseFlag) < 0) {

    socketErr(env, "setsockopt(SO_REUSEPORT) error: ");

    closeSocket(newSocket);

    return -1;

  }

#endif

#endif

#endif

 

  // Note: Windoze requires binding, even if the port number is 0

#if defined(__WIN32__) ||defined(_WIN32)

#else

  if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) {

#endif

    MAKE_SOCKADDR_IN(name,ReceivingInterfaceAddr,port.num());

    if (bind(newSocket, (structsockaddr*)&name,sizeofname) != 0) {

      char tmpBuffer[100];

      sprintf(tmpBuffer,"bind() error (port number: %d): ",

           ntohs(port.num()));

      socketErr(env,tmpBuffer);

      closeSocket(newSocket);

      return -1;

    }

#if defined(__WIN32__) ||defined(_WIN32)

#else

  }

#endif

 

  if (makeNonBlocking) {

    if (!makeSocketNonBlocking(newSocket)) {

      socketErr(env,"failed to make non-blocking: ");

      closeSocket(newSocket);

      return -1;

    }

  }

 

  return newSocket;

}

 

connectToServer函数如下:

nt RTSPClient::connectToServer(intsocketNum,portNumBitsremotePortNum) {

  MAKE_SOCKADDR_IN(remoteName,fServerAddress,htons(remotePortNum));

  if (fVerbosityLevel >= 1) {

    envir() << "Opening connection to " << AddressString(remoteName).val() <<", port " <<remotePortNum <<"...\n";

  }

  if (connect(socketNum, (structsockaddr*) &remoteName,sizeofremoteName) != 0) {

    int const err = envir().getErrno();

    if (err ==EINPROGRESS ||err ==EWOULDBLOCK) {

      // The connection is pending; we'll need to handle it later. Wait for our socket to be 'writable', or have an exception.

      envir().taskScheduler().setBackgroundHandling(socketNum,SOCKET_WRITABLE|SOCKET_EXCEPTION,

                                (TaskScheduler::BackgroundHandlerProc*)&connectionHandler,this);

      return 0;

    }

    envir().setResultErrMsg("connect() failed: ");

    if (fVerbosityLevel >= 1)envir() <<"..." <<envir().getResultMsg() <<"\n";

    return -1;

  }

  if (fVerbosityLevel >= 1)envir() <<"...local connection opened\n";

 

  return 1;

}

 

主要起作用的是这句话:

envir().taskScheduler().setBackgroundHandling(socketNum,SOCKET_WRITABLE|SOCKET_EXCEPTION,(TaskScheduler::BackgroundHandlerProc*)&connectionHandler,

 

具体函数如下:

void BasicTaskScheduler

  ::setBackgroundHandling(intsocketNum,intconditionSet,BackgroundHandlerProc*handlerProc,void*clientData) {

  if (socketNum < 0)return;

  FD_CLR((unsigned)socketNum, &fReadSet);

  FD_CLR((unsigned)socketNum, &fWriteSet);

  FD_CLR((unsigned)socketNum, &fExceptionSet);

  if (conditionSet == 0) {

    fHandlers->clearHandler(socketNum);

    if (socketNum+1 ==fMaxNumSockets) {

      --fMaxNumSockets;

    }

  } else {

    fHandlers->assignHandler(socketNum,conditionSet,handlerProc,clientData);

    if (socketNum+1 >fMaxNumSockets) {

      fMaxNumSockets = socketNum+1;

    }

    if (conditionSet&SOCKET_READABLE)FD_SET((unsigned)socketNum, &fReadSet);

    if (conditionSet&SOCKET_WRITABLE)FD_SET((unsigned)socketNum, &fWriteSet);

    if (conditionSet&SOCKET_EXCEPTION)FD_SET((unsigned)socketNum, &fExceptionSet);

  }

}

 

 

connectionHandler在SingleStep函数中会调用,调用如下:

     (*handler->handlerProc)(handler->clientData,resultConditionSet);

 

void RTSPClient::connectionHandler1() {

 

  // Restore normal handling on our sockets:

  envir().taskScheduler().disableBackgroundHandling(fOutputSocketNum);

  envir().taskScheduler().setBackgroundHandling(fInputSocketNum,SOCKET_READABLE|SOCKET_EXCEPTION,

                            (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler,this);

 

  // Move all requests awaiting connection into a new, temporary queue, to clear "fRequestsAwaitingConnection"

  // (so that "sendRequest()" doesn't get confused by "fRequestsAwaitingConnection" being nonempty, and enqueue them all over again).

// fRequestsAwaitingConnection 等待连接的队列;

 

  RequestQueue tmpRequestQueue(fRequestsAwaitingConnection); 

  RequestRecord* request;

 

  // Find out whether the connection succeeded or failed:

  do {

    int err = 0;

    SOCKLEN_T len = sizeof err;

    if (getsockopt(fInputSocketNum, SOL_SOCKET, SO_ERROR, (char*)&err, &len) < 0 || err != 0){

      envir().setResultErrMsg("Connection to server failed: ",err);

      if (fVerbosityLevel >= 1)envir() <<"..." <<envir().getResultMsg() <<"\n";

      break;

    }

 

    // The connection succeeded.  If the connection came about from an attempt to set up RTSP-over-HTTP, finish this now:

    if (fVerbosityLevel >= 1)envir() <<"...remote connection opened\n";

    if (fHTTPTunnelingConnectionIsPending && !setupHTTPTunneling2())break;

 

    // Resume sending all pending requests:

    while ((request =tmpRequestQueue.dequeue()) !=NULL) {

      sendRequest(request);//发送请求,比如Described

    }

    return;

  } while (0);

 

  // An error occurred.  Tell all pending requests about the error:

  resetTCPSockets(); // do this now, in case an error handler deletes "this"

  while ((request =tmpRequestQueue.dequeue()) !=NULL) {

    handleRequestError(request);

    delete request;

  }

}

 

SingleStep函数如下:

 

void BasicTaskScheduler::SingleStep(unsignedmaxDelayTime) {

  fd_set readSet = fReadSet; // make a copy for this select() call

  fd_set writeSet = fWriteSet; // ditto

  fd_set exceptionSet = fExceptionSet; // ditto

 

  DelayInterval const& timeToDelay = fDelayQueue.timeToNextAlarm();

  struct timeval tv_timeToDelay;

  tv_timeToDelay.tv_sec =timeToDelay.seconds();

  tv_timeToDelay.tv_usec =timeToDelay.useconds();

  // Very large "tv_sec" values cause select() to fail.

  // Don't make it any larger than 1 million seconds (11.5 days)

  const long MAX_TV_SEC = MILLION;

  if (tv_timeToDelay.tv_sec >MAX_TV_SEC) {

    tv_timeToDelay.tv_sec =MAX_TV_SEC;

  }

  // Also check our "maxDelayTime" parameter (if it's > 0):

  if (maxDelayTime > 0 &&

      (tv_timeToDelay.tv_sec > (long)maxDelayTime/MILLION ||

       (tv_timeToDelay.tv_sec == (long)maxDelayTime/MILLION &&

     tv_timeToDelay.tv_usec > (long)maxDelayTime%MILLION))) {

    tv_timeToDelay.tv_sec =maxDelayTime/MILLION;

    tv_timeToDelay.tv_usec =maxDelayTime%MILLION;

  }

 

  int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);

  if (selectResult < 0) {

#if defined(__WIN32__) ||defined(_WIN32)

    int err = WSAGetLastError();

    // For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if

    // it was called with no entries set in "readSet". If this happens, ignore it:

    if (err ==WSAEINVAL &&readSet.fd_count == 0) {

      err = EINTR;

      // To stop this from happening again, create a dummy socket:

      int dummySocketNum = socket(AF_INET,SOCK_DGRAM, 0);

      FD_SET((unsigned)dummySocketNum, &fReadSet);

    }

    if (err !=EINTR) {

#else

    if (errno != EINTR && errno != EAGAIN) {

#endif

     // Unexpected error - treat this as fatal:

#if !defined(_WIN32_WCE)

     perror("BasicTaskScheduler::SingleStep(): select() fails");

     // Because this failure is often "Bad file descriptor" - which is caused by an invalid socket number (i.e., a socket number

     // that had already been closed) being used in "select()" - we print out the sockets that were being used in "select()",

     // to assist in debugging:

     fprintf(stderr,"socket numbers used in the select() call:");

     for (inti = 0;i < 10000; ++i) {

       if (FD_ISSET(i, &fReadSet) ||FD_ISSET(i, &fWriteSet) ||FD_ISSET(i, &fExceptionSet)) {

         fprintf(stderr," %d(",i);

         if (FD_ISSET(i, &fReadSet))fprintf(stderr,"r");

         if (FD_ISSET(i, &fWriteSet))fprintf(stderr,"w");

         if (FD_ISSET(i, &fExceptionSet))fprintf(stderr,"e");

         fprintf(stderr,")");

       }

     }

     fprintf(stderr,"\n");

#endif

     internalError();

      }

  }

 

  // Call the handler function for one readable socket:

  HandlerIterator iter(*fHandlers);

  HandlerDescriptor* handler;

  // To ensure forward progress through the handlers, begin past the last

  // socket number that we handled:

  if (fLastHandledSocketNum >= 0) {

    while ((handler =iter.next()) !=NULL) {

      if (handler->socketNum ==fLastHandledSocketNum)break;

    }

    if (handler ==NULL) {

      fLastHandledSocketNum = -1;

      iter.reset();// start from the beginning instead

    }

  }

  while ((handler =iter.next()) !=NULL) {

    int sock = handler->socketNum;// alias

    int resultConditionSet = 0;

    if (FD_ISSET(sock, &readSet) &&FD_ISSET(sock, &fReadSet)/*sanity check*/)resultConditionSet |=SOCKET_READABLE;

    if (FD_ISSET(sock, &writeSet) &&FD_ISSET(sock, &fWriteSet)/*sanity check*/)resultConditionSet |=SOCKET_WRITABLE;

    if (FD_ISSET(sock, &exceptionSet) &&FD_ISSET(sock, &fExceptionSet)/*sanity check*/)resultConditionSet |=SOCKET_EXCEPTION;

    if ((resultConditionSet&handler->conditionSet) != 0 &&handler->handlerProc !=NULL) {

      fLastHandledSocketNum = sock;

          // Note: we set "fLastHandledSocketNum" before calling the handler,

          // in case the handler calls "doEventLoop()" reentrantly.

      (*handler->handlerProc)(handler->clientData,resultConditionSet);

      break;

    }

  }

  if (handler ==NULL &&fLastHandledSocketNum >= 0) {

    // We didn't call a handler, but we didn't get to check all of them,

    // so try again from the beginning:

    iter.reset();

    while ((handler =iter.next()) !=NULL) {

      int sock = handler->socketNum;// alias

      int resultConditionSet = 0;

      if (FD_ISSET(sock, &readSet) &&FD_ISSET(sock, &fReadSet)/*sanity check*/)resultConditionSet |=SOCKET_READABLE;

      if (FD_ISSET(sock, &writeSet) &&FD_ISSET(sock, &fWriteSet)/*sanity check*/)resultConditionSet |=SOCKET_WRITABLE;

      if (FD_ISSET(sock, &exceptionSet) &&FD_ISSET(sock, &fExceptionSet)/*sanity check*/)resultConditionSet |=SOCKET_EXCEPTION;

      if ((resultConditionSet&handler->conditionSet) != 0 &&handler->handlerProc !=NULL) {

     fLastHandledSocketNum = sock;

         // Note: we set "fLastHandledSocketNum" before calling the handler,

            // in case the handler calls "doEventLoop()" reentrantly.

     (*handler->handlerProc)(handler->clientData,resultConditionSet);

     break;

      }

    }

    if (handler ==NULL)fLastHandledSocketNum = -1;//because we didn't call a handler

  }

 

  // Also handle any newly-triggered event (Note that we do this *after* calling a socket handler,

  // in case the triggered event handler modifies The set of readable sockets.)

  if (fTriggersAwaitingHandling != 0) {

    if (fTriggersAwaitingHandling ==fLastUsedTriggerMask) {

      // Common-case optimization for a single event trigger:

      fTriggersAwaitingHandling = 0;

      if (fTriggeredEventHandlers[fLastUsedTriggerNum] !=NULL) {

     (*fTriggeredEventHandlers[fLastUsedTriggerNum])(fTriggeredEventClientDatas[fLastUsedTriggerNum]);

      }

    } else {

      // Look for an event trigger that needs handling (making sure that we make forward progress through all possible triggers):

      unsigned i = fLastUsedTriggerNum;

      EventTriggerId mask = fLastUsedTriggerMask;

 

      do {

     i = (i+1)%MAX_NUM_EVENT_TRIGGERS;

     mask >>= 1;

     if (mask == 0)mask = 0x80000000;

 

     if ((fTriggersAwaitingHandling&mask) != 0) {

       fTriggersAwaitingHandling &=~mask;

       if (fTriggeredEventHandlers[i] !=NULL) {

         (*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);

       }

 

       fLastUsedTriggerMask = mask;

       fLastUsedTriggerNum = i;

       break;

     }

      } while (i !=fLastUsedTriggerNum);

    }

  }

 

  // Also handle any delayed event that may have come due.

  fDelayQueue.handleAlarm();

}

 

 

数据接收基本清楚了。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值