liveMedia/MPEG1or2FileServerDemux.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 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2008 Live Networks, Inc.  All rights reserved.
00018 // A server demultiplexer for a MPEG 1 or 2 Program Stream
00019 // Implementation
00020 
00021 #include "MPEG1or2FileServerDemux.hh"
00022 #include "MPEG1or2DemuxedServerMediaSubsession.hh"
00023 #include "ByteStreamFileSource.hh"
00024 
00025 MPEG1or2FileServerDemux*
00026 MPEG1or2FileServerDemux::createNew(UsageEnvironment& env, char const* fileName,
00027                                    Boolean reuseFirstSource) {
00028   return new MPEG1or2FileServerDemux(env, fileName, reuseFirstSource);
00029 }
00030 
00031 static float MPEG1or2ProgramStreamFileDuration(UsageEnvironment& env,
00032                                                char const* fileName,
00033                                                unsigned& fileSize); // forward
00034 MPEG1or2FileServerDemux
00035 ::MPEG1or2FileServerDemux(UsageEnvironment& env, char const* fileName,
00036                           Boolean reuseFirstSource)
00037   : Medium(env),
00038     fReuseFirstSource(reuseFirstSource),
00039     fSession0Demux(NULL), fLastCreatedDemux(NULL), fLastClientSessionId(~0) {
00040   fFileName = strDup(fileName);
00041   fFileDuration = MPEG1or2ProgramStreamFileDuration(env, fileName, fFileSize);
00042 }
00043 
00044 MPEG1or2FileServerDemux::~MPEG1or2FileServerDemux() {
00045   Medium::close(fSession0Demux);
00046   delete[] (char*)fFileName;
00047 }
00048 
00049 ServerMediaSubsession*
00050 MPEG1or2FileServerDemux::newAudioServerMediaSubsession() {
00051   return MPEG1or2DemuxedServerMediaSubsession::createNew(*this, 0xC0, fReuseFirstSource);
00052 }
00053 
00054 ServerMediaSubsession*
00055 MPEG1or2FileServerDemux::newVideoServerMediaSubsession(Boolean iFramesOnly,
00056                                                        double vshPeriod) {
00057   return MPEG1or2DemuxedServerMediaSubsession::createNew(*this, 0xE0, fReuseFirstSource,
00058                                                          iFramesOnly, vshPeriod);
00059 }
00060 
00061 ServerMediaSubsession*
00062 MPEG1or2FileServerDemux::newAC3AudioServerMediaSubsession() {
00063   return MPEG1or2DemuxedServerMediaSubsession::createNew(*this, 0xBD, fReuseFirstSource);
00064   // because, in a VOB file, the AC3 audio has stream id 0xBD
00065 }
00066 
00067 MPEG1or2DemuxedElementaryStream*
00068 MPEG1or2FileServerDemux::newElementaryStream(unsigned clientSessionId,
00069                                              u_int8_t streamIdTag) {
00070   MPEG1or2Demux* demuxToUse;
00071   if (clientSessionId == 0) {
00072     // 'Session 0' is treated especially, because its audio & video streams
00073     // are created and destroyed one-at-a-time, rather than both streams being
00074     // created, and then (later) both streams being destroyed (as is the case
00075     // for other ('real') session ids).  Because of this, a separate demux is
00076     // used for session 0, and its deletion is managed by us, rather than
00077     // happening automatically.
00078     if (fSession0Demux == NULL) {
00079       // Open our input file as a 'byte-stream file source':
00080       ByteStreamFileSource* fileSource
00081         = ByteStreamFileSource::createNew(envir(), fFileName);
00082       if (fileSource == NULL) return NULL;
00083       fSession0Demux = MPEG1or2Demux::createNew(envir(), fileSource, False/*note!*/);
00084     }
00085     demuxToUse = fSession0Demux;
00086   } else {
00087     // First, check whether this is a new client session.  If so, create a new
00088     // demux for it:
00089     if (clientSessionId != fLastClientSessionId) {
00090       // Open our input file as a 'byte-stream file source':
00091       ByteStreamFileSource* fileSource
00092         = ByteStreamFileSource::createNew(envir(), fFileName);
00093       if (fileSource == NULL) return NULL;
00094 
00095       fLastCreatedDemux = MPEG1or2Demux::createNew(envir(), fileSource, True);
00096       // Note: We tell the demux to delete itself when its last
00097       // elementary stream is deleted.
00098       fLastClientSessionId = clientSessionId;
00099       // Note: This code relies upon the fact that the creation of streams for
00100       // different client sessions do not overlap - so one "MPEG1or2Demux" is used
00101       // at a time.
00102     }
00103     demuxToUse = fLastCreatedDemux;
00104   }
00105 
00106   if (demuxToUse == NULL) return NULL; // shouldn't happen
00107 
00108   return demuxToUse->newElementaryStream(streamIdTag);
00109 }
00110 
00111 
00112 static Boolean getMPEG1or2TimeCode(FramedSource* dataSource,
00113                                    MPEG1or2Demux& parentDemux,
00114                                    Boolean returnFirstSeenCode,
00115                                    float& timeCode); // forward
00116 
00117 static float MPEG1or2ProgramStreamFileDuration(UsageEnvironment& env,
00118                                                char const* fileName,
00119                                                unsigned& fileSize) {
00120   FramedSource* dataSource = NULL;
00121   float duration = 0.0; // until we learn otherwise
00122   fileSize = 0; // ditto
00123 
00124   do {
00125     // Open the input file as a 'byte-stream file source':
00126     ByteStreamFileSource* fileSource = ByteStreamFileSource::createNew(env, fileName);
00127     if (fileSource == NULL) break;
00128     dataSource = fileSource;
00129 
00130     fileSize = (unsigned)(fileSource->fileSize());
00131     if (fileSize == 0) break;
00132 
00133     // Create a MPEG demultiplexor that reads from that source.
00134     MPEG1or2Demux* baseDemux = MPEG1or2Demux::createNew(env, dataSource, True);
00135     if (baseDemux == NULL) break;
00136 
00137     // Create, from this, a source that returns raw PES packets:
00138     dataSource = baseDemux->newRawPESStream();
00139 
00140     // Read the first time code from the file:
00141     float firstTimeCode;
00142     if (!getMPEG1or2TimeCode(dataSource, *baseDemux, True, firstTimeCode)) break;
00143 
00144     // Then, read the last time code from the file.
00145     // (Before doing this, flush the demux's input buffers, 
00146     //  and seek towards the end of the file, for efficiency.)
00147     baseDemux->flushInput();
00148     unsigned const startByteFromEnd = 100000;
00149     unsigned newFilePosition
00150       = fileSize < startByteFromEnd ? 0 : fileSize - startByteFromEnd;
00151     if (newFilePosition > 0) fileSource->seekToByteAbsolute(newFilePosition);
00152 
00153     float lastTimeCode;
00154     if (!getMPEG1or2TimeCode(dataSource, *baseDemux, False, lastTimeCode)) break;
00155 
00156     // Take the difference between these time codes as being the file duration:
00157     float timeCodeDiff = lastTimeCode - firstTimeCode;
00158     if (timeCodeDiff < 0) break;
00159     duration = timeCodeDiff;
00160   } while (0);
00161 
00162   Medium::close(dataSource);
00163   return duration;
00164 }
00165 
00166 class DummySink: public MediaSink {
00167 public:
00168   DummySink(MPEG1or2Demux& demux, Boolean returnFirstSeenCode);
00169   virtual ~DummySink();
00170 
00171   char watchVariable;
00172 
00173 private:
00174   // redefined virtual function:
00175   virtual Boolean continuePlaying();
00176 
00177 private:
00178   static void afterGettingFrame(void* clientData, unsigned frameSize,
00179                                 unsigned numTruncatedBytes,
00180                                 struct timeval presentationTime,
00181                                 unsigned durationInMicroseconds);
00182   void afterGettingFrame1();
00183 
00184 private:
00185   MPEG1or2Demux& fOurDemux;
00186   Boolean fReturnFirstSeenCode;
00187   unsigned char fBuf[10000];
00188 };
00189 
00190 static void afterPlayingDummySink(DummySink* sink); // forward
00191 static float computeSCRTimeCode(MPEG1or2Demux::SCR const& scr); // forward
00192 
00193 static Boolean getMPEG1or2TimeCode(FramedSource* dataSource,
00194                                    MPEG1or2Demux& parentDemux,
00195                                    Boolean returnFirstSeenCode,
00196                                    float& timeCode) {
00197   // Start reading through "dataSource", until we see a SCR time code:
00198   parentDemux.lastSeenSCR().isValid = False;
00199   UsageEnvironment& env = dataSource->envir(); // alias
00200   DummySink sink(parentDemux, returnFirstSeenCode);
00201   sink.startPlaying(*dataSource,
00202                     (MediaSink::afterPlayingFunc*)afterPlayingDummySink, &sink);
00203   env.taskScheduler().doEventLoop(&sink.watchVariable);
00204   
00205   timeCode = computeSCRTimeCode(parentDemux.lastSeenSCR());
00206   return parentDemux.lastSeenSCR().isValid;
00207 }
00208 
00209 
00211 
00212 DummySink::DummySink(MPEG1or2Demux& demux, Boolean returnFirstSeenCode)
00213   : MediaSink(demux.envir()),
00214     watchVariable(0), fOurDemux(demux), fReturnFirstSeenCode(returnFirstSeenCode) {
00215 }
00216 
00217 DummySink::~DummySink() {
00218 }
00219 
00220 Boolean DummySink::continuePlaying() {
00221   if (fSource == NULL) return False; // sanity check
00222 
00223   fSource->getNextFrame(fBuf, sizeof fBuf,
00224                         afterGettingFrame, this,
00225                         onSourceClosure, this);
00226   return True;
00227 }
00228 
00229 void DummySink::afterGettingFrame(void* clientData, unsigned /*frameSize*/,
00230                                   unsigned /*numTruncatedBytes*/,
00231                                   struct timeval /*presentationTime*/,
00232                                   unsigned /*durationInMicroseconds*/) {
00233   DummySink* sink = (DummySink*)clientData;
00234   sink->afterGettingFrame1();
00235 }
00236 
00237 void DummySink::afterGettingFrame1() {
00238   if (fReturnFirstSeenCode && fOurDemux.lastSeenSCR().isValid) {
00239     // We were asked to return the first SCR that we saw, and we've seen one,
00240     // so we're done.  (Handle this as if the input source had closed.)
00241     onSourceClosure(this);
00242     return;
00243   }
00244 
00245   continuePlaying();
00246 }
00247 
00248 static void afterPlayingDummySink(DummySink* sink) {
00249   // Return from the "doEventLoop()" call: 
00250   sink->watchVariable = ~0;
00251 }
00252 
00253 static float computeSCRTimeCode(MPEG1or2Demux::SCR const& scr) {
00254   double result = scr.remainingBits/90000.0 + scr.extension/300.0;
00255   if (scr.highBit) {
00256     // Add (2^32)/90000 == (2^28)/5625
00257     double const highBitValue = (256*1024*1024)/5625.0;
00258     result += highBitValue;
00259   }
00260 
00261   return (float)result;
00262 }

Generated on Tue Jul 22 06:39:06 2008 for live by  doxygen 1.5.2