RTSP的Client建立流程(testProgs中的testRTSPClient示例)
参考文档:
testRtspClient流程图请参考下面链接:
http://blog.csdn.net/smilestone_322/article/details/17297817
1) Sink和source
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();
}
数据接收基本清楚了。