summaryrefslogtreecommitdiff
path: root/command/demux.cpp
diff options
context:
space:
mode:
authorJochen Dolze <vdr@dolze.de>2011-01-29 15:58:36 +0100
committerJochen Dolze <vdr@dolze.de>2011-01-29 15:58:36 +0100
commit70d113055698c6b73c8ed13af8a9e2f3b38ab1f0 (patch)
treed5264ada5b3a1366ca2404468dd57c6ea62a6349 /command/demux.cpp
parent9e964370ba635f57df44a96506fc4bf633004a86 (diff)
downloadvdr-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.cpp1247
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;
}