00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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,
00030 RECORD_PIC_IFRAME = 4,
00031 RECORD_NAL_SPS = 5,
00032 RECORD_NAL_PPS = 6,
00033 RECORD_NAL_SEI = 7,
00034 RECORD_NAL_NON_IFRAME = 8,
00035 RECORD_NAL_IFRAME = 9,
00036 RECORD_NAL_OTHER = 10,
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
00059 IndexRecord* fNext;
00060 IndexRecord* fPrev;
00061
00062 RecordType fRecordType;
00063 u_int8_t fStartOffset;
00064 u_int8_t fSize;
00065
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
00103 #define MAX_FRAME_SIZE 400000
00104
00105
00106
00107 #define PARSE_BUFFER_SIZE (2*MAX_FRAME_SIZE)
00108
00109
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),
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
00133
00134 if (deliverIndexRecord()) return;
00135
00136
00137 if (parseFrame()) {
00138 doGetNextFrame();
00139 return;
00140 }
00141
00142
00143 if (fParseBufferSize - fParseBufferDataEnd < TRANSPORT_PACKET_SIZE) {
00144
00145 compactParseBuffer();
00146 if (fParseBufferSize - fParseBufferDataEnd < TRANSPORT_PACKET_SIZE) {
00147 envir() << "ERROR: parse buffer full; increase MAX_FRAME_SIZE\n";
00148
00149 handleInputClosure1();
00150 return;
00151 }
00152 }
00153
00154
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
00183 handleInputClosure1();
00184 return;
00185 }
00186
00187 ++fInputTransportPacketCounter;
00188
00189
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
00195 if (totalHeaderSize > 5 && (fInputBuffer[5]&0x10) != 0) {
00196
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;
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
00210
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
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
00227
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
00238 Boolean payload_unit_start_indicator = (fInputBuffer[1]&0x40) != 0;
00239
00240 if (payload_unit_start_indicator) {
00241
00242 u_int8_t PES_header_data_length = fInputBuffer[totalHeaderSize+8];
00243
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
00248 handleInputClosure1();
00249 return;
00250 }
00251 }
00252
00253
00254 unsigned vesSize = TRANSPORT_PACKET_SIZE - totalHeaderSize;
00255 memmove(&fParseBuffer[fParseBufferDataEnd], &fInputBuffer[totalHeaderSize], vesSize);
00256 fParseBufferDataEnd += vesSize;
00257
00258
00259 addToTail(new IndexRecord(totalHeaderSize, vesSize, fInputTransportPacketCounter,
00260 fLastPCR - fFirstPCR));
00261
00262
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
00283
00284
00285 fParseBuffer[fParseBufferDataEnd++] = 0;
00286 fParseBuffer[fParseBufferDataEnd++] = 0;
00287 fParseBuffer[fParseBufferDataEnd++] = 1;
00288 fParseBuffer[fParseBufferDataEnd++] = PICTURE_START_CODE;
00289
00290
00291 doGetNextFrame();
00292 } else {
00293
00294 FramedSource::handleClosure(this);
00295 }
00296 }
00297
00298 void MPEG2IFrameIndexFromTransportStream
00299 ::analyzePAT(unsigned char* pkt, unsigned size) {
00300
00301 while (size >= 17) {
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
00315
00316
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
00321 if (size < 22) return;
00322 unsigned program_info_length = ((pkt[11]&0x0F)<<8) | pkt[12];
00323 pkt += 13; size -= 13;
00324 if (size < program_info_length) return;
00325 pkt += program_info_length; size -= program_info_length;
00326
00327
00328
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) {
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;
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
00350 if (head->recordType() == RECORD_UNPARSED) return False;
00351
00352
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
00363 delete head;
00364
00365 return deliverIndexRecord();
00366 }
00367
00368
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
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
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
00396 delete head;
00397
00398
00399 afterGetting(this);
00400 return True;
00401 }
00402
00403 Boolean MPEG2IFrameIndexFromTransportStream::parseFrame() {
00404
00405
00406
00407
00408
00409
00410
00411
00412 if (fParseBufferDataEnd-fParseBufferFrameStart < 4) return False;
00413 unsigned numInitialBadBytes = 0;
00414 unsigned char const* p = &fParseBuffer[fParseBufferFrameStart];
00415 if (!(p[0] == 0 && p[1] == 0 && p[2] == 1)) {
00416
00417 if (fParseBufferParseEnd == fParseBufferFrameStart + 4) {
00418
00419 fParseBufferParseEnd = fParseBufferFrameStart;
00420 }
00421 unsigned char nextCode;
00422 if (!parseToNextCode(nextCode)) return False;
00423
00424 numInitialBadBytes = fParseBufferParseEnd - fParseBufferFrameStart;
00425
00426 fParseBufferFrameStart = fParseBufferParseEnd;
00427 fParseBufferParseEnd += 4;
00428 p = &fParseBuffer[fParseBufferFrameStart];
00429 }
00430
00431 unsigned char curCode = p[3];
00432 if (fIsH264) curCode &= 0x1F;
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 ||
00442 nextCode == PICTURE_START_CODE || nextCode == VOP_START_CODE) break;
00443 fParseBufferParseEnd += 4;
00444 }
00445 break;
00446 }
00447 case GROUP_START_CODE:
00448 {
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;
00454 }
00455 break;
00456 }
00457 case 1:
00458 curRecordType = RECORD_NAL_NON_IFRAME;
00459 if (!parseToNextCode(nextCode)) return False;
00460 break;
00461 case 5:
00462 curRecordType = RECORD_NAL_IFRAME;
00463 if (!parseToNextCode(nextCode)) return False;
00464 break;
00465 case 6:
00466 curRecordType = RECORD_NAL_SEI;
00467 if (!parseToNextCode(nextCode)) return False;
00468 break;
00469 case 7:
00470 curRecordType = RECORD_NAL_SPS;
00471 if (!parseToNextCode(nextCode)) return False;
00472 break;
00473 case 8:
00474 curRecordType = RECORD_NAL_PPS;
00475 if (!parseToNextCode(nextCode)) return False;
00476 break;
00477 default: {
00478 if (fIsH264) {
00479 curRecordType = RECORD_NAL_OTHER;
00480 if (!parseToNextCode(nextCode)) return False;
00481 } else {
00482 curRecordType = RECORD_PIC_NON_IFRAME;
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;
00489 }
00490 }
00491 break;
00492 }
00493 }
00494
00495 if (curRecordType == RECORD_PIC_NON_IFRAME) {
00496 if (curCode == VOP_START_CODE) {
00497
00498 if ((fParseBuffer[fParseBufferFrameStart+4]&0xC0) == 0) {
00499
00500 curRecordType = RECORD_PIC_IFRAME;
00501 }
00502 } else {
00503
00504 if ((fParseBuffer[fParseBufferFrameStart+5]&0x38) == 0x08) {
00505
00506 curRecordType = RECORD_PIC_IFRAME;
00507 }
00508 }
00509 }
00510
00511
00512
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
00527
00528 if (r->size() > frameSize) {
00529
00530
00531
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) {
00554 envir() << "!!!!!Internal consistency error!!!!!\n";
00555 return False;
00556 }
00557 }
00558
00559
00560 fParseBufferFrameStart = fParseBufferParseEnd;
00561 fParseBufferParseEnd += 4;
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;
00572 else if (p[2] == 0) ++p;
00573 else if (p[0] == 0 && p[1] == 0) {
00574
00575 nextCode = p[3];
00576 fParseBufferParseEnd = p - &fParseBuffer[0];
00577 return True;
00578 } else p += 3;
00579 }
00580
00581 fParseBufferParseEnd = p - &fParseBuffer[0];
00582 return False;
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 }