testProgs/testOnDemandRTSPServer.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // Copyright (c) 1996-2012, Live Networks, Inc.  All rights reserved
00017 // A test program that demonstrates how to stream - via unicast RTP
00018 // - various kinds of file on demand, using a built-in RTSP server.
00019 // main program
00020 
00021 #include "liveMedia.hh"
00022 #include "BasicUsageEnvironment.hh"
00023 
00024 UsageEnvironment* env;
00025 
00026 // To make the second and subsequent client for each stream reuse the same
00027 // input stream as the first client (rather than playing the file from the
00028 // start for each client), change the following "False" to "True":
00029 Boolean reuseFirstSource = False;
00030 
00031 // To stream *only* MPEG-1 or 2 video "I" frames
00032 // (e.g., to reduce network bandwidth),
00033 // change the following "False" to "True":
00034 Boolean iFramesOnly = False;
00035 
00036 static void announceStream(RTSPServer* rtspServer, ServerMediaSession* sms,
00037                            char const* streamName, char const* inputFileName); // fwd
00038 
00039 static char newMatroskaDemuxWatchVariable;
00040 static MatroskaFileServerDemux* demux;
00041 static void onMatroskaDemuxCreation(MatroskaFileServerDemux* newDemux, void* /*clientData*/) {
00042   demux = newDemux;
00043   newMatroskaDemuxWatchVariable = 1;
00044 }
00045 
00046 int main(int argc, char** argv) {
00047   // Begin by setting up our usage environment:
00048   TaskScheduler* scheduler = BasicTaskScheduler::createNew();
00049   env = BasicUsageEnvironment::createNew(*scheduler);
00050 
00051   UserAuthenticationDatabase* authDB = NULL;
00052 #ifdef ACCESS_CONTROL
00053   // To implement client access control to the RTSP server, do the following:
00054   authDB = new UserAuthenticationDatabase;
00055   authDB->addUserRecord("username1", "password1"); // replace these with real strings
00056   // Repeat the above with each <username>, <password> that you wish to allow
00057   // access to the server.
00058 #endif
00059 
00060   // Create the RTSP server:
00061   RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
00062   if (rtspServer == NULL) {
00063     *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
00064     exit(1);
00065   }
00066 
00067   char const* descriptionString
00068     = "Session streamed by \"testOnDemandRTSPServer\"";
00069 
00070   // Set up each of the possible streams that can be served by the
00071   // RTSP server.  Each such stream is implemented using a
00072   // "ServerMediaSession" object, plus one or more
00073   // "ServerMediaSubsession" objects for each audio/video substream.
00074 
00075   // A MPEG-4 video elementary stream:
00076   {
00077     char const* streamName = "mpeg4ESVideoTest";
00078     char const* inputFileName = "test.m4e";
00079     ServerMediaSession* sms
00080       = ServerMediaSession::createNew(*env, streamName, streamName,
00081                                       descriptionString);
00082     sms->addSubsession(MPEG4VideoFileServerMediaSubsession
00083                        ::createNew(*env, inputFileName, reuseFirstSource));
00084     rtspServer->addServerMediaSession(sms);
00085 
00086     announceStream(rtspServer, sms, streamName, inputFileName);
00087   }
00088 
00089   // A H.264 video elementary stream:
00090   {
00091     char const* streamName = "h264ESVideoTest";
00092     char const* inputFileName = "test.264";
00093     ServerMediaSession* sms
00094       = ServerMediaSession::createNew(*env, streamName, streamName,
00095                                       descriptionString);
00096     sms->addSubsession(H264VideoFileServerMediaSubsession
00097                        ::createNew(*env, inputFileName, reuseFirstSource));
00098     rtspServer->addServerMediaSession(sms);
00099 
00100     announceStream(rtspServer, sms, streamName, inputFileName);
00101   }
00102 
00103   // A MPEG-1 or 2 audio+video program stream:
00104   {
00105     char const* streamName = "mpeg1or2AudioVideoTest";
00106     char const* inputFileName = "test.mpg";
00107     // NOTE: This *must* be a Program Stream; not an Elementary Stream
00108     ServerMediaSession* sms
00109       = ServerMediaSession::createNew(*env, streamName, streamName,
00110                                       descriptionString);
00111     MPEG1or2FileServerDemux* demux
00112       = MPEG1or2FileServerDemux::createNew(*env, inputFileName, reuseFirstSource);
00113     sms->addSubsession(demux->newVideoServerMediaSubsession(iFramesOnly));
00114     sms->addSubsession(demux->newAudioServerMediaSubsession());
00115     rtspServer->addServerMediaSession(sms);
00116 
00117     announceStream(rtspServer, sms, streamName, inputFileName);
00118   }
00119 
00120   // A MPEG-1 or 2 video elementary stream:
00121   {
00122     char const* streamName = "mpeg1or2ESVideoTest";
00123     char const* inputFileName = "testv.mpg";
00124     // NOTE: This *must* be a Video Elementary Stream; not a Program Stream
00125     ServerMediaSession* sms
00126       = ServerMediaSession::createNew(*env, streamName, streamName,
00127                                       descriptionString);
00128     sms->addSubsession(MPEG1or2VideoFileServerMediaSubsession
00129                ::createNew(*env, inputFileName, reuseFirstSource, iFramesOnly));
00130     rtspServer->addServerMediaSession(sms);
00131 
00132     announceStream(rtspServer, sms, streamName, inputFileName);
00133   }
00134 
00135   // A MP3 audio stream (actually, any MPEG-1 or 2 audio file will work):
00136   // To stream using 'ADUs' rather than raw MP3 frames, uncomment the following:
00137 //#define STREAM_USING_ADUS 1
00138   // To also reorder ADUs before streaming, uncomment the following:
00139 //#define INTERLEAVE_ADUS 1
00140   // (For more information about ADUs and interleaving,
00141   //  see <http://www.live555.com/rtp-mp3/>)
00142   {
00143     char const* streamName = "mp3AudioTest";
00144     char const* inputFileName = "test.mp3";
00145     ServerMediaSession* sms
00146       = ServerMediaSession::createNew(*env, streamName, streamName,
00147                                       descriptionString);
00148     Boolean useADUs = False;
00149     Interleaving* interleaving = NULL;
00150 #ifdef STREAM_USING_ADUS
00151     useADUs = True;
00152 #ifdef INTERLEAVE_ADUS
00153     unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own...
00154     unsigned const interleaveCycleSize
00155       = (sizeof interleaveCycle)/(sizeof (unsigned char));
00156     interleaving = new Interleaving(interleaveCycleSize, interleaveCycle);
00157 #endif
00158 #endif
00159     sms->addSubsession(MP3AudioFileServerMediaSubsession
00160                        ::createNew(*env, inputFileName, reuseFirstSource,
00161                                    useADUs, interleaving));
00162     rtspServer->addServerMediaSession(sms);
00163 
00164     announceStream(rtspServer, sms, streamName, inputFileName);
00165   }
00166 
00167   // A WAV audio stream:
00168   {
00169     char const* streamName = "wavAudioTest";
00170     char const* inputFileName = "test.wav";
00171     ServerMediaSession* sms
00172       = ServerMediaSession::createNew(*env, streamName, streamName,
00173                                       descriptionString);
00174     // To convert 16-bit PCM data to 8-bit u-law, prior to streaming,
00175     // change the following to True:
00176     Boolean convertToULaw = False;
00177     sms->addSubsession(WAVAudioFileServerMediaSubsession
00178                ::createNew(*env, inputFileName, reuseFirstSource, convertToULaw));
00179     rtspServer->addServerMediaSession(sms);
00180 
00181     announceStream(rtspServer, sms, streamName, inputFileName);
00182   }
00183 
00184   // An AMR audio stream:
00185   {
00186     char const* streamName = "amrAudioTest";
00187     char const* inputFileName = "test.amr";
00188     ServerMediaSession* sms
00189       = ServerMediaSession::createNew(*env, streamName, streamName,
00190                                       descriptionString);
00191     sms->addSubsession(AMRAudioFileServerMediaSubsession
00192                        ::createNew(*env, inputFileName, reuseFirstSource));
00193     rtspServer->addServerMediaSession(sms);
00194 
00195     announceStream(rtspServer, sms, streamName, inputFileName);
00196   }
00197 
00198   // A 'VOB' file (e.g., from an unencrypted DVD):
00199   {
00200     char const* streamName = "vobTest";
00201     char const* inputFileName = "test.vob";
00202     ServerMediaSession* sms
00203       = ServerMediaSession::createNew(*env, streamName, streamName,
00204                                       descriptionString);
00205     // Note: VOB files are MPEG-2 Program Stream files, but using AC-3 audio
00206     MPEG1or2FileServerDemux* demux
00207       = MPEG1or2FileServerDemux::createNew(*env, inputFileName, reuseFirstSource);
00208     sms->addSubsession(demux->newVideoServerMediaSubsession(iFramesOnly));
00209     sms->addSubsession(demux->newAC3AudioServerMediaSubsession());
00210     rtspServer->addServerMediaSession(sms);
00211 
00212     announceStream(rtspServer, sms, streamName, inputFileName);
00213   }
00214 
00215   // A MPEG-2 Transport Stream:
00216   {
00217     char const* streamName = "mpeg2TransportStreamTest";
00218     char const* inputFileName = "test.ts";
00219     char const* indexFileName = "test.tsx";
00220     ServerMediaSession* sms
00221       = ServerMediaSession::createNew(*env, streamName, streamName,
00222                                       descriptionString);
00223     sms->addSubsession(MPEG2TransportFileServerMediaSubsession
00224                        ::createNew(*env, inputFileName, indexFileName, reuseFirstSource));
00225     rtspServer->addServerMediaSession(sms);
00226 
00227     announceStream(rtspServer, sms, streamName, inputFileName);
00228   }
00229 
00230   // An AAC audio stream (ADTS-format file):
00231   {
00232     char const* streamName = "aacAudioTest";
00233     char const* inputFileName = "test.aac";
00234     ServerMediaSession* sms
00235       = ServerMediaSession::createNew(*env, streamName, streamName,
00236                                       descriptionString);
00237     sms->addSubsession(ADTSAudioFileServerMediaSubsession
00238                        ::createNew(*env, inputFileName, reuseFirstSource));
00239     rtspServer->addServerMediaSession(sms);
00240 
00241     announceStream(rtspServer, sms, streamName, inputFileName);
00242   }
00243 
00244   // A DV video stream:
00245   {
00246     // First, make sure that the RTPSinks' buffers will be large enough to handle the huge size of DV frames (as big as 288000).
00247     OutPacketBuffer::maxSize = 300000;
00248 
00249     char const* streamName = "dvVideoTest";
00250     char const* inputFileName = "test.dv";
00251     ServerMediaSession* sms
00252       = ServerMediaSession::createNew(*env, streamName, streamName,
00253                                       descriptionString);
00254     sms->addSubsession(DVVideoFileServerMediaSubsession
00255                        ::createNew(*env, inputFileName, reuseFirstSource));
00256     rtspServer->addServerMediaSession(sms);
00257 
00258     announceStream(rtspServer, sms, streamName, inputFileName);
00259   }
00260 
00261   // A AC3 video elementary stream:
00262   {
00263     char const* streamName = "ac3AudioTest";
00264     char const* inputFileName = "test.ac3";
00265     ServerMediaSession* sms
00266       = ServerMediaSession::createNew(*env, streamName, streamName,
00267                                       descriptionString);
00268 
00269     sms->addSubsession(AC3AudioFileServerMediaSubsession
00270                        ::createNew(*env, inputFileName, reuseFirstSource));
00271 
00272     rtspServer->addServerMediaSession(sms);
00273 
00274     announceStream(rtspServer, sms, streamName, inputFileName);
00275   }
00276 
00277   // A Matroska ('.mkv') file, with video+audio+subtitle streams:
00278   {
00279     char const* streamName = "matroskaFileTest";
00280     char const* inputFileName = "test.mkv";
00281     ServerMediaSession* sms
00282       = ServerMediaSession::createNew(*env, streamName, streamName,
00283                                       descriptionString);
00284 
00285     newMatroskaDemuxWatchVariable = 0;
00286     MatroskaFileServerDemux::createNew(*env, inputFileName, onMatroskaDemuxCreation, NULL);
00287     env->taskScheduler().doEventLoop(&newMatroskaDemuxWatchVariable);
00288 
00289     Boolean sessionHasTracks = False;
00290     ServerMediaSubsession* smss;
00291     while ((smss = demux->newServerMediaSubsession()) != NULL) {
00292       sms->addSubsession(smss);
00293       sessionHasTracks = True;
00294     }
00295     if (sessionHasTracks) {
00296       rtspServer->addServerMediaSession(sms);
00297     }
00298     // otherwise, because the stream has no tracks, we don't add a ServerMediaSession to the server.
00299 
00300     announceStream(rtspServer, sms, streamName, inputFileName);
00301   }
00302 
00303   // A WebM ('.webm') file, with video(VP8)+audio(Vorbis) streams:
00304   // (Note: ".webm' files are special types of Matroska files, so we use the same code as the Matroska ('.mkv') file code above.)
00305   {
00306     char const* streamName = "webmFileTest";
00307     char const* inputFileName = "test.webm";
00308     ServerMediaSession* sms
00309       = ServerMediaSession::createNew(*env, streamName, streamName,
00310                                       descriptionString);
00311 
00312     newMatroskaDemuxWatchVariable = 0;
00313     MatroskaFileServerDemux::createNew(*env, inputFileName, onMatroskaDemuxCreation, NULL);
00314     env->taskScheduler().doEventLoop(&newMatroskaDemuxWatchVariable);
00315 
00316     Boolean sessionHasTracks = False;
00317     ServerMediaSubsession* smss;
00318     while ((smss = demux->newServerMediaSubsession()) != NULL) {
00319       sms->addSubsession(smss);
00320       sessionHasTracks = True;
00321     }
00322     if (sessionHasTracks) {
00323       rtspServer->addServerMediaSession(sms);
00324     }
00325     // otherwise, because the stream has no tracks, we don't add a ServerMediaSession to the server.
00326 
00327     announceStream(rtspServer, sms, streamName, inputFileName);
00328   }
00329 
00330   // A MPEG-2 Transport Stream, coming from a live UDP (raw-UDP or RTP/UDP) source:
00331   {
00332     char const* streamName = "mpeg2TransportStreamFromUDPSourceTest";
00333     char const* inputAddressStr = "239.255.42.42";
00334         // This causes the server to take its input from the stream sent by the "testMPEG2TransportStreamer" demo application.
00335         // (Note: If the input UDP source is unicast rather than multicast, then change this to NULL.)
00336     portNumBits const inputPortNum = 1234;
00337         // This causes the server to take its input from the stream sent by the "testMPEG2TransportStreamer" demo application.
00338     Boolean const inputStreamIsRawUDP = False; 
00339     ServerMediaSession* sms
00340       = ServerMediaSession::createNew(*env, streamName, streamName,
00341                                       descriptionString);
00342     sms->addSubsession(MPEG2TransportUDPServerMediaSubsession
00343                        ::createNew(*env, inputAddressStr, inputPortNum, inputStreamIsRawUDP));
00344     rtspServer->addServerMediaSession(sms);
00345 
00346     char* url = rtspServer->rtspURL(sms);
00347     *env << "\n\"" << streamName << "\" stream, from a UDP Transport Stream input source \n\t(";
00348     if (inputAddressStr != NULL) {
00349       *env << "IP multicast address " << inputAddressStr << ",";
00350     } else {
00351       *env << "unicast;";
00352     }
00353     *env << " port " << inputPortNum << ")\n";
00354     *env << "Play this stream using the URL \"" << url << "\"\n";
00355     delete[] url;
00356   }
00357 
00358   // Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.
00359   // Try first with the default HTTP port (80), and then with the alternative HTTP
00360   // port numbers (8000 and 8080).
00361 
00362   if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
00363     *env << "\n(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling.)\n";
00364   } else {
00365     *env << "\n(RTSP-over-HTTP tunneling is not available.)\n";
00366   }
00367 
00368   env->taskScheduler().doEventLoop(); // does not return
00369 
00370   return 0; // only to prevent compiler warning
00371 }
00372 
00373 static void announceStream(RTSPServer* rtspServer, ServerMediaSession* sms,
00374                            char const* streamName, char const* inputFileName) {
00375   char* url = rtspServer->rtspURL(sms);
00376   UsageEnvironment& env = rtspServer->envir();
00377   env << "\n\"" << streamName << "\" stream, from the file \""
00378       << inputFileName << "\"\n";
00379   env << "Play this stream using the URL \"" << url << "\"\n";
00380   delete[] url;
00381 }

Generated on Thu Feb 2 23:51:32 2012 for live by  doxygen 1.5.2