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

Generated on Tue Oct 7 15:38:09 2008 for live by  doxygen 1.5.2