diff options
Diffstat (limited to 'genindex/pes.c')
-rw-r--r-- | genindex/pes.c | 938 |
1 files changed, 938 insertions, 0 deletions
diff --git a/genindex/pes.c b/genindex/pes.c new file mode 100644 index 0000000..f1a5844 --- /dev/null +++ b/genindex/pes.c @@ -0,0 +1,938 @@ +/* + * (C) 2003-2006 Stefan Huelswitt <s.huelswitt@gmx.de>, (C) RF 2016 + * pes.c: + * + * See the main source file 'genindex.c' for copyright information and + * how to reach the author. + * + *------------------------------------------------------------------------------- + * Revision History + * $Log: pes.c,v $ + * Revision 1.2 2016/09/01 15:37:42 richard + * Extensively updated to V0.2 for EN 300 743 compliant subtitles + * added -b and -n flags + * + *------------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <stdlib.h> +#define __STDC_FORMAT_MACROS // Required for format specifiers +#include <inttypes.h> +#include "pes.h" +#include "ringbuffer.h" +#include "tools.h" + + +//#define DEBUG(x...) printf(x) +#define DEBUG(x...) + +//#define PD(x...) printf(x) +#define PD(x...) + +#define PAGE_COMPOSITION_SEGMENT 0x10 +#define REGION_COMPOSITION_SEGMENT 0x11 +#define CLUT_DEFINITION_SEGMENT 0x12 +#define OBJECT_DATA_SEGMENT 0x13 +#define DISPLAY_DEFINITION_SEGMENT 0x14 +#define DISPARITY_SIGNALING_SEGMENT 0x15 // DVB BlueBook A156 +#define END_OF_DISPLAY_SET_SEGMENT 0x80 +#define STUFFING_SEGMENT 0xFF + +#include "fifo.h" + +// from VDR remux.c + +void PesSetPts(uchar *p, int64_t Pts) +{ + p[ 9] = ((Pts >> 29) & 0x0E) | (p[9] & 0xF1); + p[10] = Pts >> 22; + p[11] = ((Pts >> 14) & 0xFE) | 0x01; + p[12] = Pts >> 7; + p[13] = ((Pts << 1) & 0xFE) | 0x01; +} + +void PesSetDts(uchar *p, int64_t Dts) +{ + p[14] = ((Dts >> 29) & 0x0E) | (p[14] & 0xF1); + p[15] = Dts >> 22; + p[16] = ((Dts >> 14) & 0xFE) | 0x01; + p[17] = Dts >> 7; + p[18] = ((Dts << 1) & 0xFE) | 0x01; +} + +int64_t PtsDiff(int64_t Pts1, int64_t Pts2) +{ + int64_t d = Pts2 - Pts1; + if (d > MAX33BIT / 2) + return d - (MAX33BIT + 1); + if (d < -MAX33BIT / 2) + return d + (MAX33BIT + 1); + return d; +} + +// from VDR dvbsubtitle.c to assemble subs - DIH. + +// --- cDvbSubtitleAssembler ------------------------------------------------- + +class cDvbSubtitleAssembler { +private: + uchar *data; + int length; + int pos; + int size; + bool Realloc(int Size); +public: + cDvbSubtitleAssembler(void); + virtual ~cDvbSubtitleAssembler(); + void Reset(void); + unsigned char *Get(int &Length); + void Put(const uchar *Data, int Length); + +}; + +cDvbSubtitleAssembler::cDvbSubtitleAssembler(void) +{ + data = NULL; + size = 0; + Reset(); +} + +cDvbSubtitleAssembler::~cDvbSubtitleAssembler() +{ + free(data); +} + +void cDvbSubtitleAssembler::Reset(void) +{ + length = 0; + pos = 0; +} + +bool cDvbSubtitleAssembler::Realloc(int Size) +{ + if (Size > size) { + Size = max(Size, 2048); + if (uchar *NewBuffer = (uchar *)realloc(data, Size)) { + size = Size; + data = NewBuffer; + } else { + DEBUG("ERROR: can't allocate memory for subtitle assembler\n"); + length = 0; + size = 0; + free(data); + data = NULL; + return false; + } + } + return true; +} + +unsigned char *cDvbSubtitleAssembler::Get(int &Length) +{ + if (length > pos + 5) { + Length = (data[pos + 4] << 8) + data[pos + 5] + 6; + if (length >= pos + Length) { + unsigned char *result = data + pos; + pos += Length; + return result; + } + } + return NULL; +} + +void cDvbSubtitleAssembler::Put(const uchar *Data, int Length) +{ + if (Length && Realloc(length + Length)) { + memcpy(data + length, Data, Length); + length += Length; + } +} + +// --- cPES -------------------------------------------------------------------- + +cPES::cPES(eRule ru) +{ + rb=new cRingBufferFrame(KILOBYTE(50)); + defaultRule=ru; + Reset(); + dvbSubtitleAssembler = new cDvbSubtitleAssembler; + fifo_init(&fifo, pktbuf, sizeof(pktbuf)); +} + +cPES::~cPES() +{ + delete rb; + //?? + delete dvbSubtitleAssembler; +} + +void cPES::Reset(void) +{ + for(int i=0 ; i<NUM_RULESETS ; i++) SetDefaultRule(defaultRule,i); + UseRuleset(0); + ClearSeen(); + Clear(); +} + +void cPES::Clear(void) +{ + Lock(); + mode=pmNewSync; frame=0; mpegType=2; + rb->Clear(); + Unlock(); +} + +bool cPES::ValidRuleset(const int num) +{ + if(num>=0 && num<NUM_RULESETS) return true; + DEBUG("PES: illegal ruleset %d\n",num); + return false; +} + +void cPES::UseRuleset(const int num) +{ + if(ValidRuleset(num)) { + currRules=rules[num]; + currNum=num; + } +} + +int cPES::CurrentRuleset(void) +{ + return currNum; +} + +void cPES::SetDefaultRule(eRule ru, const int num) +{ + if(ValidRuleset(num)) { + for(int i=0 ; i<NUM_RULES ; i++) { + rules[num][i]=ru; + } + } +} + +void cPES::SetRule(uchar type, eRule ru, const int num) +{ + if(ValidRuleset(num)) { + rules[num][type]=ru; + } +} + +void cPES::SetRuleR(uchar ltype, uchar htype, eRule ru, const int num) +{ + if(ValidRuleset(num)) { + if(ltype<htype) { + for(; ltype<=htype ; ltype++) { + rules[num][ltype]=ru; + } + } else { + DEBUG("PES: bad range %x-%x\n",ltype,htype); + } + } +} + +unsigned int cPES::Seen(uchar type) const +{ + return seen[type]; +} + +void cPES::ClearSeen(void) +{ + memset(seen,0,sizeof(seen)); + totalBytes=totalSkipped=totalZeros=0; + skipped=zeros=0; +} + +void cPES::Skip(uchar *data, int count) +{ + if(data) { + Skipped(data,count); + while(count>0) { + skipped++; + if(!*data++) { + zeros++; + } + count--; + } + } else if(skipped) { + totalSkipped+=skipped; + if(skipped==zeros) { + totalZeros+=zeros; + } else { + DEBUG("PES: skipped %d bytes\n",skipped); + } + skipped=zeros=0; + } +} + +void cPES::Statistics(bool build) +{ + if(totalBytes) { + printf("PES: Stats %ld kbytes total, %ld kbytes skipped, %ld zero-gaps\n", + ROUNDUP(totalBytes,1024),ROUNDUP(totalSkipped,1024),totalZeros); + if (build) { + printf("PES: Stats %ld kbytes subtitle data output: %d subs packets (%d short packets)\n", totsubsize/1024, subspkts, errpkts); + } + for(int type=0 ; type<=0xFF ; type++) { + if(seen[type]) { + printf("PES: Stats %02X: %d packets PTS-first: %jd PTS-last: %jd\n",type,seen[type],ptsfirst[type], ptslast[type]); + } + } + } +} + +void cPES::ModifyPaketSize(int mod) +{ + if(SOP) { + int size=header[4]*256 + header[5] + mod; + header[4]=(size>>8)&0xFF; + header[5]=(size )&0xFF; + } else { + DEBUG("PES: modify paket size called in middle of packet\n"); + } +} + +void cPES::Redirect(eRule ru) +{ + if(SOP) { + currRule=ru; + redirect=true; + } else { + DEBUG("PES: redirect called in middle of packet\n"); + } +} + +int cPES::HeaderSize(uchar *head, int len) +{ + if(len<PES_MIN_SIZE) return -PES_MIN_SIZE; + switch(head[3]) { + default: + // Program end + case 0xB9: + // Programm stream map + case 0xBC: + // video stream start codes + case 0x00 ... 0xB8: + // reserved + case 0xF0 ... 0xFF: + return PES_MIN_SIZE; + + // Pack header + case 0xBA: + if(len<5) return -5; + switch(head[4]&0xC0) { + default: + DEBUG("PES: unknown mpegType in pack header (0x%02x)\n",head[4]); + // fall through + case 0x00: + mpegType=1; + return 12; + case 0x40: + mpegType=2; + if(len<14) return -14; + return 14+(head[13]&0x07); // add stuffing bytes + } + + // System header + case 0xBB: + if(len<6) return -6; + return 6+head[4]*256+head[5]; //XXX+ is there a difference between mpeg1 & mpeg2?? + + // Padding stream + case 0xBE: + // Private stream2 (navigation data) + case 0xBF: + return 6; //XXX+ is there a difference between mpeg1 & mpeg2?? + + // all the rest (the real packets) + // Private stream1 + case 0xBD: + case 0xC0 ... 0xCF: + case 0xD0 ... 0xDF: + //video + case 0xE0 ... 0xEF: + if(len<7) { + return -7; + } + int index=6; + + while((head[index]&0xC0)==0xC0) { // skip stuffing bytes + index++; + if (index >= len) { + return -(index+1); + } + } + + if((head[index]&0xC0)==0x80) { // mpeg2 + mpegType=2; + index+=2; + if(index>=len) { + return -(index+1); + } + return index+1+head[index]; // mpeg2 header data bytes + } + mpegType=1; + if((head[index]&0xC0)==0x40) { // mpeg1 buff size + index+=2; + if(index>=len) { + return -(index+1); + } + } + + switch(head[index]&0x30) { + case 0x30: + index+=9; + break; // mpeg1 pts&dts + case 0x20: + index+=4; + break; // mpeg1 pts + case 0x10: + DEBUG("PES: bad pts/dts flags in MPEG1 header (0x%02x)\n",head[index]); + break; + } + return index+1; + } +} + +int cPES::PacketSize(uchar *head, int len) +{ + switch(head[3]) { + default: + // video stream start codes + case 0x00 ... 0xB8: + // Program end + case 0xB9: + // Pack header + case 0xBA: + // System header + case 0xBB: + // Programm stream map + case 0xBC: + // reserved + case 0xF0 ... 0xFF: + return len; // packet size = header size + + // Private stream1 + case 0xBD: + // Padding stream + case 0xBE: + // Private stream2 (navigation data) + case 0xBF: + // all the rest (the real packets) + case 0xC0 ... 0xCF: + case 0xD0 ... 0xDF: + case 0xE0 ... 0xEF: + return 6 + head[4]*256 + head[5]; + } +} + +int cPES::Return(int used, int len) +{ + PD("PES: return used=%d len=%d mode=%d\n",used,len,mode); + if(SOP && unsavedHeader && used>=len) { + // if we are about to finish the current data packet and we have + // an unsaved header inside, we must save the header to the buffer + memcpy(hbuff,header,headerSize); + header=hbuff; + unsavedHeader=false; + PD("PES: header saved\n"); // RF don't see this + } + if(used>len) { + DEBUG("PES: BUG! used %d > len %d\n",used,len); + used=len; + } + if(used>0) { + totalBytes+=used; + } + + Unlock(); // release lock from Process() + return used; +} + +// RF Note that this is called with file chunks - do not assume ends on whole packet boundary! +int cPES::Process(const uchar *data, int len) +{ + Lock(); // lock is released in Return() + PD("PES: enter data=%p len=%d mode=%d have=%d need=%d old=%d\n", + data,len,mode,have,need,old); + int used=0; + static int size; + static uchar headerstore[64]; + static int savedheadersize; + static int overrun; + static bool started; + + while(used<len) { + uchar *c=(uchar *)data+used; + int rest=len-used, n; + switch(mode) { + case pmNewSync: + PD("PES: new sync from %d, rest %d\n",used,rest); + have=old=need=0; + unsavedHeader=false; + outputHeader=true; + SOP=true; + redirect=false; + mode=pmFastSync; + // fall through + + case pmFastSync: + // a short cut for the most common case + // if matched here, header isn't copied around + if(rest>=PES_MIN_SIZE) { + PD("PES: fastsync try used=%d: %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + used,c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8]); + if(c[0]==0x00 && c[1]==0x00 && c[2]==0x01) { + headerSize=HeaderSize(c,rest); + if(headerSize>0 && rest>=headerSize) { + // found a packet start :-) + PD("PES: fastsync hit used=%d headerSize=%d rest=%d\n",used,headerSize,rest); + header=c; + unsavedHeader=true; + used+=headerSize; + mode=pmHeaderOk; + continue; + } + } + else if(c[2]!=0x00) { + used+=3; + Skip(c,3); + continue; + } + else { + used++; + Skip(c); + continue; + } + } + // copy remaining bytes to buffer + memcpy(hbuff,c,rest); + have=old=rest; + used+=rest; + mode=pmSync; + PD("PES: buffering started old=%d\n",old); + break; + + case pmSync: + PD("PES: slowsync have=%d old=%d\n",have,old); + if(have<PES_MIN_SIZE && rest>=1) { + hbuff[have++]=c[0]; + used++; + continue; + } + if(have>=PES_MIN_SIZE) { + PD("PES: slowsync try used=%d: %02x %02x %02x %02x\n", + used,hbuff[0],hbuff[1],hbuff[2],hbuff[3]); + if(hbuff[0]==0x00 && hbuff[1]==0x00 && hbuff[2]==0x01) { + need=abs(HeaderSize(hbuff,have)); + mode=pmGetHeader; + continue; + } + // no sync found, move buffer one position ahead + have--; Skip(hbuff); + memmove(hbuff,hbuff+1,have); + // if all bytes from previous data block used up, switch to FastSync + if(!--old) { + used=0; mode=pmFastSync; + PD("PES: buffering ended\n"); + } + continue; + } + break; + + case pmGetHeader: + if(have<need) { + n=min(need-have,rest); + memcpy(hbuff+have,c,n); + have+=n; + used+=n; + PD("PES: get header n=%d need=%d have=%d used=%d\n",n,need,have,used); + continue; + } + if(have>=need) { + need=abs(HeaderSize(hbuff,have)); + if(have<need) continue; + // header data complete + PD("PES: slowsync hit used=%d\n",used); + if(have>need) DEBUG("PES: bug, buffered too much. have=%d need=%d\n",have,need); + if(have>(int)sizeof(hbuff)) DEBUG("PES: bug, header buffer overflow. have=%d size=%d\n",have,(int)sizeof(hbuff)); + headerSize=need; + header=hbuff; + mode=pmHeaderOk; + } + break; + + case pmHeaderOk: + type=header[3]; + seen[type]++; + if (PesHasPts(header) && len > 6) { + ptslast[type]=PesGetPts(header); + if (!ptsfirst[type]) { + ptsfirst[type]=ptslast[type]; + } + } + Skip(0); + if(type<=0xB8) { + // packet types 0x00-0xb8 are video stream start codes + DEBUG("PES: invalid packet type 0x%02x, skipping\n",type); + mode=pmNewSync; + break; + } + payloadSize=PacketSize(header,headerSize)-headerSize; + if(payloadSize<0) { + DEBUG("PES: invalid payloadsize %d, skipping\n",payloadSize); + mode=pmNewSync; + break; + } + PD("PES: found sync at offset %d, type %02x, length %d, next expected %d\n", + used-headerSize,type,headerSize+payloadSize,used+payloadSize); + PD("PES: header type=%02x mpeg=%d header=%d payload=%d:", + type,mpegType,headerSize,payloadSize); + for(int i=0 ; i<headerSize ; i++) { + PD(" %02x",header[i]); + } + PD("\n"); + currRule=currRules[type]; + have=need=0; + mode=pmPayload; + // fall through + + case pmPayload: + n=min(payloadSize-have,rest); + if(!n) { + if(payloadSize==0) { + n=1; + } else { + break; + } + } + // RF we crash into this 2nd time round, if have header (usual). So subhead set + PD("PES: payload have=%d n=%d SOP=%d\n",have,n,SOP); + switch(currRule) { + default: + DEBUG("PES: bug, unknown rule %d, assuming pass\n",currRule); + // fall through + case prPass: n=n; break; + case prSkip: n=-n; break; + case prAct1: n=Action1(type,c,n); break; + case prAct2: n=Action2(type,c,n); break; +// case prAct3: n=Action3(type,c,n); break; + case prAct3: + /* RF Because we have to assemble AN ENTIRE PES packet of possibly several existing 2k PES packets, + and usually several subtitle segments, then as far as the long main loop is concerned, + we drop all the subs packets regardless. + + VDR1.x packets can contain fragments of segments too + + We have no idea how long we have to wait - need END_OF_DISPLAY_SET_SEGMENT to complete a PES + Then write out the assembled PES independently, hoping it's PTS doesn't + upset something downstream. Should in any case be indpendent, but ffmpeg is touchy + */ + n = -n; //drop all content for main loop + if (-n ) { + int PayloadOffset = headerSize; + bool ResetSubtitleAssembler = 0; + //VDR 1.x subs, skip special 4 byte "substream header" (VDR always strips all packets) + int SubstreamHeaderLength = (have == 0 ? 4 : 0); // Full packet, not a continuation chunk from file + // c points to the payload only now + if (SOP && SubstreamHeaderLength) { // only if we have it + ResetSubtitleAssembler = c[3] == 0x00; + //PES hdr extension, RF original doesn't match vanilla 1.X recordings + if ((header[7] & 0x01) && (header[PayloadOffset - 3] & 0x81) == 0x01 && header[PayloadOffset - 2] == 0x81) { + PayloadOffset--; + SubstreamHeaderLength = 1; + ResetSubtitleAssembler = (header[8] >= 5); + } + if (PesHasPts(header)) { + int64_t oldpts = ptsnow[type]; + ptsnow[type] = PesGetPts(header); + if (ptsnow[type] < oldpts) { + // Non-monotonic subs send ffmpeg loopy + printf("PES: OOPs Non-monotonic PTS at %jd (previous %jd!) - packet dropped\n",ptsnow[type],oldpts); + break; + } + int64_t ptsdiff = (PtsDiff(ptsnow[0xc0],ptsnow[type]))/90; //millisecs + DEBUG("PES: subtitle packet pts %jd, latest main audio pts %jd, subs ahead %jdms\n",ptsnow[type],ptsnow[0xc0],ptsdiff); + if (ptsnow[0xc0] && abs((int)ptsdiff) > 5000) { + printf("genindex: OOPs large subs timing offset! %jdms\n",ptsdiff); + } + if (headerSize +2 > (int)sizeof (headerstore)) { + printf("PES: OOPs huge header! %d at %jd\n",headerSize,ptsnow[type]); + exit(1); + } + if (overrun) { // continuation packets don't have a PTS AFAIK + DEBUG("PES: Suspect %d bytes left at start of new packet!\n",overrun); + } + + memcpy (headerstore,header,headerSize); + headerstore[6] |= 0x01; // Set "Original" - per trouble-free .ts FWiW + +// if (audiopts) PesSetPts(headerstore,audiopts); // use existing if no audiopts + +// Working on theory that ffmpeg wants a DTS for some reason, copy the PTS +// - it turns out that ffmpeg does this internally. +// PesSetDtsbit(headerstore); +// PesSetDts(headerstore,ptsnow[type]-18000); // ~200ms earlier similar to other streams +// headerstore[8]+=5; // 5 for DTS (we know it has PES ext:this is safe) + + savedheadersize = headerSize+2+0; // 5 for DTS + memset (headerstore+headerSize+0,0x20,1); // subs data_identifier which is stripped below + memset (headerstore+headerSize+1+0,0x00,1); // stream_id + } + } + + if (-n > SubstreamHeaderLength) { + const uchar *data = c + SubstreamHeaderLength; // skip substream header + int length = -n - SubstreamHeaderLength; // skip substream header + if (ResetSubtitleAssembler) { + dvbSubtitleAssembler->Reset(); + } + if (length > 3) { + int Count, offset; + int substotalthispacket=0; + if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) { + offset = 2; + } else { + offset = 0; + } + int subslength = length - offset; + DEBUG("PES: Put payload length %d into dvbSubtitleAssembler\n",subslength); + dvbSubtitleAssembler->Put(data + offset, subslength); + while (true) { + unsigned char *b = dvbSubtitleAssembler->Get(Count); // Count = seg size *req'd*, not actual available! + if (b && b[0] == 0x0F && Count > 5) { + // a complete segement + uchar segmentType = b[1]; + if (segmentType == STUFFING_SEGMENT) { + continue; + } + if (fifo_write(&fifo, b, Count) != Count) { + DEBUG("PES: FIFO error\n"); + Return(used,len); + } + substotalthispacket+=Count-overrun; + size+=Count; + DEBUG("PES: subtitle complete segment length %d type %02X, subs in fifo %d (used %d this segment)\n",Count, segmentType, size, Count-overrun); + if (Count-overrun < 0) { + DEBUG("PES: Suspect overrun of %d bytes!\n",overrun); + } + overrun=0; // must have used up all available data at start ofa new segment + + if (segmentType == END_OF_DISPLAY_SET_SEGMENT) { + uchar outbuf[KILOBYTE(64)]; + if (fifo_read(&fifo, outbuf, size) != size) { + DEBUG("PES: FIFO error\n"); + Return(used,len); + } + // skip the small 10...80 subs periodic "cleardown" packets at beginning of file only. + // These currently prevent reliable ffmpeg subs detection + int err = dvbsub_probe(outbuf,size,ptsnow[type]); + if (err == 1) started = 1; + if (started) { + if (err < 0 ) errpkts++; // mark it, but pass all the same as should be validly assembled + outbuf[size++] = 0xff; // end_of_PES_data_field_marker + totsubsize+=size; // Actual subs segment data = should match ffmpeg report + subspkts+=1; + int pkt = savedheadersize -6 + size; //6 for 00.00.01.bd.HH.hh, 1 for end_of_PES_data_field_marker + headerstore[4]=(pkt>>8)&0xFF; + headerstore[5]=(pkt )&0xFF; + // Now can output ALL re-assembled subs in a SINGLE PES pkt + DEBUG("PES: *** subtitle complete PES packet with subs of %d bytes @pts %jd ***\n",size, ptsnow[type]); + PD("PES: output buffered subtitle header=%p count=%d\n",headerstore,savedheadersize); + int r=Output(headerstore,savedheadersize); + if(r<0) return Return(-1,len); + PD("PES: output buffered subtitle count=%d\n",size); + r=Output(outbuf,size); + if(r<0) return Return(-1,len); + } else { + DEBUG("PES: Skipping inital short subs packet for ffmpeg compatibility!\n"); + } + size = 0; + } + } else { + if (Count > subslength - substotalthispacket) { +// if (c[SubstreamHeaderLength+offset+substotalthispacket] == 0xff) { + if (subslength - substotalthispacket < 6) { //cruft or stuffing + break; + } + // overrun the PES packet. Keep building packet with next PES segement. + overrun += subslength - substotalthispacket; + DEBUG("PES: subtitle incomplete segment, requires %d vs. %d available (needs %d)\n",Count, overrun, Count-overrun); + if (overrun < 0) { + printf("PES: BUG! Available %d bytes, @pts %jd ***\n",overrun, ptsnow[type]); + exit (1); + } + } + break; + } + } + // We have a complete disassembly of the sub(s) + } + } + } + break; + case prAct4: n=Action4(type,c,n); break; + case prAct5: n=Action5(type,c,n); break; + case prAct6: n=Action6(type,c,n); break; + case prAct7: n=Action7(type,c,n); break; + case prAct8: n=Action8(type,c,n); break; + } + if(n==0) { + if(redirect) { + redirect=false; + continue; + } + return Return(used,len); + } + need=n; + SOP=false; + mode=pmRingGet; + // fall through + + case pmRingGet: + frame=rb->Get(); + if(frame) { // RF doesn't happen + outCount=frame->Count(); + outData=(uchar *)frame->Data(); + PD("PES: ringbuffer got frame %p count=%d\n",frame,outCount); + nextMode=pmRingDrop; + mode=pmOutput; + break; + } + mode=pmDataPut; + // fall through + + case pmDataPut: + if(need<0) { //for skipped + need=-need; + outputHeader=false; + mode=pmDataReady; + continue; + } + if(outputHeader) { + outData=header; + outCount=headerSize; + outputHeader=false; + nextMode=pmDataPut; + } + else if(payloadSize) { + outData=c; + outCount=need; + nextMode=pmDataReady; + } else { + mode=pmDataReady; + continue; + } + mode=pmOutput; + // fall through + + case pmOutput: + for(;;) { + PD("PES: output data=%p count=%d -> ",outData,outCount); + n=Output(outData,outCount); + PD("n=%d\n",n); + if(n<0) return Return(-1,len); + if(n==0) return Return(used,len); + outCount-=n; + outData+=n; + if(outCount<=0) { + mode=nextMode; + break; + } + } + break; + + case pmDataReady: + if(payloadSize) { + used+=need; + have+=need; + } + PD("PES: data ready need=%d have=%d paySize=%d used=%d\n", + need,have,payloadSize,used); + if(have>=payloadSize) { + PD("PES: packet finished\n"); + if(have>payloadSize) DEBUG("PES: payload exceeded, size=%d have=%d\n",payloadSize,have); + mode=pmNewSync; + } else { + mode=pmPayload; + } + break; + + case pmRingDrop: + PD("PES: ringbuffer drop %p\n",frame); + rb->Drop(frame); frame=0; + mode=pmRingGet; + break; + + default: + DEBUG("PES: bug, bad mode %d\n",mode); + return Return(-1,len); + } + } + PD("PES: leave\n"); + return Return(used,len); +} + + +// From ffmpeg - This is the code used to score the dvbsub packets +// (If there aren't a complete set of segments 0x10 - 13 & 5 segments total, not useful pkt) +// ***NOTE*** This is modified from ffmpeg which appears to have logical bugs + +int cPES::dvbsub_probe(uchar *p, int size, int64_t pts) +{ + int i, j, k; + const uint8_t *end = p + size; + int type, len; + int max_score = 0; + const uint8_t *ptr = p; + + for(i=0; i<size; i++) { + if (p[i] == 0x0f) { + ptr = p + i; + uint8_t histogram[6] = {0}; + int min = 255; + for(j=0; 5 < end - ptr; j++) { // was 6. Relevant to this environment as tested pkt doesn't have 0xff tail + if (*ptr != 0x0f) { + if (*ptr == 0xff) { + ptr++; // similar to dvbsub_decode, continue if (allowable) stuffing + continue; + } else { + break; + } + } + type = ptr[1]; +// page_id = AV_RB16(ptr + 2); +// len = AV_RB16(ptr + 4); + len = (*(ptr+4) << 8) + *(ptr+5); + if (type == 0x80) { + ; + } else if (type >= 0x10 && type <= 0x14) { + histogram[type - 0x10] ++; + } else + break; + if (6 + len > end - ptr) + break; + ptr += 6 + len; + } + for (k=0; k < 4; k++) { + min = FFMIN(min, histogram[k]); + } + if (min && j > max_score) + max_score = j; + } + if (max_score >= 5) { // was > 5. Test was in outer loop, not clear why, can invalidate good scores + return 1; + } else { + // if (end - ptr > 6) { + // printf("PES: dvbsub probe warning hex offset %04X in packet pts %jd : %02X %02X %02X %02X %02X %02X %02X %02X\n",int(ptr-p),pts,p[i],p[i+1],p[i+2],p[i+3],p[i+4],p[i+5],p[i+6],p[i+7]); + // return -1; + // } + + // if it wasn't one of the expected short clear-down packets + margin, flag it as suspect + return -1; + // If i incremented after scoring, stomps though FROM START + i looking for anything like 0f. + // So don't stomp once started it's not helpful, be happy with what you have + } + } + return 0; +} |