liveMedia/MPEG2TransportStreamTrickModeFilter.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 filter that converts a MPEG Transport Stream file - with corresponding index file
00019 // - to a corresponding Video Elementary Stream.  It also uses a "scale" parameter
00020 // to implement 'trick mode' (fast forward or reverse play, using I-frames) on
00021 // the video stream.
00022 // Implementation
00023 
00024 #include "MPEG2TransportStreamTrickModeFilter.hh"
00025 #include <ByteStreamFileSource.hh>
00026 
00027 // Define the following to be True if we want the output file to have the same frame rate as the original file.
00028 //    (Because the output file contains I-frames only, this means that each I-frame will appear in the output file
00029 //     several times, and therefore the output file's bitrate will be significantly higher than that of the original.)
00030 // Define the following to be False if we want the output file to include each I-frame no more than once.
00031 //    (This means that - except for high 'scale' values - both the output frame rate and the output bit rate
00032 //     will be less than that of the original.)
00033 #define KEEP_ORIGINAL_FRAME_RATE False
00034 
00035 MPEG2TransportStreamTrickModeFilter* MPEG2TransportStreamTrickModeFilter
00036 ::createNew(UsageEnvironment& env, FramedSource* inputSource,
00037             MPEG2TransportStreamIndexFile* indexFile, int scale) {
00038   return new MPEG2TransportStreamTrickModeFilter(env, inputSource, indexFile, scale);
00039 }
00040 
00041 MPEG2TransportStreamTrickModeFilter
00042 ::MPEG2TransportStreamTrickModeFilter(UsageEnvironment& env, FramedSource* inputSource,
00043                                       MPEG2TransportStreamIndexFile* indexFile, int scale)
00044   : FramedFilter(env, inputSource),
00045     fHaveStarted(False), fIndexFile(indexFile), fScale(scale), fDirection(1),
00046     fState(SKIPPING_FRAME), fFrameCount(0),
00047     fNextIndexRecordNum(0), fNextTSPacketNum(0),
00048     fCurrentTSPacketNum((unsigned long)(-1)), fUseSavedFrameNextTime(False) {
00049   if (fScale < 0) { // reverse play
00050     fScale = -fScale;
00051     fDirection = -1;
00052   }
00053 }
00054 
00055 MPEG2TransportStreamTrickModeFilter::~MPEG2TransportStreamTrickModeFilter() {
00056 }
00057 
00058 Boolean MPEG2TransportStreamTrickModeFilter::seekTo(unsigned long tsPacketNumber,
00059                                                     unsigned long indexRecordNumber) {
00060   seekToTransportPacket(tsPacketNumber);
00061   fNextIndexRecordNum = indexRecordNumber;
00062   return True;
00063 }
00064 
00065 #define isIFrameStart(type) ((type) == 0x81/*actually, a VSH*/ || (type) == 0x85/*actually, a SPS*//*for H.264*/)
00066   // This relies upon I-frames always being preceded by a VSH+GOP (for MPEG-2 data) and by a SPS (for H.264 data)
00067 #define isNonIFrameStart(type) ((type) == 0x83 || (type) == 0x88/*for H.264*/)
00068 
00069 void MPEG2TransportStreamTrickModeFilter::doGetNextFrame() {
00070   //  fprintf(stderr, "#####DGNF1\n");
00071   // If our client's buffer size is too small, then deliver
00072   // a 0-byte 'frame', to tell it to process all of the data that it has
00073   // already read, before asking for more data from us:
00074   if (fMaxSize < TRANSPORT_PACKET_SIZE) {
00075     fFrameSize = 0;
00076     afterGetting(this);
00077     return;
00078   }
00079 
00080   while (1) {
00081     // Get the next record from our index file.
00082     // This tells us the type of frame this data is, which Transport Stream packet
00083     // (from the input source) the data comes from, and where in the Transport Stream
00084     // packet it comes from:
00085     u_int8_t recordType;
00086     float recordPCR;
00087     Boolean endOfIndexFile = False;
00088     if (!fIndexFile->readIndexRecordValues(fNextIndexRecordNum,
00089                                            fDesiredTSPacketNum, fDesiredDataOffset,
00090                                            fDesiredDataSize, recordPCR,
00091                                            recordType)) {
00092       // We ran off the end of the index file.  If we're not delivering a
00093       // pre-saved frame, then handle this the same way as if the
00094       // input Transport Stream source ended.
00095       if (fState != DELIVERING_SAVED_FRAME) {
00096         onSourceClosure1();
00097         return;
00098       }
00099       endOfIndexFile = True;
00100     } else if (!fHaveStarted) {
00101       fFirstPCR = recordPCR;
00102       fHaveStarted = True;
00103     }
00104     //    fprintf(stderr, "#####read index record %ld: ts %ld: %c, PCR %f\n", fNextIndexRecordNum, fDesiredTSPacketNum, isIFrameStart(recordType) ? 'I' : isNonIFrameStart(recordType) ? 'j' : 'x', recordPCR);
00105     fNextIndexRecordNum
00106       += (fState == DELIVERING_SAVED_FRAME) ? 1 : fDirection;
00107 
00108     // Handle this index record, depending on the record type and our current state:
00109     switch (fState) {
00110     case SKIPPING_FRAME:
00111     case SAVING_AND_DELIVERING_FRAME: {
00112       //      if (fState == SKIPPING_FRAME) fprintf(stderr, "\tSKIPPING_FRAME\n"); else fprintf(stderr, "\tSAVING_AND_DELIVERING_FRAME\n");//#####
00113       if (isIFrameStart(recordType)) {
00114         // Save a record of this frame:
00115         fSavedFrameIndexRecordStart = fNextIndexRecordNum - fDirection;
00116         fUseSavedFrameNextTime = True;
00117         //      fprintf(stderr, "\trecording\n");//#####
00118         if ((fFrameCount++)%fScale == 0 && fUseSavedFrameNextTime) {
00119           // A frame is due now.
00120           fFrameCount = 1; // reset to avoid overflow
00121           if (fDirection > 0) {
00122             // Begin delivering this frame, as we're scanning it:
00123             fState = SAVING_AND_DELIVERING_FRAME;
00124             //      fprintf(stderr, "\tdelivering\n");//#####
00125             fDesiredDataPCR = recordPCR; // use this frame's PCR
00126             attemptDeliveryToClient();
00127             return;
00128           } else {
00129             // Deliver this frame, then resume normal scanning:
00130             // (This relies on the index records having begun with an I-frame.)
00131             fState = DELIVERING_SAVED_FRAME;
00132             fSavedSequentialIndexRecordNum = fNextIndexRecordNum;
00133             fDesiredDataPCR = recordPCR;
00134             // use this frame's (not the saved frame's) PCR
00135             fNextIndexRecordNum = fSavedFrameIndexRecordStart;
00136             //      fprintf(stderr, "\tbeginning delivery of saved frame\n");//#####
00137           }
00138         } else {
00139           // No frame is needed now:
00140           fState = SKIPPING_FRAME;
00141         }
00142       } else if (isNonIFrameStart(recordType)) {
00143         if ((fFrameCount++)%fScale == 0 && fUseSavedFrameNextTime) {
00144           // A frame is due now, so begin delivering the one that we had saved:
00145           // (This relies on the index records having begun with an I-frame.)
00146           fFrameCount = 1; // reset to avoid overflow
00147           fState = DELIVERING_SAVED_FRAME;
00148           fSavedSequentialIndexRecordNum = fNextIndexRecordNum;
00149           fDesiredDataPCR = recordPCR;
00150           // use this frame's (not the saved frame's) PCR
00151           fNextIndexRecordNum = fSavedFrameIndexRecordStart;
00152           //      fprintf(stderr, "\tbeginning delivery of saved frame\n");//#####
00153         } else {
00154           // No frame is needed now:
00155           fState = SKIPPING_FRAME;
00156         }
00157       } else {
00158         // Not the start of a frame, but deliver it, if it's needed:
00159         if (fState == SAVING_AND_DELIVERING_FRAME) {
00160           //      fprintf(stderr, "\tdelivering\n");//#####
00161           fDesiredDataPCR = recordPCR; // use this frame's PCR
00162           attemptDeliveryToClient();
00163           return;
00164         }
00165       }
00166       break;
00167     }
00168     case DELIVERING_SAVED_FRAME: {
00169       //      fprintf(stderr, "\tDELIVERING_SAVED_FRAME\n");//#####
00170       if (endOfIndexFile
00171           || (isIFrameStart(recordType)
00172               && fNextIndexRecordNum-1 != fSavedFrameIndexRecordStart)
00173           || isNonIFrameStart(recordType)) {
00174         //      fprintf(stderr, "\tended delivery of saved frame\n");//#####
00175         // We've reached the end of the saved frame, so revert to the
00176         // original sequence of index records:
00177         fNextIndexRecordNum = fSavedSequentialIndexRecordNum;
00178         fUseSavedFrameNextTime = KEEP_ORIGINAL_FRAME_RATE;
00179         fState = SKIPPING_FRAME;
00180       } else {
00181         // Continue delivering:
00182         //      fprintf(stderr, "\tdelivering\n");//#####
00183         attemptDeliveryToClient();
00184         return;
00185       }
00186       break;
00187     }
00188     }
00189   }
00190 }
00191 
00192 void MPEG2TransportStreamTrickModeFilter::doStopGettingFrames() {
00193   FramedFilter::doStopGettingFrames();
00194   fIndexFile->stopReading();
00195 }
00196 
00197 void MPEG2TransportStreamTrickModeFilter::attemptDeliveryToClient() {
00198   if (fCurrentTSPacketNum == fDesiredTSPacketNum) {
00199     //    fprintf(stderr, "\t\tdelivering ts %d:%d, %d bytes, PCR %f\n", fCurrentTSPacketNum, fDesiredDataOffset, fDesiredDataSize, fDesiredDataPCR);//#####
00200     // We already have the Transport Packet that we want.  Deliver its data:
00201     memmove(fTo, &fInputBuffer[fDesiredDataOffset], fDesiredDataSize);
00202     fFrameSize = fDesiredDataSize;
00203     float deliveryPCR = fDirection*(fDesiredDataPCR - fFirstPCR)/fScale;
00204     if (deliveryPCR < 0.0) deliveryPCR = 0.0;
00205     fPresentationTime.tv_sec = (unsigned long)deliveryPCR;
00206     fPresentationTime.tv_usec
00207       = (unsigned long)((deliveryPCR - fPresentationTime.tv_sec)*1000000.0f);
00208     //    fprintf(stderr, "#####DGNF9\n");
00209 
00210     afterGetting(this);
00211   } else {
00212     // Arrange to read the Transport Packet that we want:
00213     readTransportPacket(fDesiredTSPacketNum);
00214   }
00215 }
00216 
00217 void MPEG2TransportStreamTrickModeFilter::seekToTransportPacket(unsigned long tsPacketNum) {
00218   if (tsPacketNum == fNextTSPacketNum) return; // we're already there
00219 
00220   ByteStreamFileSource* tsFile = (ByteStreamFileSource*)fInputSource;
00221   u_int64_t tsPacketNum64 = (u_int64_t)tsPacketNum;
00222   tsFile->seekToByteAbsolute(tsPacketNum64*TRANSPORT_PACKET_SIZE);
00223 
00224   fNextTSPacketNum = tsPacketNum;
00225 }
00226 
00227 void MPEG2TransportStreamTrickModeFilter::readTransportPacket(unsigned long tsPacketNum) {
00228   seekToTransportPacket(tsPacketNum);
00229   fInputSource->getNextFrame(fInputBuffer, TRANSPORT_PACKET_SIZE,
00230                              afterGettingFrame, this,
00231                              onSourceClosure, this);
00232 }
00233 
00234 void MPEG2TransportStreamTrickModeFilter
00235 ::afterGettingFrame(void* clientData, unsigned frameSize,
00236                     unsigned /*numTruncatedBytes*/,
00237                     struct timeval presentationTime,
00238                     unsigned /*durationInMicroseconds*/) {
00239   MPEG2TransportStreamTrickModeFilter* filter = (MPEG2TransportStreamTrickModeFilter*)clientData;
00240   filter->afterGettingFrame1(frameSize);
00241 }
00242 
00243 void MPEG2TransportStreamTrickModeFilter::afterGettingFrame1(unsigned frameSize) {
00244   if (frameSize != TRANSPORT_PACKET_SIZE) {
00245     // Treat this as if the input source ended:
00246     onSourceClosure1();
00247     return;
00248   }
00249 
00250   fCurrentTSPacketNum = fNextTSPacketNum; // i.e., the one that we just read
00251   ++fNextTSPacketNum;
00252 
00253   // Attempt deliver again:
00254   attemptDeliveryToClient();
00255 }
00256 
00257 void MPEG2TransportStreamTrickModeFilter::onSourceClosure(void* clientData) {
00258   MPEG2TransportStreamTrickModeFilter* filter = (MPEG2TransportStreamTrickModeFilter*)clientData;
00259   filter->onSourceClosure1();
00260 }
00261 
00262 void MPEG2TransportStreamTrickModeFilter::onSourceClosure1() {
00263   fIndexFile->stopReading();
00264   FramedSource::handleClosure(this);
00265 }

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