liveMedia/MPEG1or2VideoStreamDiscreteFramer.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-2012 Live Networks, Inc.  All rights reserved.
00018 // A simplified version of "MPEG1or2VideoStreamFramer" that takes only
00019 // complete, discrete frames (rather than an arbitrary byte stream) as input.
00020 // This avoids the parsing and data copying overhead of the full
00021 // "MPEG1or2VideoStreamFramer".
00022 // Implementation
00023 
00024 #include "MPEG1or2VideoStreamDiscreteFramer.hh"
00025 
00026 MPEG1or2VideoStreamDiscreteFramer*
00027 MPEG1or2VideoStreamDiscreteFramer::createNew(UsageEnvironment& env,
00028                                              FramedSource* inputSource,
00029                                              Boolean iFramesOnly,
00030                                              double vshPeriod) {
00031   // Need to add source type checking here???  #####
00032   return new MPEG1or2VideoStreamDiscreteFramer(env, inputSource,
00033                                                iFramesOnly, vshPeriod);
00034 }
00035 
00036 MPEG1or2VideoStreamDiscreteFramer
00037 ::MPEG1or2VideoStreamDiscreteFramer(UsageEnvironment& env,
00038                                     FramedSource* inputSource,
00039                                     Boolean iFramesOnly, double vshPeriod)
00040   : MPEG1or2VideoStreamFramer(env, inputSource, iFramesOnly, vshPeriod,
00041                               False/*don't create a parser*/),
00042     fLastNonBFrameTemporal_reference(0),
00043     fSavedVSHSize(0), fSavedVSHTimestamp(0.0),
00044     fIFramesOnly(iFramesOnly), fVSHPeriod(vshPeriod) {
00045   fLastNonBFramePresentationTime.tv_sec = 0;
00046   fLastNonBFramePresentationTime.tv_usec = 0;
00047 }
00048 
00049 MPEG1or2VideoStreamDiscreteFramer::~MPEG1or2VideoStreamDiscreteFramer() {
00050 }
00051 
00052 void MPEG1or2VideoStreamDiscreteFramer::doGetNextFrame() {
00053   // Arrange to read data (which should be a complete MPEG-1 or 2 video frame)
00054   // from our data source, directly into the client's input buffer.
00055   // After reading this, we'll do some parsing on the frame.
00056   fInputSource->getNextFrame(fTo, fMaxSize,
00057                              afterGettingFrame, this,
00058                              FramedSource::handleClosure, this);
00059 }
00060 
00061 void MPEG1or2VideoStreamDiscreteFramer
00062 ::afterGettingFrame(void* clientData, unsigned frameSize,
00063                     unsigned numTruncatedBytes,
00064                     struct timeval presentationTime,
00065                     unsigned durationInMicroseconds) {
00066   MPEG1or2VideoStreamDiscreteFramer* source
00067     = (MPEG1or2VideoStreamDiscreteFramer*)clientData;
00068   source->afterGettingFrame1(frameSize, numTruncatedBytes,
00069                              presentationTime, durationInMicroseconds);
00070 }
00071 
00072 static double const frameRateFromCode[] = {
00073   0.0,          // forbidden
00074   24000/1001.0, // approx 23.976
00075   24.0,
00076   25.0,
00077   30000/1001.0, // approx 29.97
00078   30.0,
00079   50.0,
00080   60000/1001.0, // approx 59.94
00081   60.0,
00082   0.0,          // reserved
00083   0.0,          // reserved
00084   0.0,          // reserved
00085   0.0,          // reserved
00086   0.0,          // reserved
00087   0.0,          // reserved
00088   0.0           // reserved
00089 };
00090 
00091 #define MILLION 1000000
00092 
00093 void MPEG1or2VideoStreamDiscreteFramer
00094 ::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
00095                      struct timeval presentationTime,
00096                      unsigned durationInMicroseconds) {
00097   // Check that the first 4 bytes are a system code:
00098   if (frameSize >= 4 && fTo[0] == 0 && fTo[1] == 0 && fTo[2] == 1) {
00099     fPictureEndMarker = True; // Assume that we have a complete 'picture' here
00100 
00101     u_int8_t nextCode = fTo[3];
00102     if (nextCode == 0xB3) { // VIDEO_SEQUENCE_HEADER_START_CODE
00103       // Note the following 'frame rate' code:
00104       if (frameSize >= 8) {
00105         u_int8_t frame_rate_code = fTo[7]&0x0F;
00106         fFrameRate = frameRateFromCode[frame_rate_code];
00107       }
00108 
00109       // Also, save away this Video Sequence Header, in case we need it later:
00110       // First, figure out how big it is:
00111       unsigned vshSize;
00112       for (vshSize = 4; vshSize < frameSize-3; ++vshSize) {
00113         if (fTo[vshSize] == 0 && fTo[vshSize+1] == 0 && fTo[vshSize+2] == 1 &&
00114             (fTo[vshSize+3] == 0xB8 || fTo[vshSize+3] == 0x00)) break;
00115       }
00116       if (vshSize == frameSize-3) vshSize = frameSize; // There was nothing else following it
00117       if (vshSize <= sizeof fSavedVSHBuffer) {
00118         memmove(fSavedVSHBuffer, fTo, vshSize);
00119         fSavedVSHSize = vshSize;
00120         fSavedVSHTimestamp
00121           = presentationTime.tv_sec + presentationTime.tv_usec/(double)MILLION;
00122       }
00123     } else if (nextCode == 0xB8) { // GROUP_START_CODE
00124       // If necessary, insert a saved Video Sequence Header in front of this:
00125       double pts = presentationTime.tv_sec + presentationTime.tv_usec/(double)MILLION;
00126       if (pts > fSavedVSHTimestamp + fVSHPeriod &&
00127           fSavedVSHSize + frameSize <= fMaxSize) {
00128         memmove(&fTo[fSavedVSHSize], &fTo[0], frameSize); // make room for the header
00129         memmove(&fTo[0], fSavedVSHBuffer, fSavedVSHSize); // insert it
00130         frameSize += fSavedVSHSize;
00131         fSavedVSHTimestamp = pts;
00132       }
00133     }
00134 
00135     unsigned i = 3;
00136     if (nextCode == 0xB3 /*VIDEO_SEQUENCE_HEADER_START_CODE*/ ||
00137         nextCode == 0xB8 /*GROUP_START_CODE*/) {
00138       // Skip to the following PICTURE_START_CODE (if any):
00139       for (i += 4; i < frameSize; ++i) {
00140         if (fTo[i] == 0x00 /*PICTURE_START_CODE*/
00141             && fTo[i-1] == 1 && fTo[i-2] == 0 && fTo[i-3] == 0) {
00142           nextCode = fTo[i];
00143           break;
00144         }
00145       }
00146     }
00147 
00148     if (nextCode == 0x00 /*PICTURE_START_CODE*/ && i+2 < frameSize) {
00149       // Get the 'temporal_reference' and 'picture_coding_type' from the
00150       // following 2 bytes:
00151       ++i;
00152       unsigned short temporal_reference = (fTo[i]<<2)|(fTo[i+1]>>6);
00153       unsigned char picture_coding_type = (fTo[i+1]&0x38)>>3;
00154 
00155       // If this is not an "I" frame, but we were asked for "I" frames only, then try again:
00156       if (fIFramesOnly && picture_coding_type != 1) {
00157         doGetNextFrame();
00158         return;
00159       }
00160 
00161       // If this is a "B" frame, then we have to tweak "presentationTime":
00162       if (picture_coding_type == 3/*B*/
00163           && (fLastNonBFramePresentationTime.tv_usec > 0 ||
00164               fLastNonBFramePresentationTime.tv_sec > 0)) {
00165         int trIncrement
00166             = fLastNonBFrameTemporal_reference - temporal_reference;
00167         if (trIncrement < 0) trIncrement += 1024; // field is 10 bits in size
00168 
00169         unsigned usIncrement = fFrameRate == 0.0 ? 0
00170           : (unsigned)((trIncrement*MILLION)/fFrameRate);
00171         unsigned secondsToSubtract = usIncrement/MILLION;
00172         unsigned uSecondsToSubtract = usIncrement%MILLION;
00173 
00174         presentationTime = fLastNonBFramePresentationTime;
00175         if ((unsigned)presentationTime.tv_usec < uSecondsToSubtract) {
00176           presentationTime.tv_usec += MILLION;
00177           if (presentationTime.tv_sec > 0) --presentationTime.tv_sec;
00178         }
00179         presentationTime.tv_usec -= uSecondsToSubtract;
00180         if ((unsigned)presentationTime.tv_sec > secondsToSubtract) {
00181           presentationTime.tv_sec -= secondsToSubtract;
00182         } else {
00183           presentationTime.tv_sec = presentationTime.tv_usec = 0;
00184         }
00185       } else {
00186         fLastNonBFramePresentationTime = presentationTime;
00187         fLastNonBFrameTemporal_reference = temporal_reference;
00188       }
00189     }
00190   }
00191 
00192   // ##### Later:
00193   // - do "iFramesOnly" if requested
00194 
00195   // Complete delivery to the client:
00196   fFrameSize = frameSize;
00197   fNumTruncatedBytes = numTruncatedBytes;
00198   fPresentationTime = presentationTime;
00199   fDurationInMicroseconds = durationInMicroseconds;
00200   afterGetting(this);
00201 }

Generated on Thu May 17 07:11:46 2012 for live by  doxygen 1.5.2