testOnDemandRTSPServer.cpp 讲解
Main函数:
int main(intargc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
UserAuthenticationDatabase*authDB = NULL;
#ifdef ACCESS_CONTROL
// To implement client access control to the RTSP server, do the following:
authDB = new UserAuthenticationDatabase;
authDB->addUserRecord("username1", "password1"); // replace these with real strings
// Repeat the above with each <username>, <password> that you wish to allow
// access to the server.
#endif
// Create the RTSP server:
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554,authDB);
if (rtspServer ==NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() <<"\n";
exit(1);
}
char const* descriptionString
= "Session streamed by \"testOnDemandRTSPServer\"";
// Set up each of the possible streams that can be served by the
// RTSP server. Each such stream is implemented using a
// "ServerMediaSession" object, plus one or more
// "ServerMediaSubsession" objects for each audio/video substream.
// A H.264 video elementary stream:
{
char const* streamName = "h264ESVideoTest";
char const* inputFileName = "test.264";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env,streamName, streamName,
descriptionString);
sms->addSubsession(H264VideoFileServerMediaSubsession
::createNew(*env,inputFileName, reuseFirstSource));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer,sms, streamName,inputFileName);
}
env->taskScheduler().doEventLoop();// does not return
return 0; // only to prevent compiler warning
}
建立Rtsp服务端(RTSPServer):
RTSPServer*
RTSPServer::createNew(UsageEnvironment&env, Port ourPort,
UserAuthenticationDatabase*authDatabase,
unsigned reclamationTestSeconds) {
int ourSocket = setUpOurSocket(env,ourPort);
if (ourSocket == -1)return NULL;
return new RTSPServer(env,ourSocket, ourPort,authDatabase, reclamationTestSeconds);
}
#define LISTEN_BACKLOG_SIZE 20
//建立服务端socket
int RTSPServer::setUpOurSocket(UsageEnvironment&env, Port& ourPort) {
int ourSocket = -1;
do {
// The following statement is enabled by default.
// Don't disable it (by defining ALLOW_RTSP_SERVER_PORT_REUSE) unless you know what you're doing.
#ifndef ALLOW_RTSP_SERVER_PORT_REUSE
NoReuse dummy(env); // Don't use this socket if there's already a local server using it
#endif
ourSocket = setupStreamSocket(env, ourPort);
if (ourSocket < 0)break;
// Make sure we have a big send buffer:
if (!increaseSendBufferTo(env,ourSocket, 50*1024)) break;
// Allow multiple simultaneous connections:
if (listen(ourSocket,LISTEN_BACKLOG_SIZE) < 0) {
env.setResultErrMsg("listen() failed: ");
break;
}
if (ourPort.num() == 0) {
// bind() will have chosen a port for us; return it also:
if (!getSourcePort(env,ourSocket, ourPort))break;
}
return ourSocket;
} while (0);
if (ourSocket != -1) ::closeSocket(ourSocket);
return -1;
}
Create 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,sizeof name) != 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;
}
//Rtsp构造函数如下:
RTSPServer::RTSPServer(UsageEnvironment&env,
int ourSocket, Port ourPort,
UserAuthenticationDatabase*authDatabase,
unsigned reclamationTestSeconds)
: Medium(env),
fRTSPServerPort(ourPort),fRTSPServerSocket(ourSocket),fHTTPServerSocket(-1), fHTTPServerPort(0),
fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
fClientConnectionsForHTTPTunneling(NULL),// will get created if needed
fClientSessions(HashTable::create(STRING_HASH_KEYS)),
fPendingRegisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)),
fAuthDB(authDatabase),fReclamationTestSeconds(reclamationTestSeconds) {
ignoreSigPipeOnSocket(ourSocket);// so that clients on the same host that are killed don't also kill us
// Arrange to handle connections from others:
env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,
(TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP,this);
}
turnOnBackgroundReadHandling函数已经废弃了,为了兼容前面的版本在用它,其实就是调用setBackgroundHandling; turnOnBackgroundReadHandling函数如下:
void turnOnBackgroundReadHandling(int socketNum, BackgroundHandlerProc* handlerProc, void* clientData) {
setBackgroundHandling(socketNum,SOCKET_READABLE, handlerProc,clientData);
}
在incomingConnectionHandler函数中接受客户端的请求;
void RTSPServer::incomingConnectionHandler(intserverSocket) {
struct sockaddr_in clientAddr;
SOCKLEN_T clientAddrLen = sizeof clientAddr;
int clientSocket = accept(serverSocket, (structsockaddr*)&clientAddr, &clientAddrLen);
if (clientSocket < 0) {
int err = envir().getErrno();
if (err !=EWOULDBLOCK) {
envir().setResultErrMsg("accept() failed: ");
}
return;
}
// Accept后的socket clientSocket设置成非阻塞
makeSocketNonBlocking(clientSocket);
//调整发送缓冲区的size
increaseSendBufferTo(envir(),clientSocket, 50*1024);
#ifdef DEBUG
envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
#endif
// Create a new object for handling this RTSP connection:
(void)createNewClientConnection(clientSocket,clientAddr);
}
createNewClientConnection函数如下:
RTSPServer::RTSPClientConnection*
RTSPServer::createNewClientConnection(intclientSocket, structsockaddr_in clientAddr) {
return new RTSPClientConnection(*this,clientSocket, clientAddr);
}
RTSPClientConnection函数如下:
RTSPServer::RTSPClientConnection
::RTSPClientConnection(RTSPServer&ourServer, intclientSocket, structsockaddr_in clientAddr)
: fOurServer(ourServer),fIsActive(True),
fClientInputSocket(clientSocket),fClientOutputSocket(clientSocket),fClientAddr(clientAddr),
fRecursionCount(0), fOurSessionCookie(NULL) {
// Add ourself to our 'client connections' table: //将客户端socket放在表中,以便后面处理该socket的请求
fOurServer.fClientConnections->Add((charconst*)this, this);
// Arrange to handle incoming requests:
resetRequestBuffer();
envir().taskScheduler().setBackgroundHandling(fClientInputSocket,SOCKET_READABLE|SOCKET_EXCEPTION,
(TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler,this);
}
void RTSPServer::RTSPClientConnection::incomingRequestHandler(void*instance, int /*mask*/) {
RTSPClientConnection* session = (RTSPClientConnection*)instance;
session->incomingRequestHandler1();
}
//socket从客户端读数据
void RTSPServer::RTSPClientConnection::incomingRequestHandler1() {
struct sockaddr_in dummy; // 'from' address, meaningless in this case
int bytesRead = readSocket(envir(),fClientInputSocket, &fRequestBuffer[fRequestBytesAlreadySeen],fRequestBufferBytesLeft, dummy);
handleRequestBytes(bytesRead);
}
handleRequestBytes如下:
主要解析客户端的Rtsp请求信息:
void RTSPServer::RTSPClientConnection::handleRequestBytes(intnewBytesRead) {
int numBytesRemaining = 0;
++fRecursionCount;
do {
RTSPServer::RTSPClientSession*clientSession = NULL;
if (newBytesRead < 0 || (unsigned)newBytesRead >=fRequestBufferBytesLeft) {
// Either the client socket has died, or the request was too big for us.
// Terminate this connection:
#ifdef DEBUG
fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
#endif
fIsActive = False;
break;
}
Boolean endOfMsg = False;
unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
#ifdef DEBUG
ptr[newBytesRead] = '\0';
fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() %s %d new bytes:%s\n",
this, numBytesRemaining > 0 ? "processing" : "read", newBytesRead, ptr);
#endif
if (fClientOutputSocket !=fClientInputSocket) {
// We're doing RTSP-over-HTTP tunneling, and input commands are assumed to have been Base64-encoded.
// We therefore Base64-decode as much of this new data as we can (i.e., up to a multiple of 4 bytes).
// But first, we remove any whitespace that may be in the input data:
unsigned toIndex = 0;
for (intfromIndex = 0; fromIndex <newBytesRead; ++fromIndex) {
char c = ptr[fromIndex];
if (!(c ==' ' || c =='\t' || c =='\r' || c =='\n')) { // not 'whitespace': space,tab,CR,NL
ptr[toIndex++] =c;
}
}
newBytesRead = toIndex;
unsigned numBytesToDecode = fBase64RemainderCount +newBytesRead;
unsigned newBase64RemainderCount = numBytesToDecode%4;
numBytesToDecode -= newBase64RemainderCount;
if (numBytesToDecode > 0) {
ptr[newBytesRead] ='\0';
unsigned decodedSize;
unsigned char* decodedBytes = base64Decode((char const*)(ptr-fBase64RemainderCount),numBytesToDecode, decodedSize);
#ifdef DEBUG
fprintf(stderr, "Base64-decoded %d input bytes into %d new bytes:", numBytesToDecode, decodedSize);
for (unsigned k = 0; k < decodedSize; ++k) fprintf(stderr, "%c", decodedBytes[k]);
fprintf(stderr, "\n");
#endif
// Copy the new decoded bytes in place of the old ones (we can do this because there are fewer decoded bytes than original):
unsigned char* to = ptr-fBase64RemainderCount;
for (unsignedi = 0; i < decodedSize; ++i) *to++ =decodedBytes[i];
// Then copy any remaining (undecoded) bytes to the end:
for (unsignedj = 0; j < newBase64RemainderCount; ++j) *to++ = (ptr-fBase64RemainderCount+numBytesToDecode)[j];
newBytesRead = decodedSize + newBase64RemainderCount; // adjust to allow for the size of the new decoded data (+ remainder)
delete[] decodedBytes;
}
fBase64RemainderCount = newBase64RemainderCount;
if (fBase64RemainderCount > 0)break; // because we know that we have more input bytes still to receive
}
// Look for the end of the message: <CR><LF><CR><LF>
unsigned char *tmpPtr = fLastCRLF + 2;
if (tmpPtr <fRequestBuffer) tmpPtr =fRequestBuffer;
while (tmpPtr < &ptr[newBytesRead-1]) {
if (*tmpPtr =='\r' && *(tmpPtr+1) =='\n') {
if (tmpPtr -fLastCRLF == 2) { // This is it:
endOfMsg = True;
break;
}
fLastCRLF = tmpPtr;
}
++tmpPtr;
}
fRequestBufferBytesLeft -=newBytesRead;
fRequestBytesAlreadySeen +=newBytesRead;
if (!endOfMsg)break; // subsequent reads will be needed to complete the request
// Parse the request string into command name and 'CSeq', then handle the command:
fRequestBuffer[fRequestBytesAlreadySeen] ='\0';
char cmdName[RTSP_PARAM_STRING_MAX];
char urlPreSuffix[RTSP_PARAM_STRING_MAX];
char urlSuffix[RTSP_PARAM_STRING_MAX];
char cseq[RTSP_PARAM_STRING_MAX];
char sessionIdStr[RTSP_PARAM_STRING_MAX];
unsigned contentLength = 0;
fLastCRLF[2] = '\0'; // temporarily, for parsing
//解析客户端的Rtsp请求
Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer,fLastCRLF+2 - fRequestBuffer,
cmdName,sizeof cmdName,
urlPreSuffix,sizeof urlPreSuffix,
urlSuffix,sizeof urlSuffix,
cseq,sizeof cseq,
sessionIdStr,sizeof sessionIdStr,
contentLength);
fLastCRLF[2] = '\r'; // restore its value
if (parseSucceeded) {
#ifdef DEBUG
fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u, with %d bytes following the message.\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength, ptr + newBytesRead - (tmpPtr + 2));
#endif
// If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:
if (ptr +newBytesRead < tmpPtr + 2 +contentLength) break;// we still need more data; subsequent reads will give it to us
// We now have a complete RTSP request.
// Handle the specified command (beginning by checking those that don't require session ids):
fCurrentCSeq = cseq;
//请求是Opinion吗,处理Opinion消息
if (strcmp(cmdName,"OPTIONS") == 0) {
// If the request included a "Session:" id, and it refers to a client session that's current ongoing, then use this
// command to indicate 'liveness' on that client session:
if (sessionIdStr[0] !='\0') {
clientSession = (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));
if (clientSession !=NULL) clientSession->noteLiveness();
}
handleCmd_OPTIONS();
} else if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0') {
// The special "*" URL means: an operation on the entire server. This works only for GET_PARAMETER and SET_PARAMETER:
//获取参数和设置参数
if (strcmp(cmdName,"GET_PARAMETER") == 0) {
handleCmd_GET_PARAMETER((charconst*)fRequestBuffer);
} else if (strcmp(cmdName,"SET_PARAMETER") == 0) {
handleCmd_SET_PARAMETER((charconst*)fRequestBuffer);
} else {
handleCmd_notSupported();
}
//Describe 命令
} else if (strcmp(cmdName,"DESCRIBE") == 0) {
handleCmd_DESCRIBE(urlPreSuffix,urlSuffix, (charconst*)fRequestBuffer);
//Setup请求
} else if (strcmp(cmdName,"SETUP") == 0) {
if (sessionIdStr[0] =='\0') {
// No session id was present in the request. So create a new "RTSPClientSession" object for this request.
// Choose a random (unused) 32-bit integer for the session id (it will be encoded as a 8-digit hex number).
// (We avoid choosing session id 0, because that has a special use (by "OnDemandServerMediaSubsession").)
u_int32_t sessionId;
do {
sessionId = (u_int32_t)our_random32();
sprintf(sessionIdStr,"%08X", sessionId);
} while (sessionId == 0 ||fOurServer.fClientSessions->Lookup(sessionIdStr) !=NULL);
clientSession = fOurServer.createNewClientSession(sessionId);
fOurServer.fClientSessions->Add(sessionIdStr,clientSession);
} else {
// The request included a session id. Make sure it's one that we have already set up:
clientSession = (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));
if (clientSession ==NULL) {
handleCmd_sessionNotFound();
}
}
if (clientSession !=NULL) clientSession->handleCmd_SETUP(this,urlPreSuffix, urlSuffix, (charconst*)fRequestBuffer);
} else if (strcmp(cmdName,"TEARDOWN") == 0
|| strcmp(cmdName,"PLAY") == 0
|| strcmp(cmdName,"PAUSE") == 0
|| strcmp(cmdName,"GET_PARAMETER") == 0
|| strcmp(cmdName,"SET_PARAMETER") == 0) {
RTSPServer::RTSPClientSession*clientSession
= sessionIdStr[0] == '\0' ? NULL : (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));
if (clientSession ==NULL) {
handleCmd_sessionNotFound();
} else {
clientSession->handleCmd_withinSession(this,cmdName, urlPreSuffix,urlSuffix, (charconst*)fRequestBuffer);
}
} else if (strcmp(cmdName,"REGISTER") == 0 || strcmp(cmdName, "REGISTER_REMOTE") == 0) {
// Because - unlike other commands - an implementation of these commands needs the entire URL, we re-parse the
// command to get it:
char* url = strDupSize((char*)fRequestBuffer);
if (sscanf((char*)fRequestBuffer,"%*s %s", url) == 1) {
handleCmd_REGISTER(url,urlSuffix, strcmp(cmdName,"REGISTER_REMOTE") == 0);
} else {
handleCmd_bad();
}
delete[] url;
} else {
// The command is one that we don't handle:
handleCmd_notSupported();
}
} else {
#ifdef DEBUG
fprintf(stderr, "parseRTSPRequestString() failed; checking now for HTTP commands (for RTSP-over-HTTP tunneling)...\n");
#endif
// The request was not (valid) RTSP, but check for a special case: HTTP commands (for setting up RTSP-over-HTTP tunneling):
char sessionCookie[RTSP_PARAM_STRING_MAX];
char acceptStr[RTSP_PARAM_STRING_MAX];
*fLastCRLF = '\0'; // temporarily, for parsing
parseSucceeded = parseHTTPRequestString(cmdName, sizeof cmdName,
urlSuffix,sizeof urlPreSuffix,
sessionCookie,sizeof sessionCookie,
acceptStr,sizeof acceptStr);
*fLastCRLF = '\r';
if (parseSucceeded) {
#ifdef DEBUG
fprintf(stderr, "parseHTTPRequestString() succeeded, returning cmdName \"%s\", urlSuffix \"%s\", sessionCookie \"%s\", acceptStr \"%s\"\n", cmdName, urlSuffix, sessionCookie, acceptStr);
#endif
// Check that the HTTP command is valid for RTSP-over-HTTP tunneling: There must be a 'session cookie'.
Boolean isValidHTTPCmd = True;
if (sessionCookie[0] =='\0') {
// There was no "x-sessioncookie:" header. If there was an "Accept: application/x-rtsp-tunnelled" header,
// then this is a bad tunneling request. Otherwise, assume that it's an attempt to access the stream via HTTP.
if (strcmp(acceptStr,"application/x-rtsp-tunnelled") == 0) {
isValidHTTPCmd = False;
} else {
handleHTTPCmd_StreamingGET(urlSuffix, (charconst*)fRequestBuffer);
}
} else if (strcmp(cmdName,"GET") == 0) {
handleHTTPCmd_TunnelingGET(sessionCookie);
} else if (strcmp(cmdName,"POST") == 0) {
// We might have received additional data following the HTTP "POST" command - i.e., the first Base64-encoded RTSP command.
// Check for this, and handle it if it exists:
unsigned char const* extraData =fLastCRLF+4;
unsigned extraDataSize = &fRequestBuffer[fRequestBytesAlreadySeen] -extraData;
if (handleHTTPCmd_TunnelingPOST(sessionCookie,extraData, extraDataSize)) {
// We don't respond to the "POST" command, and we go away:
fIsActive = False;
break;
}
} else {
isValidHTTPCmd = False;
}
if (!isValidHTTPCmd) {
handleHTTPCmd_notSupported();
}
} else {
#ifdef DEBUG
fprintf(stderr, "parseHTTPRequestString() failed!\n");
#endif
handleCmd_bad();
}
}
#ifdef DEBUG
fprintf(stderr, "sending response: %s", fResponseBuffer);
#endif
send(fClientOutputSocket, (charconst*)fResponseBuffer,strlen((char*)fResponseBuffer), 0);
if (clientSession !=NULL && clientSession->fStreamAfterSETUP &&strcmp(cmdName,"SETUP") == 0) {
// The client has asked for streaming to commence now, rather than after a
// subsequent "PLAY" command. So, simulate the effect of a "PLAY" command:
//PLAY命令,收到setup后Play
clientSession->handleCmd_withinSession(this,"PLAY", urlPreSuffix,urlSuffix, (charconst*)fRequestBuffer);
}
// Check whether there are extra bytes remaining in the buffer, after the end of the request (a rare case).
// If so, move them to the front of our buffer, and keep processing it, because it might be a following, pipelined request.
unsigned requestSize = (fLastCRLF+4-fRequestBuffer) +contentLength;
numBytesRemaining = fRequestBytesAlreadySeen - requestSize;
resetRequestBuffer(); // to prepare for any subsequent request
if (numBytesRemaining > 0) {
memmove(fRequestBuffer, &fRequestBuffer[requestSize],numBytesRemaining);
newBytesRead = numBytesRemaining;
}
} while (numBytesRemaining > 0);
--fRecursionCount;
if (!fIsActive) {
if (fRecursionCount > 0)closeSockets(); elsedelete this;
// Note: The "fRecursionCount" test is for a pathological situation where we reenter the event loop and get called recursively
// while handling a command (e.g., while handling a "DESCRIBE", to get a SDP description).
// In such a case we don't want to actually delete ourself until we leave the outermost call.
}
}
创建完socket后;建立Rtsp服务器(ServerMediaSession):
{
char const* streamName = "h264ESVideoTest";
char const* inputFileName = "test.264";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env,streamName, streamName,
descriptionString);
sms->addSubsession(H264VideoFileServerMediaSubsession
::createNew(*env,inputFileName, reuseFirstSource));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer,sms, streamName,inputFileName);
}
首先建立sms会话,然后增加子会话:
建立视频文件子会话:
H264VideoFileServerMediaSubsession*
H264VideoFileServerMediaSubsession::createNew(UsageEnvironment&env,
charconst* fileName,
BooleanreuseFirstSource) {
return new H264VideoFileServerMediaSubsession(env,fileName, reuseFirstSource);
}
//
H264VideoFileServerMediaSubsession::H264VideoFileServerMediaSubsession(UsageEnvironment&env,
char const* fileName,Boolean reuseFirstSource)
: FileServerMediaSubsession(env,fileName, reuseFirstSource),
fAuxSDPLine(NULL),fDoneFlag(0), fDummyRTPSink(NULL) {
}
将子会话插入到ServerMediaSession的链表中;
Boolean
ServerMediaSession::addSubsession(ServerMediaSubsession*subsession) {
if (subsession->fParentSession !=NULL) return False; // it's already used
if (fSubsessionsTail ==NULL) {
fSubsessionsHead = subsession;
} else {
fSubsessionsTail->fNext =subsession;
}
fSubsessionsTail = subsession;
subsession->fParentSession =this;
subsession->fTrackNumber = ++fSubsessionCounter;
return True;
}
rtspServer->addServerMediaSession(sms);将sms保存Hash表中;
void RTSPServer::addServerMediaSession(ServerMediaSession*serverMediaSession) {
if (serverMediaSession ==NULL) return;
char const*sessionName = serverMediaSession->streamName();
if (sessionName ==NULL) sessionName ="";
removeServerMediaSession(sessionName);// in case an existing "ServerMediaSession" with this name already exists
fServerMediaSessions->Add(sessionName, (void*)serverMediaSession);
}
最后调用announceStream函数:
static voidannounceStream(RTSPServer*rtspServer, ServerMediaSession*sms,
charconst* streamName,char const* inputFileName) {
char*url = rtspServer->rtspURL(sms);
UsageEnvironment&env = rtspServer->envir();
env <<"\n\"" << streamName <<"\" stream, from the file \""
<< inputFileName << "\"\n";
env <<"Play this stream using the URL \"" << url << "\"\n";
delete[]url;
}