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 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2013 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 #define MFSD_DUMMY_SINK_BUFFER_SIZE (6+65535) /* large enough for a PES packet */
00167 
00168 class MFSD_DummySink: public MediaSink {
00169 public:
00170   MFSD_DummySink(MPEG1or2Demux& demux, Boolean returnFirstSeenCode);
00171   virtual ~MFSD_DummySink();
00172 
00173   char watchVariable;
00174 
00175 private:
00176   // redefined virtual function:
00177   virtual Boolean continuePlaying();
00178 
00179 private:
00180   static void afterGettingFrame(void* clientData, unsigned frameSize,
00181                                 unsigned numTruncatedBytes,
00182                                 struct timeval presentationTime,
00183                                 unsigned durationInMicroseconds);
00184   void afterGettingFrame1();
00185 
00186 private:
00187   MPEG1or2Demux& fOurDemux;
00188   Boolean fReturnFirstSeenCode;
00189   unsigned char fBuf[MFSD_DUMMY_SINK_BUFFER_SIZE];
00190 };
00191 
00192 static void afterPlayingMFSD_DummySink(MFSD_DummySink* sink); // forward
00193 static float computeSCRTimeCode(MPEG1or2Demux::SCR const& scr); // forward
00194 
00195 static Boolean getMPEG1or2TimeCode(FramedSource* dataSource,
00196                                    MPEG1or2Demux& parentDemux,
00197                                    Boolean returnFirstSeenCode,
00198                                    float& timeCode) {
00199   // Start reading through "dataSource", until we see a SCR time code:
00200   parentDemux.lastSeenSCR().isValid = False;
00201   UsageEnvironment& env = dataSource->envir(); // alias
00202   MFSD_DummySink sink(parentDemux, returnFirstSeenCode);
00203   sink.startPlaying(*dataSource,
00204                     (MediaSink::afterPlayingFunc*)afterPlayingMFSD_DummySink, &sink);
00205   env.taskScheduler().doEventLoop(&sink.watchVariable);
00206 
00207   timeCode = computeSCRTimeCode(parentDemux.lastSeenSCR());
00208   return parentDemux.lastSeenSCR().isValid;
00209 }
00210 
00211 
00213 
00214 MFSD_DummySink::MFSD_DummySink(MPEG1or2Demux& demux, Boolean returnFirstSeenCode)
00215   : MediaSink(demux.envir()),
00216     watchVariable(0), fOurDemux(demux), fReturnFirstSeenCode(returnFirstSeenCode) {
00217 }
00218 
00219 MFSD_DummySink::~MFSD_DummySink() {
00220 }
00221 
00222 Boolean MFSD_DummySink::continuePlaying() {
00223   if (fSource == NULL) return False; // sanity check
00224 
00225   fSource->getNextFrame(fBuf, sizeof fBuf,
00226                         afterGettingFrame, this,
00227                         onSourceClosure, this);
00228   return True;
00229 }
00230 
00231 void MFSD_DummySink::afterGettingFrame(void* clientData, unsigned /*frameSize*/,
00232                                   unsigned /*numTruncatedBytes*/,
00233                                   struct timeval /*presentationTime*/,
00234                                   unsigned /*durationInMicroseconds*/) {
00235   MFSD_DummySink* sink = (MFSD_DummySink*)clientData;
00236   sink->afterGettingFrame1();
00237 }
00238 
00239 void MFSD_DummySink::afterGettingFrame1() {
00240   if (fReturnFirstSeenCode && fOurDemux.lastSeenSCR().isValid) {
00241     // We were asked to return the first SCR that we saw, and we've seen one,
00242     // so we're done.  (Handle this as if the input source had closed.)
00243     onSourceClosure(this);
00244     return;
00245   }
00246 
00247   continuePlaying();
00248 }
00249 
00250 static void afterPlayingMFSD_DummySink(MFSD_DummySink* sink) {
00251   // Return from the "doEventLoop()" call:
00252   sink->watchVariable = ~0;
00253 }
00254 
00255 static float computeSCRTimeCode(MPEG1or2Demux::SCR const& scr) {
00256   double result = scr.remainingBits/90000.0 + scr.extension/300.0;
00257   if (scr.highBit) {
00258     // Add (2^32)/90000 == (2^28)/5625
00259     double const highBitValue = (256*1024*1024)/5625.0;
00260     result += highBitValue;
00261   }
00262 
00263   return (float)result;
00264 }

Generated on Mon Apr 29 13:28:02 2013 for live by  doxygen 1.5.2