Boolean MPEG2TransportStreamIndexFile::rewindToVSH(unsigned long&ixFound) { Boolean success = False; while (ixFound > 0) { if (!readIndexRecord(ixFound)) break; u_int8_t recordType = recordTypeFromBuf(); if ((recordType&0x80) != 0 && (recordType&0x7F) <= 2/*GOP*/) { if ((recordType&0x7F) == 2) { // This is a GOP. Hack: If the preceding record is for a Video Sequence Header, // then use it instead: unsigned long newIxFound = ixFound; while (--newIxFound > 0) { if (!readIndexRecord(newIxFound)) break; recordType = recordTypeFromBuf(); if ((recordType&0x7F) != 1) break; // not a Video Sequence Header if ((recordType&0x80) != 0) { // this is the start of the VSH; use it ixFound = newIxFound; break; } } } // Record 'ixFound' as appropriate to return: success = True; break; } --ixFound; } if (ixFound == 0) success = True; // use record 0 anyway return success; }
Boolean MPEG2TransportStreamIndexFile::rewindToCleanPoint(unsigned long&ixFound) { Boolean success = False; // until we learn otherwise while (ixFound > 0) { if (!readIndexRecord(ixFound)) break; u_int8_t recordType = recordTypeFromBuf(); setMPEGVersionFromRecordType(recordType); // A 'clean point' is the start of a 'frame' from which a decoder can cleanly resume // handling the stream. For H.264, this is a SPS. For H.265, this is a VPS. // For MPEG-2, this is a Video Sequence Header, or a GOP. if ((recordType&0x80) != 0) { // This is the start of a 'frame' recordType &=~ 0x80; // remove the 'start of frame' bit if (fMPEGVersion == 5) { // H.264 if (recordType == 5/*SPS*/) { success = True; break; } } else if (fMPEGVersion == 6) { // H.265 if (recordType == 11/*VPS*/) { success = True; break; } } else { // MPEG-1, 2, or 4 if (recordType == 1/*VSH*/) { success = True; break; } else if (recordType == 2/*GOP*/) { // Hack: If the preceding record is for a Video Sequence Header, then use it instead: unsigned long newIxFound = ixFound; while (--newIxFound > 0) { if (!readIndexRecord(newIxFound)) break; recordType = recordTypeFromBuf(); if ((recordType&0x7F) != 1) break; // not a Video Sequence Header if ((recordType&0x80) != 0) { // this is the start of the VSH; use it ixFound = newIxFound; break; } } } success = True; break; } } // Keep checking, from the previous record: --ixFound; } if (ixFound == 0) success = True; // use record 0 anyway return success; }
Boolean MPEG2TransportStreamIndexFile ::readIndexRecordValues(unsigned long indexRecordNum, unsigned long& transportPacketNum, u_int8_t& offset, u_int8_t& size, float& pcr, u_int8_t& recordType) { if (!readIndexRecord(indexRecordNum)) return False; transportPacketNum = tsPacketNumFromBuf(); offset = offsetFromBuf(); size = sizeFromBuf(); pcr = pcrFromBuf(); recordType = recordTypeFromBuf(); return True; }
void MPEG2TransportStreamIndexFile ::lookupTSPacketNumFromNPT(float& npt, unsigned long& tsPacketNumber, unsigned long& indexRecordNumber) { if (npt <= 0.0 || fNumIndexRecords == 0) { // Fast-track a common case: npt = 0.0f; tsPacketNumber = indexRecordNumber = 0; return; } // If "npt" is the same as the one that we last looked up, return its cached result: if (npt == fCachedPCR) { tsPacketNumber = fCachedTSPacketNumber; indexRecordNumber = fCachedIndexRecordNumber; return; } // Search for the pair of neighboring index records whose PCR values span "npt". // Use the 'regula-falsi' method. Boolean success = False; unsigned long ixFound = 0; do { unsigned long ixLeft = 0, ixRight = fNumIndexRecords-1; float pcrLeft = 0.0f, pcrRight; if (!readIndexRecord(ixRight)) break; pcrRight = pcrFromBuf(); if (npt > pcrRight) npt = pcrRight; // handle "npt" too large by seeking to the last frame of the file while (ixRight-ixLeft > 1 && pcrLeft < npt && npt <= pcrRight) { unsigned long ixNew = ixLeft + (unsigned long)(((npt-pcrLeft)/(pcrRight-pcrLeft))*(ixRight-ixLeft)); if (ixNew == ixLeft || ixNew == ixRight) { // use bisection instead: ixNew = (ixLeft+ixRight)/2; } if (!readIndexRecord(ixNew)) break; float pcrNew = pcrFromBuf(); if (pcrNew < npt) { pcrLeft = pcrNew; ixLeft = ixNew; } else { pcrRight = pcrNew; ixRight = ixNew; } } if (ixRight-ixLeft > 1 || npt <= pcrLeft || npt > pcrRight) break; // bad PCR values in index file? ixFound = ixRight; // "Rewind' until we reach the start of a Video Sequence or GOP header: success = rewindToCleanPoint(ixFound); } while (0); if (success && readIndexRecord(ixFound)) { // Return (and cache) information from record "ixFound": npt = fCachedPCR = pcrFromBuf(); tsPacketNumber = fCachedTSPacketNumber = tsPacketNumFromBuf(); indexRecordNumber = fCachedIndexRecordNumber = ixFound; } else { // An error occurred: Return the default values, for npt == 0: npt = 0.0f; tsPacketNumber = indexRecordNumber = 0; } closeFid(); }
Boolean MPEG2TransportStreamIndexFile::readOneIndexRecord(unsigned long indexRecordNum) { Boolean result = readIndexRecord(indexRecordNum); closeFid(); return result; }
void MPEG2TransportStreamIndexFile ::lookupPCRFromTSPacketNum(unsigned long& tsPacketNumber, Boolean reverseToPreviousCleanPoint, float& pcr, unsigned long& indexRecordNumber) { if (tsPacketNumber == 0 || fNumIndexRecords == 0) { // Fast-track a common case: pcr = 0.0f; indexRecordNumber = 0; return; } // If "tsPacketNumber" is the same as the one that we last looked up, return its cached result: if (tsPacketNumber == fCachedTSPacketNumber) { pcr = fCachedPCR; indexRecordNumber = fCachedIndexRecordNumber; return; } // Search for the pair of neighboring index records whose TS packet #s span "tsPacketNumber". // Use the 'regula-falsi' method. Boolean success = False; unsigned long ixFound = 0; do { unsigned long ixLeft = 0, ixRight = fNumIndexRecords-1; unsigned long tsLeft = 0, tsRight; if (!readIndexRecord(ixRight)) break; tsRight = tsPacketNumFromBuf(); if (tsPacketNumber > tsRight) tsPacketNumber = tsRight; // handle "tsPacketNumber" too large by seeking to the last frame of the file while (ixRight-ixLeft > 1 && tsLeft < tsPacketNumber && tsPacketNumber <= tsRight) { unsigned long ixNew = ixLeft + (unsigned long)(((tsPacketNumber-tsLeft)/(tsRight-tsLeft))*(ixRight-ixLeft)); if (ixNew == ixLeft || ixNew == ixRight) { // Use bisection instead: ixNew = (ixLeft+ixRight)/2; } if (!readIndexRecord(ixNew)) break; unsigned long tsNew = tsPacketNumFromBuf(); if (tsNew < tsPacketNumber) { tsLeft = tsNew; ixLeft = ixNew; } else { tsRight = tsNew; ixRight = ixNew; } } if (ixRight-ixLeft > 1 || tsPacketNumber <= tsLeft || tsPacketNumber > tsRight) break; // bad PCR values in index file? ixFound = ixRight; if (reverseToPreviousCleanPoint) { // "Rewind' until we reach the start of a Video Sequence or GOP header: success = rewindToCleanPoint(ixFound); } else { success = True; } } while (0); if (success && readIndexRecord(ixFound)) { // Return (and cache) information from record "ixFound": pcr = fCachedPCR = pcrFromBuf(); fCachedTSPacketNumber = tsPacketNumFromBuf(); if (reverseToPreviousCleanPoint) tsPacketNumber = fCachedTSPacketNumber; indexRecordNumber = fCachedIndexRecordNumber = ixFound; } else { // An error occurred: Return the default values, for tsPacketNumber == 0: pcr = 0.0f; indexRecordNumber = 0; } closeFid(); }