📄 rtspserver.cpp
字号:
/**********This library is free software; you can redistribute it and/or modify it underthe terms of the GNU Lesser General Public License as published by theFree Software Foundation; either version 2.1 of the License, or (at youroption) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)This library is distributed in the hope that it will be useful, but WITHOUTANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESSFOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License formore details.You should have received a copy of the GNU Lesser General Public Licensealong with this library; if not, write to the Free Software Foundation, Inc.,59 Temple Place, Suite 330, Boston, MA 02111-1307 USA**********/// "liveMedia"// Copyright (c) 1996-2007 Live Networks, Inc. All rights reserved.// A RTSP server// Implementation#include "RTSPServer.hh"#include "RTSPCommon.hh"#include <GroupsockHelper.hh>#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)#else#include <signal.h>#define USE_SIGNALS 1#endif#include <time.h> // for "strftime()" and "gmtime()"#define RTPINFO_INCLUDE_RTPTIME 1////////// RTSPServer //////////RTSPServer*RTSPServer::createNew(UsageEnvironment& env, Port ourPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) { int ourSocket = -1; do { int ourSocket = setUpOurSocket(env, ourPort); if (ourSocket == -1) break; return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds); } while (0); if (ourSocket != -1) ::closeSocket(ourSocket); return NULL;}Boolean RTSPServer::lookupByName(UsageEnvironment& env, char const* name, RTSPServer*& resultServer) { resultServer = NULL; // unless we succeed Medium* medium; if (!Medium::lookupByName(env, name, medium)) return False; if (!medium->isRTSPServer()) { env.setResultMsg(name, " is not a RTSP server"); return False; } resultServer = (RTSPServer*)medium; return True;}void RTSPServer::addServerMediaSession(ServerMediaSession* serverMediaSession) { if (serverMediaSession == NULL) return; char const* sessionName = serverMediaSession->streamName(); if (sessionName == NULL) sessionName = ""; ServerMediaSession* existingSession = (ServerMediaSession*) (fServerMediaSessions->Add(sessionName, (void*)serverMediaSession)); removeServerMediaSession(existingSession); // if any}ServerMediaSession* RTSPServer::lookupServerMediaSession(char const* streamName) { return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName));}void RTSPServer::removeServerMediaSession(ServerMediaSession* serverMediaSession) { if (serverMediaSession == NULL) return; fServerMediaSessions->Remove(serverMediaSession->streamName()); if (serverMediaSession->referenceCount() == 0) { Medium::close(serverMediaSession); } else { serverMediaSession->deleteWhenUnreferenced() = True; }}void RTSPServer::removeServerMediaSession(char const* streamName) { removeServerMediaSession(lookupServerMediaSession(streamName));}char* RTSPServer::rtspURLPrefix(int clientSocket) const { struct sockaddr_in ourAddress; if (clientSocket < 0) { // Use our default IP address in the URL: ourAddress.sin_addr.s_addr = ReceivingInterfaceAddr != 0 ? ReceivingInterfaceAddr : ourIPAddress(envir()); // hack } else { SOCKLEN_T namelen = sizeof ourAddress; getsockname(clientSocket, (struct sockaddr*)&ourAddress, &namelen); } char urlBuffer[100]; // more than big enough for "rtsp://<ip-address>:<port>/" portNumBits portNumHostOrder = ntohs(fServerPort.num()); if (portNumHostOrder == 554 /* the default port number */) { sprintf(urlBuffer, "rtsp://%s/", our_inet_ntoa(ourAddress.sin_addr)); } else { sprintf(urlBuffer, "rtsp://%s:%hu/", our_inet_ntoa(ourAddress.sin_addr), portNumHostOrder); } return strDup(urlBuffer);}char* RTSPServer::rtspURL(ServerMediaSession const* serverMediaSession, int clientSocket) const { char* urlPrefix = rtspURLPrefix(clientSocket); char const* sessionName = serverMediaSession->streamName(); char* resultURL = new char[strlen(urlPrefix) + strlen(sessionName) + 1]; sprintf(resultURL, "%s%s", urlPrefix, sessionName); delete[] urlPrefix; return resultURL;}#define LISTEN_BACKLOG_SIZE 20int RTSPServer::setUpOurSocket(UsageEnvironment& env, Port& ourPort) { int ourSocket = -1; do { 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;}Boolean RTSPServer::specialClientAccessCheck(int /*clientSocket*/, struct sockaddr_in& /*clientAddr*/, char const* /*urlSuffix*/) { // default implementation return True;}RTSPServer::RTSPServer(UsageEnvironment& env, int ourSocket, Port ourPort, UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) : Medium(env), fServerSocket(ourSocket), fServerPort(ourPort), fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds), fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)), fSessionIdCounter(0) {#ifdef USE_SIGNALS // Ignore the SIGPIPE signal, so that clients on the same host that are killed // don't also kill us: signal(SIGPIPE, SIG_IGN);#endif // Arrange to handle connections from others: env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandler, this);}RTSPServer::~RTSPServer() { // Turn off background read handling: envir().taskScheduler().turnOffBackgroundReadHandling(fServerSocket); ::closeSocket(fServerSocket); // Remove all server media sessions (they'll get deleted when they're finished): while (1) { ServerMediaSession* serverMediaSession = (ServerMediaSession*)fServerMediaSessions->RemoveNext(); if (serverMediaSession == NULL) break; removeServerMediaSession(serverMediaSession); } // Finally, delete the session table itself: delete fServerMediaSessions;}Boolean RTSPServer::isRTSPServer() const { return True;}void RTSPServer::incomingConnectionHandler(void* instance, int /*mask*/) { RTSPServer* server = (RTSPServer*)instance; server->incomingConnectionHandler1();}void RTSPServer::incomingConnectionHandler1() { struct sockaddr_in clientAddr; SOCKLEN_T clientAddrLen = sizeof clientAddr; int clientSocket = accept(fServerSocket, (struct sockaddr*)&clientAddr, &clientAddrLen); if (clientSocket < 0) { int err = envir().getErrno(); if (err != EWOULDBLOCK) { envir().setResultErrMsg("accept() failed: "); } return; } makeSocketNonBlocking(clientSocket); increaseSendBufferTo(envir(), clientSocket, 50*1024);#if defined(DEBUG) || defined(DEBUG_CONNECTIONS) fprintf(stderr, "accept()ed connection from %s\n", our_inet_ntoa(clientAddr.sin_addr));#endif // Create a new object for this RTSP session: new RTSPClientSession(*this, ++fSessionIdCounter, clientSocket, clientAddr);}////////// RTSPServer::RTSPClientSession //////////RTSPServer::RTSPClientSession::RTSPClientSession(RTSPServer& ourServer, unsigned sessionId, int clientSocket, struct sockaddr_in clientAddr) : fOurServer(ourServer), fOurSessionId(sessionId), fOurServerMediaSession(NULL), fClientSocket(clientSocket), fClientAddr(clientAddr), fLivenessCheckTask(NULL), fIsMulticast(False), fSessionIsActive(True), fStreamAfterSETUP(False), fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL) { // Arrange to handle incoming requests: resetRequestBuffer(); envir().taskScheduler().turnOnBackgroundReadHandling(fClientSocket, (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this); noteLiveness();}RTSPServer::RTSPClientSession::~RTSPClientSession() { // Turn off any liveness checking: envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask); // Turn off background read handling: envir().taskScheduler().turnOffBackgroundReadHandling(fClientSocket); ::closeSocket(fClientSocket); reclaimStreamStates(); if (fOurServerMediaSession != NULL) { fOurServerMediaSession->decrementReferenceCount(); if (fOurServerMediaSession->referenceCount() == 0 && fOurServerMediaSession->deleteWhenUnreferenced()) { fOurServer.removeServerMediaSession(fOurServerMediaSession); } }}void RTSPServer::RTSPClientSession::reclaimStreamStates() { for (unsigned i = 0; i < fNumStreamStates; ++i) { if (fStreamStates[i].subsession != NULL) { fStreamStates[i].subsession->deleteStream(fOurSessionId, fStreamStates[i].streamToken); } } delete[] fStreamStates; fStreamStates = NULL; fNumStreamStates = 0;}void RTSPServer::RTSPClientSession::resetRequestBuffer() { fRequestBytesAlreadySeen = 0; fRequestBufferBytesLeft = sizeof fRequestBuffer; fLastCRLF = &fRequestBuffer[-3]; // hack}void RTSPServer::RTSPClientSession::incomingRequestHandler(void* instance, int /*mask*/) { RTSPClientSession* session = (RTSPClientSession*)instance; session->incomingRequestHandler1();}void RTSPServer::RTSPClientSession::incomingRequestHandler1() { noteLiveness(); struct sockaddr_in dummy; // 'from' address, meaningless in this case Boolean endOfMsg = False; unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen]; int bytesRead = readSocket(envir(), fClientSocket, ptr, fRequestBufferBytesLeft, dummy); if (bytesRead <= 0 || (unsigned)bytesRead >= fRequestBufferBytesLeft) { // Either the client socket has died, or the request was too big for us. // Terminate this connection:#ifdef DEBUG fprintf(stderr, "RTSPClientSession[%p]::incomingRequestHandler1() read %d bytes (of %d); terminating connection!\n", this, bytesRead, fRequestBufferBytesLeft);#endif delete this; return; }#ifdef DEBUG ptr[bytesRead] = '\0'; fprintf(stderr, "RTSPClientSession[%p]::incomingRequestHandler1() read %d bytes:%s\n", this, bytesRead, ptr);#endif // Look for the end of the message: <CR><LF><CR><LF> unsigned char *tmpPtr = ptr; if (fRequestBytesAlreadySeen > 0) --tmpPtr; // in case the last read ended with a <CR> while (tmpPtr < &ptr[bytesRead-1]) { if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') { if (tmpPtr - fLastCRLF == 2) { // This is it: endOfMsg = 1; break; } fLastCRLF = tmpPtr; } ++tmpPtr; } fRequestBufferBytesLeft -= bytesRead; fRequestBytesAlreadySeen += bytesRead; if (!endOfMsg) return; // 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]; if (!parseRTSPRequestString((char*)fRequestBuffer, fRequestBytesAlreadySeen, cmdName, sizeof cmdName, urlPreSuffix, sizeof urlPreSuffix, urlSuffix, sizeof urlSuffix, cseq, sizeof cseq)) {#ifdef DEBUG fprintf(stderr, "parseRTSPRequestString() failed!\n");#endif handleCmd_bad(cseq); } else {#ifdef DEBUG fprintf(stderr, "parseRTSPRequestString() returned cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\"\n", cmdName, urlPreSuffix, urlSuffix);#endif if (strcmp(cmdName, "OPTIONS") == 0) { handleCmd_OPTIONS(cseq); } else if (strcmp(cmdName, "DESCRIBE") == 0) { handleCmd_DESCRIBE(cseq, urlSuffix, (char const*)fRequestBuffer); } else if (strcmp(cmdName, "SETUP") == 0) { handleCmd_SETUP(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer); } else if (strcmp(cmdName, "TEARDOWN") == 0 || strcmp(cmdName, "PLAY") == 0 || strcmp(cmdName, "PAUSE") == 0 || strcmp(cmdName, "GET_PARAMETER") == 0) { handleCmd_withinSession(cmdName, urlPreSuffix, urlSuffix, cseq, (char const*)fRequestBuffer); } else { handleCmd_notSupported(cseq); } } #ifdef DEBUG fprintf(stderr, "sending response: %s", fResponseBuffer);#endif send(fClientSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0); if (strcmp(cmdName, "SETUP") == 0 && fStreamAfterSETUP) { // The client has asked for streaming to commence now, rather than after a // subsequent "PLAY" command. So, simulate the effect of a "PLAY" command: handleCmd_withinSession("PLAY", urlPreSuffix, urlSuffix, cseq, (char const*)fRequestBuffer); } resetRequestBuffer(); // to prepare for any subsequent request if (!fSessionIsActive) delete this;}// Handler routines for specific RTSP commands:// Generate a "Date:" header for use in a RTSP response:static char const* dateHeader() { static char buf[200];#if !defined(_WIN32_WCE) time_t tt = time(NULL); strftime(buf, sizeof buf, "Date: %a, %b %d %Y %H:%M:%S GMT\r\n", gmtime(&tt));#else // WinCE apparently doesn't have "time()", "strftime()", or "gmtime()", // so generate the "Date:" header a different, WinCE-specific way. // (Thanks to Pierre l'Hussiez for this code) SYSTEMTIME SystemTime; GetSystemTime(&SystemTime);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -