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_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
00053 IndexRecord* fNext;
00054 IndexRecord* fPrev;
00055
00056 RecordType fRecordType;
00057 u_int8_t fStartOffset;
00058 u_int8_t fSize;
00059
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
00091 #define MAX_FRAME_SIZE 400000
00092
00093
00094
00095 #define PARSE_BUFFER_SIZE (2*MAX_FRAME_SIZE)
00096
00097
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),
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
00121
00122 if (deliverIndexRecord()) return;
00123
00124
00125 if (parseFrame()) {
00126 doGetNextFrame();
00127 return;
00128 }
00129
00130
00131 if (fParseBufferSize - fParseBufferDataEnd < TRANSPORT_PACKET_SIZE) {
00132
00133 compactParseBuffer();
00134 if (fParseBufferSize - fParseBufferDataEnd < TRANSPORT_PACKET_SIZE) {
00135 envir() << "ERROR: parse buffer full; increase MAX_FRAME_SIZE\n";
00136
00137 handleInputClosure1();
00138 return;
00139 }
00140 }
00141
00142
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) {
00165 if (fInputBuffer[0] != 0x47) {
00166 envir() << "Bad TS sync byte: 0x" << fInputBuffer[0] << "\n";
00167 }
00168
00169 handleInputClosure1();
00170 return;
00171 }
00172
00173 ++fInputTransportPacketCounter;
00174
00175
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
00181 if (totalHeaderSize > 5 && (fInputBuffer[5]&0x10) != 0) {
00182
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;
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
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
00207
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
00218 Boolean payload_unit_start_indicator = (fInputBuffer[1]&0x40) != 0;
00219
00220 if (payload_unit_start_indicator) {
00221
00222 u_int8_t PES_header_data_length = fInputBuffer[totalHeaderSize+8];
00223
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
00228 handleInputClosure1();
00229 return;
00230 }
00231 }
00232
00233
00234 unsigned vesSize = TRANSPORT_PACKET_SIZE - totalHeaderSize;
00235 memmove(&fParseBuffer[fParseBufferDataEnd], &fInputBuffer[totalHeaderSize], vesSize);
00236 fParseBufferDataEnd += vesSize;
00237
00238
00239 addToTail(new IndexRecord(totalHeaderSize, vesSize, fInputTransportPacketCounter,
00240 fLastPCR - fFirstPCR));
00241
00242
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
00263
00264
00265 fParseBuffer[fParseBufferDataEnd++] = 0;
00266 fParseBuffer[fParseBufferDataEnd++] = 0;
00267 fParseBuffer[fParseBufferDataEnd++] = 1;
00268 fParseBuffer[fParseBufferDataEnd++] = PICTURE_START_CODE;
00269
00270
00271 doGetNextFrame();
00272 } else {
00273
00274 FramedSource::handleClosure(this);
00275 }
00276 }
00277
00278 void MPEG2IFrameIndexFromTransportStream
00279 ::analyzePAT(unsigned char* pkt, unsigned size) {
00280
00281 if (size < 16) return;
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
00291
00292
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
00297 if (size < 22) return;
00298 unsigned program_info_length = ((pkt[11]&0x0F)<<8) | pkt[12];
00299 pkt += 13; size -= 13;
00300 if (size < program_info_length) return;
00301 pkt += program_info_length; size -= program_info_length;
00302
00303
00304
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;
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
00325 if (head->recordType() == RECORD_UNPARSED) return False;
00326
00327
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
00338 delete head;
00339 return True;
00340 }
00341
00342
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
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
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
00370 delete head;
00371
00372
00373 afterGetting(this);
00374 return True;
00375 }
00376
00377 Boolean MPEG2IFrameIndexFromTransportStream::parseFrame() {
00378
00379
00380
00381
00382
00383
00384
00385 if (fParseBufferDataEnd-fParseBufferFrameStart < 4) return False;
00386 unsigned numInitialBadBytes = 0;
00387 unsigned char const* p = &fParseBuffer[fParseBufferFrameStart];
00388 if (!(p[0] == 0 && p[1] == 0 && p[2] == 1)) {
00389
00390 if (fParseBufferParseEnd == fParseBufferFrameStart + 4) {
00391
00392 fParseBufferParseEnd = fParseBufferFrameStart;
00393 }
00394 unsigned char nextCode;
00395 if (!parseToNextCode(nextCode)) return False;
00396
00397 numInitialBadBytes = fParseBufferParseEnd - fParseBufferFrameStart;
00398
00399 fParseBufferFrameStart = fParseBufferParseEnd;
00400 fParseBufferParseEnd += 4;
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 ||
00414 nextCode == PICTURE_START_CODE || nextCode == VOP_START_CODE) break;
00415 fParseBufferParseEnd += 4;
00416 }
00417 break;
00418 }
00419 case GROUP_START_CODE:
00420 {
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;
00426 }
00427 break;
00428 }
00429 default: {
00430 curRecordType = RECORD_PIC_NON_IFRAME;
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;
00437 }
00438 break;
00439 }
00440 }
00441
00442 if (curRecordType == RECORD_PIC_NON_IFRAME) {
00443 if (curCode == VOP_START_CODE) {
00444
00445 if ((fParseBuffer[fParseBufferFrameStart+4]&0xC0) == 0) {
00446
00447 curRecordType = RECORD_PIC_IFRAME;
00448 }
00449 } else {
00450
00451 if ((fParseBuffer[fParseBufferFrameStart+5]&0x38) == 0x08) {
00452
00453 curRecordType = RECORD_PIC_IFRAME;
00454 }
00455 }
00456 }
00457
00458
00459
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
00474
00475 if (r->size() > frameSize) {
00476
00477
00478
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) {
00501 envir() << "!!!!!Internal consistency error!!!!!\n";
00502 return False;
00503 }
00504 }
00505
00506
00507 fParseBufferFrameStart = fParseBufferParseEnd;
00508 fParseBufferParseEnd += 4;
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;
00519 else if (p[2] == 0) ++p;
00520 else if (p[0] == 0 && p[1] == 0) {
00521
00522 nextCode = p[3];
00523 fParseBufferParseEnd = p - &fParseBuffer[0];
00524 return True;
00525 } else p += 3;
00526 }
00527
00528 fParseBufferParseEnd = p - &fParseBuffer[0];
00529 return False;
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 }