liveMedia/MPEG1or2VideoStreamFramer.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 filter that breaks up an MPEG 1 or 2 video elementary stream into
00019 //   frames for: Video_Sequence_Header, GOP_Header, Picture_Header
00020 // Implementation
00021 
00022 #include "MPEG1or2VideoStreamFramer.hh"
00023 #include "MPEGVideoStreamParser.hh"
00024 #include <string.h>
00025 
00027 
00028 // An enum representing the current state of the parser:
00029 enum MPEGParseState {
00030   PARSING_VIDEO_SEQUENCE_HEADER,
00031   PARSING_VIDEO_SEQUENCE_HEADER_SEEN_CODE,
00032   PARSING_GOP_HEADER,
00033   PARSING_GOP_HEADER_SEEN_CODE,
00034   PARSING_PICTURE_HEADER,
00035   PARSING_SLICE
00036 };
00037 
00038 #define VSH_MAX_SIZE 1000
00039 
00040 class MPEG1or2VideoStreamParser: public MPEGVideoStreamParser {
00041 public:
00042   MPEG1or2VideoStreamParser(MPEG1or2VideoStreamFramer* usingSource,
00043                         FramedSource* inputSource,
00044                         Boolean iFramesOnly, double vshPeriod);
00045   virtual ~MPEG1or2VideoStreamParser();
00046 
00047 private: // redefined virtual functions:
00048   virtual void flushInput();
00049   virtual unsigned parse();
00050 
00051 private:
00052   void reset();
00053 
00054   MPEG1or2VideoStreamFramer* usingSource() {
00055     return (MPEG1or2VideoStreamFramer*)fUsingSource;
00056   }
00057   void setParseState(MPEGParseState parseState);
00058 
00059   unsigned parseVideoSequenceHeader(Boolean haveSeenStartCode);
00060   unsigned parseGOPHeader(Boolean haveSeenStartCode);
00061   unsigned parsePictureHeader();
00062   unsigned parseSlice();
00063 
00064 private:
00065   MPEGParseState fCurrentParseState;
00066   unsigned fPicturesSinceLastGOP;
00067       // can be used to compute timestamp for a video_sequence_header
00068   unsigned short fCurPicTemporalReference;
00069       // used to compute slice timestamp
00070   unsigned char fCurrentSliceNumber; // set when parsing a slice
00071 
00072   // A saved copy of the most recently seen 'video_sequence_header',
00073   // in case we need to insert it into the stream periodically:
00074   unsigned char fSavedVSHBuffer[VSH_MAX_SIZE];
00075   unsigned fSavedVSHSize;
00076   double fSavedVSHTimestamp;
00077   double fVSHPeriod;
00078   Boolean fIFramesOnly, fSkippingCurrentPicture;
00079 
00080   void saveCurrentVSH();
00081   Boolean needToUseSavedVSH();
00082   unsigned useSavedVSH(); // returns the size of the saved VSH
00083 };
00084 
00085 
00087 
00088 MPEG1or2VideoStreamFramer::MPEG1or2VideoStreamFramer(UsageEnvironment& env,
00089                                                      FramedSource* inputSource,
00090                                                      Boolean iFramesOnly,
00091                                                      double vshPeriod,
00092                                                      Boolean createParser)
00093   : MPEGVideoStreamFramer(env, inputSource) {
00094   fParser = createParser
00095     ? new MPEG1or2VideoStreamParser(this, inputSource,
00096                                     iFramesOnly, vshPeriod)
00097     : NULL;
00098 }
00099 
00100 MPEG1or2VideoStreamFramer::~MPEG1or2VideoStreamFramer() {
00101 }
00102 
00103 MPEG1or2VideoStreamFramer*
00104 MPEG1or2VideoStreamFramer::createNew(UsageEnvironment& env,
00105                                  FramedSource* inputSource,
00106                                  Boolean iFramesOnly,
00107                                  double vshPeriod) {
00108   // Need to add source type checking here???  #####
00109   return new MPEG1or2VideoStreamFramer(env, inputSource, iFramesOnly, vshPeriod);
00110 }
00111 
00112 double MPEG1or2VideoStreamFramer::getCurrentPTS() const {
00113   return fPresentationTime.tv_sec + fPresentationTime.tv_usec/1000000.0;
00114 }
00115 
00116 Boolean MPEG1or2VideoStreamFramer::isMPEG1or2VideoStreamFramer() const {
00117   return True;
00118 }
00119 
00121 
00122 MPEG1or2VideoStreamParser
00123 ::MPEG1or2VideoStreamParser(MPEG1or2VideoStreamFramer* usingSource,
00124                         FramedSource* inputSource,
00125                         Boolean iFramesOnly, double vshPeriod)
00126   : MPEGVideoStreamParser(usingSource, inputSource),
00127     fCurrentParseState(PARSING_VIDEO_SEQUENCE_HEADER),
00128     fVSHPeriod(vshPeriod), fIFramesOnly(iFramesOnly) {
00129   reset();
00130 }
00131 
00132 MPEG1or2VideoStreamParser::~MPEG1or2VideoStreamParser() {
00133 }
00134 
00135 void MPEG1or2VideoStreamParser::setParseState(MPEGParseState parseState) {
00136   fCurrentParseState = parseState;
00137   MPEGVideoStreamParser::setParseState();
00138 }
00139 
00140 void MPEG1or2VideoStreamParser::reset() {
00141   fPicturesSinceLastGOP = 0;
00142   fCurPicTemporalReference = 0;
00143   fCurrentSliceNumber = 0;
00144   fSavedVSHSize = 0;
00145   fSkippingCurrentPicture = False;
00146 }
00147 
00148 void MPEG1or2VideoStreamParser::flushInput() {
00149   reset();
00150   StreamParser::flushInput();
00151   if (fCurrentParseState != PARSING_VIDEO_SEQUENCE_HEADER) {
00152     setParseState(PARSING_GOP_HEADER); // start from the next GOP
00153   }
00154 }
00155 
00156 unsigned MPEG1or2VideoStreamParser::parse() {
00157   try {
00158     switch (fCurrentParseState) {
00159     case PARSING_VIDEO_SEQUENCE_HEADER: {
00160       return parseVideoSequenceHeader(False);
00161     }
00162     case PARSING_VIDEO_SEQUENCE_HEADER_SEEN_CODE: {
00163       return parseVideoSequenceHeader(True);
00164     }
00165     case PARSING_GOP_HEADER: {
00166       return parseGOPHeader(False);
00167     }
00168     case PARSING_GOP_HEADER_SEEN_CODE: {
00169       return parseGOPHeader(True);
00170     }
00171     case PARSING_PICTURE_HEADER: {
00172       return parsePictureHeader();
00173     }
00174     case PARSING_SLICE: {
00175       return parseSlice();
00176     }
00177     default: {
00178       return 0; // shouldn't happen
00179     }
00180     }
00181   } catch (int /*e*/) {
00182 #ifdef DEBUG
00183     fprintf(stderr, "MPEG1or2VideoStreamParser::parse() EXCEPTION (This is normal behavior - *not* an error)\n");
00184 #endif
00185     return 0;  // the parsing got interrupted
00186   }
00187 }
00188 
00189 void MPEG1or2VideoStreamParser::saveCurrentVSH() {
00190   unsigned frameSize = curFrameSize();
00191   if (frameSize > sizeof fSavedVSHBuffer) return; // too big to save
00192 
00193   memmove(fSavedVSHBuffer, fStartOfFrame, frameSize);
00194   fSavedVSHSize = frameSize;
00195   fSavedVSHTimestamp = usingSource()->getCurrentPTS();
00196 }
00197 
00198 Boolean MPEG1or2VideoStreamParser::needToUseSavedVSH() {
00199   return usingSource()->getCurrentPTS() > fSavedVSHTimestamp+fVSHPeriod
00200     && fSavedVSHSize > 0;
00201 }
00202 
00203 unsigned MPEG1or2VideoStreamParser::useSavedVSH() {
00204   unsigned bytesToUse = fSavedVSHSize;
00205   unsigned maxBytesToUse = fLimit - fStartOfFrame;
00206   if (bytesToUse > maxBytesToUse) bytesToUse = maxBytesToUse;
00207 
00208   memmove(fStartOfFrame, fSavedVSHBuffer, bytesToUse);
00209 
00210   // Also reset the saved timestamp:
00211   fSavedVSHTimestamp = usingSource()->getCurrentPTS();
00212 
00213 #ifdef DEBUG
00214   fprintf(stderr, "used saved video_sequence_header (%d bytes)\n", bytesToUse);
00215 #endif
00216   return bytesToUse;
00217 }
00218 
00219 #define VIDEO_SEQUENCE_HEADER_START_CODE 0x000001B3
00220 #define GROUP_START_CODE                 0x000001B8
00221 #define PICTURE_START_CODE               0x00000100
00222 #define SEQUENCE_END_CODE                0x000001B7
00223 
00224 static double const frameRateFromCode[] = {
00225   0.0,          // forbidden
00226   24000/1001.0, // approx 23.976
00227   24.0,
00228   25.0,
00229   30000/1001.0, // approx 29.97
00230   30.0,
00231   50.0,
00232   60000/1001.0, // approx 59.94
00233   60.0,
00234   0.0,          // reserved
00235   0.0,          // reserved
00236   0.0,          // reserved
00237   0.0,          // reserved
00238   0.0,          // reserved
00239   0.0,          // reserved
00240   0.0           // reserved
00241 };
00242 
00243 unsigned MPEG1or2VideoStreamParser
00244 ::parseVideoSequenceHeader(Boolean haveSeenStartCode) {
00245 #ifdef DEBUG
00246   fprintf(stderr, "parsing video sequence header\n");
00247 #endif
00248   unsigned first4Bytes;
00249   if (!haveSeenStartCode) {
00250     while ((first4Bytes = test4Bytes()) != VIDEO_SEQUENCE_HEADER_START_CODE) {
00251 #ifdef DEBUG
00252       fprintf(stderr, "ignoring non video sequence header: 0x%08x\n", first4Bytes);
00253 #endif
00254       get1Byte(); setParseState(PARSING_VIDEO_SEQUENCE_HEADER);
00255           // ensures we progress over bad data
00256     }
00257     first4Bytes = get4Bytes();
00258   } else {
00259     // We've already seen the start code
00260     first4Bytes = VIDEO_SEQUENCE_HEADER_START_CODE;
00261   }
00262   save4Bytes(first4Bytes);
00263 
00264   // Next, extract the size and rate parameters from the next 8 bytes
00265   unsigned paramWord1 = get4Bytes();
00266   save4Bytes(paramWord1);
00267   unsigned next4Bytes = get4Bytes();
00268 #ifdef DEBUG
00269   unsigned short horizontal_size_value   = (paramWord1&0xFFF00000)>>(32-12);
00270   unsigned short vertical_size_value     = (paramWord1&0x000FFF00)>>8;
00271   unsigned char aspect_ratio_information = (paramWord1&0x000000F0)>>4;
00272 #endif
00273   unsigned char frame_rate_code          = (paramWord1&0x0000000F);
00274   usingSource()->fFrameRate = frameRateFromCode[frame_rate_code];
00275 #ifdef DEBUG
00276   unsigned bit_rate_value                = (next4Bytes&0xFFFFC000)>>(32-18);
00277   unsigned vbv_buffer_size_value         = (next4Bytes&0x00001FF8)>>3;
00278   fprintf(stderr, "horizontal_size_value: %d, vertical_size_value: %d, aspect_ratio_information: %d, frame_rate_code: %d (=>%f fps), bit_rate_value: %d (=>%d bps), vbv_buffer_size_value: %d\n", horizontal_size_value, vertical_size_value, aspect_ratio_information, frame_rate_code, usingSource()->fFrameRate, bit_rate_value, bit_rate_value*400, vbv_buffer_size_value);
00279 #endif
00280 
00281   // Now, copy all bytes that we see, up until we reach a GROUP_START_CODE
00282   // or a PICTURE_START_CODE:
00283   do {
00284     saveToNextCode(next4Bytes);
00285   } while (next4Bytes != GROUP_START_CODE && next4Bytes != PICTURE_START_CODE);
00286 
00287   setParseState((next4Bytes == GROUP_START_CODE)
00288                 ? PARSING_GOP_HEADER_SEEN_CODE : PARSING_PICTURE_HEADER);
00289 
00290   // Compute this frame's timestamp by noting how many pictures we've seen
00291   // since the last GOP header:
00292   usingSource()->computePresentationTime(fPicturesSinceLastGOP);
00293 
00294   // Save this video_sequence_header, in case we need to insert a copy
00295   // into the stream later:
00296   saveCurrentVSH();
00297 
00298   return curFrameSize();
00299 }
00300 
00301 unsigned MPEG1or2VideoStreamParser::parseGOPHeader(Boolean haveSeenStartCode) {
00302   // First check whether we should insert a previously-saved
00303   // 'video_sequence_header' here:
00304   if (needToUseSavedVSH()) return useSavedVSH();
00305 
00306 #ifdef DEBUG
00307   fprintf(stderr, "parsing GOP header\n");
00308 #endif
00309   unsigned first4Bytes;
00310   if (!haveSeenStartCode) {
00311     while ((first4Bytes = test4Bytes()) != GROUP_START_CODE) {
00312 #ifdef DEBUG
00313       fprintf(stderr, "ignoring non GOP start code: 0x%08x\n", first4Bytes);
00314 #endif
00315       get1Byte(); setParseState(PARSING_GOP_HEADER);
00316           // ensures we progress over bad data
00317     }
00318     first4Bytes = get4Bytes();
00319   } else {
00320     // We've already seen the GROUP_START_CODE
00321     first4Bytes = GROUP_START_CODE;
00322   }
00323   save4Bytes(first4Bytes);
00324 
00325   // Next, extract the (25-bit) time code from the next 4 bytes:
00326   unsigned next4Bytes = get4Bytes();
00327   unsigned time_code = (next4Bytes&0xFFFFFF80)>>(32-25);
00328 #if defined(DEBUG) || defined(DEBUG_TIMESTAMPS)
00329   Boolean drop_frame_flag     = (time_code&0x01000000) != 0;
00330 #endif
00331   unsigned time_code_hours    = (time_code&0x00F80000)>>19;
00332   unsigned time_code_minutes  = (time_code&0x0007E000)>>13;
00333   unsigned time_code_seconds  = (time_code&0x00000FC0)>>6;
00334   unsigned time_code_pictures = (time_code&0x0000003F);
00335 #if defined(DEBUG) || defined(DEBUG_TIMESTAMPS)
00336   fprintf(stderr, "time_code: 0x%07x, drop_frame %d, hours %d, minutes %d, seconds %d, pictures %d\n", time_code, drop_frame_flag, time_code_hours, time_code_minutes, time_code_seconds, time_code_pictures);
00337 #endif
00338 #ifdef DEBUG
00339   Boolean closed_gop  = (next4Bytes&0x00000040) != 0;
00340   Boolean broken_link = (next4Bytes&0x00000020) != 0;
00341   fprintf(stderr, "closed_gop: %d, broken_link: %d\n", closed_gop, broken_link);
00342 #endif
00343 
00344   // Now, copy all bytes that we see, up until we reach a PICTURE_START_CODE:
00345   do {
00346     saveToNextCode(next4Bytes);
00347   } while (next4Bytes != PICTURE_START_CODE);
00348 
00349   // Record the time code:
00350   usingSource()->setTimeCode(time_code_hours, time_code_minutes,
00351                              time_code_seconds, time_code_pictures,
00352                              fPicturesSinceLastGOP);
00353 
00354   fPicturesSinceLastGOP = 0;
00355 
00356   // Compute this frame's timestamp:
00357   usingSource()->computePresentationTime(0);
00358 
00359   setParseState(PARSING_PICTURE_HEADER);
00360 
00361   return curFrameSize();
00362 }
00363 
00364 inline Boolean isSliceStartCode(unsigned fourBytes) {
00365   if ((fourBytes&0xFFFFFF00) != 0x00000100) return False;
00366 
00367   unsigned char lastByte = fourBytes&0xFF;
00368   return lastByte <= 0xAF && lastByte >= 1;
00369 }
00370 
00371 unsigned MPEG1or2VideoStreamParser::parsePictureHeader() {
00372 #ifdef DEBUG
00373   fprintf(stderr, "parsing picture header\n");
00374 #endif
00375   // Note that we've already read the PICTURE_START_CODE
00376   // Next, extract the temporal reference from the next 4 bytes:
00377   unsigned next4Bytes = get4Bytes();
00378   unsigned short temporal_reference = (next4Bytes&0xFFC00000)>>(32-10);
00379   unsigned char picture_coding_type = (next4Bytes&0x00380000)>>19;
00380 #ifdef DEBUG
00381   unsigned short vbv_delay          = (next4Bytes&0x0007FFF8)>>3;
00382   fprintf(stderr, "temporal_reference: %d, picture_coding_type: %d, vbv_delay: %d\n", temporal_reference, picture_coding_type, vbv_delay);
00383 #endif
00384 
00385   fSkippingCurrentPicture = fIFramesOnly && picture_coding_type != 1;
00386   if (fSkippingCurrentPicture) {
00387     // Skip all bytes that we see, up until we reach a slice_start_code:
00388     do {
00389       skipToNextCode(next4Bytes);
00390     } while (!isSliceStartCode(next4Bytes));
00391   } else {
00392     // Save the PICTURE_START_CODE that we've already read:
00393     save4Bytes(PICTURE_START_CODE);
00394 
00395     // Copy all bytes that we see, up until we reach a slice_start_code:
00396     do {
00397       saveToNextCode(next4Bytes);
00398     } while (!isSliceStartCode(next4Bytes));
00399   }
00400 
00401   setParseState(PARSING_SLICE);
00402 
00403   fCurrentSliceNumber = next4Bytes&0xFF;
00404 
00405   // Record the temporal reference:
00406   fCurPicTemporalReference = temporal_reference;
00407 
00408   // Compute this frame's timestamp:
00409   usingSource()->computePresentationTime(fCurPicTemporalReference);
00410 
00411   if (fSkippingCurrentPicture) {
00412     return parse(); // try again, until we get a non-skipped frame
00413   } else {
00414     return curFrameSize();
00415   }
00416 }
00417 
00418 unsigned MPEG1or2VideoStreamParser::parseSlice() {
00419   // Note that we've already read the slice_start_code:
00420   unsigned next4Bytes = PICTURE_START_CODE|fCurrentSliceNumber;
00421 #ifdef DEBUG_SLICE
00422   fprintf(stderr, "parsing slice: 0x%08x\n", next4Bytes);
00423 #endif
00424 
00425   if (fSkippingCurrentPicture) {
00426     // Skip all bytes that we see, up until we reach a code of some sort:
00427     skipToNextCode(next4Bytes);
00428   } else {
00429     // Copy all bytes that we see, up until we reach a code of some sort:
00430     saveToNextCode(next4Bytes);
00431   }
00432 
00433   // The next thing to parse depends on the code that we just saw:
00434   if (isSliceStartCode(next4Bytes)) { // common case
00435     setParseState(PARSING_SLICE);
00436     fCurrentSliceNumber = next4Bytes&0xFF;
00437   } else {
00438     // Because we don't see any more slices, we are assumed to have ended
00439     // the current picture:
00440     ++fPicturesSinceLastGOP;
00441     ++usingSource()->fPictureCount;
00442     usingSource()->fPictureEndMarker = True; // HACK #####
00443 
00444     switch (next4Bytes) {
00445     case SEQUENCE_END_CODE: {
00446       setParseState(PARSING_VIDEO_SEQUENCE_HEADER);
00447       break;
00448     }
00449     case VIDEO_SEQUENCE_HEADER_START_CODE: {
00450       setParseState(PARSING_VIDEO_SEQUENCE_HEADER_SEEN_CODE);
00451       break;
00452     }
00453     case GROUP_START_CODE: {
00454       setParseState(PARSING_GOP_HEADER_SEEN_CODE);
00455       break;
00456     }
00457     case PICTURE_START_CODE: {
00458       setParseState(PARSING_PICTURE_HEADER);
00459       break;
00460     }
00461     default: {
00462       usingSource()->envir() << "MPEG1or2VideoStreamParser::parseSlice(): Saw unexpected code "
00463                             << (void*)next4Bytes << "\n";
00464       setParseState(PARSING_SLICE); // the safest way to recover...
00465       break;
00466     }
00467     }
00468   }
00469 
00470   // Compute this frame's timestamp:
00471   usingSource()->computePresentationTime(fCurPicTemporalReference);
00472 
00473   if (fSkippingCurrentPicture) {
00474     return parse(); // try again, until we get a non-skipped frame
00475   } else {
00476     return curFrameSize();
00477   }
00478 }

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