blob: 27cc58befab38024608dcfedc465cf8f3782164c [file] [log] [blame]
/**********
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 3 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
**********/
// Copyright (c) 1996-2020, Live Networks, Inc. All rights reserved
// LIVE555 Proxy Server
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
char const* progName;
UsageEnvironment* env;
UserAuthenticationDatabase* authDB = NULL;
UserAuthenticationDatabase* authDBForREGISTER = NULL;
// Default values of command-line parameters:
int verbosityLevel = 0;
Boolean streamRTPOverTCP = False;
portNumBits tunnelOverHTTPPortNum = 0;
portNumBits rtspServerPortNum = 554;
char* username = NULL;
char* password = NULL;
Boolean proxyREGISTERRequests = False;
char* usernameForREGISTER = NULL;
char* passwordForREGISTER = NULL;
static RTSPServer* createRTSPServer(Port port) {
if (proxyREGISTERRequests) {
return RTSPServerWithREGISTERProxying::createNew(*env, port, authDB, authDBForREGISTER, 65, streamRTPOverTCP, verbosityLevel, username, password);
} else {
return RTSPServer::createNew(*env, port, authDB);
}
}
void usage() {
*env << "Usage: " << progName
<< " [-v|-V]"
<< " [-t|-T <http-port>]"
<< " [-p <rtspServer-port>]"
<< " [-u <username> <password>]"
<< " [-R] [-U <username-for-REGISTER> <password-for-REGISTER>]"
<< " <rtsp-url-1> ... <rtsp-url-n>\n";
exit(1);
}
int main(int argc, char** argv) {
// Increase the maximum size of video frames that we can 'proxy' without truncation.
// (Such frames are unreasonably large; the back-end servers should really not be sending frames this large!)
OutPacketBuffer::maxSize = 100000; // bytes
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
*env << "LIVE555 Proxy Server\n"
<< "\t(LIVE555 Streaming Media library version "
<< LIVEMEDIA_LIBRARY_VERSION_STRING
<< "; licensed under the GNU LGPL)\n\n";
// Check command-line arguments: optional parameters, then one or more rtsp:// URLs (of streams to be proxied):
progName = argv[0];
if (argc < 2) usage();
while (argc > 1) {
// Process initial command-line options (beginning with "-"):
char* const opt = argv[1];
if (opt[0] != '-') break; // the remaining parameters are assumed to be "rtsp://" URLs
switch (opt[1]) {
case 'v': { // verbose output
verbosityLevel = 1;
break;
}
case 'V': { // more verbose output
verbosityLevel = 2;
break;
}
case 't': {
// Stream RTP and RTCP over the TCP 'control' connection.
// (This is for the 'back end' (i.e., proxied) stream only.)
streamRTPOverTCP = True;
break;
}
case 'T': {
// stream RTP and RTCP over a HTTP connection
if (argc > 2 && argv[2][0] != '-') {
// The next argument is the HTTP server port number:
if (sscanf(argv[2], "%hu", &tunnelOverHTTPPortNum) == 1
&& tunnelOverHTTPPortNum > 0) {
++argv; --argc;
break;
}
}
// If we get here, the option was specified incorrectly:
usage();
break;
}
case 'p': {
// specify a rtsp server port number
if (argc > 2 && argv[2][0] != '-') {
// The next argument is the rtsp server port number:
if (sscanf(argv[2], "%hu", &rtspServerPortNum) == 1
&& rtspServerPortNum > 0) {
++argv; --argc;
break;
}
}
// If we get here, the option was specified incorrectly:
usage();
break;
}
case 'u': { // specify a username and password (to be used if the 'back end' (i.e., proxied) stream requires authentication)
if (argc < 4) usage(); // there's no argv[3] (for the "password")
username = argv[2];
password = argv[3];
argv += 2; argc -= 2;
break;
}
case 'U': { // specify a username and password to use to authenticate incoming "REGISTER" commands
if (argc < 4) usage(); // there's no argv[3] (for the "password")
usernameForREGISTER = argv[2];
passwordForREGISTER = argv[3];
if (authDBForREGISTER == NULL) authDBForREGISTER = new UserAuthenticationDatabase;
authDBForREGISTER->addUserRecord(usernameForREGISTER, passwordForREGISTER);
argv += 2; argc -= 2;
break;
}
case 'R': { // Handle incoming "REGISTER" requests by proxying the specified stream:
proxyREGISTERRequests = True;
break;
}
default: {
usage();
break;
}
}
++argv; --argc;
}
if (argc < 2 && !proxyREGISTERRequests) usage(); // there must be at least one URL at the end
// Make sure that the remaining arguments appear to be "rtsp://" (or "rtsps://") URLs:
int i;
for (i = 1; i < argc; ++i) {
if (strncmp(argv[i], "rtsp://", 7) != 0 && strncmp(argv[i], "rtsps://", 8) != 0) usage();
}
// Do some additional checking for invalid command-line argument combinations:
if (authDBForREGISTER != NULL && !proxyREGISTERRequests) {
*env << "The '-U <username-for-REGISTER> <password-for-REGISTER>' option can be used only with -R\n";
usage();
}
if (streamRTPOverTCP) {
if (tunnelOverHTTPPortNum > 0) {
*env << "The -t and -T options cannot both be used!\n";
usage();
} else {
tunnelOverHTTPPortNum = (portNumBits)(~0); // hack to tell "ProxyServerMediaSession" to stream over TCP, but not using HTTP
}
}
#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 this line with each <username>, <password> that you wish to allow access to the server.
#endif
// Create the RTSP server. Try first with the configured port number,
// and then with the default port number (554) if different,
// and then with the alternative port number (8554):
RTSPServer* rtspServer;
rtspServer = createRTSPServer(rtspServerPortNum);
if (rtspServer == NULL) {
if (rtspServerPortNum != 554) {
*env << "Unable to create a RTSP server with port number " << rtspServerPortNum << ": " << env->getResultMsg() << "\n";
*env << "Trying instead with the standard port numbers (554 and 8554)...\n";
rtspServerPortNum = 554;
rtspServer = createRTSPServer(rtspServerPortNum);
}
}
if (rtspServer == NULL) {
rtspServerPortNum = 8554;
rtspServer = createRTSPServer(rtspServerPortNum);
}
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
// Create a proxy for each "rtsp://" URL specified on the command line:
for (i = 1; i < argc; ++i) {
char const* proxiedStreamURL = argv[i];
char streamName[30];
if (argc == 2) {
sprintf(streamName, "%s", "proxyStream"); // there's just one stream; give it this name
} else {
sprintf(streamName, "proxyStream-%d", i); // there's more than one stream; distinguish them by name
}
ServerMediaSession* sms
= ProxyServerMediaSession::createNew(*env, rtspServer,
proxiedStreamURL, streamName,
username, password, tunnelOverHTTPPortNum, verbosityLevel);
rtspServer->addServerMediaSession(sms);
char* proxyStreamURL = rtspServer->rtspURL(sms);
*env << "RTSP stream, proxying the stream \"" << proxiedStreamURL << "\"\n";
*env << "\tPlay this stream using the URL: " << proxyStreamURL << "\n";
delete[] proxyStreamURL;
}
if (proxyREGISTERRequests) {
*env << "(We handle incoming \"REGISTER\" requests on port " << rtspServerPortNum << ")\n";
}
// Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.
// Try first with the default HTTP port (80), and then with the alternative HTTP
// port numbers (8000 and 8080).
if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
*env << "\n(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling.)\n";
} else {
*env << "\n(RTSP-over-HTTP tunneling is not available.)\n";
}
// Now, enter the event loop:
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}