testProgs/vobStreamer.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-2008, Live Networks, Inc.  All rights reserved
00017 // A test program that reads a VOB file
00018 // splits it into Audio (AC3) and Video (MPEG) Elementary Streams,
00019 // and streams both using RTP.
00020 // main program
00021 
00022 #include "liveMedia.hh"
00023 #include "AC3AudioStreamFramer.hh"
00024 #include "BasicUsageEnvironment.hh"
00025 #include "GroupsockHelper.hh"
00026 
00027 char const* programName;
00028 // Whether to stream *only* "I" (key) frames
00029 // (e.g., to reduce network bandwidth):
00030 Boolean iFramesOnly = False;
00031 
00032 unsigned const VOB_AUDIO = 1<<0;
00033 unsigned const VOB_VIDEO = 1<<1;
00034 unsigned mediaToStream = VOB_AUDIO|VOB_VIDEO; // by default
00035 
00036 char const** inputFileNames;
00037 char const** curInputFileName;
00038 Boolean haveReadOneFile = False;
00039 
00040 UsageEnvironment* env;
00041 MPEG1or2Demux* mpegDemux;
00042 AC3AudioStreamFramer* audioSource = NULL;
00043 FramedSource* videoSource = NULL;
00044 RTPSink* audioSink = NULL;
00045 RTCPInstance* audioRTCP = NULL;
00046 RTPSink* videoSink = NULL;
00047 RTCPInstance* videoRTCP = NULL;
00048 RTSPServer* rtspServer = NULL;
00049 unsigned short const defaultRTSPServerPortNum = 554;
00050 unsigned short rtspServerPortNum = defaultRTSPServerPortNum;
00051 
00052 Groupsock* rtpGroupsockAudio;
00053 Groupsock* rtcpGroupsockAudio;
00054 Groupsock* rtpGroupsockVideo;
00055 Groupsock* rtcpGroupsockVideo;
00056 
00057 void usage() {
00058   *env << "usage: " << programName << " [-i] [-a|-v] "
00059           "[-p <RTSP-server-port-number>] "
00060           "<VOB-file>...<VOB-file>\n";
00061   exit(1);
00062 }
00063 
00064 void play(); // forward
00065 
00066 int main(int argc, char const** argv) {
00067   // Begin by setting up our usage environment:
00068   TaskScheduler* scheduler = BasicTaskScheduler::createNew();
00069   env = BasicUsageEnvironment::createNew(*scheduler);
00070 
00071   // Parse command-line options:
00072   // (Unfortunately we can't use getopt() here; Windoze doesn't have it)
00073   programName = argv[0];
00074   while (argc > 2) {
00075     char const* const opt = argv[1];
00076     if (opt[0] != '-') break;
00077     switch (opt[1]) {
00078 
00079     case 'i': { // transmit video I-frames only
00080       iFramesOnly = True;
00081       break;
00082     }
00083 
00084     case 'a': { // transmit audio, but not video
00085       mediaToStream &=~ VOB_VIDEO;
00086       break;
00087     }
00088 
00089     case 'v': { // transmit video, but not audio
00090       mediaToStream &=~ VOB_AUDIO;
00091       break;
00092     }
00093 
00094     case 'p': { // specify port number for built-in RTSP server
00095       int portArg;
00096       if (sscanf(argv[2], "%d", &portArg) != 1) {
00097         usage();
00098       }
00099       if (portArg <= 0 || portArg >= 65536) {
00100         *env << "bad port number: " << portArg
00101              << " (must be in the range (0,65536))\n";
00102         usage();
00103       }
00104       rtspServerPortNum = (unsigned short)portArg;
00105       ++argv; --argc;
00106       break;
00107     }
00108 
00109     default: {
00110       usage();
00111       break;
00112     }
00113     }
00114 
00115     ++argv; --argc;
00116   }
00117   if (argc < 2) usage();
00118   if (mediaToStream == 0) {
00119     *env << "The -a and -v flags cannot both be used!\n";
00120     usage();
00121   }
00122   if (iFramesOnly && (mediaToStream&VOB_VIDEO) == 0) {
00123     *env << "Warning: Because we're not streaming video, the -i flag has no effect.\n";
00124   }
00125 
00126   inputFileNames = &argv[1];
00127   curInputFileName = inputFileNames;
00128 
00129   // Create 'groupsocks' for RTP and RTCP:
00130   struct in_addr destinationAddress;
00131   destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
00132 
00133   const unsigned short rtpPortNumAudio = 4444;
00134   const unsigned short rtcpPortNumAudio = rtpPortNumAudio+1;
00135   const unsigned short rtpPortNumVideo = 8888;
00136   const unsigned short rtcpPortNumVideo = rtpPortNumVideo+1;
00137   const unsigned char ttl = 255;
00138 
00139   const Port rtpPortAudio(rtpPortNumAudio);
00140   const Port rtcpPortAudio(rtcpPortNumAudio);
00141   const Port rtpPortVideo(rtpPortNumVideo);
00142   const Port rtcpPortVideo(rtcpPortNumVideo);
00143 
00144   const unsigned maxCNAMElen = 100;
00145   unsigned char CNAME[maxCNAMElen+1];
00146   gethostname((char*)CNAME, maxCNAMElen);
00147   CNAME[maxCNAMElen] = '\0'; // just in case
00148 
00149   if (mediaToStream&VOB_AUDIO) {
00150     rtpGroupsockAudio
00151       = new Groupsock(*env, destinationAddress, rtpPortAudio, ttl);
00152     rtpGroupsockAudio->multicastSendOnly(); // because we're a SSM source
00153 
00154     // Create an 'AC3 Audio RTP' sink from the RTP 'groupsock':
00155     audioSink
00156       = AC3AudioRTPSink::createNew(*env, rtpGroupsockAudio, 96, 0);
00157     // set the RTP timestamp frequency 'for real' later
00158 
00159     // Create (and start) a 'RTCP instance' for this RTP sink:
00160     rtcpGroupsockAudio
00161       = new Groupsock(*env, destinationAddress, rtcpPortAudio, ttl);
00162     rtcpGroupsockAudio->multicastSendOnly(); // because we're a SSM source
00163     const unsigned estimatedSessionBandwidthAudio
00164       = 160; // in kbps; for RTCP b/w share
00165     audioRTCP = RTCPInstance::createNew(*env, rtcpGroupsockAudio,
00166                                         estimatedSessionBandwidthAudio, CNAME,
00167                                         audioSink, NULL /* we're a server */,
00168                                         True /* we're a SSM source */);
00169     // Note: This starts RTCP running automatically
00170   }
00171 
00172   if (mediaToStream&VOB_VIDEO) {
00173     rtpGroupsockVideo
00174       = new Groupsock(*env, destinationAddress, rtpPortVideo, ttl);
00175     rtpGroupsockVideo->multicastSendOnly(); // because we're a SSM source
00176 
00177     // Create a 'MPEG Video RTP' sink from the RTP 'groupsock':
00178     videoSink = MPEG1or2VideoRTPSink::createNew(*env, rtpGroupsockVideo);
00179 
00180     // Create (and start) a 'RTCP instance' for this RTP sink:
00181     rtcpGroupsockVideo
00182       = new Groupsock(*env, destinationAddress, rtcpPortVideo, ttl);
00183     rtcpGroupsockVideo->multicastSendOnly(); // because we're a SSM source
00184     const unsigned estimatedSessionBandwidthVideo
00185       = 4500; // in kbps; for RTCP b/w share
00186     videoRTCP = RTCPInstance::createNew(*env, rtcpGroupsockVideo,
00187                                         estimatedSessionBandwidthVideo, CNAME,
00188                                         videoSink, NULL /* we're a server */,
00189                                         True /* we're a SSM source */);
00190     // Note: This starts RTCP running automatically
00191   }
00192 
00193   if (rtspServer == NULL) {
00194     rtspServer = RTSPServer::createNew(*env, rtspServerPortNum);
00195     if (rtspServer == NULL) {
00196       *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
00197       *env << "To change the RTSP server's port number, use the \"-p <port number>\" option.\n";
00198       exit(1);
00199     }
00200     ServerMediaSession* sms
00201       = ServerMediaSession::createNew(*env, "vobStream", *curInputFileName,
00202              "Session streamed by \"vobStreamer\"", True /*SSM*/);
00203     if (audioSink != NULL) sms->addSubsession(PassiveServerMediaSubsession::createNew(*audioSink, audioRTCP));
00204     if (videoSink != NULL) sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, videoRTCP));
00205     rtspServer->addServerMediaSession(sms);
00206 
00207     *env << "Created RTSP server.\n";
00208 
00209     // Display our "rtsp://" URL, for clients to connect to:
00210     char* url = rtspServer->rtspURL(sms);
00211     *env << "Access this stream using the URL:\n\t" << url << "\n";
00212     delete[] url;
00213   }
00214 
00215   // Finally, start the streaming:
00216   *env << "Beginning streaming...\n";
00217   play();
00218 
00219   env->taskScheduler().doEventLoop(); // does not return
00220 
00221   return 0; // only to prevent compiler warning
00222 }
00223 
00224 void afterPlaying(void* clientData) {
00225   // One of the sinks has ended playing.
00226   // Check whether any of the sources have a pending read.  If so,
00227   // wait until its sink ends playing also:
00228   if (audioSource != NULL && audioSource->isCurrentlyAwaitingData()
00229       || videoSource != NULL && videoSource->isCurrentlyAwaitingData()) {
00230     return;
00231   }
00232 
00233   // Now that both sinks have ended, close both input sources,
00234   // and start playing again:
00235   *env << "...done reading from file\n";
00236 
00237   if (audioSink != NULL) audioSink->stopPlaying();
00238   if (videoSink != NULL) videoSink->stopPlaying();
00239       // ensures that both are shut down
00240   Medium::close(audioSource);
00241   Medium::close(videoSource);
00242   Medium::close(mpegDemux);
00243   // Note: This also closes the input file that this source read from.
00244 
00245   // Move to the next file name (if any):
00246   ++curInputFileName;
00247 
00248   // Start playing once again:
00249   play();
00250 }
00251 
00252 void play() {
00253   if (*curInputFileName == NULL) {
00254     // We have reached the end of the file name list.
00255     // Start again, unless we didn't succeed in reading any files:
00256     if (!haveReadOneFile) exit(1);
00257     haveReadOneFile = False;
00258     curInputFileName = inputFileNames;
00259   }
00260 
00261   // Open the current input file as a 'byte-stream file source':
00262   ByteStreamFileSource* fileSource
00263     = ByteStreamFileSource::createNew(*env, *curInputFileName);
00264   if (fileSource == NULL) {
00265     *env << "Unable to open file \"" << *curInputFileName
00266          << "\" as a byte-stream file source\n";
00267     // Try the next file instead:
00268     ++curInputFileName;
00269     play();
00270     return;
00271   }
00272   haveReadOneFile = True;
00273 
00274   // We must demultiplex Audio and Video Elementary Streams
00275   // from the input source:
00276   mpegDemux = MPEG1or2Demux::createNew(*env, fileSource);
00277   if (mediaToStream&VOB_AUDIO) {
00278     FramedSource* audioES = mpegDemux->newElementaryStream(0xBD);
00279       // Because, in a VOB file, the AC3 audio has stream id 0xBD
00280     audioSource
00281       = AC3AudioStreamFramer::createNew(*env, audioES, 0x80);
00282   }
00283   if (mediaToStream&VOB_VIDEO) {
00284     FramedSource* videoES = mpegDemux->newVideoStream();
00285 
00286     videoSource
00287       = MPEG1or2VideoStreamFramer::createNew(*env, videoES, iFramesOnly);
00288   }
00289 
00290   // Finally, start playing each sink.
00291   *env << "Beginning to read from \"" << *curInputFileName << "\"...\n";
00292   if (videoSink != NULL) {
00293     videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
00294   }
00295   if (audioSink != NULL) {
00296     audioSink->setRTPTimestampFrequency(audioSource->samplingRate());
00297     audioSink->startPlaying(*audioSource, afterPlaying, audioSink);
00298   }
00299 }

Generated on Tue Oct 7 15:38:10 2008 for live by  doxygen 1.5.2