diff options
author | Jochen Dolze <vdr@dolze.de> | 2011-01-29 15:58:36 +0100 |
---|---|---|
committer | Jochen Dolze <vdr@dolze.de> | 2011-01-29 15:58:36 +0100 |
commit | 70d113055698c6b73c8ed13af8a9e2f3b38ab1f0 (patch) | |
tree | d5264ada5b3a1366ca2404468dd57c6ea62a6349 /command/demux.cpp | |
parent | 9e964370ba635f57df44a96506fc4bf633004a86 (diff) | |
download | vdr-plugin-markad-70d113055698c6b73c8ed13af8a9e2f3b38ab1f0.tar.gz vdr-plugin-markad-70d113055698c6b73c8ed13af8a9e2f3b38ab1f0.tar.bz2 |
Rewrite of demux/marks recognition (still incomplete)
Diffstat (limited to 'command/demux.cpp')
-rw-r--r-- | command/demux.cpp | 1247 |
1 files changed, 1083 insertions, 164 deletions
diff --git a/command/demux.cpp b/command/demux.cpp index 6f58dde..9c9bcc8 100644 --- a/command/demux.cpp +++ b/command/demux.cpp @@ -5,282 +5,1201 @@ * */ +#include <string.h> #include "demux.h" +extern "C" +{ +#include "debug.h" +} -#include <string.h> +#include <stdlib.h> -cMarkAdDemux::cMarkAdDemux() +#ifndef TS_SIZE +#define TS_SIZE 188 +#endif + +cPaketQueue::cPaketQueue(const char *Name, int Size) { - ts2pkt=NULL; - pes2audioes=NULL; - pes2videoes=NULL; - pause=false; - pause_retval=0; - min_needed=0; - skip=0; - queue = new cMarkAdPaketQueue(NULL,2176); + maxqueue=Size; + if (Name) + { + name=strdup(Name); + } + else + { + name=NULL; + } + buffer=(uchar *) malloc(Size+8); + if (!buffer) maxqueue=0; + memset(&pktinfo,0,sizeof(pktinfo)); + percent=-1; + mpercent=0; + Clear(); } -cMarkAdDemux::~cMarkAdDemux() +void cPaketQueue::Clear() { - if (ts2pkt) delete ts2pkt; - if (pes2audioes) delete pes2audioes; - if (pes2videoes) delete pes2videoes; - if (queue) delete queue; + inptr=outptr=0; + pktinfo.pkthdr=-1; + scanner=0xFFFFFFFF; + scannerstart=-1; + skipped=0; } -void cMarkAdDemux::Clear() +cPaketQueue::~cPaketQueue() { - if (ts2pkt) ts2pkt->Clear(); - if (pes2audioes) pes2audioes->Clear(); - if (pes2videoes) pes2videoes->Clear(); - if (queue) queue->Clear(); - pause=false; - pause_retval=0; - min_needed=0; - skip=0; + if (name) + { + tsyslog("buffer usage : %-15s %3i%%",name,mpercent); + free((void *) name); + } + if (buffer) free(buffer); } -void cMarkAdDemux::ProcessVDR(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *Pkt) +void cPaketQueue::Resize(int NewSize, const char *NewName) { - if (!Pkt) return; + if (NewName) + { + if (name) free((void *) name); + name=strdup(NewName); + } + if (inptr<NewSize) + { + buffer=(uchar *) realloc(buffer,NewSize+8); + if (buffer) + { + maxqueue=NewSize; + } + else + { + maxqueue=0; + Clear(); + } + } +} - if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) || (Pid.Type==MARKAD_PIDTYPE_AUDIO_MP2)) +int cPaketQueue::findpktheader(int start, int *streamsize,int *headersize, bool longstartcode, bool pesonly=false) +{ + if ((!streamsize) || (!headersize)) return -1; + if (!start) start=outptr; + if (start>=inptr) return -1; + *streamsize=0; + if (longstartcode) + { + *headersize=4; // 0x0 0x0 0x0 0x1 + } + else + { + *headersize=3; // 0x0 0x0 0x1 + } + int i; + + if (scanner!=0xFFFFFFFF) + { + scanner<<=8; + scanner|=buffer[start++]; + } + + bool found=false; + for (i=start; i<inptr; i++) + { + if (longstartcode) + { + if (scanner==1L) + { + found=true; + break; + } + if ((scanner & 0xFFFFFFF0)==0x1E0L) + { + found=true; + break; + } + } + else + { + if ((scanner & 0x00FFFFFF)==1L) + { + if (pesonly) + { + if (buffer[i]>=0xBC) + { + found=true; + break; + } + } + else + { + found=true; + break; + } + } + } + scanner<<=8; + scanner|=buffer[i]; + } + if (!found) { - if (!pes2audioes) pes2audioes=new cMarkAdPES2ES((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) ? - "PES2ES AC3" : "PES2ES MP2"); - if (!pes2audioes) return; - pes2audioes->Process(Pid,Data,Count,Pkt); + if (longstartcode) + { + if (scanner==1L) + { + found=true; + } + if ((scanner & 0xFFFFFFF0)==0x1E0L) + { + found=true; + } + } + else + { + if (((scanner & 0x00FFFFFF)==1L) && (!pesonly)) + { + found=true; + } + } } - if ((Pid.Type==MARKAD_PIDTYPE_VIDEO_H262) || (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264)) + if (i==inptr) { - if (!pes2videoes) + if (found) { - if (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264) + if (!start) { - pes2videoes=new cMarkAdPES2ES("PES2H264ES",425984); + scanner=0xFFFFFFFF; + return -1; + } + } + else + { + return -1; + } + } + if (longstartcode) i--; + if (buffer[i]>=0xBC) // do we have a PES packet? + { +#define PESHDRSIZE 6 + if ((i+PESHDRSIZE)>inptr) + { + return -1; // we need more data (for streamsize and headersize) + } + + *streamsize=(buffer[i+1]<<8)+buffer[i+2]; + if (*streamsize) (*streamsize)+=PESHDRSIZE; // 6 Byte PES-Header + if (longstartcode) + { + struct PESHDROPT *peshdropt=(struct PESHDROPT *) &buffer[i+3]; + if (peshdropt->MarkerBits==0x2) + { + *headersize=PESHDRSIZE+sizeof(struct PESHDROPT)+ + peshdropt->Length; } else { - pes2videoes=new cMarkAdPES2ES("PES2H262ES",65536); + *headersize=PESHDRSIZE; } } - if (!pes2videoes) return; - pes2videoes->Process(Pid,Data,Count,Pkt); } - return; + return i-3; } -void cMarkAdDemux::ProcessTS(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *Pkt) +int cPaketQueue::findaudioheader(int start, int *framesize, int *headersize, bool ac3) { - if (!Pkt) return; + if ((!framesize) || (!headersize)) return -1; + if (!start) start=outptr; + if (start>=inptr) return -1; + (*framesize)=0; + if (ac3) + { + (*headersize)=2; + } + else + { + (*headersize)=3; + } + int i; - MarkAdPacket pkt; - memset(&pkt,0,sizeof(pkt)); + if (scanner!=0xFFFFFFFF) + { + scanner<<=8; + scanner|=buffer[start++]; + } + else + { + scanner<<=8; + scanner|=buffer[start++]; + scanner<<=8; + scanner|=buffer[start++]; + } - if (!ts2pkt) + for (i=start; i<inptr; i++) { - switch (Pid.Type) + + if (ac3) { - case MARKAD_PIDTYPE_VIDEO_H264: - ts2pkt=new cMarkAdTS2Pkt("TS2H264",819200); - break; + if ((scanner & 0x0000FFFF)==0xB77L) break; + } + else + { + if ((scanner & 0x0000FFE0)==0xFFE0L) break; + } - case MARKAD_PIDTYPE_VIDEO_H262: - ts2pkt=new cMarkAdTS2Pkt("TS2H262",262144); - break; + scanner<<=8; + scanner|=buffer[i]; + } + if (i==inptr) return -1; - case MARKAD_PIDTYPE_AUDIO_AC3: - ts2pkt=new cMarkAdTS2Pkt("TS2PES AC3",32768); - break; + i-=2; + + if (ac3) + { + struct AC3HDR *ac3hdr = (struct AC3HDR *) &buffer[i]; + + if (ac3hdr->SampleRateIndex==3) return -1; // reserved + if (ac3hdr->FrameSizeIndex>=38) return -1; // reserved - case MARKAD_PIDTYPE_AUDIO_MP2: - ts2pkt=new cMarkAdTS2Pkt("TS2PES MP2",16384); + if (framesize) + { + int bitRatesAC3[3][38] = // all values are specified as kbits/s + { + { 64, 64, 80, 80, 96, 96, 112, 112, 128, 128, 160, 160, 192, 192, + 224, 224, 256, 256, 320, 320, 384, 384, 448, 448, 512, 512, + 640, 640, 768, 768, 896, 896, 1024, 1024, 1152, 1152, 1280, 1280 }, // 48kHz + + { 69, 70, 87, 88, 104, 105, 121, 122, 139, 140, 174, 175, 208, 209, + 243, 244, 278, 279, 348, 349, 417, 418, 487, 488, 557, 558, + 696, 697, 835, 836, 975, 976, 1114, 1115, 1253, 1254, 1393, 1394 }, // 44.1kHz + + { 96, 96, 120, 120, 144, 144, 168, 168, 192, 192, 240, 240, 288, + 288, 336, 336, 384, 384, 480, 480, 576, 576, 672, 672, 768, + 768, 960, 960, 1152, 1152, 1344, 1344, 1536, 1536, 1728, 1728, 1920,1920 } // 32kHz + }; + + *framesize=2*bitRatesAC3[ac3hdr->SampleRateIndex][ac3hdr->FrameSizeIndex]; + } + return i; + } + else + { + struct MP2HDR *mp2hdr = (struct MP2HDR *) &buffer[i]; + if (mp2hdr->MpegID==1) return -1; // reserved + if (mp2hdr->Layer==0) return -1; // reserved + if (mp2hdr->BitRateIndex==0xF) return -1; // forbidden + if (mp2hdr->SampleRateIndex==3) return -1; //reserved + if (mp2hdr->Emphasis==2) return -1; // reserved + + if (framesize) + { + int bitRates[3][3][16] = // all values are specified as kbits/s + { + { + { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, // M1, L1 + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, // M1, L2 + { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 } // M1, L3 + }, + { + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // M2, L1 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // M2, L2 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // M2, L3 + }, + { + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // M2.5, L1 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // M2.5, L2 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // M2.5, L3 + } + }; + + int samplingFrequencies[3][4] = // all values are specified in Hz + { + { 44100, 48000, 32000, -1 }, // MPEG 1 + { 22050, 24000, 16000, -1 }, // MPEG 2 + { 32000, 16000, 8000, -1 } // MPEG 2.5 + }; + + + int slots_per_frame[3][3] = + { + { 12, 144, 144 }, // MPEG 1, Layer I, II, III + { 12, 144, 72 }, // MPEG 2, Layer I, II, III + { 12, 144, 72 } // MPEG 2.5, Layer I, II, III + }; + + int mpegIndex; + switch (mp2hdr->MpegID) + { + case 0: + mpegIndex=2; + break; + case 2: + mpegIndex=1; + break; + case 3: + mpegIndex=0; + break; + default: + mpegIndex=0; // just to get rid of compiler warnings ;) + } + int layerIndex = 3 - mp2hdr->Layer; + + // Layer I (i. e., layerIndex == 0) has a larger slot size + int slotSize = (layerIndex == 0) ? 4 : 1; // bytes + int sf = samplingFrequencies[mpegIndex][mp2hdr->SampleRateIndex]; + + if (mp2hdr->BitRateIndex == 0) + *framesize = 0; // "free" Bitrate -> we don't support this! + else + { + int br = 1000 * bitRates[mpegIndex][layerIndex][mp2hdr->BitRateIndex]; // bits/s + int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots + + *framesize = (N + mp2hdr->Padding) * slotSize; // bytes + } + } + return i; + } +} + +int cPaketQueue::FindPesHeader(int Start) +{ + int start=outptr+Start; + int ssize,hsize; + int pos=findpktheader(start,&ssize,&hsize,false,true); + if (pos==-1) return -1; + pos-=outptr; + return pos; +} + +bool cPaketQueue::Put(uchar *Data, int Size) +{ + if (!buffer) return false; + if ((inptr) && (inptr==outptr)) inptr=outptr=0; + + if (outptr) + { + if (outptr>(inptr-outptr)) + { + memcpy(buffer,&buffer[outptr],inptr-outptr); + if (scannerstart==inptr) scannerstart-=outptr; + inptr-=outptr; + if (pktinfo.pkthdr==outptr) pktinfo.pkthdr=0; + outptr=0; + } + } + + if ((inptr+Size)>maxqueue) + { + if (name) + { + esyslog("buffer %s full",name); + } + else + { + esyslog("buffer full"); + } + mpercent=100; + Clear(); + return false; + } + + memcpy(&buffer[inptr],Data,Size); + inptr+=Size; + + int npercent=(int) ((inptr*100)/maxqueue); + if (npercent>mpercent) mpercent=npercent; + + if ((npercent>90) && (name) && (npercent!=percent)) + { + dsyslog("buffer %s usage: %3i%%", + name,npercent); + percent=npercent; + } + + return true; +} + +uchar *cPaketQueue::Get(int *Size) +{ + if (!buffer) return NULL; + if (!Size) return NULL; + if (Length()<*Size) + { + *Size=0; + return NULL; + } + uchar *ret=&buffer[outptr]; + outptr+=*Size; + return ret; +} + +uchar *cPaketQueue::Peek(int Size) +{ + if (!buffer) return NULL; + if (!Size) return NULL; + if (Length()<Size) return NULL; + uchar *ret=&buffer[outptr]; + return ret; +} + +uchar *cPaketQueue::GetPacket(int *Size, int Type) +{ + if (!Size) return NULL; + *Size=0; + if (Length()<4) return NULL; + + skipped=0; + if (pktinfo.pkthdr==-1) + { + scanner=0xFFFFFFFF; + scannerstart=outptr; + switch (Type) + { + case PACKET_AC3: + pktinfo.pkthdr=findaudioheader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,true); + break; + case PACKET_MP2: + pktinfo.pkthdr=findaudioheader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,false); break; + case PACKET_H264: + pktinfo.pkthdr=findpktheader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,true); + break; + default: + pktinfo.pkthdr=findpktheader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,false); + break; + } + + if (pktinfo.pkthdr==-1) + { + return NULL; + } + else + { + if (pktinfo.pkthdr!=outptr) + { + skipped=pktinfo.pkthdr-outptr; + } + } + scannerstart=pktinfo.pkthdr+pktinfo.pktsyncsize; + } + + int streamsize,pktsyncsize,pkthdr=-1; + + if (pktinfo.streamsize) + { + if ((pktinfo.pkthdr+pktinfo.streamsize)>inptr) + { + return NULL; // need more data + } + else + { + scannerstart=pktinfo.pkthdr+pktinfo.streamsize; + scanner=0xFFFFFFFF; + } + } + + switch (Type) + { + case PACKET_AC3: + pkthdr=findaudioheader(scannerstart,&streamsize,&pktsyncsize,true); + break; + case PACKET_MP2: + pkthdr=findaudioheader(scannerstart,&streamsize,&pktsyncsize,false); + break; + case PACKET_H264: + pkthdr=findpktheader(scannerstart,&streamsize,&pktsyncsize,true); + break; + default: + pkthdr=findpktheader(scannerstart,&streamsize,&pktsyncsize,false); + break; + } + + if (pkthdr==-1) + { + if ((pktinfo.streamsize) && ((inptr-outptr)>pktinfo.streamsize)) + { + // no startcode right after streamsize? + // output streamsize packet + scannerstart=pktinfo.pkthdr+pktinfo.streamsize; + streamsize=pktsyncsize=0; + pkthdr=-1; + } + else + { + scannerstart=inptr; + return NULL; } } - if (!ts2pkt) return; + else + { + scannerstart=pkthdr+pktsyncsize; + } + + uchar *ptr=&buffer[pktinfo.pkthdr]; + + if (pktinfo.streamsize) + { + *Size=pktinfo.streamsize; + } + else + { + *Size=pkthdr-pktinfo.pkthdr; + } + if (pkthdr==-1) + { + outptr=pktinfo.pkthdr+pktinfo.streamsize; + } + else + { + outptr=pkthdr; + } + + pktinfo.pkthdr=pkthdr; + pktinfo.streamsize=streamsize; + pktinfo.pktsyncsize=pktsyncsize; - if (!ts2pkt->Process(Pid,Data,Count,&pkt)) + return ptr; +} + +// ---------------------------------------------------------------------------- + +cTS2Pkt::cTS2Pkt(int Pid, const char *QueueName, int QueueSize, bool H264) +{ + queue=new cPaketQueue(QueueName,QueueSize); + pid=Pid; + h264=H264; + firstsync=false; + Clear(); +} + +cTS2Pkt::~cTS2Pkt() +{ + if (queue) { - if (pes2audioes) pes2audioes->Clear(); - return; + if (skipped) esyslog("buffer skipped: %-15s %i bytes",queue->Name(),skipped); + delete queue; } +} - if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) || (Pid.Type==MARKAD_PIDTYPE_AUDIO_MP2)) +void cTS2Pkt::Clear() +{ + sync=false; + counter=-1; + skipped=0; + noticeFILLER=false; + noticeSEQUENCE=false; + noticeSTREAM=false; + if (queue) queue->Clear(); +} + +bool cTS2Pkt::Process(uchar *TSData, int TSSize, AvPacket *Pkt) +{ + if (!Pkt) return false; + if (TSData) { - if (!pes2audioes) pes2audioes=new cMarkAdPES2ES((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) ? - "PES2ES AC3" : "PES2ES MP2"); - if (!pes2audioes) return; - pes2audioes->Process(Pid,pkt.Data,pkt.Length,Pkt); + if (TSSize!=TS_SIZE) return false; + + struct TSHDR *tshdr = (struct TSHDR *) TSData; + + int ppid=(tshdr->PidH << 8) | tshdr->PidL; + if (ppid!=pid) + { + return false; + } + + if ((counter!=-1) && (((counter+1) & 0xF)!=tshdr->Counter)) + { + if (counter==(int) tshdr->Counter) + { + skipped+=TS_SIZE; + return true; // duplicate paket -> just ignore + } + // sequence error + if (!noticeSEQUENCE) + { + noticeSEQUENCE=true; + isyslog("sequence error"); + } + if (queue) + { + skipped+=queue->Length(); + queue->Clear(); + } + counter=-1; + sync=false; + if (!tshdr->PayloadStart) return true; + } + counter=tshdr->Counter; + + if (tshdr->PayloadStart) + { + firstsync=sync=true; + } + if (!sync) + { + if (firstsync) skipped+=TS_SIZE; // only count skipped bytes after first sync + return true; // not synced + } + + // we just ignore the infos in the adaption field (e.g. OPCR/PCR) + if ((tshdr->AFC!=1) && (tshdr->AFC!=3)) + { + return true; + } + + int buflen=TS_SIZE+1; + uchar *buf=NULL; + + if (tshdr->AFC==1) + { + // payload only + buflen=TS_SIZE-sizeof(struct TSHDR); + buf=&TSData[sizeof(struct TSHDR)]; + } + + if (tshdr->AFC==3) + { + // adaption field + payload + int alen=TSData[4]+1; + if (alen>(TS_SIZE-(int) sizeof(struct TSHDR))) alen=TS_SIZE-(int) sizeof(struct TSHDR); + buflen=TS_SIZE-(sizeof(struct TSHDR)+alen); + buf=&TSData[sizeof(struct TSHDR)+alen]; + } + + if (!buflen) + { + // no data? -> impossible? + return false; + } + + if (tshdr->PayloadStart) + { + if ((buf[0]!=0) && (buf[1]!=0)) + { + if (!noticeSTREAM) + { + isyslog("stream error"); + noticeSTREAM=true; + } + skipped+=TS_SIZE; + sync=false; + if (buflen<7) return false; + // add a pseudo padding stream + buf[0]=0; + buf[1]=0; + buf[2]=1; + buf[3]=0xbe; + buf[4]=0; + buf[5]=buflen-6; + } + } + queue->Put(buf,buflen); } - if ((Pid.Type==MARKAD_PIDTYPE_VIDEO_H262) || (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264)) + Pkt->Data=queue->GetPacket(&Pkt->Length,h264 ? PACKET_H264 : PACKET_H262); + if (Pkt->Data) { - if ((pkt.Data) && ((pkt.Data[3] & 0xF0)==0xE0) && (pkt.Data[4]!=0) && (pkt.Data[5]!=0)) + Pkt->Type=h264 ? PACKET_H264 : PACKET_H262; + if ((h264) && ((Pkt->Data[4] & 0x1F)==0x0C)) { - ts2pkt->InjectVideoPES(pkt.Data,pkt.Length); - pkt.Data=NULL; - pkt.Length=0; + if (!noticeFILLER) + { + isyslog("H264 video stream with filler nalu"); + noticeFILLER=true; + } + skipped+=Pkt->Length; // thats not accurate! + Pkt->Data=NULL; + Pkt->Length=0; + Pkt->Type=0; } - Pkt->Data=pkt.Data; - Pkt->Length=pkt.Length; - Pkt->Skipped=pkt.Skipped; } - return; + else + { + Pkt->Length=0; + Pkt->Type=0; + } + return true; } -int cMarkAdDemux::GetMinNeeded(MarkAdPid Pid, uchar *Data, int Count, bool *Offcnt) +// ---------------------------------------------------------------------------- + +cPES2ES::cPES2ES(int PacketType, const char *QueueName, int QueueSize) { - if (Pid.Num>=0) return TS_SIZE; + queue = new cPaketQueue(QueueName,QueueSize); + ptype=PacketType; + Clear(); +} - uchar *qData=queue->Peek(PESHDRSIZE); - if (!qData) +cPES2ES::~cPES2ES() +{ + if (queue) { - int len=PESHDRSIZE-queue->Length(); - int cnt=(Count>len) ? len : Count; - queue->Put(Data,cnt); - return -cnt; + if (skipped) esyslog("buffer skipped: %-15s %i bytes",queue->Name(),skipped); + delete queue; } +} - int stream=qData[3]; +void cPES2ES::Clear() +{ + skipped=0; + if (queue) queue->Clear(); +} - if ((qData[0]==0) && (qData[1]==0) && (qData[2]==1) && (stream>0xBC)) +bool cPES2ES::Process(uchar *PESData, int PESSize, AvPacket *ESPkt) +{ + if (!ESPkt) return false; + if (PESData) { - int needed=qData[4]*256+qData[5]; + struct PESHDR *peshdr=(struct PESHDR *) PESData; + + // first check some simple things + if ((peshdr->Sync1!=0) && (peshdr->Sync2!=0) && (peshdr->Sync3!=1)) return false; + if (peshdr->StreamID<=0xBC) return false; - if (((Pid.Type==MARKAD_PIDTYPE_VIDEO_H262) || - (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264)) && ((stream & 0xF0)!=0xE0)) + int Length=(peshdr->LenH<<8)+peshdr->LenL; + if (Length) { - // ignore 6 header bytes from queue->Put above - queue->Clear(); - if (Offcnt) *Offcnt=true; - return -needed; + Length+=sizeof(PESHDR); + if (Length!=PESSize) + { + skipped+=Length; + return true; + } } - if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_MP2) && ((stream & 0xF0)!=0xC0)) + + if (peshdr->StreamID==0xBE) { - // ignore 6 header bytes from queue->Put above queue->Clear(); - if (Offcnt) *Offcnt=true; - return -needed; + return true; } - if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) && (stream!=0xBD)) + + switch (ptype) { - // ignore 6 header bytes from queue->Put above - queue->Clear(); - if (Offcnt) *Offcnt=true; - return -needed; + case PACKET_H262: + if (peshdr->StreamID!=0xE0) return true; // ignore packets not for us! + break; + case PACKET_H264: + if (peshdr->StreamID!=0xE0) return true; // ignore packets not for us! + break; + case PACKET_AC3: + if (peshdr->StreamID!=0xBD) return true; // ignore packets not for us! + break; + case PACKET_MP2: + if (peshdr->StreamID!=0xC0) return true; // ignore packets not for us! + break; + default: + break; + } + + struct PESHDROPT *peshdropt=(struct PESHDROPT *) &PESData[sizeof(struct PESHDR)]; + + uchar *buf; + int buflen; + + if (peshdropt->MarkerBits==0x2) + { + // we have an optional PES header + int bpos=sizeof(struct PESHDR)+sizeof(struct PESHDROPT)+ + peshdropt->Length; + buf=&PESData[bpos]; + buflen=PESSize-bpos; + } + else + { + int bpos=sizeof(struct PESHDR); + buf=&PESData[bpos]; + buflen=PESSize-bpos; } - return needed+PESHDRSIZE; + queue->Put(buf,buflen); + } + ESPkt->Data=queue->GetPacket(&ESPkt->Length,ptype); + if (ESPkt->Data) + { + ESPkt->Type=ptype; } else { - queue->Clear(); - if (Offcnt) *Offcnt=true; - return -1; // skip one byte (maybe we get another header!) + ESPkt->Type=0; + ESPkt->Length=0; } + skipped+=queue->Skipped(); + return true; } -void cMarkAdDemux::VDRTSAddPATPMT2Offset(MarkAdPid Pid, uchar *Data, int Count, bool *Offcnt) +// ---------------------------------------------------------------------------- + +cDemux::cDemux(int VPid, int DPid, int APid, bool H264, bool VDRCount) { - if (Pid.Num<0) return; - if (Count<2) return; - if ((Data[0]==0x47) && (Data[1]==0x40) && ((Data[2]==0) || (Data[2]==0x84))) *Offcnt=false; + vpid=(VPid!=0) ? VPid : -1; + dpid=(DPid!=0) ? DPid : -1; + apid=(APid!=0) ? APid : -1; + TS=false; + if ((vpid>0) || (dpid>0) || (apid>0)) TS=true; + pes2videoes=NULL; + pes2audioes_mp2=NULL; + pes2audioes_ac3=NULL; + ts2pkt_vpid=NULL; + ts2pkt_dpid=NULL; + ts2pkt_apid=NULL; + h264=H264; + vdrcount=VDRCount; + queue = new cPaketQueue("DEMUX",5640); + skipped=0; + Clear(); } -int cMarkAdDemux::Process(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *Pkt) +cDemux::~cDemux() { - if ((!Data) && (!Count) && (!Pkt)) return -1; + if (skipped) tsyslog("buffer skipped: %-15s %i bytes",queue->Name(),skipped); + if (queue) delete queue; + + if (ts2pkt_vpid) delete ts2pkt_vpid; + if (pes2videoes) delete pes2videoes; - uchar *in=NULL; - int inlen=0; - int retval=0; + if (ts2pkt_dpid) delete ts2pkt_dpid; + if (pes2audioes_ac3) delete pes2audioes_ac3; + + if (ts2pkt_apid) delete ts2pkt_apid; + if (pes2audioes_mp2) delete pes2audioes_mp2; + +} + +int cDemux::Skipped() +{ + int val=skipped; + if (pes2videoes) val+=pes2videoes->Skipped(); + if (pes2audioes_mp2) val+=pes2audioes_mp2->Skipped(); + if (pes2audioes_ac3) val+=pes2audioes_ac3->Skipped(); + if (ts2pkt_vpid) val+=ts2pkt_vpid->Skipped(); + if (ts2pkt_dpid) val+=ts2pkt_dpid->Skipped(); + if (ts2pkt_apid) val+=ts2pkt_apid->Skipped(); + return val; +} - if (!pause) +void cDemux::Clear() +{ + if (pes2videoes) pes2videoes->Clear(); + if (pes2audioes_mp2) pes2audioes_mp2->Clear(); + if (pes2audioes_ac3) pes2audioes_ac3->Clear(); + if (ts2pkt_vpid) ts2pkt_vpid->Clear(); + if (ts2pkt_dpid) ts2pkt_dpid->Clear(); + if (ts2pkt_apid) ts2pkt_apid->Clear(); + + if (queue) queue->Clear(); + offset=0; + vdroffset=0; + last_bplen=0; + from_oldfile=0; + stream_or_pid=0; +} + +bool cDemux::isvideopes(uchar *data, int count) +{ + if (!data) return false; + if (count<6) return false; + if ((data[0]==0) && (data[1]==0) && (data[2]==1) && + ((data[3] & 0xF0)==0xE0) && + ((data[4]!=0) || (data[5]!=0))) return true; + return false; +} + +int cDemux::checkts(uchar *data, int count, int &pid) +{ + if (count<(int) sizeof(struct TSHDR)) return -1; + if (data[0]!=0x47) return 1; + + struct TSHDR *tshdr = (struct TSHDR *) data; + if ((tshdr->AFC<=0) || (tshdr->AFC>3)) return 1; + + pid = (tshdr->PidH << 8) | tshdr->PidL; + return 0; +} + +int cDemux::fillqueue(uchar *data, int count, int &stream_or_pid, int &packetsize, int &readout) +{ +#define PEEKBUF 6 + stream_or_pid=packetsize=readout=0; + + uchar *qData=NULL; + + while (!(qData=queue->Peek(PEEKBUF))) { - Pkt->Data=NULL; - Pkt->Length=0; - Pkt->Skipped=0; - Pkt->Offcnt=false; + int len=PEEKBUF-queue->Length(); + int cnt=(count>len) ? len : count; + if (!queue->Put(data,cnt)) return -1; + readout+=cnt; + data+=cnt; + count-=cnt; + if (queue->Length()<PEEKBUF) return cnt; // we need more data! + } - if (!min_needed) + if (!TS) + { + if ((qData[0]==0) && (qData[1]==0) && (qData[2]==1) && (qData[3]>=0xBC)) { - if (skip) + stream_or_pid=qData[3]; + packetsize=PEEKBUF+(qData[4]*256+qData[5]); + } + else + { + skipped++; + stream_or_pid=0; + packetsize=1; + return 0; + } + } + else + { + int error_skipbytes=checkts(qData,PEEKBUF,stream_or_pid); + if (error_skipbytes==-1) return -1; + if (error_skipbytes) + { + skipped+=error_skipbytes; + stream_or_pid=0; + packetsize=1; + return error_skipbytes; // no useable data found, try next! + } + packetsize=TS_SIZE; + } + + int needed=packetsize+PEEKBUF; + while (!(qData=queue->Peek(needed))) + { + int len=needed-queue->Length(); + int cnt=(count>len) ? len : count; + if (!queue->Put(data,cnt)) return -1; + readout+=cnt; + data+=cnt; + count-=cnt; + if (queue->Length()<needed) return cnt; // we need more data! + } + if (!TS) + { + // check length of PES-paket + qData=queue->Peek(packetsize+PEEKBUF); + if (qData) + { + int start=packetsize; + if ((qData[start]!=0) || (qData[start+1]!=0) || (qData[start+2]!=1) || (qData[start+3]<0xBC)) { - int t_skip=skip; - skip=0; - Pkt->Offcnt=true; - return t_skip; + int start=queue->FindPesHeader(1); + if (start) + { + // broken PES in queue, skip it + packetsize=start; + skipped+=start; + stream_or_pid=0; + return 0; + } + else + { + return -1; + } } - - int t_min_needed=GetMinNeeded(Pid,Data,Count,&Pkt->Offcnt); - if (t_min_needed==0) + } + } + else + { + qData=queue->Peek(packetsize+PEEKBUF); + if (qData) + { + int start=packetsize; + int pid; + int ret=checkts(&qData[start],PEEKBUF,pid); + if (ret==-1) { return -1; } - if (t_min_needed<0) + if (ret) { - if (-t_min_needed>Count) - { - skip=-t_min_needed-Count; - return Count; - } - if (t_min_needed==-1) Pkt->Skipped++; - return -t_min_needed; + return -1; } - min_needed=t_min_needed; } + } + return 0; +} - int needed=min_needed-queue->Length(); +bool cDemux::needmoredata() +{ + if (!stream_or_pid) return true; - if (Count>needed) + if (!TS) + { + switch (stream_or_pid) { - queue->Put(Data,needed); - retval=needed; + case 0xE0: + if (pes2videoes) return pes2videoes->NeedMoreData(); + break; + case 0xC0: + if (pes2audioes_mp2) return pes2audioes_mp2->NeedMoreData(); + break; + case 0xBD: + if (pes2audioes_ac3) return pes2audioes_ac3->NeedMoreData(); + break; } - else + } + + if (TS) + { + if ((stream_or_pid==vpid) && (ts2pkt_vpid)) { - queue->Put(Data,Count); - retval=Count; + if (pes2videoes) return pes2videoes->NeedMoreData(); + return ts2pkt_vpid->NeedMoreData(); } - if (queue->Length()<min_needed) + if ((stream_or_pid==dpid) && (ts2pkt_dpid)) { - return Count; + if (pes2audioes_ac3) return pes2audioes_ac3->NeedMoreData(); + return ts2pkt_dpid->NeedMoreData(); + } + if ((stream_or_pid==apid) && (ts2pkt_apid)) + { + if (pes2audioes_mp2) return pes2audioes_mp2->NeedMoreData(); + return ts2pkt_apid->NeedMoreData(); } - inlen=min_needed; - in=queue->Get(&inlen); - min_needed=0; } + return false; +} - if (Pid.Num>=0) +bool cDemux::vdraddpatpmt(uchar *data, int count) +{ + // TS-VDR adds pat/pmt to the output, e.g. if + // a picture starts @376, vdr outputs 0 (!) + int pid; + if (checkts(data,count,pid)!=0) return false; + if ((!pid) || (pid==132)) { - ProcessTS(Pid, in, inlen, Pkt); + last_bplen=0; + vdroffset+=count; } else { - ProcessVDR(Pid, in, inlen, Pkt); + last_bplen=vdroffset+count; + vdroffset=0; } + return true; +} - if (Pkt->Data) +void cDemux::addoffset() +{ + offset+=last_bplen; + if (from_oldfile) { - if (!pause_retval) pause_retval=retval; - pause=true; - return 0; + from_oldfile-=last_bplen; + if (!from_oldfile) offset=0; } + last_bplen=0; +} + +void cDemux::NewFile() +{ + from_oldfile=queue->Length(); +} + +int cDemux::Process(uchar *Data, int Count, AvPacket *pkt) +{ + if (!pkt) return -1; + pkt->Data=NULL; + pkt->Length=0; + + bool add=needmoredata(); - if (pause) + int bplen=0,readout=0; + uchar *bpkt=NULL; + if (add) + { + addoffset(); + int advance=fillqueue(Data,Count,stream_or_pid,bplen,readout); + if (advance<0) return -1; + if (advance) return advance; + bpkt=queue->Get(&bplen); + if (!bpkt) return -1; + last_bplen=bplen; + if (vdrcount) vdraddpatpmt(bpkt,bplen); + } + + if (!TS) + { + switch (stream_or_pid) + { + case 0xE0: + if (!pes2videoes) + { + if (h264) + { + pes2videoes=new cPES2ES(PACKET_H264,"PES2H264ES",524288); + } + else + { + pes2videoes=new cPES2ES(PACKET_H262,"PES2H262ES",65536); + } + } + if (!pes2videoes->Process(bpkt,bplen,pkt)) return -1; + break; + case 0xC0: + if (!pes2audioes_mp2) pes2audioes_mp2=new cPES2ES(PACKET_MP2,"PES2MP2",16384); + if (!pes2audioes_mp2->Process(bpkt,bplen,pkt)) return -1; + break; + case 0xBD: + if (!pes2audioes_ac3) pes2audioes_ac3=new cPES2ES(PACKET_AC3,"PES2AC3"); + if (!pes2audioes_ac3->Process(bpkt,bplen,pkt)) return -1; + break; + default: + stream_or_pid=0; + break; + } + } + if (TS) { - if (pause_retval) + if (stream_or_pid==vpid) + { + if (!ts2pkt_vpid) + { + if (h264) + { + ts2pkt_vpid=new cTS2Pkt(vpid,"TS2H264",524288,true); + } + else + { + ts2pkt_vpid=new cTS2Pkt(vpid,"TS2H262",65536); + } + } + if (!ts2pkt_vpid->Process(bpkt,bplen,pkt)) return -1; + if (isvideopes(pkt->Data,pkt->Length) || pes2videoes) + { + AvPacket tpkt; + memcpy(&tpkt,pkt,sizeof(AvPacket)); + memset(pkt,0,sizeof(AvPacket)); + if (!pes2videoes) + { + ts2pkt_vpid->Resize(3*tpkt.Length,"TS2PES"); + if (h264) + { + pes2videoes=new cPES2ES(PACKET_H264,"PES2H264ES",524288); + } + else + { + pes2videoes=new cPES2ES(PACKET_H262,"PES2H262ES",65536); + } + } + if (!pes2videoes->Process(tpkt.Data,tpkt.Length,pkt)) return -1; + } + } + else if (stream_or_pid==dpid) { - retval=pause_retval; - pause_retval=0; + AvPacket tpkt={NULL,0,0}; + if (!ts2pkt_dpid) ts2pkt_dpid=new cTS2Pkt(dpid,"TS2PES AC3"); + if (!ts2pkt_dpid->Process(bpkt,bplen,&tpkt)) return -1; + if (!pes2audioes_ac3) pes2audioes_ac3=new cPES2ES(PACKET_AC3,"PES2AC3"); + if (!pes2audioes_ac3->Process(tpkt.Data,tpkt.Length,pkt)) return -1; } - pause=false; + else if (stream_or_pid==apid) + { + AvPacket tpkt={NULL,0,0}; + if (!ts2pkt_apid) ts2pkt_apid=new cTS2Pkt(apid,"TS2PES MP2",16384); + if (!ts2pkt_apid->Process(bpkt,bplen,&tpkt)) return -1; + if (!pes2audioes_mp2) pes2audioes_mp2=new cPES2ES(PACKET_MP2,"PES2MP2",16384); + if (!pes2audioes_mp2->Process(tpkt.Data,tpkt.Length,pkt)) return -1; + } + else stream_or_pid=0; } - - Pkt->Offcnt=true; - VDRTSAddPATPMT2Offset(Pid, in, inlen, &Pkt->Offcnt); - return retval; + return add ? readout : 0; } |