📄 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-2004 Live Networks, Inc. All rights reserved.// A RTSP server// Implementation#include "RTSPServer.hh"#include <GroupsockHelper.hh>#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)#define _strncasecmp _strnicmp#define snprintf _snprintf#else#include <signal.h>#define USE_SIGNALS 1#define _strncasecmp strncasecmp#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; RTSPServer* newServer = NULL; 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); delete newServer; 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) { delete serverMediaSession; } else { serverMediaSession->deleteWhenUnreferenced() = True; }}void RTSPServer::removeServerMediaSession(char const* streamName) { removeServerMediaSession(lookupServerMediaSession(streamName));}char* RTSPServer::rtspURL(ServerMediaSession const* serverMediaSession) const { struct in_addr ourAddress; ourAddress.s_addr = ReceivingInterfaceAddr != 0 ? ReceivingInterfaceAddr : ourSourceAddressForMulticast(envir()); // hack char const* sessionName = serverMediaSession->streamName(); unsigned sessionNameLength = strlen(sessionName); char* urlBuffer = new char[100 + sessionNameLength]; char* resultURL; portNumBits portNumHostOrder = ntohs(fServerPort.num()); if (portNumHostOrder == 554 /* the default port number */) { sprintf(urlBuffer, "rtsp://%s/%s", our_inet_ntoa(ourAddress), sessionName); } else { sprintf(urlBuffer, "rtsp://%s:%hu/%s", our_inet_ntoa(ourAddress), portNumHostOrder, sessionName); } resultURL = strDup(urlBuffer); delete[] urlBuffer; 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;}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();}#define PARAM_STRING_MAX 100void 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; }#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: // (Later, we need to do some garbage collection on sessions that ##### // aren't closed down via TEARDOWN) ##### 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), fSessionIsActive(True), fStreamAfterSETUP(False), fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL) { // Arrange to handle incoming requests: envir().taskScheduler().turnOnBackgroundReadHandling(fClientSocket, (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this); noteClientLiveness();}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::incomingRequestHandler(void* instance, int /*mask*/) { RTSPClientSession* session = (RTSPClientSession*)instance; session->incomingRequestHandler1();}void RTSPServer::RTSPClientSession::incomingRequestHandler1() { noteClientLiveness(); struct sockaddr_in dummy; // 'from' address, meaningless in this case int bytesLeft = sizeof fBuffer; int totalBytes = 0; Boolean endOfMsg = False; unsigned char* ptr = fBuffer; unsigned char* lastCRLF = ptr-3; while (!endOfMsg) { if (bytesLeft <= 0) { // command too big delete this; return; } int bytesRead = readSocket(envir(), fClientSocket, ptr, bytesLeft, dummy); if (bytesRead <= 0) { // The client socket has apparently died - kill it: 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 (totalBytes > 0) --tmpPtr; // In case the last read ended with a <CR> while (tmpPtr < &ptr[bytesRead-1]) { if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') { if (tmpPtr - lastCRLF == 2) { // This is it: endOfMsg = 1; break; } lastCRLF = tmpPtr; } ++tmpPtr; } bytesLeft -= bytesRead; totalBytes += bytesRead; ptr += bytesRead; } fBuffer[totalBytes] = '\0'; // Parse the request string into command name and 'CSeq', // then handle the command: char cmdName[PARAM_STRING_MAX]; char urlPreSuffix[PARAM_STRING_MAX]; char urlSuffix[PARAM_STRING_MAX]; char cseq[PARAM_STRING_MAX]; if (!parseRequestString((char*)fBuffer, totalBytes, cmdName, sizeof cmdName, urlPreSuffix, sizeof urlPreSuffix, urlSuffix, sizeof urlSuffix, cseq, sizeof cseq)) {#ifdef DEBUG fprintf(stderr, "parseRequestString() failed!\n");#endif handleCmd_bad(cseq); } else {#ifdef DEBUG fprintf(stderr, "parseRequestString() 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*)fBuffer); } else if (strcmp(cmdName, "SETUP") == 0) { handleCmd_SETUP(cseq, urlPreSuffix, urlSuffix, (char const*)fBuffer); } 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*)fBuffer); } 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*)fBuffer); } 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); WCHAR dateFormat[] = L"ddd, MMM dd yyyy"; WCHAR timeFormat[] = L"HH:mm:ss GMT\r\n"; WCHAR inBuf[200]; DWORD locale = LOCALE_NEUTRAL; int ret = GetDateFormat(locale, 0, &SystemTime, (LPTSTR)dateFormat, (LPTSTR)inBuf, sizeof inBuf); inBuf[ret - 1] = ' '; ret = GetTimeFormat(locale, 0, &SystemTime, (LPTSTR)timeFormat, (LPTSTR)inBuf + ret, (sizeof inBuf) - ret); wcstombs(buf, inBuf, wcslen(inBuf));#endif return buf;}static char const* allowedCommandNames = "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE";void RTSPServer::RTSPClientSession::handleCmd_bad(char const* /*cseq*/) { // Don't do anything with "cseq", because it might be nonsense snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 400 Bad Request\r\n%sAllow: %s\r\n\r\n", dateHeader(), allowedCommandNames); fSessionIsActive = False; // triggers deletion of ourself after responding}void RTSPServer::RTSPClientSession::handleCmd_notSupported(char const* cseq) { snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n%sAllow: %s\r\n\r\n", cseq, dateHeader(), allowedCommandNames); fSessionIsActive = False; // triggers deletion of ourself after responding}void RTSPServer::RTSPClientSession::handleCmd_notFound(char const* cseq) { snprintf((char*)fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 404 Stream Not Found\r\nCSeq: %s\r\n%s\r\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -