📄 playcommon.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**********/// Copyright (c) 1996-2004, Live Networks, Inc. All rights reserved// A common framework, used for the "openRTSP" and "playSIP" applications// Implementation#include "playCommon.hh"#include "BasicUsageEnvironment.hh"#include "GroupsockHelper.hh"#ifdef SUPPORT_REAL_RTSP#include "../RealRTSP/include/RealRTSP.hh"#endif#if defined(__WIN32__) || defined(_WIN32)#define snprintf _snprintf#else#include <signal.h>#define USE_SIGNALS 1#endif// Forward function definitions:void setupStreams();void startPlayingStreams();void tearDownStreams();void closeMediaSinks();void subsessionAfterPlaying(void* clientData);void subsessionByeHandler(void* clientData);void sessionAfterPlaying(void* clientData = NULL);void sessionTimerHandler(void* clientData);void shutdown(int exitCode = 1);void signalHandlerShutdown(int sig);void checkForPacketArrival(void* clientData);void checkInterPacketGaps(void* clientData);void beginQOSMeasurement();Boolean setupDestinationRTSPServer();char const* progName;UsageEnvironment* env;Medium* ourClient = NULL;MediaSession* session = NULL;TaskToken sessionTimerTask = NULL;TaskToken arrivalCheckTimerTask = NULL;TaskToken interPacketGapCheckTimerTask = NULL;TaskToken qosMeasurementTimerTask = NULL;Boolean createReceivers = True;Boolean outputQuickTimeFile = False;Boolean generateMP4Format = False;QuickTimeFileSink* qtOut = NULL;Boolean outputAVIFile = False;AVIFileSink* aviOut = NULL;Boolean audioOnly = False;Boolean videoOnly = False;char const* singleMedium = NULL;int verbosityLevel = 0;double endTime = 0;double endTimeSlop = -1.0; // extra seconds to play at the endunsigned interPacketGapMaxTime = 0;unsigned totNumPacketsReceived = ~0; // used if checking inter-packet gapsBoolean playContinuously = False;int simpleRTPoffsetArg = -1;Boolean sendOptionsRequest = True;Boolean sendOptionsRequestOnly = False;Boolean oneFilePerFrame = False;Boolean notifyOnPacketArrival = False;Boolean streamUsingTCP = False;portNumBits tunnelOverHTTPPortNum = 0;char* username = NULL;char* password = NULL;char* proxyServerName = NULL;unsigned short proxyServerPortNum = 0;unsigned char desiredAudioRTPPayloadFormat = 0;char* mimeSubtype = NULL;unsigned short movieWidth = 240; // defaultBoolean movieWidthOptionSet = False;unsigned short movieHeight = 180; // defaultBoolean movieHeightOptionSet = False;unsigned movieFPS = 15; // defaultBoolean movieFPSOptionSet = False;char* fileNamePrefix = "";unsigned fileSinkBufferSize = 20000;unsigned socketInputBufferSize = 0;Boolean packetLossCompensate = False;Boolean syncStreams = False;Boolean generateHintTracks = False;char* destRTSPURL = NULL;unsigned qosMeasurementIntervalMS = 0; // 0 means: Don't output QOS dataunsigned statusCode = 0;struct timeval startTime;void usage() { *env << "Usage: " << progName << " [-p <startPortNum>] [-r|-q|-4|-i] [-a|-v] [-V] [-e <endTime>] [-E <max-inter-packet-gap-time> [-c] [-s <offset>] [-n] [-O]" << (controlConnectionUsesTCP ? " [-t|-T <http-port>]" : "") << " [-u <username> <password>" << (allowProxyServers ? " [<proxy-server> [<proxy-server-port>]]" : "") << "]" << (supportCodecSelection ? " [-A <audio-codec-rtp-payload-format-code>|-D <mime-subtype-name>]" : "") << " [-w <width> -h <height>] [-f <frames-per-second>] [-y] [-H] [-Q [<measurement-interval>]] [-F <filename-prefix>] [-b <file-sink-buffer-size>] [-B <input-socket-buffer-size>] [-I <input-interface-ip-address>] [-m] <url> (or " << progName << " -o [-V] <url>)\n"; //##### Add "-R <dest-rtsp-url>" ##### shutdown();}int main(int argc, char** argv) { // Begin by setting up our usage environment: TaskScheduler* scheduler = BasicTaskScheduler::createNew(); env = BasicUsageEnvironment::createNew(*scheduler); progName = argv[0]; gettimeofday(&startTime, NULL);#ifdef USE_SIGNALS // Allow ourselves to be shut down gracefully by a SIGHUP or a SIGUSR1: signal(SIGHUP, signalHandlerShutdown); signal(SIGUSR1, signalHandlerShutdown);#endif unsigned short desiredPortNum = 0; // unfortunately we can't use getopt() here, as Windoze doesn't have it while (argc > 2) { char* const opt = argv[1]; if (opt[0] != '-') usage(); switch (opt[1]) { case 'p': { // specify start port number int portArg; if (sscanf(argv[2], "%d", &portArg) != 1) { usage(); } if (portArg <= 0 || portArg >= 65536 || portArg&1) { *env << "bad port number: " << portArg << " (must be even, and in the range (0,65536))\n"; usage(); } desiredPortNum = (unsigned short)portArg; ++argv; --argc; break; } case 'r': { // do not receive data (instead, just 'play' the stream(s)) createReceivers = False; break; } case 'q': { // output a QuickTime file (to stdout) outputQuickTimeFile = True; break; } case '4': { // output a 'mp4'-format file (to stdout) outputQuickTimeFile = True; generateMP4Format = True; break; } case 'i': { // output an AVI file (to stdout) outputAVIFile = True; break; } case 'I': { // specify input interface... NetAddressList addresses(argv[2]); if (addresses.numAddresses() == 0) { *env << "Failed to find network address for \"" << argv[2] << "\""; break; } ReceivingInterfaceAddr = *(unsigned*)(addresses.firstAddress()->data()); ++argv; --argc; break; } case 'a': { // receive/record an audio stream only audioOnly = True; singleMedium = "audio"; break; } case 'v': { // receive/record a video stream only videoOnly = True; singleMedium = "video"; break; } case 'V': { // verbose output verbosityLevel = 1; break; } case 'e': { // specify end time, or how much to delay after end time float arg; if (sscanf(argv[2], "%g", &arg) != 1) { usage(); } if (argv[2][0] == '-') { // not "arg<0", in case argv[2] was "-0" // a 'negative' argument was specified; use this for "endTimeSlop": endTime = 0; // use whatever's in the SDP endTimeSlop = -arg; } else { endTime = arg; endTimeSlop = 0; } ++argv; --argc; break; } case 'E': { // specify maximum number of seconds to wait for packets: if (sscanf(argv[2], "%u", &interPacketGapMaxTime) != 1) { usage(); } ++argv; --argc; break; } case 'c': { // play continuously playContinuously = True; break; } case 's': { // specify an offset to use with "SimpleRTPSource"s if (sscanf(argv[2], "%d", &simpleRTPoffsetArg) != 1) { usage(); } if (simpleRTPoffsetArg < 0) { *env << "offset argument to \"-s\" must be >= 0\n"; usage(); } ++argv; --argc; break; } case 'O': { // Don't send an "OPTIONS" request before "DESCRIBE" sendOptionsRequest = False; break; } case 'o': { // Send only the "OPTIONS" request to the server sendOptionsRequestOnly = True; break; } case 'm': { // output multiple files - one for each frame oneFilePerFrame = True; break; } case 'n': { // notify the user when the first data packet arrives notifyOnPacketArrival = True; break; } case 't': { // stream RTP and RTCP over the TCP 'control' connection if (controlConnectionUsesTCP) { streamUsingTCP = True; } else { usage(); } break; } case 'T': { // stream RTP and RTCP over a HTTP connection if (controlConnectionUsesTCP) { if (argc > 3 && 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 'u': { // specify a username and password username = argv[2]; password = argv[3]; argv+=2; argc-=2; if (allowProxyServers && argc > 3 && argv[2][0] != '-') { // The next argument is the name of a proxy server: proxyServerName = argv[2]; ++argv; --argc; if (argc > 3 && argv[2][0] != '-') { // The next argument is the proxy server port number: if (sscanf(argv[2], "%hu", &proxyServerPortNum) != 1) { usage(); } ++argv; --argc; } } break; } case 'A': { // specify a desired audio RTP payload format unsigned formatArg; if (sscanf(argv[2], "%u", &formatArg) != 1 || formatArg >= 96) { usage(); } desiredAudioRTPPayloadFormat = (unsigned char)formatArg; ++argv; --argc; break; } case 'D': { // specify a MIME subtype for a dynamic RTP payload type mimeSubtype = argv[2]; if (desiredAudioRTPPayloadFormat==0) desiredAudioRTPPayloadFormat =96; ++argv; --argc; break; } case 'w': { // specify a width (pixels) for an output QuickTime or AVI movie if (sscanf(argv[2], "%hu", &movieWidth) != 1) { usage(); } movieWidthOptionSet = True; ++argv; --argc; break; } case 'h': { // specify a height (pixels) for an output QuickTime or AVI movie if (sscanf(argv[2], "%hu", &movieHeight) != 1) { usage(); } movieHeightOptionSet = True; ++argv; --argc; break; } case 'f': { // specify a frame rate (per second) for an output QT or AVI movie if (sscanf(argv[2], "%u", &movieFPS) != 1) { usage(); } movieFPSOptionSet = True; ++argv; --argc; break; } case 'F': { // specify a prefix for the audio and video output files fileNamePrefix = argv[2]; ++argv; --argc; break; } case 'b': { // specify the size of buffers for "FileSink"s if (sscanf(argv[2], "%u", &fileSinkBufferSize) != 1) { usage(); } ++argv; --argc; break; } case 'B': { // specify the size of input socket buffers
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -