liveMedia/MPEG2TransportFileServerMediaSubsession.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 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
00019 // on demand, from a MPEG-2 Transport Stream file.
00020 // Implementation
00021 
00022 #include "MPEG2TransportFileServerMediaSubsession.hh"
00023 #include "SimpleRTPSink.hh"
00024 
00025 MPEG2TransportFileServerMediaSubsession*
00026 MPEG2TransportFileServerMediaSubsession::createNew(UsageEnvironment& env,
00027                                                    char const* fileName,
00028                                                    char const* indexFileName,
00029                                                    Boolean reuseFirstSource) {
00030   MPEG2TransportStreamIndexFile* indexFile;
00031   if (indexFileName != NULL && reuseFirstSource) {
00032     // It makes no sense to support trick play if all clients use the same source.  Fix this:
00033     env << "MPEG2TransportFileServerMediaSubsession::createNew(): ignoring the index file name, because \"reuseFirstSource\" is set\n";
00034     indexFile = NULL;
00035   } else {
00036     indexFile = MPEG2TransportStreamIndexFile::createNew(env, indexFileName);
00037   }
00038   return new MPEG2TransportFileServerMediaSubsession(env, fileName, indexFile,
00039                                                      reuseFirstSource);
00040 }
00041 
00042 MPEG2TransportFileServerMediaSubsession
00043 ::MPEG2TransportFileServerMediaSubsession(UsageEnvironment& env,
00044                                           char const* fileName,
00045                                           MPEG2TransportStreamIndexFile* indexFile,
00046                                           Boolean reuseFirstSource)
00047   : FileServerMediaSubsession(env, fileName, reuseFirstSource),
00048     fIndexFile(indexFile), fDuration(0.0), fClientSessionHashTable(NULL) {
00049   if (fIndexFile != NULL) { // we support 'trick play'
00050     fDuration = fIndexFile->getPlayingDuration();
00051     fClientSessionHashTable = HashTable::create(ONE_WORD_HASH_KEYS);
00052   }
00053 }
00054 
00055 MPEG2TransportFileServerMediaSubsession
00056 ::~MPEG2TransportFileServerMediaSubsession() {
00057   if (fIndexFile != NULL) { // we support 'trick play'
00058     Medium::close(fIndexFile);
00059 
00060     // Clean out the client session hash table:
00061     while (1) {
00062       ClientTrickPlayState* client
00063         = (ClientTrickPlayState*)(fClientSessionHashTable->RemoveNext());
00064       if (client == NULL) break;
00065       delete client;
00066     }
00067     delete fClientSessionHashTable;
00068   }
00069 }
00070 
00071 #define TRANSPORT_PACKET_SIZE 188
00072 #define TRANSPORT_PACKETS_PER_NETWORK_PACKET 7
00073 // The product of these two numbers must be enough to fit within a network packet
00074 
00075 void MPEG2TransportFileServerMediaSubsession
00076 ::startStream(unsigned clientSessionId, void* streamToken, TaskFunc* rtcpRRHandler,
00077               void* rtcpRRHandlerClientData, unsigned short& rtpSeqNum,
00078               unsigned& rtpTimestamp,
00079               ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
00080               void* serverRequestAlternativeByteHandlerClientData) {
00081   if (fIndexFile != NULL) { // we support 'trick play'
00082     ClientTrickPlayState* client = lookupClient(clientSessionId);
00083     if (client != NULL && client->areChangingScale()) {
00084       // First, handle this like a "PAUSE", except that we back up to the previous VSH
00085       client->updateStateOnPlayChange(True);
00086       OnDemandServerMediaSubsession::pauseStream(clientSessionId, streamToken);
00087 
00088       // Then, adjust for the change of scale:
00089       client->updateStateOnScaleChange();
00090     }
00091   }
00092 
00093   // Call the original, default version of this routine:
00094   OnDemandServerMediaSubsession::startStream(clientSessionId, streamToken,
00095                                              rtcpRRHandler, rtcpRRHandlerClientData,
00096                                              rtpSeqNum, rtpTimestamp,
00097                                              serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);
00098 }
00099 
00100 void MPEG2TransportFileServerMediaSubsession
00101 ::pauseStream(unsigned clientSessionId, void* streamToken) {
00102   if (fIndexFile != NULL) { // we support 'trick play'
00103     ClientTrickPlayState* client = lookupClient(clientSessionId);
00104     if (client != NULL) {
00105       client->updateStateOnPlayChange(False);
00106     }
00107   }
00108 
00109   // Call the original, default version of this routine:
00110   OnDemandServerMediaSubsession::pauseStream(clientSessionId, streamToken);
00111 }
00112 
00113 void MPEG2TransportFileServerMediaSubsession
00114 ::seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes) {
00115   // Begin by calling the original, default version of this routine:
00116   OnDemandServerMediaSubsession::seekStream(clientSessionId, streamToken, seekNPT, streamDuration, numBytes);
00117 
00118   // Then, special handling specific to indexed Transport Stream files:
00119   if (fIndexFile != NULL) { // we support 'trick play'
00120     ClientTrickPlayState* client = lookupClient(clientSessionId);
00121     if (client != NULL) {
00122       unsigned long numTSPacketsToStream = client->updateStateFromNPT(seekNPT, streamDuration);
00123       numBytes = numTSPacketsToStream*TRANSPORT_PACKET_SIZE;
00124     }
00125   }
00126 }
00127 
00128 void MPEG2TransportFileServerMediaSubsession
00129 ::setStreamScale(unsigned clientSessionId, void* streamToken, float scale) {
00130   if (fIndexFile != NULL) { // we support 'trick play'
00131     ClientTrickPlayState* client = lookupClient(clientSessionId);
00132     if (client != NULL) {
00133       client->setNextScale(scale); // scale won't take effect until the next "PLAY"
00134     }
00135   }
00136 
00137   // Call the original, default version of this routine:
00138   OnDemandServerMediaSubsession::setStreamScale(clientSessionId, streamToken, scale);
00139 }
00140 
00141 void MPEG2TransportFileServerMediaSubsession
00142 ::deleteStream(unsigned clientSessionId, void*& streamToken) {
00143   if (fIndexFile != NULL) { // we support 'trick play'
00144     ClientTrickPlayState* client = lookupClient(clientSessionId);
00145     if (client != NULL) {
00146       client->updateStateOnPlayChange(False);
00147     }
00148   }
00149 
00150   // Call the original, default version of this routine:
00151   OnDemandServerMediaSubsession::deleteStream(clientSessionId, streamToken);
00152 }
00153 
00154 ClientTrickPlayState* MPEG2TransportFileServerMediaSubsession::newClientTrickPlayState() {
00155   return new ClientTrickPlayState(fIndexFile);
00156 }
00157 
00158 FramedSource* MPEG2TransportFileServerMediaSubsession
00159 ::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
00160   // Create the video source:
00161   unsigned const inputDataChunkSize
00162     = TRANSPORT_PACKETS_PER_NETWORK_PACKET*TRANSPORT_PACKET_SIZE;
00163   ByteStreamFileSource* fileSource
00164     = ByteStreamFileSource::createNew(envir(), fFileName, inputDataChunkSize);
00165   if (fileSource == NULL) return NULL;
00166   fFileSize = fileSource->fileSize();
00167 
00168   // Use the file size and the duration to estimate the stream's bitrate:
00169   if (fFileSize > 0 && fDuration > 0.0) {
00170     estBitrate = (unsigned)((int64_t)fFileSize/(125*fDuration) + 0.5); // kbps, rounded
00171   } else {
00172     estBitrate = 5000; // kbps, estimate
00173   }
00174 
00175 
00176   // Create a framer for the Transport Stream:
00177   MPEG2TransportStreamFramer* framer
00178     = MPEG2TransportStreamFramer::createNew(envir(), fileSource);
00179 
00180   if (fIndexFile != NULL) { // we support 'trick play'
00181     // Keep state for this client (if we don't already have it):
00182     ClientTrickPlayState* client = lookupClient(clientSessionId);
00183     if (client == NULL) {
00184       client = newClientTrickPlayState();
00185       fClientSessionHashTable->Add((char const*)clientSessionId, client);
00186     }
00187     client->setSource(framer);
00188   }
00189 
00190   return framer;
00191 }
00192 
00193 RTPSink* MPEG2TransportFileServerMediaSubsession
00194 ::createNewRTPSink(Groupsock* rtpGroupsock,
00195                    unsigned char /*rtpPayloadTypeIfDynamic*/,
00196                    FramedSource* /*inputSource*/) {
00197   return SimpleRTPSink::createNew(envir(), rtpGroupsock,
00198                                   33, 90000, "video", "MP2T",
00199                                   1, True, False /*no 'M' bit*/);
00200 }
00201 
00202 void MPEG2TransportFileServerMediaSubsession::testScaleFactor(float& scale) {
00203   if (fIndexFile != NULL && fDuration > 0.0) {
00204     // We support any integral scale, other than 0
00205     int iScale = scale < 0.0 ? (int)(scale - 0.5f) : (int)(scale + 0.5f); // round
00206     if (iScale == 0) iScale = 1;
00207     scale = (float)iScale;
00208   } else {
00209     scale = 1.0f;
00210   }
00211 }
00212 
00213 float MPEG2TransportFileServerMediaSubsession::duration() const {
00214   return fDuration;
00215 }
00216 
00217 ClientTrickPlayState* MPEG2TransportFileServerMediaSubsession
00218 ::lookupClient(unsigned clientSessionId) {
00219   return (ClientTrickPlayState*)(fClientSessionHashTable->Lookup((char const*)clientSessionId));
00220 }
00221 
00222 
00224 
00225 ClientTrickPlayState::ClientTrickPlayState(MPEG2TransportStreamIndexFile* indexFile)
00226   : fIndexFile(indexFile),
00227     fOriginalTransportStreamSource(NULL),
00228     fTrickModeFilter(NULL), fTrickPlaySource(NULL),
00229     fFramer(NULL),
00230     fScale(1.0f), fNextScale(1.0f), fNPT(0.0f),
00231     fTSRecordNum(0), fIxRecordNum(0) {
00232 }
00233 
00234 unsigned long ClientTrickPlayState::updateStateFromNPT(double npt, double streamDuration) {
00235   fNPT = (float)npt;
00236   // Map "fNPT" to the corresponding Transport Stream and Index record numbers:
00237   unsigned long tsRecordNum, ixRecordNum;
00238   fIndexFile->lookupTSPacketNumFromNPT(fNPT, tsRecordNum, ixRecordNum);
00239 
00240   updateTSRecordNum();
00241   if (tsRecordNum != fTSRecordNum) {
00242     fTSRecordNum = tsRecordNum;
00243     fIxRecordNum = ixRecordNum;
00244 
00245     // Seek the source to the new record number:
00246     reseekOriginalTransportStreamSource();
00247     // Note: We assume that we're asked to seek only in normal
00248     // (i.e., non trick play) mode, so we don't seek within the trick
00249     // play source (if any).
00250 
00251     fFramer->clearPIDStatusTable();
00252   }
00253 
00254   unsigned long numTSRecordsToStream = 0;
00255   float pcrLimit = 0.0;
00256   if (streamDuration > 0.0) {
00257     // fNPT might have changed when we looked it up in the index file.  Adjust "streamDuration" accordingly:
00258     streamDuration += npt - (double)fNPT;
00259 
00260     if (streamDuration > 0.0) {
00261       // Specify that we want to stream no more data than this.
00262 
00263       if (fNextScale == 1.0f) {
00264         // We'll be streaming from the original file.
00265         // Use the index file to figure out how many Transport Packets we get to stream:
00266         unsigned long toTSRecordNum, toIxRecordNum;    
00267         float toNPT = (float)(fNPT + streamDuration);
00268         fIndexFile->lookupTSPacketNumFromNPT(toNPT, toTSRecordNum, toIxRecordNum);
00269         if (toTSRecordNum > tsRecordNum) { // sanity check
00270           numTSRecordsToStream = toTSRecordNum - tsRecordNum;
00271         }
00272       } else {
00273         // We'll be streaming from the trick play stream.  
00274         // It'd be difficult to figure out how many Transport Packets we need to stream, so instead set a PCR
00275         // limit in the trick play stream.  (We rely upon the fact that PCRs in the trick play stream start at 0.0)
00276         int direction = fNextScale < 0.0 ? -1 : 1;
00277         pcrLimit = (float)(streamDuration/(fNextScale*direction));
00278       }
00279     }
00280   }
00281   fFramer->setNumTSPacketsToStream(numTSRecordsToStream);
00282   fFramer->setPCRLimit(pcrLimit);
00283 
00284   return numTSRecordsToStream;
00285 }
00286 
00287 void ClientTrickPlayState::updateStateOnScaleChange() {
00288   fScale = fNextScale;
00289 
00290   // Change our source objects to reflect the change in scale:
00291   // First, close the existing trick play source (if any):
00292   if (fTrickPlaySource != NULL) {
00293     fTrickModeFilter->forgetInputSource();
00294         // so that the underlying Transport Stream source doesn't get deleted by:
00295     Medium::close(fTrickPlaySource);
00296     fTrickPlaySource = NULL;
00297     fTrickModeFilter = NULL;
00298   }
00299   if (fNextScale != 1.0f) {
00300     // Create a new trick play filter from the original Transport Stream source:
00301     UsageEnvironment& env = fIndexFile->envir(); // alias
00302     fTrickModeFilter = MPEG2TransportStreamTrickModeFilter
00303       ::createNew(env, fOriginalTransportStreamSource, fIndexFile, int(fNextScale));
00304     fTrickModeFilter->seekTo(fTSRecordNum, fIxRecordNum);
00305 
00306     // And generate a Transport Stream from this:
00307     fTrickPlaySource = MPEG2TransportStreamFromESSource::createNew(env);
00308     fTrickPlaySource->addNewVideoSource(fTrickModeFilter, fIndexFile->mpegVersion());
00309 
00310     fFramer->changeInputSource(fTrickPlaySource);
00311   } else {
00312     // Switch back to the original Transport Stream source:
00313     reseekOriginalTransportStreamSource();
00314     fFramer->changeInputSource(fOriginalTransportStreamSource);
00315   }
00316 }
00317 
00318 void ClientTrickPlayState::updateStateOnPlayChange(Boolean reverseToPreviousVSH) {
00319   updateTSRecordNum();
00320   if (fTrickPlaySource == NULL) {
00321     // We were in regular (1x) play. Use the index file to look up the
00322     // index record number and npt from the current transport number:
00323     fIndexFile->lookupPCRFromTSPacketNum(fTSRecordNum, reverseToPreviousVSH, fNPT, fIxRecordNum);
00324   } else {
00325     // We were in trick mode, and so already have the index record number.
00326     // Get the transport record number and npt from this:
00327     fIxRecordNum = fTrickModeFilter->nextIndexRecordNum();
00328     if ((long)fIxRecordNum < 0) fIxRecordNum = 0; // we were at the start of the file
00329     unsigned long transportRecordNum;
00330     float pcr;
00331     u_int8_t offset, size, recordType; // all dummy
00332     if (fIndexFile->readIndexRecordValues(fIxRecordNum, transportRecordNum,
00333                                           offset, size, pcr, recordType)) {
00334       fTSRecordNum = transportRecordNum;
00335       fNPT = pcr;
00336     }
00337   }
00338 }
00339 
00340 void ClientTrickPlayState::setSource(MPEG2TransportStreamFramer* framer) {
00341   fFramer = framer;
00342   fOriginalTransportStreamSource = (ByteStreamFileSource*)(framer->inputSource());
00343 }
00344 
00345 void ClientTrickPlayState::updateTSRecordNum(){
00346   if (fFramer != NULL) fTSRecordNum += (unsigned long)(fFramer->tsPacketCount());
00347 }
00348 
00349 void ClientTrickPlayState::reseekOriginalTransportStreamSource() {
00350   u_int64_t tsRecordNum64 = (u_int64_t)fTSRecordNum;
00351   fOriginalTransportStreamSource->seekToByteAbsolute(tsRecordNum64*TRANSPORT_PACKET_SIZE);
00352 }

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