| /********** |
| This library is free software; you can redistribute it and/or modify it under |
| the terms of the GNU Lesser General Public License as published by the |
| Free Software Foundation; either version 2.1 of the License, or (at your |
| option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
| |
| This library is distributed in the hope that it will be useful, but WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
| more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with this library; if not, write to the Free Software Foundation, Inc., |
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| **********/ |
| // "liveMedia" |
| // Copyright (c) 1996-2015 Live Networks, Inc. All rights reserved. |
| // A RTSP server |
| // C++ header |
| |
| #ifndef _RTSP_SERVER_HH |
| #define _RTSP_SERVER_HH |
| |
| #ifndef _GENERIC_MEDIA_SERVER_HH |
| #include "GenericMediaServer.hh" |
| #endif |
| #ifndef _DIGEST_AUTHENTICATION_HH |
| #include "DigestAuthentication.hh" |
| #endif |
| |
| class RTSPServer: public GenericMediaServer { |
| public: |
| static RTSPServer* createNew(UsageEnvironment& env, Port ourPort = 554, |
| UserAuthenticationDatabase* authDatabase = NULL, |
| unsigned reclamationSeconds = 65); |
| // If ourPort.num() == 0, we'll choose the port number |
| // Note: The caller is responsible for reclaiming "authDatabase" |
| // If "reclamationSeconds" > 0, then the "RTSPClientSession" state for |
| // each client will get reclaimed (and the corresponding RTP stream(s) |
| // torn down) if no RTSP commands - or RTCP "RR" packets - from the |
| // client are received in at least "reclamationSeconds" seconds. |
| |
| static Boolean lookupByName(UsageEnvironment& env, char const* name, |
| RTSPServer*& resultServer); |
| |
| typedef void (responseHandlerForREGISTER)(RTSPServer* rtspServer, unsigned requestId, int resultCode, char* resultString); |
| unsigned registerStream(ServerMediaSession* serverMediaSession, |
| char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, |
| responseHandlerForREGISTER* responseHandler, |
| char const* username = NULL, char const* password = NULL, |
| Boolean receiveOurStreamViaTCP = False, |
| char const* proxyURLSuffix = NULL); |
| // 'Register' the stream represented by "serverMediaSession" with the given remote client (specifed by name and port number). |
| // This is done using our custom "REGISTER" RTSP command. |
| // The function returns a unique number that can be used to identify the request; this number is also passed to "responseHandler". |
| // When a response is received from the remote client (or the "REGISTER" request fails), the specified response handler |
| // (if non-NULL) is called. (Note that the "resultString" passed to the handler was dynamically allocated, |
| // and should be delete[]d by the handler after use.) |
| // If "receiveOurStreamViaTCP" is True, then we're requesting that the remote client access our stream using RTP/RTCP-over-TCP. |
| // (Otherwise, the remote client may choose regular RTP/RTCP-over-UDP streaming.) |
| // "proxyURLSuffix" (optional) is used only when the remote client is also a proxy server. |
| // It tells the proxy server the suffix that it should use in its "rtsp://" URL (when front-end clients access the stream) |
| |
| char* rtspURL(ServerMediaSession const* serverMediaSession, int clientSocket = -1) const; |
| // returns a "rtsp://" URL that could be used to access the |
| // specified session (which must already have been added to |
| // us using "addServerMediaSession()". |
| // This string is dynamically allocated; caller should delete[] |
| // (If "clientSocket" is non-negative, then it is used (by calling "getsockname()") to determine |
| // the IP address to be used in the URL.) |
| char* rtspURLPrefix(int clientSocket = -1) const; |
| // like "rtspURL()", except that it returns just the common prefix used by |
| // each session's "rtsp://" URL. |
| // This string is dynamically allocated; caller should delete[] |
| |
| UserAuthenticationDatabase* setAuthenticationDatabase(UserAuthenticationDatabase* newDB); |
| // Changes the server's authentication database to "newDB", returning a pointer to the old database (if there was one). |
| // "newDB" may be NULL (you can use this to disable authentication at runtime, if desired). |
| |
| void disableStreamingRTPOverTCP() { |
| fAllowStreamingRTPOverTCP = False; |
| } |
| |
| Boolean setUpTunnelingOverHTTP(Port httpPort); |
| // (Attempts to) enable RTSP-over-HTTP tunneling on the specified port. |
| // Returns True iff the specified port can be used in this way (i.e., it's not already being used for a separate HTTP server). |
| // Note: RTSP-over-HTTP tunneling is described in http://developer.apple.com/quicktime/icefloe/dispatch028.html |
| portNumBits httpServerPortNum() const; // in host byte order. (Returns 0 if not present.) |
| |
| protected: |
| RTSPServer(UsageEnvironment& env, |
| int ourSocket, Port ourPort, |
| UserAuthenticationDatabase* authDatabase, |
| unsigned reclamationSeconds); |
| // called only by createNew(); |
| virtual ~RTSPServer(); |
| |
| virtual char const* allowedCommandNames(); // used to implement "RTSPClientConnection::handleCmd_OPTIONS()" |
| virtual Boolean weImplementREGISTER(char const* proxyURLSuffix, char*& responseStr); |
| // used to implement "RTSPClientConnection::handleCmd_REGISTER()" |
| // Note: "responseStr" is dynamically allocated (or NULL), and should be delete[]d after the call |
| virtual void implementCmd_REGISTER(char const* url, char const* urlSuffix, int socketToRemoteServer, |
| Boolean deliverViaTCP, char const* proxyURLSuffix); |
| // used to implement "RTSPClientConnection::handleCmd_REGISTER()" |
| |
| virtual UserAuthenticationDatabase* getAuthenticationDatabaseForCommand(char const* cmdName); |
| virtual Boolean specialClientAccessCheck(int clientSocket, struct sockaddr_in& clientAddr, |
| char const* urlSuffix); |
| // a hook that allows subclassed servers to do server-specific access checking |
| // on each client (e.g., based on client IP address), without using digest authentication. |
| virtual Boolean specialClientUserAccessCheck(int clientSocket, struct sockaddr_in& clientAddr, |
| char const* urlSuffix, char const *username); |
| // another hook that allows subclassed servers to do server-specific access checking |
| // - this time after normal digest authentication has already taken place (and would otherwise allow access). |
| // (This test can only be used to further restrict access, not to grant additional access.) |
| |
| private: // redefined virtual functions |
| virtual Boolean isRTSPServer() const; |
| |
| public: // should be protected, but some old compilers complain otherwise |
| // The state of a TCP connection used by a RTSP client: |
| class RTSPClientSession; // forward |
| class RTSPClientConnection: public GenericMediaServer::ClientConnection { |
| public: |
| // A data structure that's used to implement the "REGISTER" command: |
| class ParamsForREGISTER { |
| public: |
| ParamsForREGISTER(RTSPClientConnection* ourConnection, char const* url, char const* urlSuffix, |
| Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix); |
| virtual ~ParamsForREGISTER(); |
| private: |
| friend class RTSPClientConnection; |
| RTSPClientConnection* fOurConnection; |
| char* fURL; |
| char* fURLSuffix; |
| Boolean fReuseConnection, fDeliverViaTCP; |
| char* fProxyURLSuffix; |
| }; |
| protected: // redefined virtual functions: |
| virtual void handleRequestBytes(int newBytesRead); |
| |
| protected: |
| RTSPClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr); |
| virtual ~RTSPClientConnection(); |
| |
| friend class RTSPServer; |
| friend class RTSPClientSession; |
| |
| // Make the handler functions for each command virtual, to allow subclasses to reimplement them, if necessary: |
| virtual void handleCmd_OPTIONS(); |
| // You probably won't need to subclass/reimplement this function; reimplement "RTSPServer::allowedCommandNames()" instead. |
| virtual void handleCmd_GET_PARAMETER(char const* fullRequestStr); // when operating on the entire server |
| virtual void handleCmd_SET_PARAMETER(char const* fullRequestStr); // when operating on the entire server |
| virtual void handleCmd_DESCRIBE(char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr); |
| virtual void handleCmd_REGISTER(char const* url, char const* urlSuffix, char const* fullRequestStr, |
| Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix); |
| // You probably won't need to subclass/reimplement this function; |
| // reimplement "RTSPServer::weImplementREGISTER()" and "RTSPServer::implementCmd_REGISTER()" instead. |
| virtual void handleCmd_bad(); |
| virtual void handleCmd_notSupported(); |
| virtual void handleCmd_notFound(); |
| virtual void handleCmd_sessionNotFound(); |
| virtual void handleCmd_unsupportedTransport(); |
| // Support for optional RTSP-over-HTTP tunneling: |
| virtual Boolean parseHTTPRequestString(char* resultCmdName, unsigned resultCmdNameMaxSize, |
| char* urlSuffix, unsigned urlSuffixMaxSize, |
| char* sessionCookie, unsigned sessionCookieMaxSize, |
| char* acceptStr, unsigned acceptStrMaxSize); |
| virtual void handleHTTPCmd_notSupported(); |
| virtual void handleHTTPCmd_notFound(); |
| virtual void handleHTTPCmd_OPTIONS(); |
| virtual void handleHTTPCmd_TunnelingGET(char const* sessionCookie); |
| virtual Boolean handleHTTPCmd_TunnelingPOST(char const* sessionCookie, unsigned char const* extraData, unsigned extraDataSize); |
| virtual void handleHTTPCmd_StreamingGET(char const* urlSuffix, char const* fullRequestStr); |
| protected: |
| void resetRequestBuffer(); |
| void closeSocketsRTSP(); |
| static void handleAlternativeRequestByte(void*, u_int8_t requestByte); |
| void handleAlternativeRequestByte1(u_int8_t requestByte); |
| Boolean authenticationOK(char const* cmdName, char const* urlSuffix, char const* fullRequestStr); |
| void changeClientInputSocket(int newSocketNum, unsigned char const* extraData, unsigned extraDataSize); |
| // used to implement RTSP-over-HTTP tunneling |
| static void continueHandlingREGISTER(ParamsForREGISTER* params); |
| virtual void continueHandlingREGISTER1(ParamsForREGISTER* params); |
| |
| // Shortcuts for setting up a RTSP response (prior to sending it): |
| void setRTSPResponse(char const* responseStr); |
| void setRTSPResponse(char const* responseStr, u_int32_t sessionId); |
| void setRTSPResponse(char const* responseStr, char const* contentStr); |
| void setRTSPResponse(char const* responseStr, u_int32_t sessionId, char const* contentStr); |
| |
| RTSPServer& fOurRTSPServer; // same as ::fOurServer |
| int& fClientInputSocket; // aliased to ::fOurSocket |
| int fClientOutputSocket; |
| Boolean fIsActive; |
| unsigned char* fLastCRLF; |
| unsigned fRecursionCount; |
| char const* fCurrentCSeq; |
| Authenticator fCurrentAuthenticator; // used if access control is needed |
| char* fOurSessionCookie; // used for optional RTSP-over-HTTP tunneling |
| unsigned fBase64RemainderCount; // used for optional RTSP-over-HTTP tunneling (possible values: 0,1,2,3) |
| }; |
| |
| // The state of an individual client session (using one or more sequential TCP connections) handled by a RTSP server: |
| class RTSPClientSession: public GenericMediaServer::ClientSession { |
| protected: |
| RTSPClientSession(RTSPServer& ourServer, u_int32_t sessionId); |
| virtual ~RTSPClientSession(); |
| |
| friend class RTSPServer; |
| friend class RTSPClientConnection; |
| // Make the handler functions for each command virtual, to allow subclasses to redefine them: |
| virtual void handleCmd_SETUP(RTSPClientConnection* ourClientConnection, |
| char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr); |
| virtual void handleCmd_withinSession(RTSPClientConnection* ourClientConnection, |
| char const* cmdName, |
| char const* urlPreSuffix, char const* urlSuffix, |
| char const* fullRequestStr); |
| virtual void handleCmd_TEARDOWN(RTSPClientConnection* ourClientConnection, |
| ServerMediaSubsession* subsession); |
| virtual void handleCmd_PLAY(RTSPClientConnection* ourClientConnection, |
| ServerMediaSubsession* subsession, char const* fullRequestStr); |
| virtual void handleCmd_PAUSE(RTSPClientConnection* ourClientConnection, |
| ServerMediaSubsession* subsession); |
| virtual void handleCmd_GET_PARAMETER(RTSPClientConnection* ourClientConnection, |
| ServerMediaSubsession* subsession, char const* fullRequestStr); |
| virtual void handleCmd_SET_PARAMETER(RTSPClientConnection* ourClientConnection, |
| ServerMediaSubsession* subsession, char const* fullRequestStr); |
| protected: |
| void deleteStreamByTrack(unsigned trackNum); |
| void reclaimStreamStates(); |
| Boolean isMulticast() const { return fIsMulticast; } |
| |
| // Shortcuts for setting up a RTSP response (prior to sending it): |
| void setRTSPResponse(RTSPClientConnection* ourClientConnection, char const* responseStr) { ourClientConnection->setRTSPResponse(responseStr); } |
| void setRTSPResponse(RTSPClientConnection* ourClientConnection, char const* responseStr, u_int32_t sessionId) { ourClientConnection->setRTSPResponse(responseStr, sessionId); } |
| void setRTSPResponse(RTSPClientConnection* ourClientConnection, char const* responseStr, char const* contentStr) { ourClientConnection->setRTSPResponse(responseStr, contentStr); } |
| void setRTSPResponse(RTSPClientConnection* ourClientConnection, char const* responseStr, u_int32_t sessionId, char const* contentStr) { ourClientConnection->setRTSPResponse(responseStr, sessionId, contentStr); } |
| |
| protected: |
| RTSPServer& fOurRTSPServer; // same as ::fOurServer |
| Boolean fIsMulticast, fStreamAfterSETUP; |
| unsigned char fTCPStreamIdCount; // used for (optional) RTP/TCP |
| Boolean usesTCPTransport() const { return fTCPStreamIdCount > 0; } |
| unsigned fNumStreamStates; |
| struct streamState { |
| ServerMediaSubsession* subsession; |
| int tcpSocketNum; |
| void* streamToken; |
| } * fStreamStates; |
| }; |
| |
| protected: // redefined virtual functions |
| // If you subclass "RTSPClientConnection", then you must also redefine this virtual function in order |
| // to create new objects of your subclass: |
| virtual ClientConnection* createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr); |
| |
| protected: |
| // If you subclass "RTSPClientSession", then you must also redefine this virtual function in order |
| // to create new objects of your subclass: |
| virtual ClientSession* createNewClientSession(u_int32_t sessionId); |
| |
| private: |
| static void incomingConnectionHandlerHTTP(void*, int /*mask*/); |
| void incomingConnectionHandlerHTTP(); |
| |
| void noteTCPStreamingOnSocket(int socketNum, RTSPClientSession* clientSession, unsigned trackNum); |
| void unnoteTCPStreamingOnSocket(int socketNum, RTSPClientSession* clientSession, unsigned trackNum); |
| void stopTCPStreamingOnSocket(int socketNum); |
| |
| private: |
| friend class RTSPClientConnection; |
| friend class RTSPClientSession; |
| friend class RegisterRequestRecord; |
| int fHTTPServerSocket; // for optional RTSP-over-HTTP tunneling |
| Port fHTTPServerPort; // ditto |
| HashTable* fClientConnectionsForHTTPTunneling; // maps client-supplied 'session cookie' strings to "RTSPClientConnection"s |
| // (used only for optional RTSP-over-HTTP tunneling) |
| HashTable* fTCPStreamingDatabase; |
| // maps TCP socket numbers to ids of sessions that are streaming over it (RTP/RTCP-over-TCP) |
| HashTable* fPendingRegisterRequests; |
| unsigned fRegisterRequestCounter; |
| UserAuthenticationDatabase* fAuthDB; |
| Boolean fAllowStreamingRTPOverTCP; // by default, True |
| }; |
| |
| |
| ////////// A subclass of "RTSPServer" that implements the "REGISTER" command to set up proxying on the specified URL ////////// |
| |
| class RTSPServerWithREGISTERProxying: public RTSPServer { |
| public: |
| static RTSPServerWithREGISTERProxying* createNew(UsageEnvironment& env, Port ourPort = 554, |
| UserAuthenticationDatabase* authDatabase = NULL, |
| UserAuthenticationDatabase* authDatabaseForREGISTER = NULL, |
| unsigned reclamationSeconds = 65, |
| Boolean streamRTPOverTCP = False, |
| int verbosityLevelForProxying = 0); |
| |
| protected: |
| RTSPServerWithREGISTERProxying(UsageEnvironment& env, int ourSocket, Port ourPort, |
| UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER, |
| unsigned reclamationSeconds, |
| Boolean streamRTPOverTCP, int verbosityLevelForProxying); |
| // called only by createNew(); |
| virtual ~RTSPServerWithREGISTERProxying(); |
| |
| protected: // redefined virtual functions |
| virtual char const* allowedCommandNames(); |
| virtual Boolean weImplementREGISTER(char const* proxyURLSuffix, char*& responseStr); |
| virtual void implementCmd_REGISTER(char const* url, char const* urlSuffix, int socketToRemoteServer, |
| Boolean deliverViaTCP, char const* proxyURLSuffix); |
| virtual UserAuthenticationDatabase* getAuthenticationDatabaseForCommand(char const* cmdName); |
| |
| private: |
| Boolean fStreamRTPOverTCP; |
| int fVerbosityLevelForProxying; |
| unsigned fRegisteredProxyCounter; |
| char* fAllowedCommandNames; |
| UserAuthenticationDatabase* fAuthDBForREGISTER; |
| }; |
| |
| #endif |