liveMedia/MPEG2IndexFromTransportStream.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 produces a sequence of I-frame indices from a MPEG-2 Transport Stream
00019 // Implementation
00020 
00021 #include "MPEG2IndexFromTransportStream.hh"
00022 
00024 
00025 enum RecordType {
00026   RECORD_UNPARSED = 0,
00027   RECORD_VSH = 1, // a MPEG Video Sequence Header
00028   RECORD_GOP = 2,
00029   RECORD_PIC_NON_IFRAME = 3, // includes slices
00030   RECORD_PIC_IFRAME = 4, // includes slices
00031   RECORD_NAL_SPS = 5, // H.264  
00032   RECORD_NAL_PPS = 6, // H.264  
00033   RECORD_NAL_SEI = 7, // H.264  
00034   RECORD_NAL_NON_IFRAME = 8, // H.264   
00035   RECORD_NAL_IFRAME = 9, // H.264       
00036   RECORD_NAL_OTHER = 10, // H.264       
00037   RECORD_JUNK
00038 };
00039 
00040 class IndexRecord {
00041 public:
00042   IndexRecord(u_int8_t startOffset, u_int8_t size,
00043               unsigned long transportPacketNumber, float pcr);
00044   virtual ~IndexRecord();
00045 
00046   RecordType& recordType() { return fRecordType; }
00047   void setFirstFlag() { fRecordType = (RecordType)(((u_int8_t)fRecordType) | 0x80); }
00048   u_int8_t startOffset() const { return fStartOffset; }
00049   u_int8_t& size() { return fSize; }
00050   float pcr() const { return fPCR; }
00051   unsigned long transportPacketNumber() const { return fTransportPacketNumber; }
00052 
00053   IndexRecord* next() const { return fNext; }
00054   void addAfter(IndexRecord* prev);
00055   void unlink();
00056 
00057 private:
00058   // Index records are maintained in a doubly-linked list:
00059   IndexRecord* fNext;
00060   IndexRecord* fPrev;
00061 
00062   RecordType fRecordType;
00063   u_int8_t fStartOffset; // within the Transport Stream packet
00064   u_int8_t fSize; // in bytes, following "fStartOffset".
00065   // Note: fStartOffset + fSize <= TRANSPORT_PACKET_SIZE
00066   float fPCR;
00067   unsigned long fTransportPacketNumber;
00068 };
00069 
00070 #ifdef DEBUG
00071 static char const* recordTypeStr[] = {
00072   "UNPARSED",
00073   "VSH",
00074   "GOP",
00075   "PIC(non-I-frame)",
00076   "PIC(I-frame)",
00077   "SPS (H.264)",
00078   "PPS (H.264)",
00079   "SEI (H.264)",
00080   "H.264 non-I-frame",
00081   "H.264 I-frame",
00082   "other NAL unit (H.264)",
00083   "JUNK"
00084 };
00085 UsageEnvironment& operator<<(UsageEnvironment& env, IndexRecord& r) {
00086   return env << "[" << ((r.recordType()&0x80) != 0 ? "1" : "")
00087              << recordTypeStr[r.recordType()&0x7F] << ":"
00088              << (unsigned)r.transportPacketNumber() << ":" << r.startOffset()
00089              << "(" << r.size() << ")@" << r.pcr() << "]";
00090 }
00091 #endif
00092 
00093 
00095 
00096 MPEG2IFrameIndexFromTransportStream*
00097 MPEG2IFrameIndexFromTransportStream::createNew(UsageEnvironment& env,
00098                                                FramedSource* inputSource) {
00099   return new MPEG2IFrameIndexFromTransportStream(env, inputSource);
00100 }
00101 
00102 // The largest expected frame size (in bytes):
00103 #define MAX_FRAME_SIZE 400000
00104 
00105 // Make our parse buffer twice as large as this, to ensure that at least one
00106 // complete frame will fit inside it:
00107 #define PARSE_BUFFER_SIZE (2*MAX_FRAME_SIZE)
00108 
00109 // The PID used for the PAT (as defined in the MPEG Transport Stream standard):
00110 #define PAT_PID 0
00111 
00112 MPEG2IFrameIndexFromTransportStream
00113 ::MPEG2IFrameIndexFromTransportStream(UsageEnvironment& env,
00114                                       FramedSource* inputSource)
00115   : FramedFilter(env, inputSource),
00116     fIsH264(False), fInputTransportPacketCounter((unsigned)-1), fClosureNumber(0),
00117     fLastContinuityCounter(~0),
00118     fFirstPCR(0.0), fLastPCR(0.0), fHaveSeenFirstPCR(False),
00119     fPMT_PID(0x10), fVideo_PID(0xE0), // default values
00120     fParseBufferSize(PARSE_BUFFER_SIZE),
00121     fParseBufferFrameStart(0), fParseBufferParseEnd(4), fParseBufferDataEnd(0),
00122     fHeadIndexRecord(NULL), fTailIndexRecord(NULL) {
00123   fParseBuffer = new unsigned char[fParseBufferSize];
00124 }
00125 
00126 MPEG2IFrameIndexFromTransportStream::~MPEG2IFrameIndexFromTransportStream() {
00127   delete fHeadIndexRecord;
00128   delete[] fParseBuffer;
00129 }
00130 
00131 void MPEG2IFrameIndexFromTransportStream::doGetNextFrame() {
00132   // Begin by trying to deliver an index record (for an already-parsed frame)
00133   // to the client:
00134   if (deliverIndexRecord()) return;
00135 
00136   // No more index records are left to deliver, so try to parse a new frame:
00137   if (parseFrame()) { // success - try again
00138     doGetNextFrame();
00139     return;
00140   }
00141 
00142   // We need to read some more Transport Stream packets.  Check whether we have room:
00143   if (fParseBufferSize - fParseBufferDataEnd < TRANSPORT_PACKET_SIZE) {
00144     // There's no room left.  Compact the buffer, and check again:
00145     compactParseBuffer();
00146     if (fParseBufferSize - fParseBufferDataEnd < TRANSPORT_PACKET_SIZE) {
00147       envir() << "ERROR: parse buffer full; increase MAX_FRAME_SIZE\n";
00148       // Treat this as if the input source ended:
00149       handleInputClosure1();
00150       return;
00151     }
00152   }
00153 
00154   // Arrange to read a new Transport Stream packet:
00155   fInputSource->getNextFrame(fInputBuffer, sizeof fInputBuffer,
00156                              afterGettingFrame, this,
00157                              handleInputClosure, this);
00158 }
00159 
00160 void MPEG2IFrameIndexFromTransportStream
00161 ::afterGettingFrame(void* clientData, unsigned frameSize,
00162                     unsigned numTruncatedBytes,
00163                     struct timeval presentationTime,
00164                     unsigned durationInMicroseconds) {
00165   MPEG2IFrameIndexFromTransportStream* source
00166     = (MPEG2IFrameIndexFromTransportStream*)clientData;
00167   source->afterGettingFrame1(frameSize, numTruncatedBytes,
00168                              presentationTime, durationInMicroseconds);
00169 }
00170 
00171 #define TRANSPORT_SYNC_BYTE 0x47
00172 
00173 void MPEG2IFrameIndexFromTransportStream
00174 ::afterGettingFrame1(unsigned frameSize,
00175                      unsigned numTruncatedBytes,
00176                      struct timeval presentationTime,
00177                      unsigned durationInMicroseconds) {
00178   if (frameSize < TRANSPORT_PACKET_SIZE || fInputBuffer[0] != TRANSPORT_SYNC_BYTE) {
00179     if (fInputBuffer[0] != TRANSPORT_SYNC_BYTE) {
00180       envir() << "Bad TS sync byte: 0x" << fInputBuffer[0] << "\n";
00181     }
00182     // Handle this as if the source ended:
00183     handleInputClosure1();
00184     return;
00185   }
00186 
00187   ++fInputTransportPacketCounter;
00188 
00189   // Figure out how much of this Transport Packet contains PES data:
00190   u_int8_t adaptation_field_control = (fInputBuffer[3]&0x30)>>4;
00191   u_int8_t totalHeaderSize
00192     = adaptation_field_control == 1 ? 4 : 5 + fInputBuffer[4];
00193 
00194   // Check for a PCR:
00195   if (totalHeaderSize > 5 && (fInputBuffer[5]&0x10) != 0) {
00196     // There's a PCR:
00197     u_int32_t pcrBaseHigh
00198       = (fInputBuffer[6]<<24)|(fInputBuffer[7]<<16)
00199       |(fInputBuffer[8]<<8)|fInputBuffer[9];
00200     float pcr = pcrBaseHigh/45000.0f;
00201     if ((fInputBuffer[10]&0x80) != 0) pcr += 1/90000.0f; // add in low-bit (if set)
00202     unsigned short pcrExt = ((fInputBuffer[10]&0x01)<<8) | fInputBuffer[11];
00203     pcr += pcrExt/27000000.0f;
00204 
00205     if (!fHaveSeenFirstPCR) {
00206       fFirstPCR = pcr;
00207       fHaveSeenFirstPCR = True;
00208     } else if (pcr < fLastPCR) {
00209       // The PCR timestamp has gone backwards.  DIsplay a warning about this (because it indicates buggy Transport Stream data),
00210       // and compensate for it.
00211       envir() << "\nWarning: At about " << fLastPCR-fFirstPCR << " seconds into the file, the PCR timestamp decreased - from "
00212               << fLastPCR << " to " << pcr << "\n";
00213       fFirstPCR -= (fLastPCR - pcr);
00214     }
00215     fLastPCR = pcr;
00216   }
00217 
00218   // Get the PID from the packet, and check for special tables: the PAT and PMT:
00219   u_int16_t PID = ((fInputBuffer[1]&0x1F)<<8) | fInputBuffer[2];
00220   if (PID == PAT_PID) {
00221     analyzePAT(&fInputBuffer[totalHeaderSize], TRANSPORT_PACKET_SIZE-totalHeaderSize);
00222   } else if (PID == fPMT_PID) {
00223     analyzePMT(&fInputBuffer[totalHeaderSize], TRANSPORT_PACKET_SIZE-totalHeaderSize);
00224   }
00225 
00226   // Ignore transport packets for non-video programs,
00227   // or packets with no data, or packets that duplicate the previous packet:
00228   u_int8_t continuity_counter = fInputBuffer[3]&0x0F;
00229   if ((PID != fVideo_PID) ||
00230       !(adaptation_field_control == 1  || adaptation_field_control == 3) ||
00231       continuity_counter == fLastContinuityCounter) {
00232     doGetNextFrame();
00233     return;
00234   }
00235   fLastContinuityCounter = continuity_counter;
00236 
00237   // Also, if this is the start of a PES packet, then skip over the PES header:
00238   Boolean payload_unit_start_indicator = (fInputBuffer[1]&0x40) != 0;
00239   //fprintf(stderr, "PUSI: %d\n", payload_unit_start_indicator);//#####
00240   if (payload_unit_start_indicator) {
00241     // Note: The following works only for MPEG-2 data #####
00242     u_int8_t PES_header_data_length = fInputBuffer[totalHeaderSize+8];
00243     //fprintf(stderr, "PES_header_data_length: %d\n", PES_header_data_length);//#####
00244     totalHeaderSize += 9 + PES_header_data_length;
00245     if (totalHeaderSize >= TRANSPORT_PACKET_SIZE) {
00246       envir() << "Unexpectedly large PES header size: " << PES_header_data_length << "\n";
00247       // Handle this as if the source ended:
00248       handleInputClosure1();
00249       return;
00250     }
00251   }
00252 
00253   // The remaining data is Video Elementary Stream data.  Add it to our parse buffer:
00254   unsigned vesSize = TRANSPORT_PACKET_SIZE - totalHeaderSize;
00255   memmove(&fParseBuffer[fParseBufferDataEnd], &fInputBuffer[totalHeaderSize], vesSize);
00256   fParseBufferDataEnd += vesSize;
00257 
00258   // And add a new index record noting where it came from:
00259   addToTail(new IndexRecord(totalHeaderSize, vesSize, fInputTransportPacketCounter,
00260                             fLastPCR - fFirstPCR));
00261 
00262   // Try again:
00263   doGetNextFrame();
00264 }
00265 
00266 void MPEG2IFrameIndexFromTransportStream::handleInputClosure(void* clientData) {
00267   MPEG2IFrameIndexFromTransportStream* source
00268     = (MPEG2IFrameIndexFromTransportStream*)clientData;
00269   source->handleInputClosure1();
00270 }
00271 
00272 #define VIDEO_SEQUENCE_START_CODE 0xB3          // MPEG-1 or 2
00273 #define VISUAL_OBJECT_SEQUENCE_START_CODE 0xB0  // MPEG-4
00274 #define GROUP_START_CODE 0xB8                   // MPEG-1 or 2
00275 #define GROUP_VOP_START_CODE 0xB3               // MPEG-4
00276 #define PICTURE_START_CODE 0x00                 // MPEG-1 or 2
00277 #define VOP_START_CODE 0xB6                     // MPEG-4
00278 
00279 void MPEG2IFrameIndexFromTransportStream::handleInputClosure1() {
00280   if (++fClosureNumber == 1 && fParseBufferDataEnd > fParseBufferFrameStart
00281       && fParseBufferDataEnd <= fParseBufferSize - 4) {
00282     // This is the first time we saw EOF, and there's still data remaining to be
00283     // parsed.  Hack: Append a Picture Header code to the end of the unparsed
00284     // data, and try again.  This should use up all of the unparsed data.
00285     fParseBuffer[fParseBufferDataEnd++] = 0;
00286     fParseBuffer[fParseBufferDataEnd++] = 0;
00287     fParseBuffer[fParseBufferDataEnd++] = 1;
00288     fParseBuffer[fParseBufferDataEnd++] = PICTURE_START_CODE;
00289 
00290     // Try again:
00291     doGetNextFrame();
00292   } else {
00293     // Handle closure in the regular way:
00294     FramedSource::handleClosure(this);
00295   }
00296 }
00297 
00298 void MPEG2IFrameIndexFromTransportStream
00299 ::analyzePAT(unsigned char* pkt, unsigned size) {
00300   // Get the PMT_PID:
00301   while (size >= 17) { // The table is large enough
00302     u_int16_t program_number = (pkt[9]<<8) | pkt[10];
00303     if (program_number != 0) {
00304       fPMT_PID = ((pkt[11]&0x1F)<<8) | pkt[12];
00305       return;
00306     }
00307 
00308     pkt += 4; size -= 4;
00309   }
00310 }
00311 
00312 void MPEG2IFrameIndexFromTransportStream
00313 ::analyzePMT(unsigned char* pkt, unsigned size) {
00314   // Scan the "elementary_PID"s in the map, until we see the first video stream.
00315 
00316   // First, get the "section_length", to get the table's size:
00317   u_int16_t section_length = ((pkt[2]&0x0F)<<8) | pkt[3];
00318   if ((unsigned)(4+section_length) < size) size = (4+section_length);
00319 
00320   // Then, skip any descriptors following the "program_info_length":
00321   if (size < 22) return; // not enough data
00322   unsigned program_info_length = ((pkt[11]&0x0F)<<8) | pkt[12];
00323   pkt += 13; size -= 13;
00324   if (size < program_info_length) return; // not enough data
00325   pkt += program_info_length; size -= program_info_length;
00326 
00327   // Look at each ("stream_type","elementary_PID") pair, looking for a video stream
00328   // ("stream_type" == 1 or 2):
00329   while (size >= 9) {
00330     u_int8_t stream_type = pkt[0];
00331     u_int16_t elementary_PID = ((pkt[1]&0x1F)<<8) | pkt[2];
00332     if (stream_type == 1 || stream_type == 2 || stream_type == 0x1B/*H.264 video*/) {
00333         if (stream_type == 0x1B) fIsH264 = True;
00334         fVideo_PID = elementary_PID;
00335       return;
00336     }
00337 
00338     u_int16_t ES_info_length = ((pkt[3]&0x0F)<<8) | pkt[4];
00339     pkt += 5; size -= 5;
00340     if (size < ES_info_length) return; // not enough data
00341     pkt += ES_info_length; size -= ES_info_length;
00342   }
00343 }
00344 
00345 Boolean MPEG2IFrameIndexFromTransportStream::deliverIndexRecord() {
00346   IndexRecord* head = fHeadIndexRecord;
00347   if (head == NULL) return False;
00348 
00349   // Check whether the head record has been parsed yet:
00350   if (head->recordType() == RECORD_UNPARSED) return False;
00351 
00352   // Remove the head record (the one whose data we'll be delivering):
00353   IndexRecord* next = head->next();
00354   head->unlink();
00355   if (next == head) {
00356     fHeadIndexRecord = fTailIndexRecord = NULL;
00357   } else {
00358     fHeadIndexRecord = next;
00359   }
00360 
00361   if (head->recordType() == RECORD_JUNK) {
00362     // Don't actually deliver the data to the client:
00363     delete head;
00364     // Try to deliver the next record instead:
00365     return deliverIndexRecord();
00366   }
00367 
00368   // Deliver data from the head record:
00369 #ifdef DEBUG
00370   envir() << "delivering: " << *head << "\n";
00371 #endif
00372   if (fMaxSize < 11) {
00373     fFrameSize = 0;
00374   } else {
00375     fTo[0] = (u_int8_t)(head->recordType());
00376     fTo[1] = head->startOffset();
00377     fTo[2] = head->size();
00378     // Deliver the PCR, as 24 bits (integer part; little endian) + 8 bits (fractional part)
00379     float pcr = head->pcr();
00380     unsigned pcr_int = (unsigned)pcr;
00381     u_int8_t pcr_frac = (u_int8_t)(256*(pcr-pcr_int));
00382     fTo[3] = (unsigned char)(pcr_int);
00383     fTo[4] = (unsigned char)(pcr_int>>8);
00384     fTo[5] = (unsigned char)(pcr_int>>16);
00385     fTo[6] = (unsigned char)(pcr_frac);
00386     // Deliver the transport packet number (in little-endian order):
00387     unsigned long tpn = head->transportPacketNumber();
00388     fTo[7] = (unsigned char)(tpn);
00389     fTo[8] = (unsigned char)(tpn>>8);
00390     fTo[9] = (unsigned char)(tpn>>16);
00391     fTo[10] = (unsigned char)(tpn>>24);
00392     fFrameSize = 11;
00393   }
00394 
00395   // Free the (former) head record (as we're now done with it):
00396   delete head;
00397 
00398   // Complete delivery to the client:
00399   afterGetting(this);
00400   return True;
00401 }
00402 
00403 Boolean MPEG2IFrameIndexFromTransportStream::parseFrame() {
00404   // At this point, we have a queue of >=0 (unparsed) index records, representing
00405   // the data in the parse buffer from "fParseBufferFrameStart"
00406   // to "fParseBufferDataEnd".  We now parse through this data, looking for
00407   // a complete 'frame', where a 'frame', in this case, means:
00408   //    for MPEG video: a Video Sequence Header, GOP Header, Picture Header, or Slice
00409   //    for H.264 video: a NAL unit
00410 
00411   // Inspect the frame's initial 4-byte code, to make sure it starts with a system code:
00412   if (fParseBufferDataEnd-fParseBufferFrameStart < 4) return False; // not enough data
00413   unsigned numInitialBadBytes = 0;
00414   unsigned char const* p = &fParseBuffer[fParseBufferFrameStart];
00415   if (!(p[0] == 0 && p[1] == 0 && p[2] == 1)) {
00416     // There's no system code at the beginning.  Parse until we find one:
00417     if (fParseBufferParseEnd == fParseBufferFrameStart + 4) {
00418       // Start parsing from the beginning of the frame data:
00419       fParseBufferParseEnd = fParseBufferFrameStart;
00420     }
00421     unsigned char nextCode;
00422     if (!parseToNextCode(nextCode)) return False;
00423 
00424     numInitialBadBytes = fParseBufferParseEnd - fParseBufferFrameStart;
00425     //fprintf(stderr, "#####numInitialBadBytes: %d (0x%x)\n", numInitialBadBytes, numInitialBadBytes);
00426     fParseBufferFrameStart = fParseBufferParseEnd;
00427     fParseBufferParseEnd += 4; // skip over the code that we just saw
00428     p = &fParseBuffer[fParseBufferFrameStart];
00429   }
00430 
00431   unsigned char curCode = p[3];
00432   if (fIsH264) curCode &= 0x1F; // nal_unit_type
00433   RecordType curRecordType;
00434   unsigned char nextCode;
00435   switch (curCode) {
00436   case VIDEO_SEQUENCE_START_CODE:
00437   case VISUAL_OBJECT_SEQUENCE_START_CODE: {
00438     curRecordType = RECORD_VSH;
00439     while (1) {
00440       if (!parseToNextCode(nextCode)) return False;
00441       if (nextCode == GROUP_START_CODE || /*nextCode == GROUP_VOP_START_CODE ||*/
00442           nextCode == PICTURE_START_CODE || nextCode == VOP_START_CODE) break;
00443       fParseBufferParseEnd += 4; // skip over the code that we just saw
00444     }
00445     break;
00446   }
00447   case GROUP_START_CODE:
00448     /*case GROUP_VOP_START_CODE:*/ {
00449     curRecordType = RECORD_GOP;
00450     while (1) {
00451       if (!parseToNextCode(nextCode)) return False;
00452       if (nextCode == PICTURE_START_CODE || nextCode == VOP_START_CODE) break;
00453       fParseBufferParseEnd += 4; // skip over the code that we just saw
00454     }
00455     break;
00456   }
00457   case 1: // Coded slice of a non-IDR picture (H.264)
00458     curRecordType = RECORD_NAL_NON_IFRAME;
00459     if (!parseToNextCode(nextCode)) return False;
00460     break;
00461   case 5: // Coded slice of an IDR picture (H.264) 
00462     curRecordType = RECORD_NAL_IFRAME;
00463     if (!parseToNextCode(nextCode)) return False;
00464     break;
00465   case 6: // Supplemental enhancement information (SEI) (H.264)
00466     curRecordType = RECORD_NAL_SEI;
00467     if (!parseToNextCode(nextCode)) return False;
00468     break;
00469   case 7: // Sequence parameter set (SPS) (H.264)
00470     curRecordType = RECORD_NAL_SPS;
00471     if (!parseToNextCode(nextCode)) return False;
00472     break;
00473   case 8: // Picture parameter set (PPS) (H.264)
00474     curRecordType = RECORD_NAL_PPS;
00475     if (!parseToNextCode(nextCode)) return False;
00476     break;
00477   default: { // picture (including slices), or some other H.264 NAL unit
00478     if (fIsH264) {
00479       curRecordType = RECORD_NAL_OTHER;
00480       if (!parseToNextCode(nextCode)) return False;
00481     } else {
00482       curRecordType = RECORD_PIC_NON_IFRAME; // may get changed to IFRAME later
00483       while (1) {
00484         if (!parseToNextCode(nextCode)) return False;
00485         if (nextCode == VIDEO_SEQUENCE_START_CODE || nextCode == VISUAL_OBJECT_SEQUENCE_START_CODE ||
00486             nextCode == GROUP_START_CODE || nextCode == GROUP_VOP_START_CODE ||
00487             nextCode == PICTURE_START_CODE || nextCode == VOP_START_CODE) break;
00488         fParseBufferParseEnd += 4; // skip over the code that we just saw
00489       }
00490     }
00491     break;
00492   }
00493   }
00494 
00495   if (curRecordType == RECORD_PIC_NON_IFRAME) {
00496     if (curCode == VOP_START_CODE) { // MPEG-4
00497       //fprintf(stderr, "#####parseFrame()1(4): 0x%x, 0x%x\n", curCode, fParseBuffer[fParseBufferFrameStart+4]&0xC0);
00498       if ((fParseBuffer[fParseBufferFrameStart+4]&0xC0) == 0) {
00499         // This is actually an I-frame.  Note it as such:
00500         curRecordType = RECORD_PIC_IFRAME;
00501       }
00502     } else { // MPEG-1 or 2
00503       //fprintf(stderr, "#####parseFrame()1(!4): 0x%x, 0x%x\n", curCode, fParseBuffer[fParseBufferFrameStart+5]&0x38);
00504       if ((fParseBuffer[fParseBufferFrameStart+5]&0x38) == 0x08) {
00505         // This is actually an I-frame.  Note it as such:
00506         curRecordType = RECORD_PIC_IFRAME;
00507       }
00508     }
00509   }
00510 
00511   // There is now a parsed 'frame', from "fParseBufferFrameStart"
00512   // to "fParseBufferParseEnd". Tag the corresponding index records to note this:
00513   unsigned frameSize = fParseBufferParseEnd - fParseBufferFrameStart + numInitialBadBytes;
00514 #ifdef DEBUG
00515   envir() << "parsed " << recordTypeStr[curRecordType] << "; length "
00516           << frameSize << "\n";
00517 #endif
00518   for (IndexRecord* r = fHeadIndexRecord; ; r = r->next()) {
00519     if (numInitialBadBytes >= r->size()) {
00520       r->recordType() = RECORD_JUNK;
00521       numInitialBadBytes -= r->size();
00522     } else {
00523       r->recordType() = curRecordType;
00524     }
00525     if (r == fHeadIndexRecord) r->setFirstFlag();
00526     // indicates that this is the first record for this frame
00527 
00528     if (r->size() > frameSize) {
00529       // This record contains extra data that's not part of the frame.
00530       // Shorten this record, and move the extra data to a new record
00531       // that comes afterwards:
00532       u_int8_t newOffset = r->startOffset() + frameSize;
00533       u_int8_t newSize = r->size() - frameSize;
00534       r->size() = frameSize;
00535 #ifdef DEBUG
00536       envir() << "tagged record (modified): " << *r << "\n";
00537 #endif
00538 
00539       IndexRecord* newRecord
00540         = new IndexRecord(newOffset, newSize, r->transportPacketNumber(), r->pcr());
00541       newRecord->addAfter(r);
00542       if (fTailIndexRecord == r) fTailIndexRecord = newRecord;
00543 #ifdef DEBUG
00544       envir() << "added extra record: " << *newRecord << "\n";
00545 #endif
00546     } else {
00547 #ifdef DEBUG
00548       envir() << "tagged record: " << *r << "\n";
00549 #endif
00550     }
00551     frameSize -= r->size();
00552     if (frameSize == 0) break;
00553     if (r == fTailIndexRecord) { // this shouldn't happen
00554       envir() << "!!!!!Internal consistency error!!!!!\n";
00555       return False;
00556     }
00557   }
00558 
00559   // Finally, update our parse state (to skip over the now-parsed data):
00560   fParseBufferFrameStart = fParseBufferParseEnd;
00561   fParseBufferParseEnd += 4; // to skip over the next code (that we found)
00562 
00563   return True;
00564 }
00565 
00566 Boolean MPEG2IFrameIndexFromTransportStream
00567 ::parseToNextCode(unsigned char& nextCode) {
00568   unsigned char const* p = &fParseBuffer[fParseBufferParseEnd];
00569   unsigned char const* end = &fParseBuffer[fParseBufferDataEnd];
00570   while (p <= end-4) {
00571     if (p[2] > 1) p += 3; // common case (optimized)
00572     else if (p[2] == 0) ++p;
00573     else if (p[0] == 0 && p[1] == 0) { // && p[2] == 1
00574       // We found a code here:
00575       nextCode = p[3];
00576       fParseBufferParseEnd = p - &fParseBuffer[0]; // where we've gotten to
00577       return True;
00578     } else p += 3;
00579   }
00580 
00581   fParseBufferParseEnd = p - &fParseBuffer[0]; // where we've gotten to
00582   return False; // no luck this time
00583 }
00584 
00585 void MPEG2IFrameIndexFromTransportStream::compactParseBuffer() {
00586 #ifdef DEBUG
00587   envir() << "Compacting parse buffer: [" << fParseBufferFrameStart
00588           << "," << fParseBufferParseEnd << "," << fParseBufferDataEnd << "]";
00589 #endif
00590   memmove(&fParseBuffer[0], &fParseBuffer[fParseBufferFrameStart],
00591           fParseBufferDataEnd - fParseBufferFrameStart);
00592   fParseBufferDataEnd -= fParseBufferFrameStart;
00593   fParseBufferParseEnd -= fParseBufferFrameStart;
00594   fParseBufferFrameStart = 0;
00595 #ifdef DEBUG
00596   envir() << "-> [" << fParseBufferFrameStart
00597           << "," << fParseBufferParseEnd << "," << fParseBufferDataEnd << "]\n";
00598 #endif
00599 }
00600 
00601 void MPEG2IFrameIndexFromTransportStream::addToTail(IndexRecord* newIndexRecord) {
00602 #ifdef DEBUG
00603   envir() << "adding new: " << *newIndexRecord << "\n";
00604 #endif
00605   if (fTailIndexRecord == NULL) {
00606     fHeadIndexRecord = fTailIndexRecord = newIndexRecord;
00607   } else {
00608     newIndexRecord->addAfter(fTailIndexRecord);
00609     fTailIndexRecord = newIndexRecord;
00610   }
00611 }
00612 
00614 
00615 IndexRecord::IndexRecord(u_int8_t startOffset, u_int8_t size,
00616                          unsigned long transportPacketNumber, float pcr)
00617   : fNext(this), fPrev(this), fRecordType(RECORD_UNPARSED),
00618     fStartOffset(startOffset), fSize(size),
00619     fPCR(pcr), fTransportPacketNumber(transportPacketNumber) {
00620 }
00621 
00622 IndexRecord::~IndexRecord() {
00623   IndexRecord* nextRecord = next();
00624   unlink();
00625   if (nextRecord != this) delete nextRecord;
00626 }
00627 
00628 void IndexRecord::addAfter(IndexRecord* prev) {
00629   fNext = prev->fNext;
00630   fPrev = prev;
00631   prev->fNext->fPrev = this;
00632   prev->fNext = this;
00633 }
00634 
00635 void IndexRecord::unlink() {
00636   fNext->fPrev = fPrev;
00637   fPrev->fNext = fNext;
00638   fNext = fPrev = this;
00639 }

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