1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15**********/
16// "liveMedia"
17// Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved.
18// A generic RTSP client - for a single "rtsp://" URL
19// C++ header
20
21#ifndef _RTSP_CLIENT_HH
22#define _RTSP_CLIENT_HH
23
24#ifndef _MEDIA_SESSION_HH
25#include "MediaSession.hh"
26#endif
27#ifndef _NET_ADDRESS_HH
28#include "NetAddress.hh"
29#endif
30#ifndef _DIGEST_AUTHENTICATION_HH
31#include "DigestAuthentication.hh"
32#endif
33#ifndef _TLS_STATE_HH
34#include "TLSState.hh"
35#endif
36#ifndef OMIT_REGISTER_HANDLING
37#ifndef _RTSP_SERVER_HH
38#include "RTSPServer.hh" // For the optional "HandlerForREGISTERCommand" mini-server
39#endif
40#endif
41
42class RTSPClient: public Medium {
43public:
44 static RTSPClient* createNew(UsageEnvironment& env, char const* rtspURL,
45 int verbosityLevel = 0,
46 char const* applicationName = NULL,
47 portNumBits tunnelOverHTTPPortNum = 0,
48 int socketNumToServer = -1);
49 // If "tunnelOverHTTPPortNum" is non-zero, we tunnel RTSP (and RTP)
50 // over a HTTP connection with the given port number, using the technique
51 // described in Apple's document <http://developer.apple.com/documentation/QuickTime/QTSS/Concepts/chapter_2_section_14.html>
52 // If "socketNumToServer" is >= 0, then it is the socket number of an already-existing TCP connection to the server.
53 // (In this case, "rtspURL" must point to the socket's endpoint, so that it can be accessed via the socket.)
54
55 typedef void (responseHandler)(RTSPClient* rtspClient,
56 int resultCode, char* resultString);
57 // A function that is called in response to a RTSP command. The parameters are as follows:
58 // "rtspClient": The "RTSPClient" object on which the original command was issued.
59 // "resultCode": If zero, then the command completed successfully. If non-zero, then the command did not complete
60 // successfully, and "resultCode" indicates the error, as follows:
61 // A positive "resultCode" is a RTSP error code (for example, 404 means "not found")
62 // A negative "resultCode" indicates a socket/network error; 0-"resultCode" is the standard "errno" code.
63 // "resultString": A ('\0'-terminated) string returned along with the response, or else NULL.
64 // In particular:
65 // "resultString" for a successful "DESCRIBE" command will be the media session's SDP description.
66 // "resultString" for a successful "OPTIONS" command will be a list of allowed commands.
67 // Note that this string can be present (i.e., not NULL) even if "resultCode" is non-zero - i.e., an error message.
68 // Also, "resultString" can be NULL, even if "resultCode" is zero (e.g., if the RTSP command succeeded, but without
69 // including an appropriate result header).
70 // Note also that this string is dynamically allocated, and must be freed by the handler (or the caller)
71 // - using "delete[]".
72
73 unsigned sendDescribeCommand(responseHandler* responseHandler, Authenticator* authenticator = NULL);
74 // Issues a RTSP "DESCRIBE" command, then returns the "CSeq" sequence number that was used in the command.
75 // The (programmer-supplied) "responseHandler" function is called later to handle the response
76 // (or is called immediately - with an error code - if the command cannot be sent).
77 // "authenticator" (optional) is used for access control. If you have username and password strings, you can use this by
78 // passing an actual parameter that you created by creating an "Authenticator(username, password) object".
79 // (Note that if you supply a non-NULL "authenticator" parameter, you need do this only for the first command you send.)
80
81 unsigned sendOptionsCommand(responseHandler* responseHandler, Authenticator* authenticator = NULL);
82 // Issues a RTSP "OPTIONS" command, then returns the "CSeq" sequence number that was used in the command.
83 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
84
85 unsigned sendAnnounceCommand(char const* sdpDescription, responseHandler* responseHandler, Authenticator* authenticator = NULL);
86 // Issues a RTSP "ANNOUNCE" command (with "sdpDescription" as parameter),
87 // then returns the "CSeq" sequence number that was used in the command.
88 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
89
90 unsigned sendSetupCommand(MediaSubsession& subsession, responseHandler* responseHandler,
91 Boolean streamOutgoing = False,
92 Boolean streamUsingTCP = False,
93 Boolean forceMulticastOnUnspecified = False,
94 Authenticator* authenticator = NULL);
95 // Issues a RTSP "SETUP" command, then returns the "CSeq" sequence number that was used in the command.
96 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
97
98 unsigned sendPlayCommand(MediaSession& session, responseHandler* responseHandler,
99 double start = 0.0f, double end = -1.0f, float scale = 1.0f,
100 Authenticator* authenticator = NULL);
101 // Issues an aggregate RTSP "PLAY" command on "session", then returns the "CSeq" sequence number that was used in the command.
102 // (Note: start=-1 means 'resume'; end=-1 means 'play to end')
103 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
104 unsigned sendPlayCommand(MediaSubsession& subsession, responseHandler* responseHandler,
105 double start = 0.0f, double end = -1.0f, float scale = 1.0f,
106 Authenticator* authenticator = NULL);
107 // Issues a RTSP "PLAY" command on "subsession", then returns the "CSeq" sequence number that was used in the command.
108 // (Note: start=-1 means 'resume'; end=-1 means 'play to end')
109 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
110
111 // Alternative forms of "sendPlayCommand()", used to send "PLAY" commands that include an 'absolute' time range:
112 // (The "absStartTime" string (and "absEndTime" string, if present) *must* be of the form
113 // "YYYYMMDDTHHMMSSZ" or "YYYYMMDDTHHMMSS.<frac>Z")
114 unsigned sendPlayCommand(MediaSession& session, responseHandler* responseHandler,
115 char const* absStartTime, char const* absEndTime = NULL, float scale = 1.0f,
116 Authenticator* authenticator = NULL);
117 unsigned sendPlayCommand(MediaSubsession& subsession, responseHandler* responseHandler,
118 char const* absStartTime, char const* absEndTime = NULL, float scale = 1.0f,
119 Authenticator* authenticator = NULL);
120
121 unsigned sendPauseCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator = NULL);
122 // Issues an aggregate RTSP "PAUSE" command on "session", then returns the "CSeq" sequence number that was used in the command.
123 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
124 unsigned sendPauseCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator = NULL);
125 // Issues a RTSP "PAUSE" command on "subsession", then returns the "CSeq" sequence number that was used in the command.
126 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
127
128 unsigned sendRecordCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator = NULL);
129 // Issues an aggregate RTSP "RECORD" command on "session", then returns the "CSeq" sequence number that was used in the command.
130 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
131 unsigned sendRecordCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator = NULL);
132 // Issues a RTSP "RECORD" command on "subsession", then returns the "CSeq" sequence number that was used in the command.
133 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
134
135 unsigned sendTeardownCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator = NULL);
136 // Issues an aggregate RTSP "TEARDOWN" command on "session", then returns the "CSeq" sequence number that was used in the command.
137 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
138 unsigned sendTeardownCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator = NULL);
139 // Issues a RTSP "TEARDOWN" command on "subsession", then returns the "CSeq" sequence number that was used in the command.
140 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
141
142 unsigned sendSetParameterCommand(MediaSession& session, responseHandler* responseHandler,
143 char const* parameterName, char const* parameterValue,
144 Authenticator* authenticator = NULL);
145 // Issues an aggregate RTSP "SET_PARAMETER" command on "session", then returns the "CSeq" sequence number that was used in the command.
146 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
147
148 unsigned sendGetParameterCommand(MediaSession& session, responseHandler* responseHandler, char const* parameterName,
149 Authenticator* authenticator = NULL);
150 // Issues an aggregate RTSP "GET_PARAMETER" command on "session", then returns the "CSeq" sequence number that was used in the command.
151 // (The "responseHandler" and "authenticator" parameters are as described for "sendDescribeCommand".)
152
153 void sendDummyUDPPackets(MediaSession& session, unsigned numDummyPackets = 2);
154 void sendDummyUDPPackets(MediaSubsession& subsession, unsigned numDummyPackets = 2);
155 // Sends short 'dummy' (i.e., non-RTP or RTCP) UDP packets towards the server, to increase
156 // the likelihood of RTP/RTCP packets from the server reaching us if we're behind a NAT.
157 // (If we requested RTP-over-TCP streaming, then these functions have no effect.)
158 // Our implementation automatically does this just prior to sending each "PLAY" command;
159 // You should not call these functions yourself unless you know what you're doing.
160
161 void setSpeed(MediaSession& session, float speed = 1.0f);
162 // Set (recorded) media download speed to given value to support faster download using 'Speed:'
163 // option on 'PLAY' command.
164
165 Boolean changeResponseHandler(unsigned cseq, responseHandler* newResponseHandler);
166 // Changes the response handler for the previously-performed command (whose operation returned "cseq").
167 // (To turn off any response handling for the command, use a "newResponseHandler" value of NULL. This might be done as part
168 // of an implementation of a 'timeout handler' on the command, for example.)
169 // This function returns True iff "cseq" was for a valid previously-performed command (whose response is still unhandled).
170
171 int socketNum() const { return fInputSocketNum; }
172
173 static Boolean lookupByName(UsageEnvironment& env,
174 char const* sourceName,
175 RTSPClient*& resultClient);
176
177 Boolean parseRTSPURL(char const* url,
178 char*& username, char*& password, NetAddress& address, portNumBits& portNum, char const** urlSuffix = NULL);
179 // Parses "url" as "rtsp://[<username>[:<password>]@]<server-address-or-name>[:<port>][/<stream-name>]"
180 // (Note that the returned "username" and "password" are either NULL, or heap-allocated strings that the caller must later delete[].)
181
182 void setUserAgentString(char const* userAgentName);
183 // sets an alternative string to be used in RTSP "User-Agent:" headers
184
185 void disallowBasicAuthentication() { fAllowBasicAuthentication = False; }
186 // call this if you don't want the server to request 'Basic' authentication
187 // (which would cause the client to send usernames and passwords over the net).
188
189 unsigned sessionTimeoutParameter() const { return fSessionTimeoutParameter; }
190
191 char const* url() const { return fBaseURL; }
192
193 void useTLS() { fTLS.isNeeded = True; }
194
195 static unsigned responseBufferSize;
196
197public: // Some compilers complain if this is "private:"
198 // The state of a request-in-progress:
199 class RequestRecord {
200 public:
201 RequestRecord(unsigned cseq, char const* commandName, responseHandler* handler,
202 MediaSession* session = NULL, MediaSubsession* subsession = NULL, u_int32_t booleanFlags = 0,
203 double start = 0.0f, double end = -1.0f, float scale = 1.0f, char const* contentStr = NULL);
204 RequestRecord(unsigned cseq, responseHandler* handler,
205 char const* absStartTime, char const* absEndTime = NULL, float scale = 1.0f,
206 MediaSession* session = NULL, MediaSubsession* subsession = NULL);
207 // alternative constructor for creating "PLAY" requests that include 'absolute' time values
208 virtual ~RequestRecord();
209
210 RequestRecord*& next() { return fNext; }
211 unsigned& cseq() { return fCSeq; }
212 char const* commandName() const { return fCommandName; }
213 MediaSession* session() const { return fSession; }
214 MediaSubsession* subsession() const { return fSubsession; }
215 u_int32_t booleanFlags() const { return fBooleanFlags; }
216 double start() const { return fStart; }
217 double end() const { return fEnd; }
218 char const* absStartTime() const { return fAbsStartTime; }
219 char const* absEndTime() const { return fAbsEndTime; }
220 float scale() const { return fScale; }
221 char* contentStr() const { return fContentStr; }
222 responseHandler*& handler() { return fHandler; }
223
224 private:
225 RequestRecord* fNext;
226 unsigned fCSeq;
227 char const* fCommandName;
228 MediaSession* fSession;
229 MediaSubsession* fSubsession;
230 u_int32_t fBooleanFlags;
231 double fStart, fEnd;
232 char *fAbsStartTime, *fAbsEndTime; // used for optional 'absolute' (i.e., "time=") range specifications
233 float fScale;
234 char* fContentStr;
235 responseHandler* fHandler;
236 };
237
238protected:
239 RTSPClient(UsageEnvironment& env, char const* rtspURL,
240 int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum, int socketNumToServer);
241 // called only by createNew();
242 virtual ~RTSPClient();
243
244 void reset();
245 void setBaseURL(char const* url);
246 int grabSocket(); // allows a subclass to reuse our input socket, so that it won't get closed when we're deleted
247 virtual unsigned sendRequest(RequestRecord* request);
248 virtual Boolean setRequestFields(RequestRecord* request,
249 char*& cmdURL, Boolean& cmdURLWasAllocated,
250 char const*& protocolStr,
251 char*& extraHeaders, Boolean& extraHeadersWereAllocated);
252 // used to implement "sendRequest()"; subclasses may reimplement this (e.g., when implementing a new command name)
253 virtual int connectToServer(int socketNum, portNumBits remotePortNum); // used to implement "openConnection()"; result values: -1: failure; 0: pending; 1: success
254
255private: // redefined virtual functions
256 virtual Boolean isRTSPClient() const;
257
258private:
259 class RequestQueue {
260 public:
261 RequestQueue();
262 RequestQueue(RequestQueue& origQueue); // moves the queue contents to the new queue
263 virtual ~RequestQueue();
264
265 void enqueue(RequestRecord* request); // "request" must not be NULL
266 RequestRecord* dequeue();
267 void putAtHead(RequestRecord* request); // "request" must not be NULL
268 RequestRecord* findByCSeq(unsigned cseq);
269 Boolean isEmpty() const { return fHead == NULL; }
270 void reset();
271
272 private:
273 RequestRecord* fHead;
274 RequestRecord* fTail;
275 };
276
277 void resetTCPSockets();
278 void resetResponseBuffer();
279 int openConnection(); // result values: -1: failure; 0: pending; 1: success
280 char* createAuthenticatorString(char const* cmd, char const* url);
281 char* createBlocksizeString(Boolean streamUsingTCP);
282 void handleRequestError(RequestRecord* request);
283 Boolean parseResponseCode(char const* line, unsigned& responseCode, char const*& responseString);
284 void handleIncomingRequest();
285 static Boolean checkForHeader(char const* line, char const* headerName, unsigned headerNameLength, char const*& headerParams);
286 Boolean parseTransportParams(char const* paramsStr,
287 char*& serverAddressStr, portNumBits& serverPortNum,
288 unsigned char& rtpChannelId, unsigned char& rtcpChannelId);
289 Boolean parseScaleParam(char const* paramStr, float& scale);
290 Boolean parseSpeedParam(char const* paramStr, float& speed);
291 Boolean parseRTPInfoParams(char const*& paramStr, u_int16_t& seqNum, u_int32_t& timestamp);
292 Boolean handleSETUPResponse(MediaSubsession& subsession, char const* sessionParamsStr, char const* transportParamsStr,
293 Boolean streamUsingTCP);
294 Boolean handlePLAYResponse(MediaSession* session, MediaSubsession* subsession,
295 char const* scaleParamsStr, const char* speedParamsStr,
296 char const* rangeParamsStr, char const* rtpInfoParamsStr);
297 Boolean handleTEARDOWNResponse(MediaSession& session, MediaSubsession& subsession);
298 Boolean handleGET_PARAMETERResponse(char const* parameterName, char*& resultValueString, char* resultValueStringEnd);
299 Boolean handleAuthenticationFailure(char const* wwwAuthenticateParamsStr);
300 Boolean resendCommand(RequestRecord* request);
301 char const* sessionURL(MediaSession const& session) const;
302 static void handleAlternativeRequestByte(void*, u_int8_t requestByte);
303 void handleAlternativeRequestByte1(u_int8_t requestByte);
304 void constructSubsessionURL(MediaSubsession const& subsession,
305 char const*& prefix,
306 char const*& separator,
307 char const*& suffix);
308
309 // Support for tunneling RTSP-over-HTTP:
310 Boolean setupHTTPTunneling1(); // send the HTTP "GET"
311 static void responseHandlerForHTTP_GET(RTSPClient* rtspClient, int responseCode, char* responseString);
312 void responseHandlerForHTTP_GET1(int responseCode, char* responseString);
313 Boolean setupHTTPTunneling2(); // send the HTTP "POST"
314
315 // Support for asynchronous connections to the server:
316 static void connectionHandler(void*, int /*mask*/);
317 void connectionHandler1();
318
319 // Support for handling data sent back by a server:
320 static void incomingDataHandler(void*, int /*mask*/);
321 void incomingDataHandler1();
322 void handleResponseBytes(int newBytesRead);
323
324 // Writing/reading data over a (already set-up) connection:
325 int write(const char* data, unsigned count);
326 int read(u_int8_t* buffer, unsigned bufferSize);
327
328public:
329 u_int16_t desiredMaxIncomingPacketSize;
330 // If set to a value >0, then a "Blocksize:" header with this value (minus an allowance for
331 // IP, UDP, and RTP headers) will be sent with each "SETUP" request.
332
333protected:
334 int fVerbosityLevel;
335 unsigned fCSeq; // sequence number, used in consecutive requests
336 Authenticator fCurrentAuthenticator;
337 Boolean fAllowBasicAuthentication;
338 netAddressBits fServerAddress;
339
340private:
341 portNumBits fTunnelOverHTTPPortNum;
342 char* fUserAgentHeaderStr;
343 unsigned fUserAgentHeaderStrLen;
344 int fInputSocketNum, fOutputSocketNum;
345 char* fBaseURL;
346 unsigned char fTCPStreamIdCount; // used for (optional) RTP/TCP
347 char* fLastSessionId;
348 unsigned fSessionTimeoutParameter; // optionally set in response "Session:" headers
349 char* fResponseBuffer;
350 unsigned fResponseBytesAlreadySeen, fResponseBufferBytesLeft;
351 RequestQueue fRequestsAwaitingConnection, fRequestsAwaitingHTTPTunneling, fRequestsAwaitingResponse;
352
353 // Support for tunneling RTSP-over-HTTP:
354 char fSessionCookie[33];
355 unsigned fSessionCookieCounter;
356 Boolean fHTTPTunnelingConnectionIsPending;
357
358 // Optional support for TLS:
359 TLSState fTLS;
360 friend class TLSState;
361};
362
363
364#ifndef OMIT_REGISTER_HANDLING
365////////// HandlerServerForREGISTERCommand /////////
366
367// A simple server that creates a new "RTSPClient" object whenever a "REGISTER" request arrives (specifying the "rtsp://" URL
368// of a stream). The new "RTSPClient" object will be created with the specified URL, and passed to the provided handler function.
369
370typedef void onRTSPClientCreationFunc(RTSPClient* newRTSPClient, Boolean requestStreamingOverTCP);
371
372class HandlerServerForREGISTERCommand: public RTSPServer {
373public:
374 static HandlerServerForREGISTERCommand* createNew(UsageEnvironment& env, onRTSPClientCreationFunc* creationFunc,
375 Port ourPort = 0, UserAuthenticationDatabase* authDatabase = NULL,
376 int verbosityLevel = 0, char const* applicationName = NULL);
377 // If ourPort.num() == 0, we'll choose the port number ourself. (Use the following function to get it.)
378 portNumBits serverPortNum() const { return ntohs(fServerPort.num()); }
379
380protected:
381 HandlerServerForREGISTERCommand(UsageEnvironment& env, onRTSPClientCreationFunc* creationFunc, int ourSocket, Port ourPort,
382 UserAuthenticationDatabase* authDatabase, int verbosityLevel, char const* applicationName);
383 // called only by createNew();
384 virtual ~HandlerServerForREGISTERCommand();
385
386 virtual RTSPClient* createNewRTSPClient(char const* rtspURL, int verbosityLevel, char const* applicationName,
387 int socketNumToServer);
388 // This function - by default - creates a (base) "RTSPClient" object. If you want to create a subclass
389 // of "RTSPClient" instead, then subclass this class, and redefine this virtual function.
390
391protected: // redefined virtual functions
392 virtual char const* allowedCommandNames(); // "OPTIONS", "REGISTER", and (perhaps) "DEREGISTER" only
393 virtual Boolean weImplementREGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
394 char const* proxyURLSuffix, char*& responseStr);
395 // redefined to return True (for cmd=="REGISTER")
396 virtual void implementCmd_REGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
397 char const* url, char const* urlSuffix, int socketToRemoteServer,
398 Boolean deliverViaTCP, char const* proxyURLSuffix);
399
400private:
401 onRTSPClientCreationFunc* fCreationFunc;
402 int fVerbosityLevel;
403 char* fApplicationName;
404};
405#endif
406
407#endif
408